22
33#include < algorithm>
44#include < cstddef>
5- #include < cstdint >
5+ #include < iterator >
66#include < limits>
77#include < memory>
88#include < string>
1212#include " absl/algorithm/container.h"
1313#include " absl/container/btree_map.h"
1414#include " absl/container/flat_hash_map.h"
15+ #include " absl/container/flat_hash_set.h"
1516#include " absl/strings/numbers.h"
1617#include " absl/strings/str_cat.h"
1718#include " absl/strings/string_view.h"
1819#include " absl/types/span.h"
20+ #include " third_party/dear_imgui/imgui.h"
21+ #include " xprof/frontend/app/components/trace_viewer_v2/color/color_generator.h"
1922#include " xprof/frontend/app/components/trace_viewer_v2/helper/time_formatter.h"
2023#include " xprof/frontend/app/components/trace_viewer_v2/timeline/time_range.h"
2124#include " xprof/frontend/app/components/trace_viewer_v2/timeline/timeline.h"
@@ -44,6 +47,8 @@ struct TraceInformation {
4447 absl::btree_map<std::pair<ProcessId, ThreadId>, std::string> thread_names;
4548 absl::btree_map<ProcessId, std::string> process_names;
4649 absl::flat_hash_map<ProcessId, uint32_t > process_sort_indices;
50+ absl::btree_map<std::string, std::vector<const TraceEvent*>>
51+ flow_events_by_id;
4752};
4853
4954std::string GetDefaultThreadName (ThreadId tid) {
@@ -119,6 +124,14 @@ void HandleCompleteEvent(const TraceEvent& event,
119124 trace_info.events_by_pid_tid [event.pid ][event.tid ].push_back (&event);
120125}
121126
127+ void HandleFlowEvent (const TraceEvent& event, TraceInformation& trace_info,
128+ absl::flat_hash_set<int >& present_categories) {
129+ if (!event.id .empty ()) {
130+ trace_info.flow_events_by_id [event.id ].push_back (&event);
131+ present_categories.insert (static_cast <int >(event.category ));
132+ }
133+ }
134+
122135// Handles a counter event ('ph' == 'C'). These events represent a counter value
123136// at a specific timestamp. The function groups events by process ID and counter
124137// name.
@@ -151,6 +164,81 @@ struct TimeBounds {
151164 Microseconds max = std::numeric_limits<Microseconds>::min();
152165};
153166
167+ struct ThreadLevelInfo {
168+ int start_level;
169+ int end_level;
170+ };
171+
172+ // Returns the flame chart level of the given event.
173+ int GetEventFlameChartLevel (
174+ const TraceEvent* e,
175+ const absl::btree_map<std::pair<ProcessId, ThreadId>, ThreadLevelInfo>&
176+ thread_levels,
177+ const FlameChartTimelineData& data) {
178+ auto it = thread_levels.find ({e->pid , e->tid });
179+ if (it == thread_levels.end ()) return 0 ;
180+ int start = it->second .start_level ;
181+ int end = it->second .end_level ;
182+
183+ // Search from deepest level up
184+ for (int lvl = end - 1 ; lvl >= start; --lvl) {
185+ const auto & indices = data.events_by_level [lvl];
186+ // Binary search for event covering e->ts
187+ // events are likely sorted by start time.
188+ auto it_idx = std::upper_bound (indices.begin (), indices.end (), e->ts ,
189+ [&](Microseconds ts, int idx) {
190+ return ts < data.entry_start_times [idx];
191+ });
192+
193+ // it_idx points to first event starting AFTER e->ts.
194+ // Check the one before it.
195+ if (it_idx != indices.begin ()) {
196+ int idx = *std::prev (it_idx);
197+ if (data.entry_start_times [idx] + data.entry_total_times [idx] >= e->ts ) {
198+ return lvl;
199+ }
200+ }
201+ }
202+ return start; // Default to thread top
203+ }
204+
205+ void GenerateFlowLines (const TraceInformation& trace_info,
206+ const absl::btree_map<std::pair<ProcessId, ThreadId>,
207+ ThreadLevelInfo>& thread_levels,
208+ FlameChartTimelineData& data, TimeBounds& bounds) {
209+ for (const auto & [id, flow_events] : trace_info.flow_events_by_id ) {
210+ const ImU32 flow_color =
211+ GetColorForId (id); // Use the flow ID to generate a consistent color
212+
213+ for (const TraceEvent* event : flow_events) {
214+ std::vector<std::string>& event_flow_ids =
215+ data.flow_ids_by_event_id [event->event_id ];
216+ if (event_flow_ids.empty () || event_flow_ids.back () != id) {
217+ event_flow_ids.push_back (id);
218+ }
219+ }
220+
221+ for (size_t i = 0 ; i < flow_events.size () - 1 ; ++i) {
222+ const TraceEvent* u = flow_events[i];
223+ const TraceEvent* v = flow_events[i + 1 ];
224+
225+ FlowLine flow_line{
226+ .source_ts = u->ts ,
227+ .target_ts = v->ts ,
228+ .source_level = GetEventFlameChartLevel (u, thread_levels, data),
229+ .target_level = GetEventFlameChartLevel (v, thread_levels, data),
230+ .color = flow_color,
231+ .category = u->category };
232+ data.flow_lines .push_back (flow_line);
233+ data.flow_lines_by_flow_id [id].push_back (flow_line);
234+ bounds.min = std::min (bounds.min , u->ts );
235+ bounds.max = std::max (bounds.max , u->ts );
236+ bounds.min = std::min (bounds.min , v->ts );
237+ bounds.max = std::max (bounds.max , v->ts );
238+ }
239+ }
240+ }
241+
154242// Appends the given nodes (an array of trees) to the data, starting at the
155243// given level. Returns the maximum level of the nodes.
156244int AppendNodesAtLevel (absl::Span<const std::unique_ptr<TraceEventNode>> nodes,
@@ -165,6 +253,7 @@ int AppendNodesAtLevel(absl::Span<const std::unique_ptr<TraceEventNode>> nodes,
165253 data.entry_total_times .push_back (event->dur );
166254 data.entry_levels .push_back (current_level);
167255 data.entry_names .push_back (event->name );
256+ data.entry_event_ids .push_back (event->event_id );
168257
169258 bounds.min = std::min (bounds.min , event->ts );
170259 bounds.max = std::max (bounds.max , event->ts + event->dur );
@@ -182,22 +271,28 @@ int AppendNodesAtLevel(absl::Span<const std::unique_ptr<TraceEventNode>> nodes,
182271void PopulateThreadTrack (ProcessId pid, ThreadId tid,
183272 absl::Span<const TraceEvent* const > events,
184273 const TraceInformation& trace_info, int & current_level,
185- FlameChartTimelineData& data, TimeBounds& bounds) {
274+ FlameChartTimelineData& data, TimeBounds& bounds,
275+ absl::btree_map<std::pair<ProcessId, ThreadId>,
276+ ThreadLevelInfo>& thread_levels) {
186277 const auto it = trace_info.thread_names .find ({pid, tid});
187278 const std::string thread_group_name = it == trace_info.thread_names .end ()
188279 ? GetDefaultThreadName (tid)
189280 : it->second ;
281+
190282 data.groups .push_back ({.name = thread_group_name,
191283 .start_level = current_level,
192284 .nesting_level = kThreadNestingLevel });
193285
286+ int start_level = current_level;
287+
194288 TraceEventTree event_tree = BuildTree (events);
195289
196290 // Get the maximum level index used by events in this thread.
197291 int max_level =
198292 AppendNodesAtLevel (event_tree.roots , current_level, data, bounds);
199293
200294 current_level = max_level + 1 ;
295+ thread_levels[{pid, tid}] = {start_level, current_level};
201296}
202297
203298void PopulateCounterTrack (ProcessId pid, const std::string& name,
@@ -256,7 +351,9 @@ void PopulateCounterTrack(ProcessId pid, const std::string& name,
256351
257352void PopulateProcessTrack (ProcessId pid, const TraceInformation& trace_info,
258353 int & current_level, FlameChartTimelineData& data,
259- TimeBounds& bounds) {
354+ TimeBounds& bounds,
355+ absl::btree_map<std::pair<ProcessId, ThreadId>,
356+ ThreadLevelInfo>& thread_levels) {
260357 const auto it_events = trace_info.events_by_pid_tid .find (pid);
261358 const bool has_events = it_events != trace_info.events_by_pid_tid .end () &&
262359 !it_events->second .empty ();
@@ -282,7 +379,7 @@ void PopulateProcessTrack(ProcessId pid, const TraceInformation& trace_info,
282379 if (has_events) {
283380 for (const auto & [tid, events] : it_events->second ) {
284381 PopulateThreadTrack (pid, tid, events, trace_info, current_level, data,
285- bounds);
382+ bounds, thread_levels );
286383 }
287384 }
288385
@@ -322,17 +419,20 @@ FlameChartTimelineData CreateTimelineData(const TraceInformation& trace_info,
322419 TimeBounds& bounds) {
323420 FlameChartTimelineData data;
324421 int current_level = 0 ;
422+ absl::btree_map<std::pair<ProcessId, ThreadId>, ThreadLevelInfo>
423+ thread_levels;
325424
326- std::vector<ProcessId> pids = GetSortedProcessIds (trace_info);
327-
328- for (ProcessId pid : pids) {
329- PopulateProcessTrack (pid, trace_info, current_level, data, bounds);
425+ for (const ProcessId pid : GetSortedProcessIds (trace_info)) {
426+ PopulateProcessTrack (pid, trace_info, current_level, data, bounds,
427+ thread_levels);
330428 }
331429
332430 data.events_by_level .resize (current_level);
333431 for (int i = 0 ; i < data.entry_levels .size (); ++i) {
334432 data.events_by_level [data.entry_levels [i]].push_back (i);
335433 }
434+
435+ GenerateFlowLines (trace_info, thread_levels, data, bounds);
336436 return data;
337437}
338438
@@ -343,7 +443,8 @@ FlameChartTimelineData CreateTimelineData(const TraceInformation& trace_info,
343443void DataProvider::ProcessTraceEvents (const ParsedTraceEvents& parsed_events,
344444 Timeline& timeline) {
345445 if (parsed_events.flame_events .empty () &&
346- parsed_events.counter_events .empty ()) {
446+ parsed_events.counter_events .empty () &&
447+ parsed_events.flow_events .empty ()) {
347448 timeline.set_timeline_data ({});
348449 timeline.set_fetched_data_time_range (TimeRange::Zero ());
349450 timeline.SetVisibleRange (TimeRange::Zero ());
@@ -352,6 +453,7 @@ void DataProvider::ProcessTraceEvents(const ParsedTraceEvents& parsed_events,
352453
353454 timeline.set_mpmd_pipeline_view_enabled (parsed_events.mpmd_pipeline_view );
354455
456+ absl::flat_hash_set<int > present_flow_categories_set;
355457 TraceInformation trace_info;
356458 for (const auto & event : parsed_events.flame_events ) {
357459 switch (event.ph ) {
@@ -369,10 +471,27 @@ void DataProvider::ProcessTraceEvents(const ParsedTraceEvents& parsed_events,
369471 }
370472 }
371473
474+ for (const auto & event : parsed_events.flow_events ) {
475+ HandleFlowEvent (event, trace_info, present_flow_categories_set);
476+ }
477+ present_flow_categories_.assign (present_flow_categories_set.begin (),
478+ present_flow_categories_set.end ());
479+ absl::c_sort (present_flow_categories_);
480+
372481 for (const auto & event : parsed_events.counter_events ) {
373482 HandleCounterEvent (event, trace_info);
374483 }
375484
485+ // Ensure all pids/tids from flow events are registered so that thread tracks
486+ // are created for them, which is required for level calculation.
487+ for (const auto & [id, events] : trace_info.flow_events_by_id ) {
488+ for (const auto * event : events) {
489+ trace_info.process_names .try_emplace (event->pid ,
490+ GetDefaultProcessName (event->pid ));
491+ trace_info.events_by_pid_tid [event->pid ].try_emplace (event->tid );
492+ }
493+ }
494+
376495 // Sort events, first by timestamp (ascending), then by duration
377496 // (descending).
378497 // Ensure all processes have a name in process_names.
@@ -395,6 +514,11 @@ void DataProvider::ProcessTraceEvents(const ParsedTraceEvents& parsed_events,
395514 }
396515 }
397516
517+ for (auto & [id, events] : trace_info.flow_events_by_id ) {
518+ absl::c_stable_sort (
519+ events, gtl::OrderBy ([](const TraceEvent* e) { return e->ts ; }));
520+ }
521+
398522 for (auto & [pid, counters_by_name] : trace_info.counters_by_pid_name ) {
399523 for (auto & [name, events] : counters_by_name) {
400524 absl::c_stable_sort (
@@ -457,4 +581,8 @@ std::vector<std::string> DataProvider::GetProcessList() const {
457581 return process_list_;
458582}
459583
584+ const std::vector<int >& DataProvider::GetFlowCategories () const {
585+ return present_flow_categories_;
586+ }
587+
460588} // namespace traceviewer
0 commit comments