xref: /aosp_15_r20/external/perfetto/src/trace_processor/importers/common/async_track_set_tracker.h (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_ASYNC_TRACK_SET_TRACKER_H_
18 #define SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_ASYNC_TRACK_SET_TRACKER_H_
19 
20 #include "src/trace_processor/storage/trace_storage.h"
21 
22 namespace perfetto {
23 namespace trace_processor {
24 
25 class TraceProcessorContext;
26 class AsyncTrackSetTrackerUnittest;
27 
28 // Tracker used to reduce the number of trace processor tracks corresponding
29 // to a single "UI track".
30 //
31 // UIs using trace processor want to display all slices in the same context
32 // (e.g. same upid) and same name into a single track. However, because trace
33 // processor does not allow parallel slices on a single track (because it breaks
34 // things like span join, self time computation etc.), at the trace processor
35 // level these parallel slices are put on different tracks.
36 //
37 // Creating a new track for every event, however, leads to an explosion of
38 // tracks which is undesirable. This class exists to multiplex slices so that
39 // n events correspond to a single track in a way which minimises the number of
40 // tracks which needs to be merged by the UI.
41 //
42 // The intended usage of this class is for callers to first call one of the
43 // Intern* methods to obtain a TrackSetId followed by Begin/End just before
44 // calling into SliceTracker's Begin/End respectively. For example:
45 //  TrackSetId set_id = track_set_tracker->InternProcessTrackSet(upid, name);
46 //  if (event.begin) {
47 //    TrackId id = track_set_tracker->Begin(set_id, cookie);
48 //    slice_tracker->Begin(ts, id, ...)
49 //  } else {
50 //    ... (same thing with end)
51 //  }
52 // Alternatively, instead of Begin/End, Scoped can also be called if supported
53 // by the track type.
54 class AsyncTrackSetTracker {
55  public:
56   using TrackSetId = uint32_t;
57 
58   explicit AsyncTrackSetTracker(TraceProcessorContext* context);
59   ~AsyncTrackSetTracker() = default;
60 
61   // Interns a set of global async slice tracks associated with the given name.
62   TrackSetId InternGlobalTrackSet(StringId name);
63 
64   // Interns a set of process async slice tracks associated with the given name
65   // and upid.
66   TrackSetId InternProcessTrackSet(UniquePid, StringId name);
67 
68   // Interns a set of Android legacy unnesteable async slice tracks
69   // associated with the given upid and name.
70   // Scoped is *not* supported for this track set type.
71   TrackSetId InternAndroidLegacyUnnestableTrackSet(UniquePid, StringId name);
72 
73   // Starts a new slice on the given async track set which has the given cookie.
74   TrackId Begin(TrackSetId id, int64_t cookie);
75 
76   // Ends a new slice on the given async track set which has the given cookie.
77   TrackId End(TrackSetId id, int64_t cookie);
78 
79   // Creates a scoped slice on the given async track set.
80   // This method makes sure that any other slice in this track set does
81   // not happen simultaneously on the returned track.
82   // Only supported on selected track set types; read the documentation for
83   // the Intern* method for your track type to check if supported.
84   TrackId Scoped(TrackSetId id, int64_t ts, int64_t dur);
85 
86  private:
87   friend class AsyncTrackSetTrackerUnittest;
88 
89   struct ProcessTuple {
90     UniquePid upid;
91     StringId name;
92 
93     friend bool operator<(const ProcessTuple& l, const ProcessTuple& r) {
94       return std::tie(l.upid, l.name) < std::tie(r.upid, r.name);
95     }
96   };
97 
98   // Indicates the nesting behaviour of slices associated to a single slice
99   // stack.
100   enum class NestingBehaviour {
101     // Indicates that slices are nestable; that is, a stack of slices with
102     // the same cookie should stack correctly (but are not allowed to overlap).
103     // This pattern should be the default behaviour that most async slices
104     // should use.
105     kNestable,
106 
107     // Indicates that slices are unnestable but also saturating; that is
108     // calling Begin -> Begin only causes a single Begin to be recorded.
109     // This is only really useful for Android async slices which have this
110     // behaviour for legacy reasons. See the comment in
111     // SystraceParser::ParseSystracePoint for information on why
112     // this behaviour exists.
113     kLegacySaturatingUnnestable,
114   };
115 
116   enum class TrackSetScope {
117     kGlobal,
118     kProcess,
119   };
120 
121   struct TrackState {
122     TrackId id;
123 
124     enum class SliceType { kCookie, kTimestamp };
125     SliceType slice_type;
126 
127     union {
128       // Only valid for |slice_type| == |SliceType::kCookie|.
129       int64_t cookie;
130 
131       // Only valid for |slice_type| == |SliceType::kTimestamp|.
132       int64_t ts_end;
133     };
134 
135     // Only used for |slice_type| == |SliceType::kCookie|.
136     uint32_t nest_count;
137   };
138 
139   struct TrackSet {
140     TrackSetScope scope;
141     union {
142       // Only set when |scope| == |TrackSetScope::kGlobal|.
143       StringId global_track_name;
144       // Only set when |scope| == |TrackSetScope::kFrameTimeline| or
145       // |TrackSetScope::kAndroidLegacyUnnestable|.
146       ProcessTuple process_tuple;
147     };
148     NestingBehaviour nesting_behaviour;
149     std::vector<TrackState> tracks;
150   };
151 
152   // Returns the state for a track using the following algorithm:
153   // 1. If a track exists with the given cookie in the track set, returns
154   //    that track.
155   // 2. Otherwise, looks for any track in the set which is "open" (i.e.
156   //    does not have another slice currently scheduled).
157   // 3. Otherwise, creates a new track and associates it with the set.
158   TrackState& GetOrCreateTrackForCookie(TrackSet& set, int64_t cookie);
159 
160   TrackId CreateTrackForSet(const TrackSet& set);
161 
162   std::map<StringId, TrackSetId> global_track_set_ids_;
163   std::map<ProcessTuple, TrackSetId> process_track_set_ids_;
164   std::map<ProcessTuple, TrackSetId> android_legacy_unnestable_track_set_ids_;
165   std::vector<TrackSet> track_sets_;
166 
167   const StringId android_source_ = kNullStringId;
168 
169   TraceProcessorContext* const context_;
170 };
171 
172 }  // namespace trace_processor
173 }  // namespace perfetto
174 
175 #endif  // SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_ASYNC_TRACK_SET_TRACKER_H_
176