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