Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | #!/usr/bin/env python3 # # Copyright (c) 2017 Intel Corporation # # SPDX-License-Identifier: Apache-2.0 import sys import re import argparse import os import json api_regex = re.compile(r''' __syscall\s+ # __syscall attribute, must be first ([^(]+) # type and name of system call (split later) [(] # Function opening parenthesis ([^)]*) # Arg list (split later) [)] # Closing parenthesis ''', re.MULTILINE | re.VERBOSE) typename_regex = re.compile(r'(.*?)([A-Za-z0-9_]+)$') class SyscallParseException(Exception): pass def typename_split(item): if "[" in item: raise SyscallParseException("Please pass arrays to syscalls as pointers, unable to process '%s'" % item) if "(" in item: raise SyscallParseException("Please use typedefs for function pointers") mo = typename_regex.match(item) if not mo: raise SyscallParseException("Malformed system call invocation") m = mo.groups() return (m[0].strip(), m[1]) def analyze_fn(match_group, fn): func, args = match_group try: if args == "void": args = [] else: args = [typename_split(a.strip()) for a in args.split(",")] func_type, func_name = typename_split(func) except SyscallParseException: sys.stderr.write("In declaration of %s\n" % func) raise sys_id = "K_SYSCALL_" + func_name.upper() if func_type == "void": suffix = "_VOID" is_void = True else: is_void = False if func_type in ["s64_t", "u64_t"]: suffix = "_RET64" else: suffix = "" is_void = (func_type == "void") # Get the proper system call macro invocation, which depends on the # number of arguments, the return type, and whether the implementation # is an inline function macro = "K_SYSCALL_DECLARE%d%s" % (len(args), suffix) # Flatten the argument lists and generate a comma separated list # of t0, p0, t1, p1, ... tN, pN as expected by the macros flat_args = [i for sublist in args for i in sublist] if not is_void: flat_args = [func_type] + flat_args flat_args = [sys_id, func_name] + flat_args argslist = ", ".join(flat_args) invocation = "%s(%s);" % (macro, argslist) handler = "_handler_" + func_name # Entry in _k_syscall_table table_entry = "[%s] = %s" % (sys_id, handler) return (fn, handler, invocation, sys_id, table_entry) def analyze_headers(base_path): ret = [] for root, dirs, files in os.walk(base_path): for fn in files: # toolchain/common.h has the definition of __syscall which we # don't want to trip over path = os.path.join(root, fn) if not fn.endswith(".h") or path.endswith("toolchain/common.h"): continue with open(path, "r", encoding="utf-8") as fp: try: result = [analyze_fn(mo.groups(), fn) for mo in api_regex.finditer(fp.read())] except Exception: sys.stderr.write("While parsing %s\n" % fn) raise ret.extend(result) return ret def parse_args(): global args parser = argparse.ArgumentParser(description = __doc__, formatter_class = argparse.RawDescriptionHelpFormatter) parser.add_argument("-i", "--include", required=True, help="Base include directory") parser.add_argument("-j", "--json-file", required=True, help="Write system call prototype information as json to file") args = parser.parse_args() def main(): parse_args() syscalls = analyze_headers(args.include) syscalls_in_json = json.dumps( syscalls, indent=4, sort_keys=True ) # Check if the file already exists, and if there are no changes, # don't touch it since that will force an incremental rebuild path = args.json_file new = syscalls_in_json if os.path.exists(path): with open(path, 'r') as fp: old = fp.read() if new != old: with open(path, 'w') as fp: fp.write(new) else: with open(path, 'w') as fp: fp.write(new) if __name__ == "__main__": main() |