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 <unordered_map> 21 22 #include "src/trace_processor/storage/trace_storage.h" 23 24 namespace perfetto { 25 namespace trace_processor { 26 27 class TraceProcessorContext; 28 class AsyncTrackSetTrackerUnittest; 29 30 // Tracker used to reduce the number of trace processor tracks corresponding 31 // to a single "UI track". 32 // 33 // UIs using trace processor want to display all slices in the same context 34 // (e.g. same upid) and same name into a single track. However, because trace 35 // processor does not allow parallel slices on a single track (because it breaks 36 // things like span join, self time computation etc.), at the trace processor 37 // level these parallel slices are put on different tracks. 38 // 39 // Creating a new track for every event, however, leads to an explosion of 40 // tracks which is undesirable. This class exists to multiplex slices so that 41 // n events correspond to a single track in a way which minimises the number of 42 // tracks which needs to be merged by the UI. 43 // 44 // The intended usage of this class is for callers to first call one of the 45 // Intern* methods to obtain a TrackSetId followed by Begin/End just before 46 // calling into SliceTracker's Begin/End respectively. For example: 47 // TrackSetId set_id = track_set_tracker->InternAndroidSet(upid, name); 48 // if (event.begin) { 49 // TrackId id = track_set_tracker->Begin(set_id, cookie); 50 // slice_tracker->Begin(ts, id, ...) 51 // } else { 52 // ... (same thing with end) 53 // } 54 // Alternatively, instead of Begin/End, Scoped can also be called if supported 55 // by the track type. 56 class AsyncTrackSetTracker { 57 public: 58 using TrackSetId = uint32_t; 59 60 explicit AsyncTrackSetTracker(TraceProcessorContext* context); 61 ~AsyncTrackSetTracker() = default; 62 63 // Interns a set of global async slice tracks associated with the given name. 64 TrackSetId InternGlobalTrackSet(StringId name); 65 66 // Interns a set of Android async slice tracks associated with the given 67 // upid and name. 68 // Scoped is *not* supported for this track set type. 69 TrackSetId InternAndroidSet(UniquePid, StringId name); 70 71 // Starts a new slice on the given async track set which has the given cookie. 72 TrackId Begin(TrackSetId id, int64_t cookie); 73 74 // Interns the expected and actual timeline tracks coming from FrameTimeline 75 // producer for the associated upid. 76 TrackSetId InternFrameTimelineSet(UniquePid, StringId name); 77 78 // Ends a new slice on the given async track set which has the given cookie. 79 TrackId End(TrackSetId id, int64_t cookie); 80 81 // Creates a scoped slice on the given async track set. 82 // This method makes sure that any other slice in this track set does 83 // not happen simultaneously on the returned track. 84 // Only supported on selected track set types; read the documentation for 85 // the Intern* method for your track type to check if supported. 86 TrackId Scoped(TrackSetId id, int64_t ts, int64_t dur); 87 88 private: 89 friend class AsyncTrackSetTrackerUnittest; 90 91 struct AndroidTuple { 92 UniquePid upid; 93 StringId name; 94 95 friend bool operator<(const AndroidTuple& l, const AndroidTuple& r) { 96 return std::tie(l.upid, l.name) < std::tie(r.upid, r.name); 97 } 98 }; 99 100 struct FrameTimelineTuple { 101 UniquePid upid; 102 StringId name; 103 104 friend bool operator<(const FrameTimelineTuple& l, 105 const FrameTimelineTuple& r) { 106 return std::tie(l.upid, l.name) < std::tie(r.upid, r.name); 107 } 108 }; 109 110 // Indicates the nesting behaviour of slices associated to a single slice 111 // stack. 112 enum class NestingBehaviour { 113 // Indicates that slices are unnestable; that is, it is an error 114 // to call Begin -> Begin with a single cookie without End inbetween. 115 // This pattern should be the default behaviour that most async slices 116 // should use. 117 kUnnestable, 118 119 // Indicates that slices are unnestable but also saturating; that is 120 // calling Begin -> Begin only causes a single Begin to be recorded. 121 // This is only really useful for Android async slices which have this 122 // behaviour for legacy reasons. See the comment in 123 // SystraceParser::ParseSystracePoint for information on why 124 // this behaviour exists. 125 kLegacySaturatingUnnestable, 126 }; 127 128 enum class TrackSetType { 129 kGlobal, 130 kAndroid, 131 kFrameTimeline, 132 }; 133 134 struct TrackState { 135 TrackId id; 136 137 enum class SliceType { kCookie, kTimestamp }; 138 SliceType slice_type; 139 140 union { 141 // Only valid for |slice_type| == |SliceType::kCookie|. 142 int64_t cookie; 143 144 // Only valid for |slice_type| == |SliceType::kTimestamp|. 145 int64_t ts_end; 146 }; 147 148 // Only used for |slice_type| == |SliceType::kCookie|. 149 uint32_t nest_count; 150 }; 151 152 struct TrackSet { 153 TrackSetType type; 154 union { 155 StringId global_track_name; 156 // Only set when |type| == |TrackSetType::kAndroid|. 157 AndroidTuple android_tuple; 158 // Only set when |type| == |TrackSetType::kFrameTimeline|. 159 FrameTimelineTuple frame_timeline_tuple; 160 }; 161 NestingBehaviour nesting_behaviour; 162 std::vector<TrackState> tracks; 163 }; 164 CreateUnnestableTrackSetForTesting(UniquePid upid,StringId name)165 TrackSetId CreateUnnestableTrackSetForTesting(UniquePid upid, StringId name) { 166 AsyncTrackSetTracker::TrackSet set; 167 set.android_tuple = AndroidTuple{upid, name}; 168 set.type = AsyncTrackSetTracker::TrackSetType::kAndroid; 169 set.nesting_behaviour = NestingBehaviour::kUnnestable; 170 track_sets_.emplace_back(set); 171 return static_cast<TrackSetId>(track_sets_.size() - 1); 172 } 173 174 // Returns the state for a track using the following algorithm: 175 // 1. If a track exists with the given cookie in the track set, returns 176 // that track. 177 // 2. Otherwise, looks for any track in the set which is "open" (i.e. 178 // does not have another slice currently scheduled). 179 // 3. Otherwise, creates a new track and associates it with the set. 180 TrackState& GetOrCreateTrackForCookie(TrackSet& set, int64_t cookie); 181 182 TrackId CreateTrackForSet(const TrackSet& set); 183 184 std::map<StringId, TrackSetId> global_track_set_ids_; 185 std::map<AndroidTuple, TrackSetId> android_track_set_ids_; 186 std::map<FrameTimelineTuple, TrackSetId> frame_timeline_track_set_ids_; 187 std::vector<TrackSet> track_sets_; 188 189 TraceProcessorContext* const context_; 190 }; 191 192 } // namespace trace_processor 193 } // namespace perfetto 194 195 #endif // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_ASYNC_TRACK_SET_TRACKER_H_ 196