Add explicit MIT license and copyright
[deliverable/exatracer.git] / scripts / gen-hip-wrappers
CommitLineData
74c0b9b3
OD
1#!/usr/bin/env python3
2#
c38914e1
MJ
3# SPDX-FileCopyrightText: 2023 EfficiOS, Inc.
4#
5# SPDX-License-Identifier: MIT
74c0b9b3
OD
6#
7# Author: Olivier Dion <odion@efficios.com>
8#
c75d6f3f 9# Auto-generate lttng-ust wrappers for HIP.
74c0b9b3
OD
10#
11# Require: python-clang (libclang)
12
13import argparse
14import re
15import subprocess
16
17from string import Template
18
19import clang.cindex
20
21def list_function_declarations(root):
c75d6f3f 22 """Return all function declarations under ROOT."""
74c0b9b3
OD
23 return [
24 child
25 for child in root.get_children()
26 if child.kind == clang.cindex.CursorKind.FUNCTION_DECL
27 ]
28
29def get_system_include_paths():
c75d6f3f 30 """Get default system include paths from Clang."""
74c0b9b3
OD
31 clang_args = ["clang", "-v", "-c", "-xc", "-o", "/dev/null", "/dev/null"]
32 paths = []
33
34 with subprocess.Popen(clang_args, stderr=subprocess.PIPE, text=True) as proc:
35 start_sys_search = False
36 for line in proc.stderr:
37 if start_sys_search:
38 if line == "End of search list.\n":
39 break
40 paths.append("-isystem")
41 paths.append(line.strip())
42 elif line == "#include <...> search starts here:\n":
43 start_sys_search = True
44
45 return paths
46
47def parse_header(header_file, includes, defines):
c75d6f3f
OD
48 """
49 Parse HEADER_FILE with Clang with INCLUDES (-I) and DEFINES (-D).
50 Return a cursor to root.
51 """
52
53 # For some reason, python-clang does not use all system include paths.
54 # Thus, compiler dependant headers, like stddef.h can not be found without
55 # this.
74c0b9b3
OD
56 args = get_system_include_paths()
57
58 if includes:
59 for inc in includes:
60 args.append("-I")
61 args.append(inc)
62
63 if defines:
64 for d in defines:
65 args.append("-D")
66 args.append(d)
67
68 tu = clang.cindex.Index.create().parse(header_file, args=args)
69
70 for d in tu.diagnostics:
71 print(f"WARNING: {d}")
72
73 return tu.cursor
74
75def list_functions(root):
c75d6f3f 76 """Return the list of function declarations with `hip' prefix from ROOT."""
74c0b9b3
OD
77 return [
78 fn
79 for fn in list_function_declarations(root)
80 if fn.spelling.startswith("hip") and fn.spelling
81 ]
82
83def exact_definition(arg):
c75d6f3f 84 """Given a cursor ARG that is a function argument, return its exact definition."""
74c0b9b3
OD
85 ct = arg.type.get_canonical()
86 if ct.kind == clang.cindex.TypeKind.POINTER:
87 pt = ct.get_pointee()
88 if pt.kind == clang.cindex.TypeKind.FUNCTIONPROTO:
89 ret_type = pt.get_result().spelling
90 argument_types = ", ".join([a.spelling for a in pt.argument_types()])
91 return f"{ret_type} (*{arg.spelling})({argument_types})"
92 m = re.search(r'(\[[0-9]*\])+', arg.type.spelling)
93 if m:
94 return f"{arg.type.spelling[:m.start(0)]} {arg.spelling}{m.group(0)}"
95 else:
96 return f"{arg.type.spelling} {arg.spelling}"
97
98def cast(arg):
c75d6f3f
OD
99 """
100 Cast argument ARG to something that LTTng-UST can consume. Typically,
101 this is used to cast any pointer to void * because pointers are not
102 dereferenced anyway. Furthermore, array are also cast void *.
103 """
74c0b9b3
OD
104 canon = arg.type.get_canonical()
105 if canon.kind == clang.cindex.TypeKind.POINTER:
106 return "void *"
107 return re.sub(r'\[[0-9]*\]', '*', canon.spelling)
108
74c0b9b3
OD
109def main():
110
c75d6f3f
OD
111 # Extra works to do for functions.
112 #
113 # Format:
114 # key = str: function name ; e.g. hipMalloc
115 # value = str: C code ; e.g. printf("hello\n");
116 extra_works = {
117 }
118
74c0b9b3
OD
119 parser = argparse.ArgumentParser(prog="gen-hip-wrappers")
120
121 parser.add_argument("api",
122 help="HIP API header")
123
124 parser.add_argument("wrappers",
125 help="Path to HIP wrappers")
126
127 parser.add_argument("--ignores",
128 dest="ignores",
129 metavar="FILE",
130 default=None,
131 help="Ignore list")
132
133 parser.add_argument("-I",
134 action="append",
135 metavar="DIR",
136 dest="includes",
137 help="Add DIR to list of directories to include")
138
139 parser.add_argument("-D",
140 action="append",
141 metavar="DEFINITION",
142 dest="defines",
143 help="Add DEFINITION to list of definitions")
144
145 args = parser.parse_args()
146
c75d6f3f
OD
147 # The set of function to not instrument.
148 forbiden_list = set()
149
74c0b9b3
OD
150 if args.ignores:
151 with open(args.ignores, "r") as f:
152 for ignore in f.read().splitlines():
153 forbiden_list.add(ignore)
154
155 prologue_tpl = Template("""/* Auto-generated */
156#include "lttng-ust-hip-states.h"
157""")
158
159 ret_fn_tpl = Template("""
160static ${ret_type} lttng_${fn_name}(${fn_arguments})
161{
162 ${ret_type} ret;
163 {
164 lttng_hip::api_object_${fn_name} lttng_api_object {${fn_rest_argument_names}};
165 ret = next_hip_table.${fn_name}_fn(${fn_pass_argument_names});
166 lttng_api_object.mark_return(ret);
167 }
168$extra_work
169 return ret;
170}
171""")
172
c75d6f3f 173 # Void function are special because they do not return anything ..
74c0b9b3
OD
174 void_fn_tpl = Template("""
175static void lttng_${fn_name}(${fn_arguments})
176{
177 {
178 lttng_hip::api_object_${fn_name} lttng_api_object {${fn_rest_argument_names}};
179 next_hip_table.${fn_name}_fn(${fn_pass_argument_names});
180 lttng_api_object.mark_return();
181 }
182$extra_work
183}
184""")
185
c75d6f3f 186 # Because C++ does not support designated initializer.
74c0b9b3
OD
187 epilogue_tpl = Template("""
188static void lttng_hip_install_wrappers(void)
189{
190 ${wrappers}
191}
192""")
193
194 functions = list_functions(parse_header(args.api,
195 args.includes,
196 args.defines))
197
198 with open(args.wrappers, "w") as output:
199
200 output.write(prologue_tpl.substitute())
201
202 for fn in functions:
203
204 if fn.spelling in forbiden_list:
205 continue
206
207 args = list(fn.get_arguments())
208 fn_pass_argument_names = ", ".join([
209 f"{arg.spelling}"
210 for arg in args
211 ])
212
213 if args:
214 fn_rest_argument_names = ", ".join([
215 "(%s)%s" % (cast(arg), arg.spelling)
216 for arg in args
217 ])
218 else:
219 fn_rest_argument_names=""
220
221 if fn.spelling in extra_works:
222 extra_work = extra_works[fn.spelling]
223 else:
224 extra_work = ""
225
226 if "void"== fn.type.get_result().spelling:
227 fn_tpl = void_fn_tpl
228 else:
229 fn_tpl = ret_fn_tpl
230
231 output.write(fn_tpl.substitute(fn_name=fn.spelling,
232 fn_arguments=", ".join([
233 exact_definition(arg)
234 for arg in fn.get_arguments()
235 ]),
236 fn_pass_argument_names=fn_pass_argument_names,
237 fn_rest_argument_names=fn_rest_argument_names,
238 ret_type=fn.type.get_result().spelling,
239 extra_work=extra_work))
240
241 output.write(epilogue_tpl.substitute(wrappers="\n ".join([
242 f"lttng_hip_table.{fn.spelling}_fn = &lttng_{fn.spelling};"
243 for fn in functions if fn.spelling not in forbiden_list
244 ])))
245
246
247if __name__ == "__main__":
248 main()
This page took 0.0929 seconds and 4 git commands to generate.