Commit | Line | Data |
---|---|---|
66e983e2 OD |
1 | #!/usr/bin/env python3 |
2 | # | |
3 | # SPDX-License-Identifier: MIT | |
4 | # | |
e8418583 | 5 | # SPDX-FileCopyrightText: 2023 EfficiOS, Inc. |
66e983e2 OD |
6 | # |
7 | # Author: Olivier Dion <odion@efficios.com> | |
8 | # | |
9 | # Auto-generate lttng-ust tracepoints for OpenMPI. | |
10 | # | |
11 | # Require: python-clang (libclang) | |
12 | ||
13 | import argparse | |
14 | import re | |
15 | import os | |
16 | import subprocess | |
17 | ||
18 | from string import Template | |
19 | ||
20 | import clang.cindex | |
21 | ||
22 | COMMON_PREFIX = None | |
23 | IGNORE = None | |
24 | PROVIDER = None | |
25 | ||
26 | # LTTNG_UST_TP_ARGS is limited to 10 arguments. Since we introduce two | |
27 | # arguments of our own (thread-id and local-id), the maximum is 8. | |
28 | # | |
29 | # If a function has more arguments than this limit, all arguments -- at the | |
30 | # exception of the IDs -- will be passed through a data structure instead. | |
31 | MAX_TP_ARGS_COUNT = 8 | |
32 | ||
33 | class EnumValue: | |
34 | ||
35 | def __init__(self, ev): | |
36 | self.name = ev.spelling | |
37 | self.value = ev.enum_value | |
38 | ||
39 | class EnumType: | |
40 | ||
41 | def __init__(self, en, name=None): | |
42 | self.name = name or en.spelling | |
43 | self.values = [EnumValue(ev) for ev in en.get_children()] | |
44 | ||
45 | class Typedef: | |
46 | ||
47 | def __init__(self, spelling, value): | |
48 | self.spelling = spelling | |
49 | self.value = value | |
50 | ||
51 | class ArgumentType: | |
52 | ||
53 | integer_set = { | |
54 | clang.cindex.TypeKind.UCHAR, | |
55 | clang.cindex.TypeKind.USHORT, | |
56 | clang.cindex.TypeKind.UINT, | |
57 | clang.cindex.TypeKind.ULONG, | |
58 | clang.cindex.TypeKind.ULONGLONG, | |
59 | clang.cindex.TypeKind.SHORT, | |
60 | clang.cindex.TypeKind.INT, | |
61 | clang.cindex.TypeKind.LONG, | |
62 | clang.cindex.TypeKind.LONGLONG, | |
63 | } | |
64 | ||
65 | float_set = { | |
66 | clang.cindex.TypeKind.FLOAT, | |
67 | clang.cindex.TypeKind.DOUBLE, | |
68 | } | |
69 | ||
70 | address_set = { | |
71 | clang.cindex.TypeKind.POINTER, | |
72 | clang.cindex.TypeKind.INCOMPLETEARRAY, | |
73 | } | |
74 | ||
75 | def __init__(self, arg, name_prefix="", expr_prefix=""): | |
76 | self.type = arg.type | |
77 | self.arg = arg | |
78 | self.const = "" | |
79 | self.name_prefix = name_prefix | |
80 | self.expr_prefix = expr_prefix | |
81 | ||
82 | if self.kind() == clang.cindex.TypeKind.POINTER: | |
83 | if self.type.get_pointee().is_const_qualified(): | |
84 | self.const = "const " | |
85 | elif self.type.is_const_qualified(): | |
86 | self.const = "const " | |
87 | ||
88 | def name(self): | |
89 | return self.arg.spelling | |
90 | ||
91 | def type_name(self): | |
92 | if self.kind() == clang.cindex.TypeKind.INCOMPLETEARRAY: | |
93 | return self.const + re.sub(r"\[[0-9]*\]", "*", self.type.spelling) | |
94 | if self.kind() == clang.cindex.TypeKind.POINTER: | |
95 | return f"{self.const}void *" | |
96 | return self.const + self.type.spelling | |
97 | ||
98 | def kind(self): | |
99 | return self.type.get_canonical().kind | |
100 | ||
101 | def to_lttng_field(self): | |
102 | if self.name() == "reserved": | |
103 | return "" | |
104 | elif self.kind() in ArgumentType.address_set: | |
105 | return f"lttng_ust_field_integer_hex(uintptr_t, {self.name_prefix}{self.name()}, (uintptr_t){self.expr_prefix}{self.name()})" | |
106 | elif self.kind() in ArgumentType.integer_set: | |
107 | return f"lttng_ust_field_integer({self.type_name()}, {self.name_prefix}{self.name()}, {self.expr_prefix}{self.name()})" | |
108 | elif self.kind() in ArgumentType.float_set: | |
109 | return f"lttng_ust_field_float({self.type_name()}, {self.name_prefix}{self.name()}, {self.expr_prefix}{self.name()})" | |
110 | elif self.kind() == clang.cindex.TypeKind.ENUM: | |
111 | enum_name = self.type_name().removeprefix("enum ") | |
112 | return f"lttng_ust_field_enum({PROVIDER}, {enum_name}, int, {self.name_prefix}{self.name()}, {self.expr_prefix}{self.name()})" | |
113 | elif self.kind() == clang.cindex.TypeKind.RECORD: | |
114 | return [ | |
115 | ArgumentType(field, f"{self.name()}_", f"{self.expr_prefix}{self.name()}.").to_lttng_field() | |
116 | for field in self.type.get_canonical().get_fields() | |
117 | ] | |
118 | else: | |
119 | raise Exception("Unsupported kind: %s" % self.kind()) | |
120 | ||
121 | class FunctionType: | |
122 | ||
123 | struct_tpl = Template(""" | |
124 | $name { | |
125 | $fields | |
126 | }; | |
127 | """) | |
128 | ||
129 | def __init__(self, fn): | |
130 | self.name = fn.spelling | |
131 | self.args = [ArgumentType(arg) for arg in fn.get_arguments()] | |
132 | self.fn = fn | |
133 | ||
134 | def tp_args(self): | |
135 | if len(self.args) == 0: | |
136 | return "" | |
137 | elif len(self.args) > MAX_TP_ARGS_COUNT: | |
138 | return ",\n " + f"{self.arguments_struct_name()} *, lttng_args" | |
139 | else: | |
140 | return ",\n " + ",\n ".join([f"{arg.type_name()}, {arg.name()}" | |
141 | for arg in self.args]) | |
142 | ||
143 | def tp_fields(self): | |
144 | if len(self.args) == 0: | |
145 | return "" | |
146 | elif len(self.args) > MAX_TP_ARGS_COUNT: | |
147 | packed_args = [ArgumentType(arg.arg, "", "lttng_args->") for arg in self.args] | |
148 | return "\n ".join(flatten([arg.to_lttng_field() | |
149 | for arg in packed_args])) | |
150 | else: | |
151 | return "\n ".join(flatten([arg.to_lttng_field() | |
152 | for arg in self.args])) | |
153 | def get_return_type_name(self): | |
154 | return self.fn.type.get_result().spelling | |
155 | ||
156 | def ctor_params(self): | |
157 | if len(self.args) == 0: | |
158 | return "" | |
159 | elif len(self.args) > MAX_TP_ARGS_COUNT: | |
160 | return ", <tng_args" | |
161 | else: | |
162 | return ", " + ", ".join(arg.name() for arg in self.args) | |
163 | ||
164 | def arguments_struct_variable(self): | |
165 | if len(self.args) > MAX_TP_ARGS_COUNT: | |
166 | return "%s lttng_args = {%s};" % (self.arguments_struct_name(), | |
167 | ", ".join([arg.name() for arg in self.args])) | |
168 | else: | |
169 | return f"/* {self.arguments_struct_name()} lttng_args */" | |
170 | ||
171 | ||
172 | def arguments_struct_name(self): | |
173 | return f"struct lttng_arguments_of_{self.name}" | |
174 | ||
175 | def arguments_struct(self): | |
176 | if len(self.args) > MAX_TP_ARGS_COUNT: | |
177 | return self.struct_tpl.substitute(name=self.arguments_struct_name(), | |
178 | fields="\n ".join([ | |
179 | f"{arg.type_name()} {arg.name()};" | |
180 | for arg in self.args | |
181 | ])) | |
182 | else: | |
183 | return "" | |
184 | ||
185 | def flatten(lst): | |
186 | new_lst = [] | |
187 | for e in lst: | |
188 | if isinstance(e, list): | |
189 | for e in flatten(e): | |
190 | new_lst.append(e) | |
191 | else: | |
192 | new_lst.append(e) | |
193 | return new_lst | |
194 | ||
195 | def list_function_declarations(root): | |
196 | return [ child | |
197 | for child in root.get_children() | |
198 | if child.kind == clang.cindex.CursorKind.FUNCTION_DECL ] | |
199 | ||
200 | def list_enum_declarations(root): | |
201 | return [ | |
202 | child | |
203 | for child in root.get_children() | |
204 | if child.kind == clang.cindex.CursorKind.ENUM_DECL | |
205 | ] | |
206 | ||
207 | def list_typedef_enums(root): | |
208 | enums = [] | |
209 | for child in root.get_children(): | |
210 | if child.kind == clang.cindex.CursorKind.TYPEDEF_DECL: | |
211 | maybe_enum = child.underlying_typedef_type.get_declaration() | |
212 | if maybe_enum.kind == clang.cindex.CursorKind.ENUM_DECL: | |
213 | enums.append(Typedef(child.spelling, maybe_enum)) | |
214 | return enums | |
215 | ||
216 | def search_header_in(name, paths): | |
217 | for path in paths.split(":"): | |
218 | for dirpath, _, files in os.walk(path, followlinks=True): | |
219 | for file in files: | |
220 | if file == name: | |
221 | return os.path.join(dirpath, file) | |
222 | return None | |
223 | ||
224 | def search_c_header(name): | |
225 | return search_header_in(name, os.environ["C_INCLUDE_PATH"]) | |
226 | ||
227 | def search_cxx_header(name): | |
228 | return search_header_in(name, os.environ["CPLUS_INCLUDE_PATH"]) | |
229 | ||
230 | def get_system_include_paths(): | |
231 | ||
232 | clang_args = ["clang", "-v", "-c", "-xc", "/dev/null"] | |
233 | paths = [] | |
234 | ||
235 | with subprocess.Popen(clang_args, stderr=subprocess.PIPE) as proc: | |
236 | start_sys_search = False | |
237 | for line in proc.stderr: | |
238 | if start_sys_search: | |
239 | if line == "End of search list.\n": | |
240 | break | |
241 | paths.append("-isystem") | |
242 | paths.append(line.strip()) | |
243 | elif line == "#include <...> search starts here:\n": | |
244 | start_sys_search = True | |
245 | ||
246 | return paths | |
247 | ||
248 | def parse_header(header_file, includes, defines, | |
249 | required_c_headers, required_cxx_headers): | |
250 | ||
251 | args = get_system_include_paths() | |
252 | ||
253 | if includes: | |
254 | for inc in includes: | |
255 | args.append("-I") | |
256 | args.append(inc) | |
257 | ||
258 | if defines: | |
259 | for d in defines: | |
260 | args.append("-D") | |
261 | args.append(d) | |
262 | ||
263 | for header in required_c_headers: | |
264 | found = search_c_header(header) | |
265 | if found: | |
266 | args.append("-I") | |
267 | args.append(os.path.dirname(found)) | |
268 | ||
269 | for header in required_cxx_headers: | |
270 | found = search_cxx_header(header) | |
271 | if found: | |
272 | args.append("-I") | |
273 | args.append(os.path.dirname(found)) | |
274 | ||
275 | tu = clang.cindex.Index.create().parse(header_file, args=args) | |
276 | ||
277 | for d in tu.diagnostics: | |
278 | print(d) | |
279 | ||
280 | return tu.cursor | |
281 | ||
282 | def list_functions(root): | |
283 | return [ | |
284 | FunctionType(fn) | |
285 | for fn in list_function_declarations(root) | |
286 | if fn.spelling.startswith(COMMON_PREFIX) and fn.spelling not in IGNORE | |
287 | ] | |
288 | ||
289 | def list_enums(root): | |
290 | enums = [ | |
291 | EnumType(en) | |
292 | for en in list_enum_declarations(root) | |
293 | if en.spelling.startswith(COMMON_PREFIX) and en.spelling not in IGNORE | |
294 | ] | |
295 | ||
296 | typedef_enums = [ | |
297 | EnumType(typedef.value, typedef.spelling) | |
298 | for typedef in list_typedef_enums(root) | |
299 | if typedef.spelling.startswith(COMMON_PREFIX) and typedef.spelling not in IGNORE | |
300 | ] | |
301 | ||
302 | return enums + typedef_enums | |
303 | ||
304 | def generate_tracepoint_definitions(function_declarations, enum_declarations, | |
305 | api_file, output_defs, output_interface, | |
306 | header_guard): | |
307 | defs_tpl = Template("""/* Auto-generated file! */ | |
308 | #undef LTTNG_UST_TRACEPOINT_PROVIDER | |
309 | #define LTTNG_UST_TRACEPOINT_PROVIDER $provider | |
310 | ||
311 | #undef LTTNG_UST_TRACEPOINT_INCLUDE | |
312 | #define LTTNG_UST_TRACEPOINT_INCLUDE "$output_defs" | |
313 | ||
314 | #if !defined($header_guard) | |
315 | #include <$api_file> | |
316 | $pass_by_struct | |
317 | #endif | |
318 | ||
319 | #if !defined($header_guard) || defined(LTTNG_UST_TRACEPOINT_HEADER_MULTI_READ) | |
320 | #define $header_guard | |
321 | ||
322 | #include <lttng/tracepoint.h> | |
323 | ||
324 | $enum_definitions | |
325 | $tracepoint_definitions | |
326 | ||
327 | #endif /* $header_guard */ | |
328 | ||
329 | #include <lttng/tracepoint-event.h> | |
330 | """) | |
331 | ||
332 | interface_tpl = Template("""/* Auto-generated file! */ | |
333 | #ifndef ${header_guard}_IMPL | |
334 | #define ${header_guard}_IMPL | |
335 | ||
336 | #include "${output_defs}" | |
337 | ||
338 | #endif /* ${header_guard}_IMPL */ | |
339 | """) | |
340 | ||
341 | tp_tpl = Template(""" | |
342 | LTTNG_UST_TRACEPOINT_EVENT( | |
343 | $provider, | |
344 | enter_$name, | |
345 | LTTNG_UST_TP_ARGS( | |
346 | uint64_t, lttng_thread_id, | |
347 | uint64_t, lttng_local_id$tp_args | |
348 | ), | |
349 | LTTNG_UST_TP_FIELDS( | |
350 | lttng_ust_field_integer(uint64_t, lttng_thread_id, lttng_thread_id) | |
351 | lttng_ust_field_integer(uint64_t, lttng_local_id, lttng_local_id) | |
352 | $tp_fields | |
353 | ) | |
354 | ) | |
355 | """) | |
356 | ||
357 | tp_ret_tpl = Template(""" | |
358 | LTTNG_UST_TRACEPOINT_EVENT( | |
359 | $provider, | |
360 | exit_$name, | |
361 | LTTNG_UST_TP_ARGS( | |
362 | uint64_t, lttng_thread_id, | |
363 | uint64_t, lttng_local_id, | |
364 | int, lttng_has_ret, | |
365 | $ret_type, lttng_ret | |
366 | ), | |
367 | LTTNG_UST_TP_FIELDS( | |
368 | lttng_ust_field_integer(uint64_t, lttng_thread_id, lttng_thread_id) | |
369 | lttng_ust_field_integer(uint64_t, lttng_local_id, lttng_local_id) | |
370 | lttng_ust_field_integer(int, lttng_has_ret, lttng_has_ret) | |
371 | lttng_ust_field_integer($ret_type, lttng_ret, lttng_ret) | |
372 | ) | |
373 | ) | |
374 | """) | |
375 | ||
376 | tp_void_tpl = Template(""" | |
377 | LTTNG_UST_TRACEPOINT_EVENT( | |
378 | $provider, | |
379 | exit_$name, | |
380 | LTTNG_UST_TP_ARGS( | |
381 | uint64_t, lttng_thread_id, | |
382 | uint64_t, lttng_local_id, | |
383 | int, lttng_has_ret | |
384 | ), | |
385 | LTTNG_UST_TP_FIELDS( | |
386 | lttng_ust_field_integer(uint64_t, lttng_thread_id, lttng_thread_id) | |
387 | lttng_ust_field_integer(uint64_t, lttng_local_id, lttng_local_id) | |
388 | lttng_ust_field_integer(int, lttng_has_ret, lttng_has_ret) | |
389 | ) | |
390 | ) | |
391 | """) | |
392 | enum_tpl = Template(""" | |
393 | LTTNG_UST_TRACEPOINT_ENUM($provider, $name, | |
394 | LTTNG_UST_TP_ENUM_VALUES( | |
395 | $values | |
396 | ) | |
397 | ) | |
398 | """) | |
399 | with open(output_defs, "w") as output: | |
400 | definitions = [] | |
401 | for fn in function_declarations: | |
402 | ret_type = fn.get_return_type_name() | |
403 | definitions.append(tp_tpl.substitute(provider=PROVIDER, | |
404 | name=fn.name, | |
405 | tp_args=fn.tp_args(), | |
406 | tp_fields=fn.tp_fields())) | |
407 | if ret_type == "void": | |
408 | tpl = tp_void_tpl | |
409 | else: | |
410 | tpl = tp_ret_tpl | |
411 | ||
412 | definitions.append(tpl.substitute(provider=PROVIDER, | |
413 | name=fn.name, | |
414 | ret_type=ret_type)) | |
415 | ||
416 | tracepoint_definitions = "\n".join(definitions) | |
417 | ||
418 | enum_definitions = "\n".join([ | |
419 | enum_tpl.substitute(provider=PROVIDER, | |
420 | name=en.name, | |
421 | values="\n ".join([f'lttng_ust_field_enum_value("{ev.name}", {ev.value})' | |
422 | for ev in en.values])) | |
423 | for en in enum_declarations | |
424 | ]) | |
425 | ||
426 | output.write(defs_tpl.substitute(provider=PROVIDER, | |
427 | output_defs=output_defs, | |
428 | header_guard=header_guard, | |
429 | tracepoint_definitions=tracepoint_definitions, | |
430 | enum_definitions=enum_definitions, | |
431 | api_file=api_file, | |
432 | pass_by_struct="".join([fn.arguments_struct() | |
433 | for fn in function_declarations]))) | |
434 | with open(output_interface, "w") as output: | |
435 | output.write(interface_tpl.substitute(header_guard=header_guard, | |
436 | output_defs=output_defs,)) | |
437 | ||
438 | def generate_tracepoint_classes(function_declarations, api_file, output_path, header_guard, namespace): | |
439 | global_tpl = Template("""/* Auto-generated file! */ | |
440 | #include <atomic> | |
441 | #include <cstdint> | |
442 | #include <$api_file> | |
443 | namespace $namespace { | |
444 | struct unique_id { | |
445 | uint64_t thread_id; | |
446 | uint64_t local_id; | |
447 | }; | |
448 | ||
449 | class id_generator { | |
450 | static std::atomic<uint64_t> _thread_counter; | |
451 | uint64_t _thread_id; | |
452 | uint64_t _local_id; | |
453 | public: | |
454 | id_generator() { | |
455 | _thread_id = _thread_counter++; | |
456 | _local_id = 0; | |
457 | } | |
458 | ||
459 | unique_id next_id() { | |
460 | return { | |
461 | .thread_id = _thread_id, | |
462 | .local_id = _local_id++, | |
463 | }; | |
464 | } | |
465 | }; | |
466 | ||
467 | extern thread_local id_generator generator; | |
468 | ||
469 | template<typename RetType> | |
470 | class base_api_object { | |
471 | protected: | |
472 | unique_id _id; | |
473 | int _has_ret; | |
474 | RetType _ret; | |
475 | public: | |
476 | void generate_id() { | |
477 | _id = generator.next_id(); | |
478 | } | |
479 | ||
480 | void mark_return(RetType ret) { | |
481 | _ret = ret; | |
482 | _has_ret = 1; | |
483 | } | |
484 | }; | |
485 | ||
486 | class base_api_object_void { | |
487 | protected: | |
488 | unique_id _id; | |
489 | int _has_ret; | |
490 | public: | |
491 | void generate_id() { | |
492 | _id = generator.next_id(); | |
493 | } | |
494 | ||
495 | void mark_return(void) { | |
496 | _has_ret = 1; | |
497 | } | |
498 | }; | |
499 | ||
500 | $classes | |
501 | }; | |
502 | """) | |
503 | ||
504 | cls_ret_tpl = Template(""" | |
505 | class api_object_$fn_name : public base_api_object<$ret_type> | |
506 | { | |
507 | public: | |
508 | api_object_$fn_name($ctor_type_params) { | |
509 | if (lttng_ust_tracepoint_enabled($provider, enter_$fn_name)) { | |
510 | generate_id(); | |
511 | $pass_by_struct | |
512 | lttng_ust_do_tracepoint($provider, | |
513 | enter_$fn_name, | |
514 | _id.thread_id, | |
515 | _id.local_id$ctor_params); | |
516 | } | |
517 | } | |
518 | ~api_object_$fn_name() { | |
519 | if (lttng_ust_tracepoint_enabled($provider, exit_$fn_name)) { | |
520 | lttng_ust_do_tracepoint($provider, | |
521 | exit_$fn_name, | |
522 | _id.thread_id, | |
523 | _id.local_id, | |
524 | _has_ret, | |
525 | _ret); | |
526 | } | |
527 | } | |
528 | }; | |
529 | """) | |
530 | ||
531 | cls_void_tpl = Template(""" | |
532 | class api_object_$fn_name : public base_api_object_void | |
533 | { | |
534 | public: | |
535 | api_object_$fn_name($ctor_type_params) { | |
536 | if (lttng_ust_tracepoint_enabled($provider, enter_$fn_name)) { | |
537 | generate_id(); | |
538 | $pass_by_struct | |
539 | lttng_ust_do_tracepoint($provider, | |
540 | enter_$fn_name, | |
541 | _id.thread_id, | |
542 | _id.local_id$ctor_params); | |
543 | } | |
544 | } | |
545 | ~api_object_$fn_name() { | |
546 | if (lttng_ust_tracepoint_enabled($provider, exit_$fn_name)) { | |
547 | lttng_ust_do_tracepoint($provider, | |
548 | exit_$fn_name, | |
549 | _id.thread_id, | |
550 | _id.local_id, | |
551 | _has_ret); | |
552 | } | |
553 | } | |
554 | }; | |
555 | """) | |
556 | ||
557 | with open(output_path, "w") as output: | |
558 | classes = [] | |
559 | for fn in function_declarations: | |
560 | ret_type = fn.get_return_type_name() | |
561 | if ret_type == "void": | |
562 | cls_tpl = cls_void_tpl | |
563 | else: | |
564 | cls_tpl = cls_ret_tpl | |
565 | classes.append(cls_tpl.substitute(provider=PROVIDER, | |
566 | fn_name=fn.name, | |
567 | pass_by_struct=fn.arguments_struct_variable(), | |
568 | ctor_type_params=", ".join([f"{arg.type_name()} {arg.name()}" | |
569 | for arg in fn.args]), | |
570 | ctor_params=fn.ctor_params(), | |
571 | ret_type=ret_type)) | |
572 | output.write(global_tpl.substitute(api_file=api_file, | |
573 | namespace=namespace, | |
574 | classes="".join(classes))) | |
575 | ||
576 | def generate_tracepoint_emulated_classes(function_declarations, api_file, output_path, | |
577 | header_guard, namespace): | |
578 | global_tpl = Template("""/* Auto-generated file! */ | |
579 | #include <stdint.h> | |
580 | #include <$api_file> | |
581 | #define ${NAMESPACE}_CAT_PRIMITIVE(A, B) A##B | |
582 | #define ${NAMESPACE}_CAT(A, B) ${NAMESPACE}_CAT_PRIMITIVE(A, B) | |
583 | ||
584 | struct ${namespace}_unique_id { | |
585 | uint64_t thread_id; | |
586 | uint64_t local_id; | |
587 | }; | |
588 | ||
589 | struct ${namespace}_id_generator { | |
590 | uint64_t thread_id; | |
591 | uint64_t local_id; | |
592 | int initialized; | |
593 | }; | |
594 | ||
595 | extern uint64_t ${namespace}_id_generator_thread_counter; | |
596 | extern _Thread_local struct ${namespace}_id_generator ${namespace}_generator; | |
597 | ||
598 | #define ${namespace}_unlikely(x) __builtin_expect(!!(x), 0) | |
599 | ||
600 | static inline void ${namespace}_id_generator_next_id(struct ${namespace}_unique_id *id) | |
601 | { | |
602 | if (${namespace}_unlikely(!${namespace}_generator.initialized)) { | |
603 | ${namespace}_generator.thread_id = | |
604 | __atomic_fetch_add(&${namespace}_id_generator_thread_counter, | |
605 | 1, | |
606 | __ATOMIC_RELAXED); | |
607 | ${namespace}_generator.initialized = 1; | |
608 | } | |
609 | ||
610 | id->thread_id = ${namespace}_generator.thread_id; | |
611 | id->local_id = ${namespace}_generator.local_id++; | |
612 | } | |
613 | ||
614 | #define ${NAMESPACE}_API_OBJECT_NAME ${namespace}_api_object | |
615 | ||
616 | #define ${NAMESPACE}_MAKE_API_OBJECT(name, ...) \\ | |
617 | struct ${NAMESPACE}_CAT(${namespace}_api_state_, name) __attribute__((cleanup(${NAMESPACE}_CAT(exit_, name)))) \\ | |
618 | ${NAMESPACE}_API_OBJECT_NAME = { 0 }; \\ | |
619 | ${NAMESPACE}_CAT(enter_, name)(&${NAMESPACE}_API_OBJECT_NAME, ##__VA_ARGS__); \\ | |
620 | do { } while (0) | |
621 | ||
622 | #define ${NAMESPACE}_MARK_RETURN_API_OBJECT(code) \\ | |
623 | ({ \\ | |
624 | ${NAMESPACE}_API_OBJECT_NAME.ret = code; \\ | |
625 | ${NAMESPACE}_API_OBJECT_NAME.has_ret = 1; \\ | |
626 | }) | |
627 | ${classes} | |
628 | """) | |
629 | ||
630 | cls_tpl = Template(""" | |
631 | struct ${namespace}_api_state_${fn_name} { | |
632 | struct ${namespace}_unique_id id; | |
633 | int has_ret; | |
634 | $ret_type ret; | |
635 | }; | |
636 | ||
637 | static inline void enter_${fn_name}(${ctor_type_params}) | |
638 | { | |
639 | if (${namespace}_ust_tracepoint_enabled(${provider}, enter_${fn_name})) { | |
640 | ${namespace}_id_generator_next_id(<tng_state->id); | |
641 | ${pass_by_struct} | |
642 | ${namespace}_ust_do_tracepoint($provider, enter_${fn_name}, | |
643 | lttng_state->id.thread_id, | |
644 | lttng_state->id.local_id${ctor_params}); | |
645 | } | |
646 | } | |
647 | ||
648 | static inline void exit_${fn_name}(const struct ${namespace}_api_state_${fn_name} *lttng_state) | |
649 | { | |
650 | lttng_ust_tracepoint(${provider}, exit_${fn_name}, | |
651 | lttng_state->id.thread_id, | |
652 | lttng_state->id.local_id, | |
653 | lttng_state->has_ret, | |
654 | lttng_state->ret); | |
655 | } | |
656 | """) | |
657 | with open(output_path, "w") as output: | |
658 | output.write(global_tpl.substitute(api_file=api_file, | |
659 | namespace=namespace, | |
660 | NAMESPACE=namespace.upper(), | |
661 | classes="".join([ | |
662 | cls_tpl.substitute(provider=PROVIDER, | |
663 | fn_name=fn.name, | |
664 | pass_by_struct=fn.arguments_struct_variable(), | |
665 | ctor_params=fn.ctor_params(), | |
666 | ctor_type_params=", ".join([f"struct {namespace}_api_state_{fn.name} *lttng_state"] + | |
667 | [f"{arg.type_name()} {arg.name()}" | |
668 | for arg in fn.args]), | |
669 | namespace=namespace, | |
670 | NAMESPACE=namespace.upper(), | |
671 | ret_type=fn.get_return_type_name()) | |
672 | for fn in function_declarations | |
673 | ]))) | |
674 | ||
675 | ||
676 | def generate_tracepoint_implementations(guard, namespace, defs, impls): | |
677 | tpl = Template("""/* Auto-generated !*/ | |
678 | #ifdef ${guard} | |
679 | ||
680 | #define LTTNG_UST_TRACEPOINT_CREATE_PROBES | |
681 | #define LTTNG_UST_TRACEPOINT_DEFINE | |
682 | #include "${defs}" | |
683 | ||
684 | #endif /* ${guard} */ | |
685 | """) | |
686 | ||
687 | with open(impls, "w") as output: | |
688 | output.write(tpl.substitute(guard=guard, | |
689 | defs=defs)) | |
690 | ||
691 | def generate_tracepoint_states(states_guard, | |
692 | namespace, | |
693 | interface, | |
694 | classes, | |
695 | states, | |
696 | emulated_classes): | |
697 | ||
698 | if emulated_classes: | |
699 | body_tpl = Template(""" | |
700 | uint64_t ${namespace}_id_generator_thread_counter = 0; | |
701 | _Thread_local struct ${namespace}_id_generator ${namespace}_generator; | |
702 | """) | |
703 | else: | |
704 | body_tpl = Template(""" | |
705 | #include <atomic> | |
706 | namespace ${namespace} { | |
707 | std::atomic<uint64_t> id_generator::_thread_counter{0}; | |
708 | thread_local id_generator generator; | |
709 | }; | |
710 | """) | |
711 | ||
712 | tpl = Template("""/* Auto-generated! */ | |
713 | #ifdef ${states_guard} | |
714 | #include "${interface}" | |
715 | #include "${classes}" | |
716 | ||
717 | $body | |
718 | #endif | |
719 | """) | |
720 | ||
721 | with open(states, "w") as output: | |
722 | output.write(tpl.substitute(states_guard=states_guard, | |
723 | interface=interface, | |
724 | classes=classes, | |
725 | body=body_tpl.substitute(namespace=namespace))) | |
726 | ||
727 | def main(): | |
728 | ||
729 | global COMMON_PREFIX | |
730 | global IGNORE | |
731 | global PROVIDER | |
732 | ||
845aa3e2 KS |
733 | if os.getenv("LTTNG_UST_MPI_CLANG_LIBRARY_FILE", None) is not None: |
734 | clang.cindex.Config.set_library_file(os.getenv("LTTNG_UST_MPI_CLANG_LIBRARY_FILE")) | |
735 | ||
66e983e2 OD |
736 | parser = argparse.ArgumentParser(prog="lttng-ust-autogen-api", |
737 | description="Generate LTTng classes and tracepoint definitions") | |
738 | ||
739 | parser.add_argument("api", | |
740 | help="Header file that has the API") | |
741 | ||
742 | parser.add_argument("defs", | |
743 | help="Path to tracepoint definitions") | |
744 | ||
745 | parser.add_argument("interface", | |
746 | help="Path to tracepoints interfaces") | |
747 | ||
748 | parser.add_argument("classes", | |
749 | help="Path to tracepoint classes") | |
750 | ||
751 | parser.add_argument("impl", | |
752 | help="Path to tracepoint implementations") | |
753 | ||
754 | parser.add_argument("states", | |
755 | help="Path to states") | |
756 | ||
757 | parser.add_argument("--provider", | |
758 | dest="provider", | |
759 | metavar="PROVIDER", | |
760 | default="noprovider", | |
761 | help="Tracepoints PROVIDER") | |
762 | ||
763 | parser.add_argument("--common-prefix", | |
764 | dest="common_prefix", | |
765 | metavar="PREFIX", | |
766 | default="", | |
767 | help="Common PREFIX of API functions (C namespace)") | |
768 | ||
769 | parser.add_argument("-I", | |
770 | action="append", | |
771 | metavar="DIR", | |
772 | dest="includes", | |
773 | help="Add DIR to list of directories to include") | |
774 | ||
775 | parser.add_argument("-D", | |
776 | action="append", | |
777 | metavar="DEFINITION", | |
778 | dest="defines", | |
779 | help="Add DEFINITION to list of definitions") | |
780 | ||
781 | parser.add_argument("--tp-guard", | |
782 | dest="tp_guard", | |
783 | metavar="GUARD", | |
784 | default="LTTNG_TRACEPOINT_DEF_H", | |
785 | help="Use GUARD as header guard for tracepoint definitions") | |
786 | ||
787 | parser.add_argument("--classes-guard", | |
788 | dest="classes_guard", | |
789 | metavar="GUARD", | |
790 | default="LTTNG_TRACEPOINT_CLASSES_HPP", | |
791 | help="Use GUARD as header guard for classes definitions") | |
792 | ||
793 | parser.add_argument("--impl-guard", | |
794 | dest="impl_guard", | |
795 | metavar="GUARD", | |
796 | default="ENABLE_LTTNG_TRACEPOINTS", | |
797 | help="Use GUARD around implementations") | |
798 | ||
799 | parser.add_argument("--states-guard", | |
800 | dest="states_guard", | |
801 | metavar="GUARD", | |
802 | default="ENABLE_LTTNG_TRACEPOINTS", | |
803 | help="Use GUARD around states") | |
804 | ||
805 | parser.add_argument("--emulated-classes", | |
806 | dest="emulated_classes", | |
807 | action="store_true", | |
808 | default=False, | |
809 | help="Emulate C++ classes") | |
810 | ||
811 | parser.add_argument("--namespace", | |
812 | dest="namespace", | |
813 | metavar="NAMESPACE", | |
814 | default="lttng", | |
815 | help="Generate classes in NAMESPACE") | |
816 | ||
817 | parser.add_argument("--ignore", | |
818 | dest="ignore", | |
819 | metavar="FUNCTION", | |
820 | action="append", | |
821 | default=[], | |
822 | help="Ignore FUNCTION") | |
823 | ||
824 | parser.add_argument("--c-header", | |
825 | dest="required_c_headers", | |
826 | metavar="HEADER", | |
827 | action="append", | |
828 | default=[], | |
829 | help="Search for HEADER in C_INCLUDE_PATH and add its directory to search path") | |
830 | ||
831 | parser.add_argument("--cxx-header", | |
832 | dest="required_cxx_headers", | |
833 | metavar="HEADER", | |
834 | action="append", | |
835 | default=[], | |
836 | help="Search for HEADER in CPLUS_INCLUDE_PATH add its directory to search path") | |
837 | ||
838 | args = parser.parse_args() | |
839 | ||
840 | PROVIDER = args.provider | |
841 | COMMON_PREFIX = args.common_prefix | |
842 | IGNORE = set(args.ignore) | |
843 | ||
844 | root = parse_header(args.api, args.includes, args.defines, | |
845 | args.required_c_headers, | |
846 | args.required_cxx_headers) | |
847 | ||
848 | function_declarations = list_functions(root) | |
849 | enum_declarations = list_enums(root) | |
850 | ||
851 | generate_tracepoint_definitions(function_declarations, | |
852 | enum_declarations, | |
853 | args.api, args.defs, args.interface, | |
854 | args.tp_guard) | |
855 | ||
856 | if args.emulated_classes: | |
857 | generate_tracepoint_emulated_classes(function_declarations, | |
858 | args.api, | |
859 | args.classes, | |
860 | args.classes_guard, | |
861 | args.namespace) | |
862 | else: | |
863 | generate_tracepoint_classes(function_declarations, | |
864 | args.api, | |
865 | args.classes, | |
866 | args.classes_guard, | |
867 | args.namespace) | |
868 | ||
869 | generate_tracepoint_implementations(args.impl_guard, | |
870 | args.namespace, | |
871 | args.interface, | |
872 | args.impl) | |
873 | ||
874 | generate_tracepoint_states(args.states_guard, | |
875 | args.namespace, | |
876 | args.interface, | |
877 | args.classes, | |
878 | args.states, | |
879 | args.emulated_classes) | |
880 | ||
881 | ||
882 | if __name__ == "__main__": | |
883 | main() |