From 809649797597b3faa73e59c6d46d2a3015fc1e39 Mon Sep 17 00:00:00 2001 From: Thomas Applencourt Date: Wed, 13 Jul 2022 18:09:25 -0500 Subject: [PATCH] Move from json to protobuf (#41) * Move from json to protobuf * Add tooling for protobuf trace generation. * fix build system. * Device as process * Fix ze and header * Implement bug workarround * Fix version Co-authored-by: Brice Videau --- .github/workflows/presubmit.yml | 10 +- configure.ac | 5 + utils/xprof_utils.hpp | 13 + xprof/Makefile.am | 27 +- xprof/perfetto_prunned.proto | 75 +++++ xprof/timeline.cpp | 523 +++++++++++++++++++------------- xprof/timeline.hpp | 16 +- ze/tracer_ze_helpers.include.c | 4 +- 8 files changed, 444 insertions(+), 229 deletions(-) create mode 100644 xprof/perfetto_prunned.proto diff --git a/.github/workflows/presubmit.yml b/.github/workflows/presubmit.yml index b2cfcf52..bc3864d5 100644 --- a/.github/workflows/presubmit.yml +++ b/.github/workflows/presubmit.yml @@ -28,7 +28,7 @@ jobs: with: path: ~/babeltrace2/2.0.4 key: ${{ runner.os }}-build-${{ env.cache-name }} - - run: sudo apt update; sudo apt install -y gcc g++ lttng-tools liblttng-ust-dev ruby ruby-dev elfutils libelf-dev libdw-dev + - run: sudo apt update; sudo apt install -y gcc g++ lttng-tools liblttng-ust-dev ruby ruby-dev elfutils libelf-dev libdw-dev libprotobuf-dev protobuf-compiler if: steps.babeltrace2.outputs.cache-hit != 'true' - run: gem install --user-install cast-to-yaml nokogiri babeltrace2 opencl_ruby_ffi if: steps.babeltrace2.outputs.cache-hit != 'true' @@ -69,7 +69,7 @@ jobs: with: path: ~/babeltrace2/2.0.4 key: ${{ runner.os }}-build-${{ env.cache-name }} - - run: sudo apt update; sudo apt install -y gcc g++ lttng-tools liblttng-ust-dev ruby ruby-dev elfutils libelf-dev libdw-dev + - run: sudo apt update; sudo apt install -y gcc g++ lttng-tools liblttng-ust-dev ruby ruby-dev elfutils libelf-dev libdw-dev libprotobuf-dev protobuf-compiler - run: gem install --user-install cast-to-yaml nokogiri babeltrace2 opencl_ruby_ffi - name: Load Babeltrace2 run: | @@ -101,7 +101,7 @@ jobs: with: path: ~/babeltrace2/2.0.4 key: ${{ runner.os }}-build-${{ env.cache-name }} - - run: sudo apt update; sudo apt install -y gcc g++ lttng-tools liblttng-ust-dev ruby ruby-dev elfutils libelf-dev libdw-dev + - run: sudo apt update; sudo apt install -y gcc g++ lttng-tools liblttng-ust-dev ruby ruby-dev elfutils libelf-dev libdw-dev libprotobuf-dev protobuf-compiler - run: gem install --user-install cast-to-yaml nokogiri babeltrace2 opencl_ruby_ffi - name: Load Babeltrace2 run: | @@ -129,7 +129,7 @@ jobs: with: path: ~/babeltrace2/2.0.4 key: ${{ runner.os }}-build-${{ env.cache-name }} - - run: sudo apt update; sudo apt install -y gcc g++ lttng-tools liblttng-ust-dev ruby ruby-dev elfutils libelf-dev libdw-dev + - run: sudo apt update; sudo apt install -y gcc g++ lttng-tools liblttng-ust-dev ruby ruby-dev elfutils libelf-dev libdw-dev libprotobuf-dev protobuf-compiler - run: gem install --user-install cast-to-yaml nokogiri babeltrace2 opencl_ruby_ffi - name: Load Babeltrace2 run: | @@ -159,7 +159,7 @@ jobs: with: path: ~/babeltrace2/2.0.4 key: ${{ runner.os }}-build-${{ env.cache-name }} - - run: sudo apt update; sudo apt install -y gcc g++ lttng-tools liblttng-ust-dev ruby ruby-dev elfutils libelf-dev libdw-dev + - run: sudo apt update; sudo apt install -y gcc g++ lttng-tools liblttng-ust-dev ruby ruby-dev elfutils libelf-dev libdw-dev libprotobuf-dev protobuf-compiler - run: gem install --user-install cast-to-yaml nokogiri babeltrace2 opencl_ruby_ffi - name: Load Babeltrace2 run: | diff --git a/configure.ac b/configure.ac index 4a48a82b..dda55eba 100644 --- a/configure.ac +++ b/configure.ac @@ -42,6 +42,10 @@ if test -z "$PATCH"; then AC_CHECK_PROG(PATCH,[patch],[patch],[no]) test "$PATCH" == "no" && AC_MSG_ERROR([Required program 'patch' not found.]) fi +if test -z "$PROTOC"; then + AC_CHECK_PROG(PROTOC,[protoc],[protoc],[no]) + test "$PROTOC" == "no" && AC_MSG_ERROR([Required program 'protoc' not found.]) +fi # Checks for libraries. AC_CHECK_LIB([dl], [dlopen]) @@ -56,6 +60,7 @@ AM_CONDITIONAL([STRICT], [test "x$enable_strict" = xyes]) PKG_CHECK_MODULES([LIBFFI], [libffi >= 3.2]) PKG_CHECK_MODULES([BABELTRACE2], [babeltrace2 >= 2.0]) PKG_CHECK_MODULES([LTTNG_UST], [lttng-ust >= 2.10]) +PKG_CHECK_MODULES([PROTOBUF], [protobuf >= 3.0]) AX_RUBY_EXTENSION([cast-to-yaml], [yes]) AX_RUBY_EXTENSION([nokogiri], [yes]) diff --git a/utils/xprof_utils.hpp b/utils/xprof_utils.hpp index 8bc34a49..c68a1138 100644 --- a/utils/xprof_utils.hpp +++ b/utils/xprof_utils.hpp @@ -99,6 +99,19 @@ namespace std{ } }; + + template + struct hash> + { + size_t + operator()(std::pair const& tt) const + { + size_t seed = 0; + HashValueImpl >::apply(seed, tt); + return seed; + } + + }; } const char* borrow_hostname(const bt_event*); diff --git a/xprof/Makefile.am b/xprof/Makefile.am index 1baa96cc..8d88e27b 100644 --- a/xprof/Makefile.am +++ b/xprof/Makefile.am @@ -17,15 +17,22 @@ nodist_noinst_HEADERS = xprof.sh.erb xprof_utils.hpp: $(top_srcdir)/utils/xprof_utils.hpp cp $< $@ +%.pb.h %.pb.cc: %.proto + $(PROTOC) -I=$(srcdir) --cpp_out=$(builddir) $< + BUILT_SOURCES = \ - xprof_utils.hpp + xprof_utils.hpp \ + perfetto_prunned.pb.h \ + perfetto_prunned.pb.cc # Library to be packaged lib_LTLIBRARIES = libXProf.la # File to compiled nodist_libXProf_la_SOURCES = \ - xprof_utils.hpp + xprof_utils.hpp \ + perfetto_prunned.pb.h \ + perfetto_prunned.pb.cc # File to compiled libXProf_la_SOURCES = \ @@ -40,13 +47,16 @@ libXProf_la_SOURCES = \ my_demangle.c \ my_demangle.h \ $(top_srcdir)/utils/include/json.hpp + # Right now we harcode it. We should use pkgtools at some point https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json.hpp +# -I/home/tapplencourt/tmp/proto/protobuf-3.20.1/ici/include/ -L/home/tapplencourt/tmp/proto/protobuf-3.20.1/ici/lib/ -lprotobuf + # Compiler flags -libXProf_la_CPPFLAGS = -I$(top_srcdir)/utils/include -I$(srcdir)/include -I./ +libXProf_la_CPPFLAGS = -I$(top_srcdir)/utils/include -I$(srcdir)/include -I./ -I/home/tapplencourt/tmp/proto/protobuf-3.20.1/ici/include/ libXProf_la_CFLAGS = -Wall -Wextra -Wno-unused-parameter $(WERROR) $(BABELTRACE2_CFLAGS) -libXProf_la_CXXFLAGS = -std=c++17 -Wall -Wextra -Wno-unused-parameter $(WERROR) $(BABELTRACE2_CFLAGS) -libXProf_la_LDFLAGS = $(BABELTRACE2_LIBS) -avoid-version -module +libXProf_la_CXXFLAGS = -std=c++17 -Wall -Wextra -Wno-unused-parameter $(WERROR) $(BABELTRACE2_CFLAGS) $(PROTOBUF_CFLAGS) +libXProf_la_LDFLAGS = $(BABELTRACE2_LIBS) $(PROTOBUF_LIBS) -avoid-version -module # Cannot use check_LTLIBRARIES because we need the shared version of those # Thanks Vincent Danjean @@ -83,9 +93,12 @@ EXTRA_DIST = \ interval.h.erb \ interval_model.yaml \ $(TRACE_FILES) \ - tests/tally.dust.erb + tests/tally.dust.erb \ + perfetto_prunned.proto CLEANFILES = \ iprof \ xprof_utils.hpp \ - $(TALLY_DUST_FILES) + $(TALLY_DUST_FILES) \ + perfetto_prunned.pb.h \ + perfetto_prunned.pb.cc diff --git a/xprof/perfetto_prunned.proto b/xprof/perfetto_prunned.proto new file mode 100644 index 00000000..3fdc0023 --- /dev/null +++ b/xprof/perfetto_prunned.proto @@ -0,0 +1,75 @@ +syntax = "proto2"; +// "There Is a Light That Never Goes Out" +// https://android.googlesource.com/platform/external/perfetto/+/refs/heads/master/protos/perfetto/trace/ +package perfetto_pruned; + +enum BuiltinClock { + BUILTIN_CLOCK_UNKNOWN = 0; + BUILTIN_CLOCK_REALTIME = 1; + BUILTIN_CLOCK_REALTIME_COARSE = 2; + BUILTIN_CLOCK_MONOTONIC = 3; + BUILTIN_CLOCK_MONOTONIC_COARSE = 4; + BUILTIN_CLOCK_MONOTONIC_RAW = 5; + BUILTIN_CLOCK_BOOTTIME = 6; + BUILTIN_CLOCK_MAX_ID = 63; + reserved 7, 8, 9; +} + +message ProcessDescriptor { + optional int32 pid = 1; + optional string process_name = 6; +} + +message TracePacketDefaults { + optional uint32 timestamp_clock_id = 58; +} + +message ThreadDescriptor { + optional int32 pid = 1; + optional int32 tid = 2; +} + +message TrackDescriptor { + optional uint64 uuid = 1; + optional uint64 parent_uuid = 5; + optional string name = 2; + optional ProcessDescriptor process = 3; + optional ThreadDescriptor thread = 4; +} + +message TrackEvent { + + enum Type { + TYPE_SLICE_BEGIN = 1; + TYPE_SLICE_END = 2; + } + + optional Type type = 9; + optional uint64 track_uuid = 11; + + oneof name_field { + string name = 23; + } + +} + +message TracePacket { + optional uint64 timestamp = 8; + + oneof data { + TrackEvent track_event = 11; + TrackDescriptor track_descriptor = 60; + } + + oneof optional_trusted_packet_sequence_id { + uint32 trusted_packet_sequence_id = 10; + } + + required TracePacketDefaults trace_packet_defaults = 59; + optional bool previous_packet_dropped = 42; +} + +message Trace { + repeated TracePacket packet = 1; +} + diff --git a/xprof/timeline.cpp b/xprof/timeline.cpp index 7f5aaa79..6c1ceafc 100644 --- a/xprof/timeline.cpp +++ b/xprof/timeline.cpp @@ -1,216 +1,321 @@ #include "timeline.h" #include "timeline.hpp" #include "xprof_utils.hpp" // typedef -#include "json.hpp" -#include // set precision +#include +#include // set precision #include // stdcout +#include -bt_component_class_sink_consume_method_status timeline_dispatch_consume( - bt_self_component_sink *self_component_sink) -{ - bt_component_class_sink_consume_method_status status = - BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK; - - // Internal datatrastruct to convern hostname process to Google trace format process - // https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview - - /* Retrieve our private data from the component's user data */ - struct timeline_dispatch *dispatch = (timeline_dispatch*) bt_self_component_get_data( - bt_self_component_sink_as_self_component(self_component_sink)); - - /* Consume a batch of messages from the upstream message iterator */ - bt_message_array_const messages; - uint64_t message_count; - bt_message_iterator_next_status next_status = - bt_message_iterator_next(dispatch->message_iterator, &messages, - &message_count); - - - - switch (next_status) { - case BT_MESSAGE_ITERATOR_NEXT_STATUS_END: - /* End of iteration: put the message iterator's reference */ - bt_message_iterator_put_ref(dispatch->message_iterator); - status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_END; - goto end; - case BT_MESSAGE_ITERATOR_NEXT_STATUS_AGAIN: - status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_AGAIN; - goto end; - case BT_MESSAGE_ITERATOR_NEXT_STATUS_MEMORY_ERROR: - status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_MEMORY_ERROR; - goto end; - case BT_MESSAGE_ITERATOR_NEXT_STATUS_ERROR: - status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR; - goto end; - default: - break; +#include "perfetto_prunned.pb.h" + +static perfetto_uuid_t gen_perfetto_uuid() { + // Start at one, Look like UUID 0 is special + static std::atomic uuid{1}; + return uuid++; +} + +static void add_event_begin(struct timeline_dispatch *dispatch, perfetto_uuid_t uuid, + timestamp_t begin, std::string name) { + auto *packet = dispatch->trace.add_packet(); + packet->set_timestamp(begin); + packet->set_trusted_packet_sequence_id(10); + auto *track_event = packet->mutable_track_event(); + track_event->set_type(perfetto_pruned::TrackEvent::TYPE_SLICE_BEGIN); + track_event->set_name(name); + track_event->set_track_uuid(uuid); +} + +static void add_event_end(struct timeline_dispatch *dispatch, perfetto_uuid_t uuid, uint64_t end) { + auto *packet = dispatch->trace.add_packet(); + packet->set_trusted_packet_sequence_id(10); + packet->set_timestamp(end); + auto *track_event = packet->mutable_track_event(); + track_event->set_type(perfetto_pruned::TrackEvent::TYPE_SLICE_END); + track_event->set_track_uuid(uuid); +} + +static perfetto_uuid_t get_parent_uuid(struct timeline_dispatch *dispatch, std::string hostname, + uint64_t process_id, uint64_t thread_id, + thapi_device_id did = 0, thapi_device_id sdid = 0) { + + perfetto_uuid_t hp_uuid = 0; + { + // This is so easy... + // Because element keys in a map are unique, + // the insertion operation checks whether each inserted element has a key equivalent to the one + // of an element already in the container, and if so, the element is not inserted, returning an + // iterator to this existing element (if the function returns a value). + + // In the case we where not able to insert, we use the iterator to get the value, + auto r = dispatch->hp2uuid.insert({{hostname, process_id, did, sdid}, hp_uuid}); + auto &potential_uuid = r.first->second; + if (!r.second) { + hp_uuid = potential_uuid; + // In the case we where able to insert our dummy value, + // We generate the a new uuid, and mutate the value in the map + } else { + hp_uuid = gen_perfetto_uuid(); + potential_uuid = hp_uuid; + + { + auto *packet = dispatch->trace.add_packet(); + packet->set_trusted_packet_sequence_id(10); + packet->set_timestamp(0); + + auto *track_descriptor = packet->mutable_track_descriptor(); + track_descriptor->set_uuid(hp_uuid); + auto *process = track_descriptor->mutable_process(); + process->set_pid(hp_uuid); + std::ostringstream oss; + oss << hostname << " | Process " << process_id ; + if (did !=0) { + oss << " | Device " << did; + if (sdid !=0) + oss << " | SubDevice " << sdid; + } + oss << " | uuid "; + process->set_process_name(oss.str()); + } + } + } + // Due to Perfetto https://github.com/google/perfetto/issues/321, + // each GPU thread wil be mapped to a "virtual" process + // We will add the thread_id to the process name + perfetto_uuid_t parent_uuid = 0; + { + // Same strategy used previsouly to do only one table lookup + auto r = dispatch->hpt2uuid.insert({{hp_uuid, thread_id}, parent_uuid}); + auto &potential_uuid = r.first->second; + if (!r.second) { + parent_uuid = potential_uuid; + } else { + parent_uuid = gen_perfetto_uuid(); + potential_uuid = parent_uuid; + { + auto *packet = dispatch->trace.add_packet(); + packet->set_trusted_packet_sequence_id(10); + packet->set_timestamp(0); + + auto *track_descriptor = packet->mutable_track_descriptor(); + track_descriptor->set_uuid(parent_uuid); + track_descriptor->set_parent_uuid(hp_uuid); + // This is the workarround for the bug: https://github.com/google/perfetto/issues/321 + // We trick perfetto to this they are processes + if (did == 0) { + auto *thread = track_descriptor->mutable_thread(); + thread->set_pid(hp_uuid); + thread->set_tid(thread_id); + } + } } + } + return parent_uuid; +} - static bool first = false; - static uint64_t begin_ts = 0; - - /* For each consumed message */ - for (uint64_t i = 0; i < message_count; i++) { - const bt_message *message = messages[i]; - if (bt_message_get_type(message) == BT_MESSAGE_TYPE_EVENT) { - const bt_event *event = bt_message_event_borrow_event_const(message); - const bt_event_class *event_class = bt_event_borrow_class_const(event); - const char * class_name = bt_event_class_get_name(event_class); - - auto dur_tuple0 = std::make_tuple(std::make_tuple(0, &bt_field_string_get_value, - (hostname_t) ""), // hostname - std::make_tuple(1, &bt_field_integer_signed_get_value, - (process_id_t)0), // process - std::make_tuple(2, &bt_field_integer_unsigned_get_value, - (thread_id_t)0), // thread - std::make_tuple(3, &bt_field_integer_unsigned_get_value, - (uint64_t) 0)); - - const bt_field *common_context_field = bt_event_borrow_common_context_field_const(event); - const auto& [hostname, process_id, thread_id, ts] = thapi_bt2_getter(common_context_field, dur_tuple0); - //Payload - if (!first) { - begin_ts = ts; - first = true; - } - // - const bt_field *payload_field = bt_event_borrow_payload_field_const(event); - - const bt_field *name_field = bt_field_structure_borrow_member_field_by_index_const(payload_field, 0); - const std::string name = std::string{bt_field_string_get_value(name_field)}; - - const bt_field *dur_field = bt_field_structure_borrow_member_field_by_index_const(payload_field, 1); - const long dur = bt_field_integer_unsigned_get_value(dur_field); - - auto &s_gtf_pid = dispatch->s_gtf_pid; - auto &s_gtf_pid_gpu = dispatch->s_gtf_pid_gpu; - //auto &s_gtf_tid = dispatch->s_gtf_tid; - if (std::string(class_name) == "lttng:host") { - - int gtf_pid; - - { - const auto thapi_pid = hp_t(hostname, process_id); - auto it = s_gtf_pid.find(thapi_pid); - if (it == s_gtf_pid.end() ) { - gtf_pid = s_gtf_pid.size() + s_gtf_pid_gpu.size(); - s_gtf_pid[thapi_pid] = gtf_pid; - std::cout << nlohmann::json{ {"name", "process_name"}, - {"ph", "M"}, - {"pid", gtf_pid}, - {"args", { {"name", hostname + ", pid " + std::to_string(process_id) + " |"} } }, - // Double "{" to generate a dict and not array - } - << "," << std::endl; - } else { - gtf_pid = it->second; - } - } - - int gtf_tid = thread_id; - std::cout << nlohmann::json{ {"pid", gtf_pid}, - {"tid", gtf_tid}, - {"ts", static_cast((ts-begin_ts)*1E-3)}, - {"dur", static_cast(dur*1E-3)}, - {"name", name}, - {"ph", "X"} - } - << "," << std::endl; - } else if ( std::string(class_name) == "lttng:device" ) { - const bt_field *did_field = bt_field_structure_borrow_member_field_by_index_const(payload_field, 2); - const thapi_device_id did = bt_field_integer_unsigned_get_value(did_field); - - const bt_field *sdid_field = bt_field_structure_borrow_member_field_by_index_const(payload_field, 3); - const thapi_device_id sdid = bt_field_integer_unsigned_get_value(sdid_field); - - const bt_field *err_field = bt_field_structure_borrow_member_field_by_index_const(payload_field, 4); - const bool err = bt_field_bool_get_value(err_field); - if (err) - continue; - - const auto thapi_pid = hp_dsd_t(hostname, process_id, did, sdid); - auto it = s_gtf_pid_gpu.find(thapi_pid); - int gtf_pid; - if (it == s_gtf_pid_gpu.end() ) { - gtf_pid = s_gtf_pid.size() + s_gtf_pid_gpu.size(); - s_gtf_pid_gpu[thapi_pid] = gtf_pid; - std::cout << nlohmann::json{ {"name", "process_name"}, - {"ph", "M"}, - {"pid", gtf_pid}, - {"args", { {"name", "GPU " + hostname + ", pid " + std::to_string(process_id) + " | "} } }, - } - << "," << std::endl; - } else { - gtf_pid = s_gtf_pid_gpu[thapi_pid]; - } - std::cout << nlohmann::json{ {"pid", gtf_pid}, - {"tid", thread_id}, - {"ts", static_cast((ts-begin_ts)*1E-3)}, - {"dur", static_cast(dur*1E-3)}, - {"name", name}, - {"ph", "X"} - } - << "," << std::endl; - - } - } +static void add_event_cpu(struct timeline_dispatch *dispatch, std::string hostname, + uint64_t process_id, uint64_t thread_id, std::string name, uint64_t begin, + uint64_t dur) { + // Assume perfecly nessted + const uint64_t end = begin + dur; + + perfetto_uuid_t parent_uuid = get_parent_uuid(dispatch, hostname, process_id, thread_id); + // Handling perfecly nested event + add_event_begin(dispatch, parent_uuid, begin, name); + std::stack &s = dispatch->uuid2stack[parent_uuid]; + while ((!s.empty()) && (s.top() <= begin)) { + add_event_end(dispatch, parent_uuid, s.top()); + s.pop(); + } + s.push(end); +} + +static void add_event_gpu(struct timeline_dispatch *dispatch, std::string hostname, + uint64_t process_id, uint64_t thread_id, thapi_device_id did, + thapi_device_id sdid, std::string name, uint64_t begin, uint64_t dur) { + // This function Assume non perfecly nested + const uint64_t end = begin + dur; + perfetto_uuid_t parent_uuid = get_parent_uuid(dispatch, hostname, process_id, thread_id, did, sdid); + // Now see if we need a to generate a new children + std::map &m = dispatch->parents2tracks[parent_uuid]; + perfetto_uuid_t uuid; + + // Pre-historical event + if (m.empty() || begin < m.begin()->first) { + uuid = gen_perfetto_uuid(); + // Generate a new children track + { + auto *packet = dispatch->trace.add_packet(); + packet->set_trusted_packet_sequence_id(10); + packet->set_timestamp(0); + + auto *track_descriptor = packet->mutable_track_descriptor(); + track_descriptor->set_uuid(uuid); + track_descriptor->set_parent_uuid(parent_uuid); + + std::ostringstream oss; + oss << "Thread " << thread_id; + track_descriptor->set_name(oss.str()); + } + } else { + // Find the uuid who finished just before this one + auto it_ub = std::prev(m.upper_bound(begin)); + uuid = it_ub->second; + // Erase the old timestamps + m.erase(it_ub); + } + // Update the table + m[end] = uuid; + // Add event + add_event_begin(dispatch, uuid, begin, name); + add_event_end(dispatch, uuid, end); +} + +bt_component_class_sink_consume_method_status +timeline_dispatch_consume(bt_self_component_sink *self_component_sink) { + bt_component_class_sink_consume_method_status status = + BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK; + + // Internal datatrastruct to convern hostname process to Google trace format process + // https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview + + /* Retrieve our private data from the component's user data */ + struct timeline_dispatch *dispatch = (timeline_dispatch *)bt_self_component_get_data( + bt_self_component_sink_as_self_component(self_component_sink)); + + /* Consume a batch of messages from the upstream message iterator */ + bt_message_array_const messages; + uint64_t message_count; + bt_message_iterator_next_status next_status = + bt_message_iterator_next(dispatch->message_iterator, &messages, &message_count); + + switch (next_status) { + case BT_MESSAGE_ITERATOR_NEXT_STATUS_END: + /* End of iteration: put the message iterator's reference */ + bt_message_iterator_put_ref(dispatch->message_iterator); + status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_END; + goto end; + case BT_MESSAGE_ITERATOR_NEXT_STATUS_AGAIN: + status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_AGAIN; + goto end; + case BT_MESSAGE_ITERATOR_NEXT_STATUS_MEMORY_ERROR: + status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_MEMORY_ERROR; + goto end; + case BT_MESSAGE_ITERATOR_NEXT_STATUS_ERROR: + status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR; + goto end; + default: + break; + } + + /* For each consumed message */ + for (uint64_t i = 0; i < message_count; i++) { + const bt_message *message = messages[i]; + if (bt_message_get_type(message) == BT_MESSAGE_TYPE_EVENT) { + const bt_event *event = bt_message_event_borrow_event_const(message); + const bt_event_class *event_class = bt_event_borrow_class_const(event); + const char *class_name = bt_event_class_get_name(event_class); + + auto dur_tuple0 = + std::make_tuple(std::make_tuple(0, &bt_field_string_get_value, + (hostname_t) ""), // hostname + std::make_tuple(1, &bt_field_integer_signed_get_value, + (process_id_t)0), // process + std::make_tuple(2, &bt_field_integer_unsigned_get_value, + (thread_id_t)0), // thread + std::make_tuple(3, &bt_field_integer_unsigned_get_value, (uint64_t)0)); + + const bt_field *common_context_field = bt_event_borrow_common_context_field_const(event); + const auto & [ hostname, process_id, thread_id, ts ] = + thapi_bt2_getter(common_context_field, dur_tuple0); + + const bt_field *payload_field = bt_event_borrow_payload_field_const(event); + + const bt_field *name_field = + bt_field_structure_borrow_member_field_by_index_const(payload_field, 0); + const std::string name = std::string{bt_field_string_get_value(name_field)}; + + const bt_field *dur_field = + bt_field_structure_borrow_member_field_by_index_const(payload_field, 1); + const long dur = bt_field_integer_unsigned_get_value(dur_field); + + if (std::string(class_name) == "lttng:host") { + add_event_cpu(dispatch, hostname, process_id, thread_id, name, ts, dur); + } else if (std::string(class_name) == "lttng:device") { + + const bt_field *did_field = + bt_field_structure_borrow_member_field_by_index_const(payload_field, 2); + const thapi_device_id did = bt_field_integer_unsigned_get_value(did_field); + + const bt_field *sdid_field = + bt_field_structure_borrow_member_field_by_index_const(payload_field, 3); + const thapi_device_id sdid = bt_field_integer_unsigned_get_value(sdid_field); + add_event_gpu(dispatch, hostname, process_id, thread_id, did, sdid, name, ts, dur); + } + } bt_message_put_ref(message); - } + } end: - return status; + return status; } /* * Initializes the sink component. */ -bt_component_class_initialize_method_status timeline_dispatch_initialize( - bt_self_component_sink *self_component_sink, - bt_self_component_sink_configuration *configuration, - const bt_value *params, void *initialize_method_data) -{ - /* Allocate a private data structure */ - struct timeline_dispatch *dispatch = new timeline_dispatch; - - /* Set the component's user data to our private data structure */ - bt_self_component_set_data( - bt_self_component_sink_as_self_component(self_component_sink), - dispatch); - - /* - * Add an input port named `in` to the sink component. - * - * This is needed so that this sink component can be connected to a - * filter or a source component. With a connected upstream - * component, this sink component can create a message iterator - * to consume messages. - */ - bt_self_component_sink_add_input_port(self_component_sink, - "in", NULL, NULL); - - std::cout << "{" << std::endl; - std::cout << "\"traceEvents\":[" << std::endl; - - return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK; +bt_component_class_initialize_method_status +timeline_dispatch_initialize(bt_self_component_sink *self_component_sink, + bt_self_component_sink_configuration *configuration, + const bt_value *params, void *initialize_method_data) { + /* Allocate a private data structure */ + struct timeline_dispatch *dispatch = new timeline_dispatch; + + /* Set the component's user data to our private data structure */ + bt_self_component_set_data(bt_self_component_sink_as_self_component(self_component_sink), + dispatch); + + /* + * Add an input port named `in` to the sink component. + * + * This is needed so that this sink component can be connected to a + * filter or a source component. With a connected upstream + * component, this sink component can create a message iterator + * to consume messages. + */ + bt_self_component_sink_add_input_port(self_component_sink, "in", NULL, NULL); + + { + auto *packet = dispatch->trace.add_packet(); + packet->set_trusted_packet_sequence_id(10); + packet->set_timestamp(0); + + auto *trace_packet_defaults = packet->mutable_trace_packet_defaults(); + trace_packet_defaults->set_timestamp_clock_id(perfetto_pruned::BUILTIN_CLOCK_BOOTTIME); + packet->set_previous_packet_dropped(true); + } + + return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK; } /* * Finalizes the sink component. */ -void timeline_dispatch_finalize(bt_self_component_sink *self_component_sink) -{ - - // JSON is stupornd, it doesn't allow trailling comma. - // To avoid creating yet anothing state, we will create a dummy object at the end. - std::cout << nlohmann::json{ {"name", "thread_sort_index"}, - {"ph", "M"}, - {"pid", -1}, - {"tid", -1}, - { "args", { {"sort_index", -1 } } }, - // Double "{" to generate a dict and not array - } - << std::endl; - - std::cout << "]}" << std::endl; +void timeline_dispatch_finalize(bt_self_component_sink *self_component_sink) { + struct timeline_dispatch *dispatch = (timeline_dispatch *)bt_self_component_get_data( + bt_self_component_sink_as_self_component(self_component_sink)); + + for (auto & [ uuid, s ] : dispatch->uuid2stack) { + while (!s.empty()) { + add_event_end(dispatch, uuid, s.top()); + s.pop(); + } + } + std::string path{"out.pftrace"}; + // Write the new address book back to disk. + std::fstream output(path, std::ios::out | std::ios::trunc | std::ios::binary); + if (!dispatch->trace.SerializeToOstream(&output)) + std::cerr << "Failed to write the trace." << std::endl; + else + std::cout << "Perfetto trace saved: " << path << std::endl; + google::protobuf::ShutdownProtobufLibrary(); } /* @@ -220,22 +325,18 @@ void timeline_dispatch_finalize(bt_self_component_sink *self_component_sink) * This is where we can create our upstream message iterator. */ bt_component_class_sink_graph_is_configured_method_status -timeline_dispatch_graph_is_configured(bt_self_component_sink *self_component_sink) -{ - /* Retrieve our private data from the component's user data */ - struct timeline_dispatch *dispatch = (timeline_dispatch*) bt_self_component_get_data( - bt_self_component_sink_as_self_component(self_component_sink)); - - /* Borrow our unique port */ - bt_self_component_port_input *in_port = - bt_self_component_sink_borrow_input_port_by_index( - self_component_sink, 0); - - /* Create the uptream message iterator */ - bt_message_iterator_create_from_sink_component(self_component_sink, - in_port, &dispatch->message_iterator); - - return BT_COMPONENT_CLASS_SINK_GRAPH_IS_CONFIGURED_METHOD_STATUS_OK; -} +timeline_dispatch_graph_is_configured(bt_self_component_sink *self_component_sink) { + /* Retrieve our private data from the component's user data */ + struct timeline_dispatch *dispatch = (timeline_dispatch *)bt_self_component_get_data( + bt_self_component_sink_as_self_component(self_component_sink)); + /* Borrow our unique port */ + bt_self_component_port_input *in_port = + bt_self_component_sink_borrow_input_port_by_index(self_component_sink, 0); + /* Create the uptream message iterator */ + bt_message_iterator_create_from_sink_component(self_component_sink, in_port, + &dispatch->message_iterator); + + return BT_COMPONENT_CLASS_SINK_GRAPH_IS_CONFIGURED_METHOD_STATUS_OK; +} diff --git a/xprof/timeline.hpp b/xprof/timeline.hpp index e8099317..90b3faa6 100644 --- a/xprof/timeline.hpp +++ b/xprof/timeline.hpp @@ -2,14 +2,22 @@ #include "xprof_utils.hpp" // typedef #include +#include +#include // pair #include +#include "perfetto_prunned.pb.h" -typedef std::tuple hptl_t; +typedef uint64_t perfetto_uuid_t; +typedef uint64_t timestamp_t; /* Sink component's private data */ struct timeline_dispatch { bt_message_iterator *message_iterator; - std::unordered_map s_gtf_pid; - std::unordered_map s_gtf_pid_gpu; - std::unordered_map s_gtf_tid; + // Perfetto + std::unordered_map hp2uuid; + std::unordered_map, perfetto_uuid_t> hpt2uuid; + std::map> parents2tracks; + std::map> uuid2stack; + + perfetto_pruned::Trace trace; }; diff --git a/ze/tracer_ze_helpers.include.c b/ze/tracer_ze_helpers.include.c index b6e8f39f..c78b5a18 100644 --- a/ze/tracer_ze_helpers.include.c +++ b/ze/tracer_ze_helpers.include.c @@ -110,7 +110,7 @@ static inline void _register_ze_device( o_h->obj_data = (void *)d_data; d_data->properties.stype = ZE_STRUCTURE_TYPE_DEVICE_PROPERTIES; - ze_result_t res = zeDeviceGetProperties(device, &(d_data->properties)); + ze_result_t res = ZE_DEVICE_GET_PROPERTIES_PTR(device, &(d_data->properties)); if (res != ZE_RESULT_SUCCESS) { free((void *)mem); return; @@ -612,7 +612,7 @@ static void _dump_kernel_properties(ze_kernel_handle_t hKernel) { ze_kernel_properties_t kernelProperties; kernelProperties.stype=ZE_STRUCTURE_TYPE_KERNEL_PROPERTIES; kernelProperties.pNext=NULL; - if (zeKernelGetProperties(hKernel, &kernelProperties) == ZE_RESULT_SUCCESS) + if (ZE_KERNEL_GET_PROPERTIES_PTR(hKernel, &kernelProperties) == ZE_RESULT_SUCCESS) tracepoint(lttng_ust_ze_properties, kernel, hKernel, &kernelProperties); }