Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions frontend/app/components/trace_viewer_v2/application.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@
#include <stdlib.h>

#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "absl/base/no_destructor.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#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 {
Expand Down Expand Up @@ -43,13 +47,21 @@ 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<Application>;

// Members are initialized to nullptr here and will be properly allocated in
// the Initialize() method.
Application() = default;

ParsedTraceEvents parsed_events_;

std::unique_ptr<WGPURenderPlatform> platform_;
std::unique_ptr<Timeline> timeline_;
// The data provider for trace events.
Expand Down
2 changes: 2 additions & 0 deletions frontend/app/components/trace_viewer_v2/event_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
35 changes: 35 additions & 0 deletions frontend/app/components/trace_viewer_v2/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ export declare interface TraceViewerV2Module extends WasmModule {
processTraceEvents(data: TraceData): void;
loadJsonData?(url: string): Promise<void>;
getProcessList?(url: string): Promise<string[] | undefined>;
getEventData?(
name: string,
start: number,
duration: number,
): Promise<EventData|undefined>;
StringVector: {
size(): number;
get(index: number): string;
Expand All @@ -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;
};
};
};
Expand All @@ -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 (
Expand Down Expand Up @@ -206,5 +225,21 @@ export async function traceViewerV2Main(): Promise<TraceViewerV2Module | null> {
return processArray;
};

traceviewerModule.getEventData = async(
name: string,
start: number,
duration: number,
): Promise<EventData|undefined> => {
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;
}
49 changes: 38 additions & 11 deletions frontend/app/components/trace_viewer_v2/timeline/data_provider.cc
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#include "xprof/frontend/app/components/trace_viewer_v2/timeline/data_provider.h"

#include <algorithm>
#include <cmath>
#include <cstddef>
#include <limits>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
Expand Down Expand Up @@ -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) {
Expand All @@ -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.
Expand All @@ -326,20 +331,18 @@ 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));
}
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).
Expand All @@ -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
Expand All @@ -387,7 +383,38 @@ void DataProvider::ProcessTraceEvents(const ParsedTraceEvents& parsed_events,
}

std::vector<std::string> DataProvider::GetProcessList() const {
return process_list_;
std::vector<std::string> 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<EventMetaData> 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
26 changes: 25 additions & 1 deletion frontend/app/components/trace_viewer_v2/timeline/data_provider.h
Original file line number Diff line number Diff line change
@@ -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 <map>
#include <optional>
#include <string>
#include <tuple>
#include <vector>

#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"
Expand All @@ -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<std::string, std::string> arguments;
};

class DataProvider {
public:
// Returns a list of process names.
Expand All @@ -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<EventMetaData> GetEventMetaData(const std::string& name,
double start_us,
double duration_us) const;

private:
std::vector<std::string> process_list_;
// Use btree_map to keep process tracks sorted by PID.
absl::btree_map<ProcessId, std::string> process_names_;
// A map of (name, start time, duration) to the TraceEvent.
std::map<std::tuple<std::string, Microseconds, Microseconds>,
const TraceEvent*>
event_map_;
};

} // namespace traceviewer
Expand Down
10 changes: 9 additions & 1 deletion frontend/app/components/trace_viewer_v2/timeline/timeline.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::string>
emscripten::register_vector<std::string>("StringVector");
emscripten::register_map<std::string, std::string>("StringMap");

emscripten::value_object<traceviewer::EventMetaData>("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_<traceviewer::DataProvider>("DataProvider")
.function("getProcessList", &traceviewer::DataProvider::GetProcessList);
.function("getProcessList", &traceviewer::DataProvider::GetProcessList)
.function("getEventMetaData",
&traceviewer::DataProvider::GetEventMetaData);

emscripten::register_optional<traceviewer::EventMetaData>();

emscripten::function("processTraceEvents",
&traceviewer::ParseAndProcessTraceEvents);
Expand All @@ -179,7 +192,8 @@ EMSCRIPTEN_BINDINGS(trace_event_parser) {
emscripten::class_<traceviewer::Application>("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
28 changes: 28 additions & 0 deletions plugin/trace_viewer/tf_trace_viewer/tf-trace-viewer.html
Original file line number Diff line number Diff line change
Expand Up @@ -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 = '<div id="event-details" ' +
'style="display: block;">Loading event details...</div>';
}
await this._maybeFetchEventArgs(event);
if (this._eventDetails) {
this._eventDetails.innerHTML = '<div id="event-details" ' +
'style="display: block;"></div>';
const div = document.createElement('div');
div.innerHTML = `
<div><b>Name:</b> ${event.title}</div>
<div><b>Process:</b> ${event.parentContainer.parent.name}</div>
<div><b>Start:</b> ${event.start.toFixed(3)} ms</div>
<div><b>Duration:</b> ${this._formatDuration(event.duration)}</div>
`;
this._eventDetails.appendChild(div);
if (event.args && Object.keys(event.args).length > 0) {
const argsDiv = document.createElement('div');
argsDiv.innerHTML = '<b>Arguments:</b>';
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 = `<b>${key}:</b> ${event.args[key]}`;
argsList.appendChild(li);
}
}
argsDiv.appendChild(argsList);
this._eventDetails.appendChild(argsDiv);
}
}
var currentThread = event.parentContainer;
var currentProcess = currentThread.parent;
Expand Down