diff --git a/frontend/app/components/trace_viewer_v2/application.h b/frontend/app/components/trace_viewer_v2/application.h index f574a5a2..029e5842 100644 --- a/frontend/app/components/trace_viewer_v2/application.h +++ b/frontend/app/components/trace_viewer_v2/application.h @@ -3,6 +3,9 @@ #include #include +#include +#include +#include #include "absl/base/no_destructor.h" #include "absl/time/clock.h" @@ -10,6 +13,7 @@ #include "third_party/dear_imgui/imgui.h" #include "xprof/frontend/app/components/trace_viewer_v2/timeline/data_provider.h" #include "xprof/frontend/app/components/trace_viewer_v2/timeline/timeline.h" +#include "xprof/frontend/app/components/trace_viewer_v2/trace_helper/trace_event.h" #include "xprof/frontend/app/components/trace_viewer_v2/webgpu_render_platform.h" namespace traceviewer { @@ -43,6 +47,12 @@ class Application { }; DataProvider& data_provider() { return data_provider_; }; + void set_parsed_events(ParsedTraceEvents events) { + parsed_events_ = std::move(events); + } + + ParsedTraceEvents& parsed_events() { return parsed_events_; } + private: friend class absl::NoDestructor; @@ -50,6 +60,8 @@ class Application { // the Initialize() method. Application() = default; + ParsedTraceEvents parsed_events_; + std::unique_ptr platform_; std::unique_ptr timeline_; // The data provider for trace events. diff --git a/frontend/app/components/trace_viewer_v2/event_data.h b/frontend/app/components/trace_viewer_v2/event_data.h index 90ef47f7..7f24def7 100644 --- a/frontend/app/components/trace_viewer_v2/event_data.h +++ b/frontend/app/components/trace_viewer_v2/event_data.h @@ -22,6 +22,8 @@ inline constexpr absl::string_view kEventSelected = "eventselected"; inline constexpr absl::string_view kEventSelectedIndex = "eventIndex"; inline constexpr absl::string_view kEventSelectedName = "name"; +inline constexpr absl::string_view kEventSelectedStartUs = "startUs"; +inline constexpr absl::string_view kEventSelectedDurationUs = "durationUs"; } // namespace traceviewer diff --git a/frontend/app/components/trace_viewer_v2/main.ts b/frontend/app/components/trace_viewer_v2/main.ts index 79494fef..fa7d4245 100644 --- a/frontend/app/components/trace_viewer_v2/main.ts +++ b/frontend/app/components/trace_viewer_v2/main.ts @@ -20,6 +20,11 @@ export declare interface TraceViewerV2Module extends WasmModule { processTraceEvents(data: TraceData): void; loadJsonData?(url: string): Promise; getProcessList?(url: string): Promise; + getEventData?( + name: string, + start: number, + duration: number, + ): Promise; StringVector: { size(): number; get(index: number): string; @@ -29,6 +34,12 @@ export declare interface TraceViewerV2Module extends WasmModule { Instance(): { data_provider(): { getProcessList(): TraceViewerV2Module['StringVector']; + getEventMetaData( + name: string, + start: number, + duration: number, + ): EventData | + undefined; }; }; }; @@ -38,6 +49,14 @@ declare interface TraceData { traceEvents: Array<{[key: string]: unknown}>; } +declare interface EventData { + name: string; + processName: string; + start: number; + duration: number; + arguments: {[key: string]: string}; +} + // Type guard to check if an object conforms to the TraceData interface function isTraceData(data: unknown): data is TraceData { return ( @@ -206,5 +225,21 @@ export async function traceViewerV2Main(): Promise { return processArray; }; + traceviewerModule.getEventData = async( + name: string, + start: number, + duration: number, + ): Promise => { + try { + const eventData = traceviewerModule.Application.Instance() + .data_provider() + .getEventMetaData(name, start, duration); + return eventData || undefined; + } catch (e) { + console.error('Error in getEventData:', e); + return undefined; + } + }; + return traceviewerModule; } diff --git a/frontend/app/components/trace_viewer_v2/timeline/data_provider.cc b/frontend/app/components/trace_viewer_v2/timeline/data_provider.cc index 79668afa..bbc3494c 100644 --- a/frontend/app/components/trace_viewer_v2/timeline/data_provider.cc +++ b/frontend/app/components/trace_viewer_v2/timeline/data_provider.cc @@ -1,9 +1,11 @@ #include "xprof/frontend/app/components/trace_viewer_v2/timeline/data_provider.h" #include +#include #include #include #include +#include #include #include #include @@ -310,6 +312,8 @@ void DataProvider::ProcessTraceEvents(const ParsedTraceEvents& parsed_events, return; } + event_map_.clear(); + process_names_.clear(); TraceInformation trace_info; for (const auto& event : parsed_events.flame_events) { switch (event.ph) { @@ -318,6 +322,7 @@ void DataProvider::ProcessTraceEvents(const ParsedTraceEvents& parsed_events, break; case Phase::kComplete: HandleCompleteEvent(event, trace_info); + event_map_[{event.name, event.ts, event.dur}] = &event; break; default: // Ignore other event types. @@ -326,13 +331,10 @@ void DataProvider::ProcessTraceEvents(const ParsedTraceEvents& parsed_events, break; } } - for (const auto& event : parsed_events.counter_events) { HandleCounterEvent(event, trace_info); } - // Sort events, first by timestamp (ascending), then by duration - // (descending). // Ensure all processes have a name in process_names. for (const auto& [pid, _] : trace_info.events_by_pid_tid) { trace_info.process_names.try_emplace(pid, GetDefaultProcessName(pid)); @@ -340,6 +342,7 @@ void DataProvider::ProcessTraceEvents(const ParsedTraceEvents& parsed_events, for (const auto& [pid, _] : trace_info.counters_by_pid_name) { trace_info.process_names.try_emplace(pid, GetDefaultProcessName(pid)); } + process_names_ = trace_info.process_names; // Sort events, first by timestamp (ascending), then by duration // (descending). @@ -366,13 +369,6 @@ void DataProvider::ProcessTraceEvents(const ParsedTraceEvents& parsed_events, TimeBounds time_bounds; - // Populate process_list_ from trace_info. - if (process_list_.empty()) { - for (const auto& [pid, name] : trace_info.process_names) { - process_list_.push_back(absl::StrCat(name, " (pid: ", pid, ")")); - } - } - timeline.set_timeline_data(CreateTimelineData(trace_info, time_bounds)); // Don't need to check for max_time because the TimeRange constructor will @@ -387,7 +383,38 @@ void DataProvider::ProcessTraceEvents(const ParsedTraceEvents& parsed_events, } std::vector DataProvider::GetProcessList() const { - return process_list_; + std::vector process_list; + process_list.reserve(process_names_.size()); + for (const auto& [pid, name] : process_names_) { + process_list.push_back(absl::StrCat(name, " (pid: ", pid, ")")); + } + std::sort(process_list.begin(), process_list.end()); + return process_list; +} + +constexpr double kEpsilon = 1e-3; + +std::optional DataProvider::GetEventMetaData( + const std::string& name, double start_us, double duration_us) const { + for (const auto& [key, event] : event_map_) { + if (std::get<0>(key) == name && + std::abs(std::get<1>(key) - start_us) < kEpsilon && + std::abs(std::get<2>(key) - duration_us) < kEpsilon) { + EventMetaData metadata; + metadata.name = event->name; + metadata.start = event->ts; + metadata.duration = event->dur; + metadata.arguments = event->args; + auto proc_it = process_names_.find(event->pid); + if (proc_it != process_names_.end()) { + metadata.processName = proc_it->second; + } else { + metadata.processName = GetDefaultProcessName(event->pid); + } + return metadata; + } + } + return std::nullopt; } } // namespace traceviewer diff --git a/frontend/app/components/trace_viewer_v2/timeline/data_provider.h b/frontend/app/components/trace_viewer_v2/timeline/data_provider.h index c6d701b9..e8767f7f 100644 --- a/frontend/app/components/trace_viewer_v2/timeline/data_provider.h +++ b/frontend/app/components/trace_viewer_v2/timeline/data_provider.h @@ -1,8 +1,13 @@ #ifndef THIRD_PARTY_XPROF_FRONTEND_APP_COMPONENTS_TRACE_VIEWER_V2_TIMELINE_DATA_PROVIDER_H_ #define THIRD_PARTY_XPROF_FRONTEND_APP_COMPONENTS_TRACE_VIEWER_V2_TIMELINE_DATA_PROVIDER_H_ +#include +#include +#include +#include #include +#include "absl/container/btree_map.h" #include "absl/strings/string_view.h" #include "xprof/frontend/app/components/trace_viewer_v2/timeline/timeline.h" #include "xprof/frontend/app/components/trace_viewer_v2/trace_helper/trace_event.h" @@ -13,6 +18,14 @@ inline constexpr absl::string_view kThreadName = "thread_name"; inline constexpr absl::string_view kProcessName = "process_name"; inline constexpr absl::string_view kName = "name"; +struct EventMetaData { + std::string name; + Microseconds start; + Microseconds duration; + std::string processName; + std::map arguments; +}; + class DataProvider { public: // Returns a list of process names. @@ -22,8 +35,19 @@ class DataProvider { void ProcessTraceEvents(const ParsedTraceEvents& parsed_events, Timeline& timeline); + // Returns detailed event data for a given eventIndex. + // emscripten::val GetEventData(int eventIndex) const; + std::optional GetEventMetaData(const std::string& name, + double start_us, + double duration_us) const; + private: - std::vector process_list_; + // Use btree_map to keep process tracks sorted by PID. + absl::btree_map process_names_; + // A map of (name, start time, duration) to the TraceEvent. + std::map, + const TraceEvent*> + event_map_; }; } // namespace traceviewer diff --git a/frontend/app/components/trace_viewer_v2/timeline/timeline.cc b/frontend/app/components/trace_viewer_v2/timeline/timeline.cc index 32ef8ed8..663c73b5 100644 --- a/frontend/app/components/trace_viewer_v2/timeline/timeline.cc +++ b/frontend/app/components/trace_viewer_v2/timeline/timeline.cc @@ -509,9 +509,15 @@ void Timeline::DrawEvent(int group_index, int event_index, selected_counter_index_ = -1; EventData event_data; + event_data.try_emplace(kEventSelectedIndex, selected_event_index_); event_data.try_emplace(kEventSelectedName, event_name); - + event_data.try_emplace( + kEventSelectedStartUs, + timeline_data_.entry_start_times[event_index]); + event_data.try_emplace( + kEventSelectedDurationUs, + timeline_data_.entry_total_times[event_index]); event_callback_(kEventSelected, event_data); } } @@ -964,6 +970,8 @@ void Timeline::HandleEventDeselection() { EventData event_data; event_data[std::string(kEventSelectedIndex)] = -1; event_data[std::string(kEventSelectedName)] = std::string(""); + event_data[std::string(kEventSelectedStartUs)] = 0.0; + event_data[std::string(kEventSelectedDurationUs)] = 0.0; event_callback_(kEventSelected, event_data); } diff --git a/frontend/app/components/trace_viewer_v2/trace_helper/trace_event_parser.cc b/frontend/app/components/trace_viewer_v2/trace_helper/trace_event_parser.cc index fed212ff..48ef3f90 100644 --- a/frontend/app/components/trace_viewer_v2/trace_helper/trace_event_parser.cc +++ b/frontend/app/components/trace_viewer_v2/trace_helper/trace_event_parser.cc @@ -158,19 +158,32 @@ ParsedTraceEvents ParseTraceEvents(const emscripten::val& trace_data) { } void ParseAndProcessTraceEvents(const emscripten::val& trace_data) { - const ParsedTraceEvents parsed_events = ParseTraceEvents(trace_data); - + ParsedTraceEvents parsed_events = ParseTraceEvents(trace_data); + Application::Instance().set_parsed_events(std::move(parsed_events)); Application::Instance().data_provider().ProcessTraceEvents( - parsed_events, Application::Instance().timeline()); + Application::Instance().parsed_events(), + Application::Instance().timeline()); } EMSCRIPTEN_BINDINGS(trace_event_parser) { // Bind std::vector emscripten::register_vector("StringVector"); + emscripten::register_map("StringMap"); + + emscripten::value_object("EventMetaData") + .field("name", &traceviewer::EventMetaData::name) + .field("start", &traceviewer::EventMetaData::start) + .field("duration", &traceviewer::EventMetaData::duration) + .field("processName", &traceviewer::EventMetaData::processName) + .field("arguments", &traceviewer::EventMetaData::arguments); // Bind DataProvider class emscripten::class_("DataProvider") - .function("getProcessList", &traceviewer::DataProvider::GetProcessList); + .function("getProcessList", &traceviewer::DataProvider::GetProcessList) + .function("getEventMetaData", + &traceviewer::DataProvider::GetEventMetaData); + + emscripten::register_optional(); emscripten::function("processTraceEvents", &traceviewer::ParseAndProcessTraceEvents); @@ -179,7 +192,8 @@ EMSCRIPTEN_BINDINGS(trace_event_parser) { emscripten::class_("Application") .class_function("Instance", &traceviewer::Application::Instance, emscripten::return_value_policy::reference()) - .function("data_provider", &traceviewer::Application::data_provider); + .function("data_provider", &traceviewer::Application::data_provider, + emscripten::return_value_policy::reference()); } } // namespace traceviewer diff --git a/plugin/trace_viewer/tf_trace_viewer/tf-trace-viewer.html b/plugin/trace_viewer/tf_trace_viewer/tf-trace-viewer.html index d82ae38f..dcd44994 100644 --- a/plugin/trace_viewer/tf_trace_viewer/tf-trace-viewer.html +++ b/plugin/trace_viewer/tf_trace_viewer/tf-trace-viewer.html @@ -833,10 +833,38 @@ // Don't need to repopulate the details for the same event selection. if (this._selectedSingleEvent == event) return; + if (this._eventDetails) { + this._eventDetails.innerHTML = '
Loading event details...
'; + } await this._maybeFetchEventArgs(event); if (this._eventDetails) { this._eventDetails.innerHTML = '
'; + const div = document.createElement('div'); + div.innerHTML = ` +
Name: ${event.title}
+
Process: ${event.parentContainer.parent.name}
+
Start: ${event.start.toFixed(3)} ms
+
Duration: ${this._formatDuration(event.duration)}
+ `; + this._eventDetails.appendChild(div); + if (event.args && Object.keys(event.args).length > 0) { + const argsDiv = document.createElement('div'); + argsDiv.innerHTML = 'Arguments:'; + const argsList = document.createElement('ul'); + argsList.style.listStyle = 'none'; + argsList.style.paddingLeft = '10px'; + for (const key in event.args) { + if (event.args.hasOwnProperty(key)) { + const li = document.createElement('li'); + li.innerHTML = `${key}: ${event.args[key]}`; + argsList.appendChild(li); + } + } + argsDiv.appendChild(argsList); + this._eventDetails.appendChild(argsDiv); + } } var currentThread = event.parentContainer; var currentProcess = currentThread.parent;