You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
600 lines
24 KiB
600 lines
24 KiB
#!/usr/bin/python
|
|
#
|
|
# Copyright 2017 - The Android Open Source Project
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
#
|
|
|
|
"""Generates a report on CKI syscall coverage in VTS LTP.
|
|
|
|
This module generates a report on the syscalls in the Android CKI and
|
|
their coverage in VTS LTP.
|
|
|
|
The coverage report provides, for each syscall in the CKI, the number of
|
|
enabled and disabled LTP tests for the syscall in VTS. If VTS test output is
|
|
supplied, the report instead provides the number of disabled, skipped, failing,
|
|
and passing tests for each syscall.
|
|
|
|
Assumptions are made about the structure of files in LTP source
|
|
and the naming convention.
|
|
"""
|
|
|
|
import argparse
|
|
import os.path
|
|
import re
|
|
import sys
|
|
import xml.etree.ElementTree as ET
|
|
import subprocess
|
|
|
|
if "ANDROID_BUILD_TOP" not in os.environ:
|
|
print ("Please set up your Android build environment by running "
|
|
"\". build/envsetup.sh\" and \"lunch\".")
|
|
sys.exit(-1)
|
|
|
|
sys.path.append(os.path.join(os.environ["ANDROID_BUILD_TOP"],
|
|
"bionic/libc/tools"))
|
|
import gensyscalls
|
|
|
|
sys.path.append(os.path.join(os.environ["ANDROID_BUILD_TOP"],
|
|
"test/vts-testcase/kernel/ltp/configs"))
|
|
import disabled_tests as vts_disabled
|
|
import stable_tests as vts_stable
|
|
|
|
bionic_libc_root = os.path.join(os.environ["ANDROID_BUILD_TOP"], "bionic/libc")
|
|
|
|
src_url_start = 'https://git.kernel.org/pub/scm/linux/kernel/git/'
|
|
tip_url = 'torvalds/linux.git/plain/'
|
|
stable_url = 'stable/linux.git/plain/'
|
|
unistd_h = 'include/uapi/asm-generic/unistd.h'
|
|
arm64_unistd32_h = 'arch/arm64/include/asm/unistd32.h'
|
|
arm_syscall_tbl = 'arch/arm/tools/syscall.tbl'
|
|
x86_syscall_tbl = 'arch/x86/entry/syscalls/syscall_32.tbl'
|
|
x86_64_syscall_tbl = 'arch/x86/entry/syscalls/syscall_64.tbl'
|
|
|
|
unistd_h_url = src_url_start
|
|
arm64_unistd32_h_url = src_url_start
|
|
arm_syscall_tbl_url = src_url_start
|
|
x86_syscall_tbl_url = src_url_start
|
|
x86_64_syscall_tbl_url = src_url_start
|
|
|
|
# Syscalls which are either banned, optional, or deprecated, so not part of the
|
|
# CKI.
|
|
CKI_BLACKLIST = [
|
|
'acct', # CONFIG_BSD_PROCESS_ACCT
|
|
'fanotify_init', # CONFIG_FANOTIFY
|
|
'fanotify_mark', # CONFIG_FANOTIFY
|
|
'get_mempolicy', # CONFIG_NUMA
|
|
'init_module', # b/112470257 (use finit_module)
|
|
'ipc', # CONFIG_SYSVIPC
|
|
'kcmp', # CONFIG_CHECKPOINT_RESTORE
|
|
'kexec_file_load', # CONFIG_EXEC_FILE
|
|
'kexec_load', # CONFIG_KEXEC
|
|
'lookup_dcookie', # b/112474343 (requires kernel module)
|
|
'mbind', # CONFIG_NUMA
|
|
'membarrier', # CONFIG_MEMBARRIER
|
|
'migrate_pages', # CONFIG_NUMA
|
|
'move_pages', # CONFIG_MIGRATION
|
|
'mq_getsetattr', # CONFIG_POSIX_MQUEUE
|
|
'mq_notify', # CONFIG_POSIX_MQUEUE
|
|
'mq_open', # CONFIG_POSIX_MQUEUE
|
|
'mq_timedreceive', # CONFIG_POSIX_MQUEUE
|
|
'mq_timedsend', # CONFIG_POSIX_MQUEUE
|
|
'mq_unlink', # CONFIG_POSIX_MQUEUE
|
|
'msgctl', # CONFIG_SYSVIPC
|
|
'msgget', # CONFIG_SYSVIPC
|
|
'msgrcv', # CONFIG_SYSVIPC
|
|
'msgsnd', # CONFIG_SYSVIPC
|
|
'name_to_handle_at', # CONFIG_FHANDLE
|
|
'nfsservctl', # not present after 3.1
|
|
'open_by_handle_at', # CONFIG_FHANDLE
|
|
'pciconfig_iobase', # not present for arm/x86
|
|
'pciconfig_read', # CONFIG_PCI_SYSCALL
|
|
'pciconfig_write', # CONFIG_PCI_SYSCALL
|
|
'pkey_alloc', # CONFIG_MMU, added in 4.9
|
|
'pkey_free', # CONFIG_MMU, added in 4.9
|
|
'pkey_mprotect', # CONFIG_MMU, added in 4.9
|
|
'rseq', # CONFIG_RSEQ
|
|
'semctl', # CONFIG_SYSVIPC
|
|
'semget', # CONFIG_SYSVIPC
|
|
'semop', # CONFIG_SYSVIPC
|
|
'semtimedop', # CONFIG_SYSVIPC
|
|
'set_mempolicy', # CONFIG_NUMA
|
|
'sgetmask', # CONFIG_SGETMASK_SYSCALL
|
|
'shmat', # CONFIG_SYSVIPC
|
|
'shmctl', # CONFIG_SYSVIPC
|
|
'shmdt', # CONFIG_SYSVIPC
|
|
'shmget', # CONFIG_SYSVIPC
|
|
'ssetmask', # CONFIG_SGETMASK_SYSCALL
|
|
'stime', # deprecated
|
|
'syscall', # deprecated
|
|
'_sysctl', # CONFIG_SYSCTL_SYSCALL
|
|
'sysfs', # CONFIG_SYSFS_SYSCALL
|
|
'uselib', # CONFIG_USELIB
|
|
'userfaultfd', # CONFIG_USERFAULTFD
|
|
'vm86', # CONFIG_X86_LEGACY_VM86
|
|
'vm86old', # CONFIG_X86_LEGACY_VM86
|
|
'vserver', # deprecated
|
|
]
|
|
|
|
EXTERNAL_TESTS = [ ("bpf", "libbpf_android/BpfLoadTest.cpp"),
|
|
("bpf", "libbpf_android/BpfMapTest.cpp"),
|
|
("bpf", "netd/libbpf/BpfMapTest.cpp"),
|
|
("bpf", "api/bpf_native_test/BpfTest.cpp"),
|
|
("clock_adjtime", "kselftest/timers/valid-adjtimex.c"),
|
|
("seccomp", "kselftest/seccomp_bpf")
|
|
]
|
|
|
|
class CKI_Coverage(object):
|
|
"""Determines current test coverage of CKI system calls in LTP.
|
|
|
|
Many of the system calls in the CKI are tested by LTP. For a given
|
|
system call an LTP test may or may not exist, that LTP test may or may
|
|
not be currently compiling properly for Android, the test may not be
|
|
stable, the test may not be running due to environment issues or
|
|
passing. This class looks at various sources of information to determine
|
|
the current test coverage of system calls in the CKI from LTP.
|
|
|
|
Note that due to some deviations in LTP of tests from the common naming
|
|
convention there there may be tests that are flagged here as not having
|
|
coverage when in fact they do.
|
|
"""
|
|
|
|
LTP_KERNEL_ROOT = os.path.join(os.environ["ANDROID_BUILD_TOP"],
|
|
"external/ltp/testcases/kernel")
|
|
LTP_KERNEL_TESTSUITES = ["syscalls", "timers"]
|
|
DISABLED_IN_LTP_PATH = os.path.join(os.environ["ANDROID_BUILD_TOP"],
|
|
"external/ltp/android/tools/disabled_tests.txt")
|
|
|
|
ltp_full_set = []
|
|
|
|
cki_syscalls = []
|
|
|
|
disabled_in_ltp = []
|
|
disabled_in_vts_ltp = vts_disabled.DISABLED_TESTS
|
|
stable_in_vts_ltp = vts_stable.STABLE_TESTS
|
|
|
|
syscall_tests = {}
|
|
disabled_tests = {}
|
|
|
|
def __init__(self, arch):
|
|
self._arch = arch
|
|
|
|
def load_ltp_tests(self):
|
|
"""Load the list of LTP syscall tests.
|
|
|
|
Load the list of all syscall tests existing in LTP.
|
|
"""
|
|
for testsuite in self.LTP_KERNEL_TESTSUITES:
|
|
self.__load_ltp_testsuite(testsuite)
|
|
|
|
def __load_ltp_testsuite(self, testsuite):
|
|
root = os.path.join(self.LTP_KERNEL_ROOT, testsuite)
|
|
for path, dirs, files in os.walk(root):
|
|
for filename in files:
|
|
basename, ext = os.path.splitext(filename)
|
|
if ext != ".c": continue
|
|
self.ltp_full_set.append("%s.%s" % (testsuite, basename))
|
|
|
|
def load_ltp_disabled_tests(self):
|
|
"""Load the list of LTP tests not being compiled.
|
|
|
|
The LTP repository in Android contains a list of tests which are not
|
|
compiled due to incompatibilities with Android.
|
|
"""
|
|
with open(self.DISABLED_IN_LTP_PATH) as fp:
|
|
for line in fp:
|
|
line = line.strip()
|
|
if not line: continue
|
|
test_re = re.compile(r"^(\w+)")
|
|
test_match = re.match(test_re, line)
|
|
if not test_match: continue
|
|
self.disabled_in_ltp.append(test_match.group(1))
|
|
|
|
def ltp_test_special_cases(self, syscall, test):
|
|
"""Detect special cases in syscall to LTP mapping.
|
|
|
|
Most syscall tests in LTP follow a predictable naming
|
|
convention, but some do not. Detect known special cases.
|
|
|
|
Args:
|
|
syscall: The name of a syscall.
|
|
test: The name of a testcase.
|
|
|
|
Returns:
|
|
A boolean indicating whether the given syscall is tested
|
|
by the given testcase.
|
|
"""
|
|
compat_syscalls = [ "chown32", "fchown32", "getegid32", "geteuid32",
|
|
"getgid32", "getgroups32", "getresgid32", "getresuid32",
|
|
"getuid32", "lchown32", "setfsgid32", "setfsuid32", "setgid32",
|
|
"setgroups32", "setregid32", "setresgid32", "setresuid32",
|
|
"setreuid32", "setuid32"]
|
|
if syscall in compat_syscalls:
|
|
test_re = re.compile(r"^%s\d+$" % syscall[0:-2])
|
|
if re.match(test_re, test):
|
|
return True
|
|
if syscall == "_llseek" and test.startswith("llseek"):
|
|
return True
|
|
if syscall in ("arm_fadvise64_", "fadvise64_") and \
|
|
test.startswith("posix_fadvise"):
|
|
return True
|
|
if syscall in ("arm_sync_file_range", "sync_file_range2") and \
|
|
test.startswith("sync_file_range"):
|
|
return True
|
|
if syscall == "clock_nanosleep" and test == "clock_nanosleep2_01":
|
|
return True
|
|
if syscall in ("epoll_ctl", "epoll_create") and test == "epoll-ltp":
|
|
return True
|
|
if syscall == "futex" and test.startswith("futex_"):
|
|
return True
|
|
if syscall == "get_thread_area" and test == "set_thread_area01":
|
|
return True
|
|
if syscall == "inotify_add_watch" or syscall == "inotify_rm_watch":
|
|
test_re = re.compile(r"^inotify\d+$")
|
|
if re.match(test_re, test):
|
|
return True
|
|
inotify_init_tests = [ "inotify01", "inotify02", "inotify03", "inotify04" ]
|
|
if syscall == "inotify_init" and test in inotify_init_tests:
|
|
return True
|
|
if syscall == "lsetxattr" and test.startswith("lgetxattr"):
|
|
return True
|
|
if syscall == "newfstatat":
|
|
test_re = re.compile(r"^fstatat\d+$")
|
|
if re.match(test_re, test):
|
|
return True
|
|
if syscall in ("prlimit", "ugetrlimit") and test == "getrlimit03":
|
|
return True
|
|
if syscall == "rt_sigtimedwait" and test == "sigwaitinfo01":
|
|
return True
|
|
shutdown_tests = [ "send01", "sendmsg01", "sendto01" ]
|
|
if syscall == "shutdown" and test in shutdown_tests:
|
|
return True
|
|
|
|
return False
|
|
|
|
def match_syscalls_to_tests(self, syscalls):
|
|
"""Match syscalls with tests in LTP.
|
|
|
|
Create a mapping from CKI syscalls and tests in LTP. This mapping can
|
|
largely be determined using a common naming convention in the LTP file
|
|
hierarchy but there are special cases that have to be taken care of.
|
|
|
|
Args:
|
|
syscalls: List of syscall structures containing all syscalls
|
|
in the CKI.
|
|
"""
|
|
for syscall in syscalls:
|
|
if self._arch is not None and self._arch not in syscall:
|
|
continue
|
|
self.cki_syscalls.append(syscall)
|
|
self.syscall_tests[syscall["name"]] = []
|
|
# LTP does not use the 64 at the end of syscall names for testcases.
|
|
ltp_syscall_name = syscall["name"]
|
|
if ltp_syscall_name.endswith("64"):
|
|
ltp_syscall_name = ltp_syscall_name[0:-2]
|
|
# Most LTP syscalls have source files for the tests that follow
|
|
# a naming convention in the regexp below. Exceptions exist though.
|
|
# For now those are checked for specifically.
|
|
test_re = re.compile(r"^%s_?0?\d\d?$" % ltp_syscall_name)
|
|
for full_test_name in self.ltp_full_set:
|
|
testsuite, test = full_test_name.split('.')
|
|
if (re.match(test_re, test) or
|
|
self.ltp_test_special_cases(ltp_syscall_name, test)):
|
|
# The filenames of the ioctl tests in LTP do not match the name
|
|
# of the testcase defined in that source, which is what shows
|
|
# up in VTS.
|
|
if testsuite == "syscalls" and ltp_syscall_name == "ioctl":
|
|
full_test_name = "syscalls.ioctl01_02"
|
|
# Likewise LTP has a test named epoll01, which is built as an
|
|
# executable named epoll-ltp, and tests the epoll_{create,ctl}
|
|
# syscalls.
|
|
if full_test_name == "syscalls.epoll-ltp":
|
|
full_test_name = "syscalls.epoll01"
|
|
self.syscall_tests[syscall["name"]].append(full_test_name)
|
|
for e in EXTERNAL_TESTS:
|
|
if e[0] == syscall["name"]:
|
|
self.syscall_tests[syscall["name"]].append(e[1])
|
|
self.cki_syscalls.sort(key=lambda tup: tup["name"])
|
|
|
|
def update_test_status(self):
|
|
"""Populate test configuration and output for all CKI syscalls.
|
|
|
|
Go through VTS test configuration to populate data for all CKI syscalls.
|
|
"""
|
|
for syscall in self.cki_syscalls:
|
|
self.disabled_tests[syscall["name"]] = []
|
|
if not self.syscall_tests[syscall["name"]]:
|
|
continue
|
|
for full_test_name in self.syscall_tests[syscall["name"]]:
|
|
if full_test_name in [t[1] for t in EXTERNAL_TESTS]:
|
|
continue
|
|
_, test = full_test_name.split('.')
|
|
# The VTS LTP stable list is composed of tuples of the test name and
|
|
# a boolean flag indicating whether it is mandatory.
|
|
stable_vts_ltp_testnames = [i[0] for i in self.stable_in_vts_ltp]
|
|
if (test in self.disabled_in_ltp or
|
|
full_test_name in self.disabled_in_vts_ltp or
|
|
("%s_32bit" % full_test_name not in stable_vts_ltp_testnames and
|
|
"%s_64bit" % full_test_name not in stable_vts_ltp_testnames)):
|
|
self.disabled_tests[syscall["name"]].append(full_test_name)
|
|
continue
|
|
|
|
def syscall_arch_string(self, syscall, arch):
|
|
"""Return a string showing whether the arch supports the given syscall."""
|
|
if arch not in syscall or not syscall[arch]:
|
|
return " "
|
|
else:
|
|
return "*"
|
|
|
|
def output_results(self):
|
|
"""Pretty print the CKI syscall LTP coverage."""
|
|
count = 0
|
|
uncovered = 0
|
|
|
|
print ""
|
|
print " Covered Syscalls"
|
|
for syscall in self.cki_syscalls:
|
|
if (len(self.syscall_tests[syscall["name"]]) -
|
|
len(self.disabled_tests[syscall["name"]]) <= 0):
|
|
continue
|
|
if not count % 20:
|
|
print ("%25s Disabled Enabled arm64 arm x86_64 x86 -----------" %
|
|
"-------------")
|
|
enabled = (len(self.syscall_tests[syscall["name"]]) -
|
|
len(self.disabled_tests[syscall["name"]]))
|
|
if enabled > 9:
|
|
column_sp = " "
|
|
else:
|
|
column_sp = " "
|
|
sys.stdout.write("%25s %s %s%s%s %s %s %s\n" %
|
|
(syscall["name"], len(self.disabled_tests[syscall["name"]]),
|
|
enabled, column_sp,
|
|
self.syscall_arch_string(syscall, "arm64"),
|
|
self.syscall_arch_string(syscall, "arm"),
|
|
self.syscall_arch_string(syscall, "x86_64"),
|
|
self.syscall_arch_string(syscall, "x86")))
|
|
count += 1
|
|
|
|
count = 0
|
|
print "\n"
|
|
print " Uncovered Syscalls"
|
|
for syscall in self.cki_syscalls:
|
|
if (len(self.syscall_tests[syscall["name"]]) -
|
|
len(self.disabled_tests[syscall["name"]]) > 0):
|
|
continue
|
|
if not count % 20:
|
|
print ("%25s Disabled Enabled arm64 arm x86_64 x86 -----------" %
|
|
"-------------")
|
|
enabled = (len(self.syscall_tests[syscall["name"]]) -
|
|
len(self.disabled_tests[syscall["name"]]))
|
|
if enabled > 9:
|
|
column_sp = " "
|
|
else:
|
|
column_sp = " "
|
|
sys.stdout.write("%25s %s %s%s%s %s %s %s\n" %
|
|
(syscall["name"], len(self.disabled_tests[syscall["name"]]),
|
|
enabled, column_sp,
|
|
self.syscall_arch_string(syscall, "arm64"),
|
|
self.syscall_arch_string(syscall, "arm"),
|
|
self.syscall_arch_string(syscall, "x86_64"),
|
|
self.syscall_arch_string(syscall, "x86")))
|
|
uncovered += 1
|
|
count += 1
|
|
|
|
print ""
|
|
print ("Total uncovered syscalls: %s out of %s" %
|
|
(uncovered, len(self.cki_syscalls)))
|
|
|
|
def output_summary(self):
|
|
"""Print a one line summary of the CKI syscall LTP coverage.
|
|
|
|
Pretty prints a one line summary of the CKI syscall coverage in LTP
|
|
for the specified architecture.
|
|
"""
|
|
uncovered_with_test = 0
|
|
uncovered_without_test = 0
|
|
for syscall in self.cki_syscalls:
|
|
if (len(self.syscall_tests[syscall["name"]]) -
|
|
len(self.disabled_tests[syscall["name"]]) > 0):
|
|
continue
|
|
if (len(self.disabled_tests[syscall["name"]]) > 0):
|
|
uncovered_with_test += 1
|
|
else:
|
|
uncovered_without_test += 1
|
|
print ("arch, cki syscalls, uncovered with disabled test(s), "
|
|
"uncovered with no tests, total uncovered")
|
|
print ("%s, %s, %s, %s, %s" % (self._arch, len(self.cki_syscalls),
|
|
uncovered_with_test, uncovered_without_test,
|
|
uncovered_with_test + uncovered_without_test))
|
|
|
|
def add_syscall(self, cki, syscall, arch):
|
|
"""Note that a syscall has been seen for a particular arch."""
|
|
seen = False
|
|
for s in cki.syscalls:
|
|
if s["name"] == syscall:
|
|
s[arch]= True
|
|
seen = True
|
|
break
|
|
if not seen:
|
|
cki.syscalls.append({"name":syscall, arch:True})
|
|
|
|
def delete_syscall(self, cki, syscall):
|
|
cki.syscalls = list(filter(lambda i: i["name"] != syscall, cki.syscalls))
|
|
|
|
def check_blacklist(self, cki, error_on_match):
|
|
unlisted_syscalls = []
|
|
for s in cki.syscalls:
|
|
if s["name"] in CKI_BLACKLIST:
|
|
if error_on_match:
|
|
print "Syscall %s found in both bionic CKI and blacklist!" % s["name"]
|
|
sys.exit()
|
|
else:
|
|
unlisted_syscalls.append(s)
|
|
cki.syscalls = unlisted_syscalls
|
|
|
|
def get_x86_64_kernel_syscalls(self, cki):
|
|
"""Retrieve the list of syscalls for x86_64."""
|
|
proc = subprocess.Popen(['curl', x86_64_syscall_tbl_url], stdout=subprocess.PIPE)
|
|
while True:
|
|
line = proc.stdout.readline()
|
|
if line != b'':
|
|
test_re = re.compile(r"^\d+\s+\w+\s+(\w+)\s+(__x64_sys|__x32_compat_sys)")
|
|
test_match = re.match(test_re, line)
|
|
if test_match:
|
|
syscall = test_match.group(1)
|
|
self.add_syscall(cki, syscall, "x86_64")
|
|
else:
|
|
break
|
|
|
|
def get_x86_kernel_syscalls(self, cki):
|
|
"""Retrieve the list of syscalls for x86."""
|
|
proc = subprocess.Popen(['curl', x86_syscall_tbl_url], stdout=subprocess.PIPE)
|
|
while True:
|
|
line = proc.stdout.readline()
|
|
if line != b'':
|
|
test_re = re.compile(r"^\d+\s+i386\s+(\w+)\s+sys_")
|
|
test_match = re.match(test_re, line)
|
|
if test_match:
|
|
syscall = test_match.group(1)
|
|
self.add_syscall(cki, syscall, "x86")
|
|
else:
|
|
break
|
|
|
|
def get_arm_kernel_syscalls(self, cki):
|
|
"""Retrieve the list of syscalls for arm."""
|
|
proc = subprocess.Popen(['curl', arm_syscall_tbl_url], stdout=subprocess.PIPE)
|
|
while True:
|
|
line = proc.stdout.readline()
|
|
if line != b'':
|
|
test_re = re.compile(r"^\d+\s+\w+\s+(\w+)\s+sys_")
|
|
test_match = re.match(test_re, line)
|
|
if test_match:
|
|
syscall = test_match.group(1)
|
|
self.add_syscall(cki, syscall, "arm")
|
|
else:
|
|
break
|
|
|
|
def get_arm64_kernel_syscalls(self, cki):
|
|
"""Retrieve the list of syscalls for arm64."""
|
|
# Add AArch64 syscalls
|
|
proc = subprocess.Popen(['curl', unistd_h_url], stdout=subprocess.PIPE)
|
|
while True:
|
|
line = proc.stdout.readline()
|
|
if line != b'':
|
|
test_re = re.compile(r"^#define __NR(3264)?_(\w+)\s+(\d+)$")
|
|
test_match = re.match(test_re, line)
|
|
if test_match:
|
|
syscall = test_match.group(2)
|
|
if (syscall == "sync_file_range2" or
|
|
syscall == "arch_specific_syscall" or
|
|
syscall == "syscalls"):
|
|
continue
|
|
self.add_syscall(cki, syscall, "arm64")
|
|
else:
|
|
break
|
|
# Add AArch32 syscalls
|
|
proc = subprocess.Popen(['curl', arm64_unistd32_h_url], stdout=subprocess.PIPE)
|
|
while True:
|
|
line = proc.stdout.readline()
|
|
if line != b'':
|
|
test_re = re.compile(r"^#define __NR(3264)?_(\w+)\s+(\d+)$")
|
|
test_match = re.match(test_re, line)
|
|
if test_match:
|
|
syscall = test_match.group(2)
|
|
self.add_syscall(cki, syscall, "arm64")
|
|
else:
|
|
break
|
|
|
|
def get_kernel_syscalls(self, cki, arch):
|
|
self.get_arm64_kernel_syscalls(cki)
|
|
self.get_arm_kernel_syscalls(cki)
|
|
self.get_x86_kernel_syscalls(cki)
|
|
self.get_x86_64_kernel_syscalls(cki)
|
|
|
|
# restart_syscall is a special syscall which the kernel issues internally
|
|
# when a process is resumed with SIGCONT. seccomp whitelists this syscall,
|
|
# but it is not part of the CKI or meaningfully testable from userspace.
|
|
# See restart_syscall(2) for more details.
|
|
self.delete_syscall(cki, "restart_syscall")
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(description="Output list of system calls "
|
|
"in the Common Kernel Interface and their VTS LTP coverage.")
|
|
parser.add_argument("-a", "--arch", help="only show syscall CKI for specific arch")
|
|
parser.add_argument("-l", action="store_true",
|
|
help="list CKI syscalls only, without coverage")
|
|
parser.add_argument("-s", action="store_true",
|
|
help="print one line summary of CKI coverage for arch")
|
|
parser.add_argument("-f", action="store_true",
|
|
help="only check syscalls with known Android use")
|
|
parser.add_argument("-k", action="store_true",
|
|
help="use lowest supported kernel version instead of tip")
|
|
|
|
args = parser.parse_args()
|
|
if args.arch is not None and args.arch not in gensyscalls.all_arches:
|
|
print "Arch must be one of the following:"
|
|
print gensyscalls.all_arches
|
|
exit(-1)
|
|
|
|
if args.k:
|
|
minversion = "4.9"
|
|
print "Checking kernel version %s" % minversion
|
|
minversion = "?h=v" + minversion
|
|
unistd_h_url += stable_url + unistd_h + minversion
|
|
arm64_unistd32_h_url += stable_url + arm64_unistd32_h + minversion
|
|
arm_syscall_tbl_url += stable_url + arm_syscall_tbl + minversion
|
|
x86_syscall_tbl_url += stable_url + x86_syscall_tbl + minversion
|
|
x86_64_syscall_tbl_url += stable_url + x86_64_syscall_tbl + minversion
|
|
else:
|
|
unistd_h_url += tip_url + unistd_h
|
|
arm64_unistd32_h_url += tip_url + arm64_unistd32_h
|
|
arm_syscall_tbl_url += tip_url + arm_syscall_tbl
|
|
x86_syscall_tbl_url += tip_url + x86_syscall_tbl
|
|
x86_64_syscall_tbl_url += tip_url + x86_64_syscall_tbl
|
|
|
|
cki = gensyscalls.SysCallsTxtParser()
|
|
cki_cov = CKI_Coverage(args.arch)
|
|
|
|
if args.f:
|
|
cki.parse_file(os.path.join(bionic_libc_root, "SYSCALLS.TXT"))
|
|
cki.parse_file(os.path.join(bionic_libc_root, "SECCOMP_WHITELIST_APP.TXT"))
|
|
cki.parse_file(os.path.join(bionic_libc_root, "SECCOMP_WHITELIST_COMMON.TXT"))
|
|
cki.parse_file(os.path.join(bionic_libc_root, "SECCOMP_WHITELIST_SYSTEM.TXT"))
|
|
cki.parse_file(os.path.join(bionic_libc_root, "SECCOMP_WHITELIST_GLOBAL.TXT"))
|
|
cki_cov.check_blacklist(cki, True)
|
|
else:
|
|
cki_cov.get_kernel_syscalls(cki, args.arch)
|
|
cki_cov.check_blacklist(cki, False)
|
|
|
|
if args.l:
|
|
for syscall in cki.syscalls:
|
|
if args.arch is None or syscall[args.arch]:
|
|
print syscall["name"]
|
|
exit(0)
|
|
|
|
cki_cov.load_ltp_tests()
|
|
cki_cov.load_ltp_disabled_tests()
|
|
cki_cov.match_syscalls_to_tests(cki.syscalls)
|
|
cki_cov.update_test_status()
|
|
|
|
beta_string = ("*** WARNING: This script is still in development and may\n"
|
|
"*** report both false positives and negatives.")
|
|
print beta_string
|
|
|
|
if args.s:
|
|
cki_cov.output_summary()
|
|
exit(0)
|
|
|
|
cki_cov.output_results()
|
|
print beta_string
|