3 # SPDX-License-Identifier: MIT
5 # SPDX-FileCopyrightText: 2023 EfficiOS, Inc.
7 # Author: Olivier Dion <odion@efficios.com>
9 # Auto-generate lttng-ust tracepoints for OpenMPI.
11 # Require: python-clang (libclang)
18 from string
import Template
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.
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.
35 def __init__(self
, ev
):
36 self
.name
= ev
.spelling
37 self
.value
= ev
.enum_value
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()]
47 def __init__(self
, spelling
, value
):
48 self
.spelling
= spelling
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
,
66 clang
.cindex
.TypeKind
.FLOAT
,
67 clang
.cindex
.TypeKind
.DOUBLE
,
71 clang
.cindex
.TypeKind
.POINTER
,
72 clang
.cindex
.TypeKind
.INCOMPLETEARRAY
,
75 def __init__(self
, arg
, name_prefix
="", expr_prefix
=""):
79 self
.name_prefix
= name_prefix
80 self
.expr_prefix
= expr_prefix
82 if self
.kind() == clang
.cindex
.TypeKind
.POINTER
:
83 if self
.type.get_pointee().is_const_qualified():
85 elif self
.type.is_const_qualified():
89 return self
.arg
.spelling
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
99 return self
.type.get_canonical().kind
101 def to_lttng_field(self
):
102 if self
.name() == "reserved":
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
:
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()
119 raise Exception("Unsupported kind: %s" % self
.kind())
123 struct_tpl
= Template("""
129 def __init__(self
, fn
):
130 self
.name
= fn
.spelling
131 self
.args
= [ArgumentType(arg
) for arg
in fn
.get_arguments()]
135 if len(self
.args
) == 0:
137 elif len(self
.args
) > MAX_TP_ARGS_COUNT
:
138 return ",\n " + f
"{self.arguments_struct_name()} *, lttng_args"
140 return ",\n " + ",\n ".join([f
"{arg.type_name()}, {arg.name()}"
141 for arg
in self
.args
])
144 if len(self
.args
) == 0:
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
]))
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
156 def ctor_params(self
):
157 if len(self
.args
) == 0:
159 elif len(self
.args
) > MAX_TP_ARGS_COUNT
:
160 return ", <tng_args"
162 return ", " + ", ".join(arg
.name() for arg
in self
.args
)
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
]))
169 return f
"/* {self.arguments_struct_name()} lttng_args */"
172 def arguments_struct_name(self
):
173 return f
"struct lttng_arguments_of_{self.name}"
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(),
179 f
"{arg.type_name()} {arg.name()};"
188 if isinstance(e
, list):
195 def list_function_declarations(root
):
197 for child
in root
.get_children()
198 if child
.kind
== clang
.cindex
.CursorKind
.FUNCTION_DECL
]
200 def list_enum_declarations(root
):
203 for child
in root
.get_children()
204 if child
.kind
== clang
.cindex
.CursorKind
.ENUM_DECL
207 def list_typedef_enums(root
):
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
))
216 def search_header_in(name
, paths
):
217 for path
in paths
.split(":"):
218 for dirpath
, _
, files
in os
.walk(path
, followlinks
=True):
221 return os
.path
.join(dirpath
, file)
224 def search_c_header(name
):
225 return search_header_in(name
, os
.environ
["C_INCLUDE_PATH"])
227 def search_cxx_header(name
):
228 return search_header_in(name
, os
.environ
["CPLUS_INCLUDE_PATH"])
230 def get_system_include_paths():
232 clang_args
= ["clang", "-v", "-c", "-xc", "/dev/null"]
235 with subprocess
.Popen(clang_args
, stderr
=subprocess
.PIPE
) as proc
:
236 start_sys_search
= False
237 for line
in proc
.stderr
:
239 if line
== "End of search list.\n":
241 paths
.append("-isystem")
242 paths
.append(line
.strip())
243 elif line
== "#include <...> search starts here:\n":
244 start_sys_search
= True
248 def parse_header(header_file
, includes
, defines
,
249 required_c_headers
, required_cxx_headers
):
251 args
= get_system_include_paths()
263 for header
in required_c_headers
:
264 found
= search_c_header(header
)
267 args
.append(os
.path
.dirname(found
))
269 for header
in required_cxx_headers
:
270 found
= search_cxx_header(header
)
273 args
.append(os
.path
.dirname(found
))
275 tu
= clang
.cindex
.Index
.create().parse(header_file
, args
=args
)
277 for d
in tu
.diagnostics
:
282 def list_functions(root
):
285 for fn
in list_function_declarations(root
)
286 if fn
.spelling
.startswith(COMMON_PREFIX
) and fn
.spelling
not in IGNORE
289 def list_enums(root
):
292 for en
in list_enum_declarations(root
)
293 if en
.spelling
.startswith(COMMON_PREFIX
) and en
.spelling
not in IGNORE
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
302 return enums
+ typedef_enums
304 def generate_tracepoint_definitions(function_declarations
, enum_declarations
,
305 api_file
, output_defs
, output_interface
,
307 defs_tpl
= Template("""/* Auto-generated file! */
308 #undef LTTNG_UST_TRACEPOINT_PROVIDER
309 #define LTTNG_UST_TRACEPOINT_PROVIDER $provider
311 #undef LTTNG_UST_TRACEPOINT_INCLUDE
312 #define LTTNG_UST_TRACEPOINT_INCLUDE "$output_defs"
314 #if !defined($header_guard)
319 #if !defined($header_guard) || defined(LTTNG_UST_TRACEPOINT_HEADER_MULTI_READ)
320 #define $header_guard
322 #include <lttng/tracepoint.h>
325 $tracepoint_definitions
327 #endif /* $header_guard */
329 #include <lttng/tracepoint-event.h>
332 interface_tpl
= Template("""/* Auto-generated file! */
333 #ifndef ${header_guard}_IMPL
334 #define ${header_guard}_IMPL
336 #include "${output_defs}"
338 #endif /* ${header_guard}_IMPL */
341 tp_tpl
= Template("""
342 LTTNG_UST_TRACEPOINT_EVENT(
346 uint64_t, lttng_thread_id,
347 uint64_t, lttng_local_id$tp_args
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)
357 tp_ret_tpl
= Template("""
358 LTTNG_UST_TRACEPOINT_EVENT(
362 uint64_t, lttng_thread_id,
363 uint64_t, lttng_local_id,
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)
376 tp_void_tpl
= Template("""
377 LTTNG_UST_TRACEPOINT_EVENT(
381 uint64_t, lttng_thread_id,
382 uint64_t, lttng_local_id,
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)
392 enum_tpl
= Template("""
393 LTTNG_UST_TRACEPOINT_ENUM($provider, $name,
394 LTTNG_UST_TP_ENUM_VALUES(
399 with
open(output_defs
, "w") as output
:
401 for fn
in function_declarations
:
402 ret_type
= fn
.get_return_type_name()
403 definitions
.append(tp_tpl
.substitute(provider
=PROVIDER
,
405 tp_args
=fn
.tp_args(),
406 tp_fields
=fn
.tp_fields()))
407 if ret_type
== "void":
412 definitions
.append(tpl
.substitute(provider
=PROVIDER
,
416 tracepoint_definitions
= "\n".join(definitions
)
418 enum_definitions
= "\n".join([
419 enum_tpl
.substitute(provider
=PROVIDER
,
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
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
,
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
,))
438 def generate_tracepoint_classes(function_declarations
, api_file
, output_path
, header_guard
, namespace
):
439 global_tpl
= Template("""/* Auto-generated file! */
443 namespace $namespace {
450 static std::atomic<uint64_t> _thread_counter;
455 _thread_id = _thread_counter++;
459 unique_id next_id() {
461 .thread_id = _thread_id,
462 .local_id = _local_id++,
467 extern thread_local id_generator generator;
469 template<typename RetType>
470 class base_api_object {
477 _id = generator.next_id();
480 void mark_return(RetType ret) {
486 class base_api_object_void {
492 _id = generator.next_id();
495 void mark_return(void) {
504 cls_ret_tpl
= Template("""
505 class api_object_$fn_name : public base_api_object<$ret_type>
508 api_object_$fn_name($ctor_type_params) {
509 if (lttng_ust_tracepoint_enabled($provider, enter_$fn_name)) {
512 lttng_ust_do_tracepoint($provider,
515 _id.local_id$ctor_params);
518 ~api_object_$fn_name() {
519 if (lttng_ust_tracepoint_enabled($provider, exit_$fn_name)) {
520 lttng_ust_do_tracepoint($provider,
531 cls_void_tpl
= Template("""
532 class api_object_$fn_name : public base_api_object_void
535 api_object_$fn_name($ctor_type_params) {
536 if (lttng_ust_tracepoint_enabled($provider, enter_$fn_name)) {
539 lttng_ust_do_tracepoint($provider,
542 _id.local_id$ctor_params);
545 ~api_object_$fn_name() {
546 if (lttng_ust_tracepoint_enabled($provider, exit_$fn_name)) {
547 lttng_ust_do_tracepoint($provider,
557 with
open(output_path
, "w") as output
:
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
564 cls_tpl
= cls_ret_tpl
565 classes
.append(cls_tpl
.substitute(provider
=PROVIDER
,
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(),
572 output
.write(global_tpl
.substitute(api_file
=api_file
,
574 classes
="".join(classes
)))
576 def generate_tracepoint_emulated_classes(function_declarations
, api_file
, output_path
,
577 header_guard
, namespace
):
578 global_tpl
= Template("""/* Auto-generated file! */
581 #define ${NAMESPACE}_CAT_PRIMITIVE(A, B) A##B
582 #define ${NAMESPACE}_CAT(A, B) ${NAMESPACE}_CAT_PRIMITIVE(A, B)
584 struct ${namespace}_unique_id {
589 struct ${namespace}_id_generator {
595 extern uint64_t ${namespace}_id_generator_thread_counter;
596 extern _Thread_local struct ${namespace}_id_generator ${namespace}_generator;
598 #define ${namespace}_unlikely(x) __builtin_expect(!!(x), 0)
600 static inline void ${namespace}_id_generator_next_id(struct ${namespace}_unique_id *id)
602 if (${namespace}_unlikely(!${namespace}_generator.initialized)) {
603 ${namespace}_generator.thread_id =
604 __atomic_fetch_add(&${namespace}_id_generator_thread_counter,
607 ${namespace}_generator.initialized = 1;
610 id->thread_id = ${namespace}_generator.thread_id;
611 id->local_id = ${namespace}_generator.local_id++;
614 #define ${NAMESPACE}_API_OBJECT_NAME ${namespace}_api_object
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__); \\
622 #define ${NAMESPACE}_MARK_RETURN_API_OBJECT(code) \\
624 ${NAMESPACE}_API_OBJECT_NAME.ret = code; \\
625 ${NAMESPACE}_API_OBJECT_NAME.has_ret = 1; \\
630 cls_tpl
= Template("""
631 struct ${namespace}_api_state_${fn_name} {
632 struct ${namespace}_unique_id id;
637 static inline void enter_${fn_name}(${ctor_type_params})
639 if (${namespace}_ust_tracepoint_enabled(${provider}, enter_${fn_name})) {
640 ${namespace}_id_generator_next_id(<tng_state->id);
642 ${namespace}_ust_do_tracepoint($provider, enter_${fn_name},
643 lttng_state->id.thread_id,
644 lttng_state->id.local_id${ctor_params});
648 static inline void exit_${fn_name}(const struct ${namespace}_api_state_${fn_name} *lttng_state)
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,
657 with
open(output_path
, "w") as output
:
658 output
.write(global_tpl
.substitute(api_file
=api_file
,
660 NAMESPACE
=namespace
.upper(),
662 cls_tpl
.substitute(provider
=PROVIDER
,
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
]),
670 NAMESPACE
=namespace
.upper(),
671 ret_type
=fn
.get_return_type_name())
672 for fn
in function_declarations
676 def generate_tracepoint_implementations(guard
, namespace
, defs
, impls
):
677 tpl
= Template("""/* Auto-generated !*/
680 #define LTTNG_UST_TRACEPOINT_CREATE_PROBES
681 #define LTTNG_UST_TRACEPOINT_DEFINE
684 #endif /* ${guard} */
687 with
open(impls
, "w") as output
:
688 output
.write(tpl
.substitute(guard
=guard
,
691 def generate_tracepoint_states(states_guard
,
699 body_tpl
= Template("""
700 uint64_t ${namespace}_id_generator_thread_counter = 0;
701 _Thread_local struct ${namespace}_id_generator ${namespace}_generator;
704 body_tpl
= Template("""
706 namespace ${namespace} {
707 std::atomic<uint64_t> id_generator::_thread_counter{0};
708 thread_local id_generator generator;
712 tpl
= Template("""/* Auto-generated! */
713 #ifdef ${states_guard}
714 #include "${interface}"
715 #include "${classes}"
721 with
open(states
, "w") as output
:
722 output
.write(tpl
.substitute(states_guard
=states_guard
,
725 body
=body_tpl
.substitute(namespace
=namespace
)))
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"))
736 parser
= argparse
.ArgumentParser(prog
="lttng-ust-autogen-api",
737 description
="Generate LTTng classes and tracepoint definitions")
739 parser
.add_argument("api",
740 help="Header file that has the API")
742 parser
.add_argument("defs",
743 help="Path to tracepoint definitions")
745 parser
.add_argument("interface",
746 help="Path to tracepoints interfaces")
748 parser
.add_argument("classes",
749 help="Path to tracepoint classes")
751 parser
.add_argument("impl",
752 help="Path to tracepoint implementations")
754 parser
.add_argument("states",
755 help="Path to states")
757 parser
.add_argument("--provider",
760 default
="noprovider",
761 help="Tracepoints PROVIDER")
763 parser
.add_argument("--common-prefix",
764 dest
="common_prefix",
767 help="Common PREFIX of API functions (C namespace)")
769 parser
.add_argument("-I",
773 help="Add DIR to list of directories to include")
775 parser
.add_argument("-D",
777 metavar
="DEFINITION",
779 help="Add DEFINITION to list of definitions")
781 parser
.add_argument("--tp-guard",
784 default
="LTTNG_TRACEPOINT_DEF_H",
785 help="Use GUARD as header guard for tracepoint definitions")
787 parser
.add_argument("--classes-guard",
788 dest
="classes_guard",
790 default
="LTTNG_TRACEPOINT_CLASSES_HPP",
791 help="Use GUARD as header guard for classes definitions")
793 parser
.add_argument("--impl-guard",
796 default
="ENABLE_LTTNG_TRACEPOINTS",
797 help="Use GUARD around implementations")
799 parser
.add_argument("--states-guard",
802 default
="ENABLE_LTTNG_TRACEPOINTS",
803 help="Use GUARD around states")
805 parser
.add_argument("--emulated-classes",
806 dest
="emulated_classes",
809 help="Emulate C++ classes")
811 parser
.add_argument("--namespace",
815 help="Generate classes in NAMESPACE")
817 parser
.add_argument("--ignore",
822 help="Ignore FUNCTION")
824 parser
.add_argument("--c-header",
825 dest
="required_c_headers",
829 help="Search for HEADER in C_INCLUDE_PATH and add its directory to search path")
831 parser
.add_argument("--cxx-header",
832 dest
="required_cxx_headers",
836 help="Search for HEADER in CPLUS_INCLUDE_PATH add its directory to search path")
838 args
= parser
.parse_args()
840 PROVIDER
= args
.provider
841 COMMON_PREFIX
= args
.common_prefix
842 IGNORE
= set(args
.ignore
)
844 root
= parse_header(args
.api
, args
.includes
, args
.defines
,
845 args
.required_c_headers
,
846 args
.required_cxx_headers
)
848 function_declarations
= list_functions(root
)
849 enum_declarations
= list_enums(root
)
851 generate_tracepoint_definitions(function_declarations
,
853 args
.api
, args
.defs
, args
.interface
,
856 if args
.emulated_classes
:
857 generate_tracepoint_emulated_classes(function_declarations
,
863 generate_tracepoint_classes(function_declarations
,
869 generate_tracepoint_implementations(args
.impl_guard
,
874 generate_tracepoint_states(args
.states_guard
,
879 args
.emulated_classes
)
882 if __name__
== "__main__":
This page took 0.047853 seconds and 4 git commands to generate.