• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_PROTO_ASYNC_TRACK_SET_TRACKER_H_
18 #define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_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 unnestable; that is, it is an error
102     // to call Begin -> Begin with a single cookie without End inbetween.
103     // This pattern should be the default behaviour that most async slices
104     // should use.
105     kUnnestable,
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 TrackSetType {
117     kGlobal,
118     kProcess,
119     kAndroidLegacyUnnestable,
120   };
121 
122   struct TrackState {
123     TrackId id;
124 
125     enum class SliceType { kCookie, kTimestamp };
126     SliceType slice_type;
127 
128     union {
129       // Only valid for |slice_type| == |SliceType::kCookie|.
130       int64_t cookie;
131 
132       // Only valid for |slice_type| == |SliceType::kTimestamp|.
133       int64_t ts_end;
134     };
135 
136     // Only used for |slice_type| == |SliceType::kCookie|.
137     uint32_t nest_count;
138   };
139 
140   struct TrackSet {
141     TrackSetType type;
142     union {
143       // Only set when |type| == |TrackSetType::kGlobal|.
144       StringId global_track_name;
145       // Only set when |type| == |TrackSetType::kFrameTimeline| or
146       // |TrackSetType::kAndroidLegacyUnnestable|.
147       ProcessTuple process_tuple;
148     };
149     NestingBehaviour nesting_behaviour;
150     std::vector<TrackState> tracks;
151   };
152 
CreateUnnestableTrackSetForTesting(UniquePid upid,StringId name)153   TrackSetId CreateUnnestableTrackSetForTesting(UniquePid upid, StringId name) {
154     AsyncTrackSetTracker::TrackSet set;
155     set.process_tuple = ProcessTuple{upid, name};
156     set.type = AsyncTrackSetTracker::TrackSetType::kAndroidLegacyUnnestable;
157     set.nesting_behaviour = NestingBehaviour::kUnnestable;
158     track_sets_.emplace_back(set);
159     return static_cast<TrackSetId>(track_sets_.size() - 1);
160   }
161 
162   // Returns the state for a track using the following algorithm:
163   // 1. If a track exists with the given cookie in the track set, returns
164   //    that track.
165   // 2. Otherwise, looks for any track in the set which is "open" (i.e.
166   //    does not have another slice currently scheduled).
167   // 3. Otherwise, creates a new track and associates it with the set.
168   TrackState& GetOrCreateTrackForCookie(TrackSet& set, int64_t cookie);
169 
170   TrackId CreateTrackForSet(const TrackSet& set);
171 
172   std::map<StringId, TrackSetId> global_track_set_ids_;
173   std::map<ProcessTuple, TrackSetId> process_track_set_ids_;
174   std::map<ProcessTuple, TrackSetId> android_legacy_unnestable_track_set_ids_;
175   std::vector<TrackSet> track_sets_;
176 
177   const StringId android_source_ = kNullStringId;
178 
179   TraceProcessorContext* const context_;
180 };
181 
182 }  // namespace trace_processor
183 }  // namespace perfetto
184 
185 #endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_ASYNC_TRACK_SET_TRACKER_H_
186