diff --git a/.github/workflows/presubmit.yml b/.github/workflows/presubmit.yml index 6fdbff83..ebacd576 100644 --- a/.github/workflows/presubmit.yml +++ b/.github/workflows/presubmit.yml @@ -234,7 +234,7 @@ jobs: make -C backends/cuda tracer_cuda.c cuda_library.rb btx_cuda_model.yaml libcuda.la make -C backends/cuda tracer_cudart.c libcudart.la make -C backends/ze tracer_ze.c ze_library.rb btx_ze_model.yaml libze_loader.la - make -C backends/opencl libOpenCL.la + make -C backends/opencl opencl_model.yaml btx_cl_model.yaml libOpenCL.la make -C backends/cxi cxi_sampling.tp make -C backends/itt tracer_itt.c itt_library.rb btx_itt_model.yaml libittnotify.la make -C backends/opencl opencl_profiling.tp diff --git a/.gitignore b/.gitignore index fa728da5..362d83d4 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ m4 aclocal.m4 configure Makefile.in +__pycache__ diff --git a/backends/cuda/cuda_model.rb b/backends/cuda/cuda_model.rb index a02a0b06..b646d360 100644 --- a/backends/cuda/cuda_model.rb +++ b/backends/cuda/cuda_model.rb @@ -81,18 +81,8 @@ def initialize(command, name) end end -$cuda_meta_parameters = YAML.load_file(File.join(SRC_DIR, 'cuda_meta_parameters.yaml')) -$cuda_meta_parameters['meta_parameters'].each do |func, list| - list.each do |type, *args| - register_meta_parameter func, Kernel.const_get(type), *args - end -end -$cuda_exports_meta_parameters = YAML.load_file(File.join(SRC_DIR, 'cuda_exports_meta_parameters.yaml')) -$cuda_exports_meta_parameters['meta_parameters'].each do |func, list| - list.each do |type, *args| - register_meta_parameter func, Kernel.const_get(type), *args - end -end +load_meta_parameters('cuda_meta_parameters.yaml') +load_meta_parameters('cuda_exports_meta_parameters.yaml') $cuda_commands = cuda_funcs_e.collect do |func| Command.new(func) @@ -102,10 +92,6 @@ def initialize(command, name) Command.new(func) end -def upper_snake_case(str) - str.gsub(/([A-Z][A-Z0-9]*)/, '_\1').upcase -end - CUDA_POINTER_NAMES = ($cuda_commands + $cuda_exports_commands).collect do |c| [c, upper_snake_case(c.pointer_name)] diff --git a/backends/cuda/cudart_model.rb b/backends/cuda/cudart_model.rb index 65618351..329f3131 100644 --- a/backends/cuda/cudart_model.rb +++ b/backends/cuda/cudart_model.rb @@ -24,21 +24,12 @@ # Currently ignored by gen_cudart.rb INIT_FUNCTIONS = /.*/ -$cudart_meta_parameters = YAML.load_file(File.join(SRC_DIR, 'cudart_meta_parameters.yaml')) -$cudart_meta_parameters['meta_parameters'].each do |func, list| - list.each do |type, *args| - register_meta_parameter func, Kernel.const_get(type), *args - end -end +load_meta_parameters('cudart_meta_parameters.yaml') $cudart_commands = funcs.collect do |func| Command.new(func) end -def upper_snake_case(str) - str.gsub(/([A-Z][A-Z0-9]*)/, '_\1').upcase -end - CUDART_POINTER_NAMES = $cudart_commands.collect do |c| [c, upper_snake_case(c.pointer_name)] end.to_h diff --git a/backends/cuda/gen_babeltrace_cuda_model.rb b/backends/cuda/gen_babeltrace_cuda_model.rb index c0c1ede7..c4b0c900 100644 --- a/backends/cuda/gen_babeltrace_cuda_model.rb +++ b/backends/cuda/gen_babeltrace_cuda_model.rb @@ -1,20 +1,11 @@ require_relative 'gen_cuda_library_base' require_relative '../../utils/gen_babeltrace_model_helper' -event_classes = - [[:lttng_ust_cuda, $cuda_commands], - [:lttng_ust_cuda_exports, $cuda_exports_commands]].collect do |provider, commands| - commands.collect do |c| - [gen_event_bt_model(provider, c, :start), - gen_event_bt_model(provider, c, :stop)] - end - end.flatten(2) +registry = build_ast_registry('cuda', expect_bitfields: false) -cuda_events = YAML.load_file(File.join(SRC_DIR, 'cuda_events.yaml')) -event_classes += cuda_events.collect do |provider, es| - es['events'].collect do |event| - gen_extra_event_bt_model(provider, event) - end -end.flatten +event_classes = gen_command_events_bt_model(registry, + [[:lttng_ust_cuda, $cuda_commands], + [:lttng_ust_cuda_exports, $cuda_exports_commands]]) +event_classes += gen_extra_events_bt_model(registry, 'cuda_events.yaml') puts YAML.dump(gen_yaml(event_classes, 'cuda')) diff --git a/backends/cuda/gen_cuda.rb b/backends/cuda/gen_cuda.rb index 587507c2..7db03506 100644 --- a/backends/cuda/gen_cuda.rb +++ b/backends/cuda/gen_cuda.rb @@ -14,9 +14,6 @@ static void _init_tracer(void); EOF -# puts < -# EOF ($cuda_commands + $cuda_exports_commands).each do |c| puts "#define #{CUDA_POINTER_NAMES[c]} #{c.pointer_name}" @@ -65,9 +62,6 @@ #{c.decl_hidden_alias}; static void wrap_#{c.name}(void **pfn); EOF - # puts <c_ptr; - # } else { - # closure = (struct cuda_closure *)malloc(sizeof(struct cuda_closure) + #{c.parameters.size} * sizeof(ffi_type *)); - # if (closure != NULL) { - # closure->types = (ffi_type **)((intptr_t)closure + sizeof(struct cuda_closure)); - # closure->closure = ffi_closure_alloc(sizeof(ffi_closure), &(closure->c_ptr)); - # if (closure->closure != NULL) { - # closure->ptr = *pfn; - # EOF - # c.parameters.each_with_index { |a, i| - # if a.type.kind_of?(YAMLCAst::Pointer) - # ffi_type = "ffi_type_pointer" - # elsif FFI_TYPE_MAP["#{a.type}"] - # ffi_type = FFI_TYPE_MAP["#{a.type}"] - # else - # raise "Unsupported type: #{a.type}" - # end - # str << <types[#{i}] = &#{ffi_type}; - # EOF - # } - # if c.type.kind_of?(YAMLCAst::Void) - # ffi_ret_type = "ffi_type_void" - # elsif c.type.kind_of?(YAMLCAst::Pointer) - # ffi_ret_type = "ffi_type_pointer" - # elsif FFI_TYPE_MAP["#{c.type}"] - # ffi_ret_type = FFI_TYPE_MAP["#{c.type}"] - # else - # raise "Unsupported type: #{c.type}" - # end - # str << <cif), FFI_DEFAULT_ABI, #{c.parameters.size}, &#{ffi_ret_type}, closure->types) == FFI_OK) { - # if (ffi_prep_closure_loc(closure->closure, &(closure->cif), (void (*)(ffi_cif*, void *, void **, void *))#{c.ffi_name}, *pfn, closure->c_ptr) == FFI_OK) { - # pthread_mutex_lock(&cuda_closures_mutex); - # HASH_ADD_PTR(cuda_closures, ptr, closure); - # pthread_mutex_unlock(&cuda_closures_mutex); - # *pfn = closure->c_ptr; - # } else { - # ffi_closure_free(closure->closure); - # free(closure); - # } - # } else { - # ffi_closure_free(closure->closure); - # free(closure); - # } - # } else { - # free(closure); - # } - # } - # } - # EOF - # rescue => e - # str = < START, 'stop' => STOP } -MEMBER_SEPARATOR = '__' - -GENERATE_ENUMS_TRACEPOINTS = false - HOST_PROFILE = true WINDOWS = /D3D|DX9/ @@ -28,10 +25,6 @@ LTTNG_AVAILABLE_PARAMS = 25 LTTNG_USABLE_PARAMS = LTTNG_AVAILABLE_PARAMS - 1 -ENUMS = {} -ENUM_PARAM_NAME_MAP = {} -ENUM_TYPES = [] - # map = Hash::new { |h, k| h[k] = [] } doc = Nokogiri::XML(open('cl.xml.patched')) @@ -178,24 +171,6 @@ def enums Require.new(r) end -if GENERATE_ENUMS_TRACEPOINTS - enums = YAML.load_file(File.join(SRC_DIR, 'supported_enums.yaml')) - - enums.each do |e| - vals = $requires.select do |r| - r.comment && r.comment.match(/#{e['name']}(\z| )/) - end.collect do |r| - r.enums - end.reduce(:+).collect do |v| - [v, $constants[v]] - end.to_h - ENUMS[e['name']] = { 'values' => vals, 'trace_name' => e['trace_name'], 'type_name' => e['type_name'] } - ENUM_PARAM_NAME_MAP[e['trace_name']] = e['type_name'] - ENUM_TYPES.push(e['type_name'] || e['name']) - end - ENUM_TYPES.push 'cl_bool' -end - class Declaration < CLXML attr_reader :type, :name @@ -292,7 +267,6 @@ def lttng_in_type t = @type t = CL_TYPE_MAP[@type] if CL_TYPE_MAP[@type] - return ['ctf_enum', 'lttng_ust_opencl', @type, t, @name, @name] if ENUM_TYPES.include? @type case t when *CL_OBJECTS, *CL_EXT_OBJECTS @@ -375,8 +349,6 @@ def lttng_return_type case @return_type when 'cl_int' - return %w[ctf_enum lttng_ust_opencl cl_errcode cl_int errcode_ret_val _retval] if GENERATE_ENUMS_TRACEPOINTS - return %w[ctf_integer cl_int errcode_ret_val _retval] when *CL_OBJECTS @@ -447,24 +419,19 @@ def initialize(command, name, nocheck: false) type = command[name].type.gsub('*', '') type = CL_TYPE_MAP[type] if CL_TYPE_MAP[type] - if ENUM_PARAM_NAME_MAP[name] - @lttng_in_type = ['ctf_enum', 'lttng_ust_opencl', ENUM_PARAM_NAME_MAP[name], type, name + '_val', - nocheck ? "*#{name}" : "#{name} == NULL ? 0 : *#{name}"] + case type + when *CL_OBJECTS, *CL_EXT_OBJECTS, 'void' + @lttng_in_type = ['ctf_integer_hex', 'uintptr_t', name + '_val', + nocheck ? "(uintptr_t)(*#{name})" : "(uintptr_t)(#{name} == NULL ? 0 : *#{name})"] + when *CL_INT_SCALARS + @lttng_in_type = ['ctf_integer', type, name + '_val', nocheck ? "*#{name}" : "#{name} == NULL ? 0 : *#{name}"] + when *CL_FLOAT_SCALARS + @lttng_in_type = ['ctf_float', type, name + '_val', nocheck ? "*#{name}" : "#{name} == NULL ? 0 : *#{name}"] + when *CL_STRUCTS + @lttng_in_type = ['ctf_sequence_text', 'uint8_t', name + '_val', "(uint8_t *)#{name}", 'size_t', + "#{name} == NULL ? 0 : sizeof(#{type})"] else - case type - when *CL_OBJECTS, *CL_EXT_OBJECTS, 'void' - @lttng_in_type = ['ctf_integer_hex', 'uintptr_t', name + '_val', - nocheck ? "(uintptr_t)(*#{name})" : "(uintptr_t)(#{name} == NULL ? 0 : *#{name})"] - when *CL_INT_SCALARS - @lttng_in_type = ['ctf_integer', type, name + '_val', nocheck ? "*#{name}" : "#{name} == NULL ? 0 : *#{name}"] - when *CL_FLOAT_SCALARS - @lttng_in_type = ['ctf_float', type, name + '_val', nocheck ? "*#{name}" : "#{name} == NULL ? 0 : *#{name}"] - when *CL_STRUCTS - @lttng_in_type = ['ctf_sequence_text', 'uint8_t', name + '_val', "(uint8_t *)#{name}", 'size_t', - "#{name} == NULL ? 0 : sizeof(#{type})"] - else - raise "Unknown Type: #{type.inspect}!" - end + raise "Unknown Type: #{type.inspect}!" end end end @@ -476,21 +443,16 @@ def initialize(command, name, nocheck: false) type = command[name].type.gsub('*', '') type = CL_TYPE_MAP[type] if CL_TYPE_MAP[type] - if ENUM_PARAM_NAME_MAP[name] - @lttng_out_type = ['ctf_enum', 'lttng_ust_opencl', ENUM_PARAM_NAME_MAP[name], type, name + '_val', - nocheck ? "*#{name}" : "#{name} == NULL ? 0 : *#{name}"] + case type + when *CL_OBJECTS, *CL_EXT_OBJECTS, 'void' + @lttng_out_type = ['ctf_integer_hex', 'uintptr_t', name + '_val', + nocheck ? "(uintptr_t)(*#{name})" : "(uintptr_t)(#{name} == NULL ? 0 : *#{name})"] + when *CL_INT_SCALARS + @lttng_out_type = ['ctf_integer', type, name + '_val', nocheck ? "*#{name}" : "#{name} == NULL ? 0 : *#{name}"] + when *CL_FLOAT_SCALARS + @lttng_out_type = ['ctf_float', type, name + '_val', nocheck ? "*#{name}" : "#{name} == NULL ? 0 : *#{name}"] else - case type - when *CL_OBJECTS, *CL_EXT_OBJECTS, 'void' - @lttng_out_type = ['ctf_integer_hex', 'uintptr_t', name + '_val', - nocheck ? "(uintptr_t)(*#{name})" : "(uintptr_t)(#{name} == NULL ? 0 : *#{name})"] - when *CL_INT_SCALARS - @lttng_out_type = ['ctf_integer', type, name + '_val', nocheck ? "*#{name}" : "#{name} == NULL ? 0 : *#{name}"] - when *CL_FLOAT_SCALARS - @lttng_out_type = ['ctf_float', type, name + '_val', nocheck ? "*#{name}" : "#{name} == NULL ? 0 : *#{name}"] - else - raise "Unknown Type: #{type.inspect}!" - end + raise "Unknown Type: #{type.inspect}!" end end end @@ -756,10 +718,6 @@ def void_parameters? eval "$#{c.prototype.name} = c" end -def upper_snake_case(str) - str.gsub(/([A-Z][A-Z0-9]*)/, '_\1').upcase -end - OPENCL_POINTER_NAMES = ($opencl_commands.collect do |c| [c, upper_snake_case(c.prototype.pointer_name)] end + $opencl_extension_commands.collect do |c| diff --git a/backends/opencl/opencl_tracepoints.rb b/backends/opencl/opencl_tracepoints.rb index d657b09c..85e4d3b0 100644 --- a/backends/opencl/opencl_tracepoints.rb +++ b/backends/opencl/opencl_tracepoints.rb @@ -1,4 +1,11 @@ -class LTTng +require_relative '../../utils/LTTng' + +# Opencl's model is YAML-intermediate-driven (see gen_opencl_model.rb), so its +# tracepoint fields travel as raw [macro, *args] tuples rather than as +# utils/LTTng.rb's TracepointField objects. LTTngFieldTuple provides the same +# kind of accessors (name/array?/string?/enum?/expression) over that tuple +# representation. +module LTTngFieldTuple def self.name(*args) case args[0] when 'ctf_string' @@ -34,64 +41,16 @@ def self.expression(*args) end end -def print_enum(namespace, en) - puts <<~EOF - TRACEPOINT_ENUM( - #{namespace}, - #{en['name']}, - TP_ENUM_VALUES( - EOF - print ' ' - puts en['values'].collect { |(f, sy, *args)| - "#{f}(#{sy.to_s.inspect}, #{args.join(', ')})" - }.join("\n ") - puts <<~EOF - ) - ) - - EOF -end - -def print_tracepoint(namespace, tp, dir = nil) - puts <<~EOF - TRACEPOINT_EVENT( - #{namespace}, - #{tp['name']}#{"_#{SUFFIXES[dir]}" if dir}, - TP_ARGS( - EOF - print ' ' - args = tp['args'] - if args.empty? - puts 'void' - else - puts args.collect { |a| a.join(', ') }.join(",\n ") - end - puts < 0 ? #{be_class}.new(FFI::MemoryPointer.from_string(defi["#{name}"])) : nil}"} - end - else - %(s << "#{name}: \#{defi["#{name}"].inspect}") - end - when 'array_dynamic', 'array_static' - case fc[:element_field_class][:type] - when 'integer_signed', 'integer_unsigned' - if fc[:element_field_class][:preferred_display_base] == 16 - %{s << "#{name}: [ \#{defi["#{name}"].collect { |v| "0x%016x" % v }.join(", ")} ]"} - else - default_command - end - else - raise "Unsupported field type for array: #{fc[:element_field_class][:type]}" - end - else - raise "Unsupported field type: #{fc[:type]}" - end + case fc[:type] + when 'integer_signed', 'integer_unsigned' + if be_class + # Remaining read of the $all_bitfield_names global. This is + # in the library/bindings path (not the babeltrace-model + # path that was de-globalized): callers require their + # gen__library_base first, which populates the global + # before this call, so there is no load-order hazard. The + # global cannot be dropped while gen_library_base.rb still + # reads $all_struct_names, so it is left explicit here. + if $all_bitfield_names.include?(fc[:cast_type]) + %{s << "#{name}: [ \#{#{be_class}.from_native(defi["#{name}"], nil).join(", ")} ]"} + else + %{s << "#{name}: \#{#{be_class}.from_native(defi["#{name}"], nil)}"} + end + elsif fc[:preferred_display_base] == 16 + %(s << "#{name}: \#{"0x%016x" % defi["#{name}"]}") + else + default_command + end + when 'double', 'single' + default_command + when 'string' + if be_class + if name.end_with?('_vals') + %{s << "#{name}: [ \#{p = FFI::MemoryPointer.from_string(defi["#{name}"]); sz = #{be_class}.size; n = p.size / sz; (0...n).collect { |i| #{be_class}.new(p + i*sz).to_s}.join(", ")} ]"} + else + %{s << "#{name}: \#{defi["#{name}"].size > 0 ? #{be_class}.new(FFI::MemoryPointer.from_string(defi["#{name}"])) : nil}"} + end + else + %(s << "#{name}: \#{defi["#{name}"].inspect}") + end + when 'array_dynamic', 'array_static' + case fc[:element_field_class][:type] + when 'integer_signed', 'integer_unsigned' + if fc[:element_field_class][:preferred_display_base] == 16 + %{s << "#{name}: [ \#{defi["#{name}"].collect { |v| "0x%016x" % v }.join(", ")} ]"} + else + default_command + end + else + raise "Unsupported field type for array: #{fc[:element_field_class][:type]}" + end + else + raise "Unsupported field type: #{fc[:type]}" + end end.join("\n s << ', '\n ") # Now just print the full strings to pretty printf the struct diff --git a/utils/gen_babeltrace_model_helper.rb b/utils/gen_babeltrace_model_helper.rb index e08e291f..e6f21ef9 100644 --- a/utils/gen_babeltrace_model_helper.rb +++ b/utils/gen_babeltrace_model_helper.rb @@ -1,38 +1,24 @@ -# Include global variable INT_SIGN_MAP, ScalarMetaParameter, etc +# ScalarMetaParameter, YAMLCAst node classes, etc. require_relative 'yaml_ast' - -$integer_sizes = INT_SIZE_MAP.transform_values { |v| v * 8 } -$integer_signed = INT_SIGN_MAP - -$all_enums.each do |t| - $integer_sizes["enum #{t.name}"] = 32 -end - -$all_enums.each do |t| - $integer_signed["enum #{t.name}"] = true -end - -def integer_size(t) - return 64 if t.match(/\*/) - return 64 if t.match(/\[.*\]/) - - r = $integer_sizes[t] - raise "unknown integer type #{t}" if r.nil? - - r -end - -def integer_signed?(t) - return false if t.match(/\*/) - return false if t.match(/\[.*\]/) - - r = $integer_signed[t] - raise "unknown integer type #{t}" if r.nil? - - r +require_relative 'type_registry' + +# Build the babeltrace TypeRegistry for an AST backend from its load-time +# globals and assert the per-backend bitfield invariant: ze/omp have bitfield +# types, cuda/hip/mpi/itt have none (see the babeltrace-model bitfield note). +def build_ast_registry(backend, expect_bitfields:) + registry = TypeRegistry.from_ast( + all_types: $all_types, all_enums: $all_enums, + enum_names: $all_enum_names, bitfield_names: $all_bitfield_names, struct_names: $all_struct_names, + class_namer: method(:to_scoped_class_name) + ) + if expect_bitfields + raise "#{backend}: expected bitfield types" if registry.bitfield_names.empty? + else + raise "#{backend}: expected no bitfield types" unless registry.bitfield_names.empty? + end + registry end -# End of global variable use def meta_parameter_types_name(m, dir = nil) lttng = if dir == :start m.lttng_in_type @@ -86,13 +72,12 @@ def get_extra_fields_types_name(event) end.flatten(1) end -$types_by_name = $all_types.map { |ty| [ty.name, ty] }.to_h - -def gen_bt_field_model(lttng_name, type, name, lttng) +def gen_bt_field_model(registry, lttng_name, type, name, lttng) + types_by_name = registry.types_by_name member = { name: name } field = { cast_type: type.gsub(/\[.*\]/, '*') } - if $types_by_name[type].is_a?(YAMLCAst::Declaration) && $types_by_name[type].type.is_a?(YAMLCAst::Function) + if types_by_name[type].is_a?(YAMLCAst::Declaration) && types_by_name[type].type.is_a?(YAMLCAst::Function) field[:cast_type] = "#{type} *" end @@ -101,18 +86,18 @@ def gen_bt_field_model(lttng_name, type, name, lttng) when 'ctf_float' field[:type] = type == 'float' ? 'single' : type when 'ctf_integer', 'ctf_integer_hex' - field[:type] = integer_signed?(type) ? 'integer_signed' : 'integer_unsigned' - field[:field_value_range] = integer_size(type) + field[:type] = registry.integer_signed?(type) ? 'integer_signed' : 'integer_unsigned' + field[:field_value_range] = registry.integer_size(type) field[:preferred_display_base] = 16 if lttng_name.end_with?('_hex') - if $all_enum_names.include?(type) || $all_bitfield_names.include?(type) - member[:metadata] = { be_class: to_scoped_class_name(type) } + if registry.enum_names.include?(type) || registry.bitfield_names.include?(type) + member[:metadata] = { be_class: registry.class_namer.call(type) } end when 'ctf_sequence', 'ctf_sequence_hex' array_type = lttng.type.to_s field[:type] = 'array_dynamic' field[:element_field_class] = - { type: integer_signed?(array_type) ? 'integer_signed' : 'integer_unsigned', - field_value_range: integer_size(array_type) } + { type: registry.integer_signed?(array_type) ? 'integer_signed' : 'integer_unsigned', + field_value_range: registry.integer_size(array_type) } field[:element_field_class][:preferred_display_base] = 16 if lttng_name.end_with?('_hex') @@ -124,8 +109,8 @@ def gen_bt_field_model(lttng_name, type, name, lttng) array_type = lttng.type.to_s field[:type] = 'array_static' field[:element_field_class] = - { type: integer_signed?(array_type) ? 'integer_signed' : 'integer_unsigned', - field_value_range: integer_size(array_type) } + { type: registry.integer_signed?(array_type) ? 'integer_signed' : 'integer_unsigned', + field_value_range: registry.integer_size(array_type) } field[:element_field_class][:preferred_display_base] = 16 if lttng_name.end_with?('_hex') field[:length] = lttng.length when 'ctf_string' @@ -133,13 +118,11 @@ def gen_bt_field_model(lttng_name, type, name, lttng) when 'ctf_sequence_text', 'ctf_array_text' field[:type] = 'string' t = type.sub(' *', '') - while $types_by_name.include?(t) && $types_by_name[t].type.is_a?(YAMLCAst::CustomType) - t = $types_by_name[t].type.name - end - member[:metadata] = { be_class: to_scoped_class_name(t) } if $all_struct_names.include?(t) + t = types_by_name[t].type.name while types_by_name.include?(t) && types_by_name[t].type.is_a?(YAMLCAst::CustomType) + member[:metadata] = { be_class: registry.class_namer.call(t) } if registry.struct_names.include?(t) # Too complicated, not sure why `all_struct_names` is not enough - if !field[:cast_type].end_with?('*') && ($all_struct_names.include?(t) || $types_by_name[t]&.type.is_a?(YAMLCAst::Union) || type.start_with?('struct')) + if !field[:cast_type].end_with?('*') && (registry.struct_names.include?(t) || types_by_name[t]&.type.is_a?(YAMLCAst::Union) || type.start_with?('struct')) field[:cast_type_is_struct] = true end else @@ -171,21 +154,21 @@ def get_fields_types_name(c, dir) end.flatten(1) end -def gen_event_fields_bt_model(c, dir) +def gen_event_fields_bt_model(registry, c, dir) types_name = get_fields_types_name(c, dir) types_name.collect do |lttng_name, type, name, lttng| - gen_bt_field_model(lttng_name, type.sub(/\Aconst /, ''), name, lttng) + gen_bt_field_model(registry, lttng_name, type.sub(/\Aconst /, ''), name, lttng) end end -def gen_extra_event_fields_bt_model(event) +def gen_extra_event_fields_bt_model(registry, event) types_name = get_extra_fields_types_name(event) types_name.collect do |lttng_name, type, name, lttng| - gen_bt_field_model(lttng_name, type.sub(/\Aconst /, ''), name, lttng) + gen_bt_field_model(registry, lttng_name, type.sub(/\Aconst /, ''), name, lttng) end end -def gen_event_bt_model(provider, c, dir = nil) +def gen_event_bt_model(registry, provider, c, dir = nil) d = if dir { name: "#{provider}:#{c.name}_#{SUFFIXES[dir]}" } # OMP backend @@ -193,7 +176,7 @@ def gen_event_bt_model(provider, c, dir = nil) { name: "#{provider}:#{c.name.gsub(/_func\z/, '')}" } end - m = gen_event_fields_bt_model(c, dir) + m = gen_event_fields_bt_model(registry, c, dir) unless m.empty? d[:payload_field_class] = @@ -205,9 +188,9 @@ def gen_event_bt_model(provider, c, dir = nil) d end -def gen_extra_event_bt_model(provider, event) +def gen_extra_event_bt_model(registry, provider, event) d = { name: "#{provider}:#{event['name']}" } - m = gen_extra_event_fields_bt_model(event) + m = gen_extra_event_fields_bt_model(registry, event) unless m.empty? d[:payload_field_class] = @@ -219,6 +202,31 @@ def gen_extra_event_bt_model(provider, event) d end +# Build the command event classes for a backend: one [start, stop] pair per +# command (phased), or a single event per command for itt/omp (phased: false). +# provider_commands is a list of [provider_symbol, commands] pairs. +def gen_command_events_bt_model(registry, provider_commands, phased: true) + provider_commands.collect do |provider, commands| + commands.collect do |c| + if phased + [gen_event_bt_model(registry, provider, c, :start), + gen_event_bt_model(registry, provider, c, :stop)] + else + [gen_event_bt_model(registry, provider, c)] + end + end + end.flatten(2) +end + +# Build the "extra" event classes declared in a backend's _events.yaml. +def gen_extra_events_bt_model(registry, filename) + YAML.load_file(File.join(SRC_DIR, filename)).collect do |provider, es| + es['events'].collect do |event| + gen_extra_event_bt_model(registry, provider, event) + end + end.flatten +end + def gen_yaml(event_classes, backend) { environment: { entries: [ diff --git a/utils/gen_library_base.rb b/utils/gen_library_base.rb index 7c5b26b6..802b08d6 100644 --- a/utils/gen_library_base.rb +++ b/utils/gen_library_base.rb @@ -1,5 +1,60 @@ require_relative 'yaml_ast' +# Classify a backend's typedef'd types into enum / bitfield / struct / union +# name lists (in typedef order). A typedef'd enum whose underlying enum name +# ends in `flag_t` is treated as a bitfield (OR-able flags); every other enum +# is a plain enum. Each `_flag_t` bitfield additionally aliases the `_flags_t` +# name the headers use for the OR'd value. Backends with no `flag_t` enums get +# an empty bitfield list (the `flag_t` test and `_flags_t` derivation are then +# no-ops), so the same rule serves all backends. +def classify_ast_types(all_types, all_enums) + enum_names = [] + bitfield_names = [] + struct_names = [] + union_names = [] + all_types.each do |t| + case t.type + when YAMLCAst::Enum + enum = all_enums.find { |e| t.type.name == e.name } + if enum&.name&.end_with?('flag_t') + bitfield_names.push t.name + else + enum_names.push t.name + end + when YAMLCAst::Struct + struct_names.push t.name + when YAMLCAst::Union + union_names.push t.name + end + end + bitfield_names += bitfield_names.select { |n| n.end_with?('_flag_t') } + .map { |n| n.gsub('_flag_t', '_flags_t') } + [enum_names, bitfield_names, struct_names, union_names] +end + +# Collect the "object" type names: typedefs of pointer-to-struct, plus any +# CustomType aliasing a known OBJECT_TYPES name. `extra` names are inserted +# after the pointer-to-struct seed and before the alias pass (hip seeds one). +def find_objects(all_types, extra: []) + objects = all_types.filter_map do |t| + t.name if t.type.is_a?(YAMLCAst::Pointer) && t.type.type.is_a?(YAMLCAst::Struct) + end + objects.concat(extra) + all_types.each do |t| + objects.push t.name if t.type.is_a?(YAMLCAst::CustomType) && OBJECT_TYPES.include?(t.type.name) + end + objects +end + +# Map each typedef that aliases an integer type to that underlying type name. +def find_int_scalars(all_types) + int_scalars = {} + all_types.each do |t| + int_scalars[t.name] = t.type.name if t.type.is_a?(YAMLCAst::CustomType) && INT_TYPES.include?(t.type.name) + end + int_scalars +end + def has_typedef?(name) $all_types.any? { |t| t.type.respond_to?(:name) && t.type.name == name } end @@ -126,6 +181,47 @@ def print_object(object) EOF end +# ze has its own Handle/UUID: :data/:id fields, and a UUID printed back to front. +def print_handle_uuid_modules + puts <<'EOF' + module Handle + def to_s + s = '{ reserved: "' + s << self[:reserved].to_a.collect { |v| "\\x%02x" % ((v + 256)%256) }.join + s << '" }' + end + end + + module UUID + def to_s + a = self[:bytes].to_a.collect { |v| v < 0 ? 0x100 + v : v } + s = "{ id: " + s << "%02x" % a[0] + s << "%02x" % a[1] + s << "%02x" % a[2] + s << "%02x" % a[3] + s << "-" + s << "%02x" % a[4] + s << "%02x" % a[5] + s << "-" + s << "%02x" % a[6] + s << "%02x" % a[7] + s << "-" + s << "%02x" % a[8] + s << "%02x" % a[9] + s << "-" + s << "%02x" % a[10] + s << "%02x" % a[11] + s << "%02x" % a[12] + s << "%02x" % a[13] + s << "%02x" % a[14] + s << "%02x" % a[15] + s << " }" + end + end +EOF +end + def print_ffi_module(namespace, struct: true, union: true, enum: true, bitmask: true, inline_array: true, enclosing_module: true) puts <<~EOF @@ -313,6 +409,12 @@ def print_function_pointer_type(name, func) EOF end +# Structs whose class name mentions UUID prepend the UUID module for its to_s. +def print_struct_prepending_uuid(namespace, name, struct) + prepends = to_class_name(name).match('UUID') ? ['UUID'] : [] + print_struct_with_namespace(namespace, name, struct, prepends: prepends) +end + def print_struct_with_namespace(namespace, name, struct, prepends: [], initializer: nil, close: true) members = struct.to_ffi print_lambda = lambda { |m| diff --git a/utils/test_compare_generated_file.py b/utils/test_compare_generated_file.py index 02b31f67..4ffd26e8 100644 --- a/utils/test_compare_generated_file.py +++ b/utils/test_compare_generated_file.py @@ -69,6 +69,15 @@ ] filenames += [ + "backends/opencl/tracer_opencl.c", + "backends/opencl/btx_cl_model.yaml", + "backends/opencl/opencl_model.yaml", + "backends/opencl/opencl_arguments.tp", + "backends/opencl/opencl_build.tp", + "backends/opencl/opencl_devices.tp", + "backends/opencl/opencl_dump.tp", + "backends/opencl/opencl_source.tp", + "backends/opencl/opencl_tracepoints.tp", "backends/opencl/opencl_profiling.tp", ] diff --git a/utils/type_registry.rb b/utils/type_registry.rb new file mode 100644 index 00000000..122ec1ec --- /dev/null +++ b/utils/type_registry.rb @@ -0,0 +1,56 @@ +# Immutable value object holding the type facts the babeltrace-model generator +# needs to classify a field. Threaded explicitly instead of read from globals. +require_relative 'yaml_ast' + +class TypeRegistry + attr_reader :types_by_name, :enum_names, :bitfield_names, :struct_names, + :integer_sizes, :integer_signed, :class_namer + + # Build a registry from an AST backend's parsed model. The enum/bitfield/ + # struct name lists are classified by the backend (their rules differ); this + # only derives the integer-size/-sign lookups (scalar widths plus a 32-bit + # signed entry per enum) and the by-name type index. + def self.from_ast(all_types:, all_enums:, enum_names:, bitfield_names:, struct_names:, class_namer:) + integer_sizes = INT_SIZE_MAP.transform_values { |v| v * 8 } + integer_signed = INT_SIGN_MAP.dup + all_enums.each do |e| + integer_sizes["enum #{e.name}"] = 32 + integer_signed["enum #{e.name}"] = true + end + + new(types_by_name: all_types.map { |t| [t.name, t] }.to_h, + enum_names: enum_names, bitfield_names: bitfield_names, struct_names: struct_names, + integer_sizes: integer_sizes, integer_signed: integer_signed, class_namer: class_namer) + end + + def initialize(types_by_name:, enum_names:, bitfield_names:, struct_names:, + integer_sizes:, integer_signed:, class_namer:) + @types_by_name = types_by_name + @enum_names = enum_names + @bitfield_names = bitfield_names + @struct_names = struct_names + @integer_sizes = integer_sizes + @integer_signed = integer_signed + @class_namer = class_namer + end + + def integer_size(t) + return 64 if t.match(/\*/) + return 64 if t.match(/\[.*\]/) + + r = integer_sizes[t] + raise "unknown integer type #{t}" if r.nil? + + r + end + + def integer_signed?(t) + return false if t.match(/\*/) + return false if t.match(/\[.*\]/) + + r = integer_signed[t] + raise "unknown integer type #{t}" if r.nil? + + r + end +end