1 /* 2 * Copyright (C) 2019 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_TRACK_TRACKER_H_ 18 #define SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_TRACK_TRACKER_H_ 19 20 #include <array> 21 #include <cstddef> 22 #include <cstdint> 23 #include <functional> 24 #include <optional> 25 #include <tuple> 26 #include <type_traits> 27 #include <variant> 28 29 #include "perfetto/base/compiler.h" 30 #include "perfetto/ext/base/flat_hash_map.h" 31 #include "perfetto/ext/base/hash.h" 32 #include "perfetto/ext/base/string_utils.h" 33 #include "perfetto/ext/base/string_view.h" 34 #include "perfetto/public/compiler.h" 35 #include "src/trace_processor/importers/common/args_tracker.h" 36 #include "src/trace_processor/importers/common/global_args_tracker.h" 37 #include "src/trace_processor/importers/common/tracks.h" 38 #include "src/trace_processor/importers/common/tracks_internal.h" 39 #include "src/trace_processor/storage/trace_storage.h" 40 #include "src/trace_processor/tables/track_tables_py.h" 41 #include "src/trace_processor/types/trace_processor_context.h" 42 #include "src/trace_processor/types/variadic.h" 43 44 namespace perfetto::trace_processor { 45 46 // Tracks and stores tracks based on track types, ids and scopes. 47 class TrackTracker { 48 public: 49 using SetArgsCallback = std::function<void(ArgsTracker::BoundInserter&)>; 50 51 explicit TrackTracker(TraceProcessorContext*); 52 53 // Given a blueprint (i.e. the schema of a track), and the dimensions checks 54 // whether the track has been seen before and if so, returns the id of the 55 // seen track. 56 // 57 // If the track was *not* seen before, creates an entry in the track table 58 // and returns the id. 59 // 60 // Usage (for slice tracks): 61 // ``` 62 // void ParseMySpecialThreadScopedSlice(UniqueTid utid, ...(other args)) { 63 // static constexpr auto kBlueprint = tracks::SliceBlueprint( 64 // // The classification of the track. 65 // "my_special_thread_scoped_slice", 66 // // The dimensions of the track. Can be >1 if the track is broken down 67 // // by multiple fields. 68 // tracks::DimensionBlueprints(tracks::kThreadDimension) 69 // ); 70 // TrackId track_id = track_tracker_->InternTrack( 71 // kBlueprint, tracks::Dimensions(utid)); 72 // 73 // ... add slices using SliceTracker 74 // } 75 // ``` 76 // 77 // Usage (for counter tracks): 78 // ``` 79 // void ParseMySpecialCustomScopedCounter(uint32_t custom_scope, 80 // ... other args) { 81 // static constexpr auto kBlueprint = tracks::CounterBlueprint( 82 // // The classification of the track. 83 // "my_special_custom_scoped_counter", 84 // // The dimensions of the track. Can be >1 if the track is broken down 85 // // by multiple fields. 86 // tracks::DimensionBlueprints( 87 // tracks::UnitDimensionBlueprint("custom_scope")) 88 // ); 89 // TrackId track_id = track_tracker_->InternTrack( 90 // kBlueprint, tracks::Dimensions(custom_scope)); 91 // 92 // ... add counters using EventTracker 93 // } 94 // ``` 95 // 96 // Note: when using this function, always try and check the blueprints in 97 // `tracks_common.h` to see if there is a blueprint there which already does 98 // what you need. 99 template <typename Blueprint> 100 PERFETTO_ALWAYS_INLINE TrackId 101 InternTrack(const Blueprint& bp, 102 typename Blueprint::dimensions_t dims = {}, 103 typename Blueprint::name_t name = tracks::BlueprintName(), 104 const SetArgsCallback& args = {}, 105 typename Blueprint::unit_t unit = tracks::BlueprintUnit()) { 106 base::Hasher hasher(bp.hasher); 107 std::apply([&](auto&&... args) { ((hasher.Update(args)), ...); }, dims); 108 auto [it, inserted] = tracks_new_.Insert(hasher.digest(), {}); 109 if (inserted) { 110 std::array<GlobalArgsTracker::CompactArg, 8> a; 111 DimensionsToArgs<0>(dims, bp.dimension_blueprints.data(), a.data()); 112 StringId n; 113 using NBT = tracks::NameBlueprintT; 114 using name_blueprint_t = typename Blueprint::name_blueprint_t; 115 if constexpr (std::is_same_v<NBT::Auto, name_blueprint_t>) { 116 n = kNullStringId; 117 } else if constexpr (std::is_same_v<NBT::Static, name_blueprint_t>) { 118 n = context_->storage->InternString(bp.name_blueprint.name); 119 } else if constexpr (std::is_base_of_v<NBT::FnBase, name_blueprint_t>) { 120 n = context_->storage->InternString( 121 std::apply(bp.name_blueprint.fn, dims).string_view()); 122 } else { 123 static_assert(std::is_same_v<NBT::Dynamic, name_blueprint_t>); 124 n = name; 125 } 126 using UBT = tracks::UnitBlueprintT; 127 using unit_blueprint_t = typename Blueprint::unit_blueprint_t; 128 StringId u; 129 if constexpr (std::is_same_v<UBT::Unknown, unit_blueprint_t>) { 130 u = kNullStringId; 131 } else if constexpr (std::is_same_v<UBT::Static, unit_blueprint_t>) { 132 u = context_->storage->InternString(bp.unit_blueprint.name); 133 } else { 134 static_assert(std::is_same_v<UBT::Dynamic, unit_blueprint_t>); 135 u = unit; 136 } 137 // GCC warns about the variables being unused even they are in certain 138 // constexpr branches above. Just use them here to suppress the warning. 139 base::ignore_result(name, unit); 140 static constexpr uint32_t kDimensionCount = 141 std::tuple_size_v<typename Blueprint::dimensions_t>; 142 *it = AddTrack(bp, n, u, a.data(), kDimensionCount, args); 143 } 144 return *it; 145 } 146 147 // ********WARNING************ 148 // EVERYTHING BELOW THIS POINT IS LEGACY AND SHOULD BE REMOVED WITH TIME. 149 // ********WARNING************ 150 151 // Dimensions of the data in a track. Used as an argument in 152 // `TrackTracker::InternTrack()`. Use `TrackTracker::DimensionsBuilder` to 153 // create. 154 struct Dimensions { 155 ArgSetId arg_set_id; 156 157 bool operator==(const Dimensions& o) const { 158 return arg_set_id == o.arg_set_id; 159 } 160 }; 161 162 // Used to create `Dimensions` required to intern a new track. 163 class DimensionsBuilder { 164 public: DimensionsBuilder(TrackTracker * tt)165 explicit DimensionsBuilder(TrackTracker* tt) : tt_(tt) {} 166 167 // Append Upid (unique pid) dimension of a track. AppendUpid(UniquePid upid)168 void AppendUpid(UniquePid upid) { 169 AppendDimension(tt_->upid_id_, Variadic::Integer(upid)); 170 } 171 172 // Append gpu id dimension of a track. AppendGpu(int64_t gpu)173 void AppendGpu(int64_t gpu) { 174 AppendDimension(tt_->gpu_id_, Variadic::Integer(gpu)); 175 } 176 177 // Append name dimension of a track. Only use in cases where name is a 178 // dimension, it is not a way to force the name of the track in a table. AppendName(StringId name)179 void AppendName(StringId name) { 180 AppendDimension(tt_->name_id_, Variadic::String(name)); 181 } 182 183 // Append custom dimension. Only use if none of the other Append functions 184 // are suitable. AppendDimension(StringId key,const Variadic & val)185 void AppendDimension(StringId key, const Variadic& val) { 186 GlobalArgsTracker::CompactArg& arg = args_[count_args++]; 187 arg.flat_key = key; 188 arg.key = key; 189 arg.value = val; 190 } 191 192 // Build to fetch the `Dimensions` value of the Appended dimensions. Pushes 193 // the dimensions into args table. Use the result in 194 // `TrackTracker::InternTrack`. Build()195 Dimensions Build() && { 196 return Dimensions{tt_->context_->global_args_tracker->AddArgSet( 197 args_.data(), 0, count_args)}; 198 } 199 200 private: 201 TrackTracker* tt_; 202 std::array<GlobalArgsTracker::CompactArg, 8> args_; 203 uint32_t count_args = 0; 204 }; 205 206 // Indicates that the track name will be automatically generated by the trace 207 // processor. 208 // All tracks *MUST* use this option unless it is explicitly approved by a 209 // trace processor maintainer. 210 struct AutoName {}; 211 // Indicates that the track name is synthesied in trace processor as a 212 // StringId and works this way due to legacy reasons. 213 // 214 // Tracks *MUST NOT* use this method: this only exists for legacy tracks which 215 // we named before the introduction of classification/dimension system. 216 struct LegacyStringIdName { 217 StringId id; 218 }; 219 // Indicates that the track name is synthesied in trace processor as a 220 // StackString and works this way due to legacy reasons. 221 // 222 // Tracks *MUST NOT* use this method: this only exists for legacy tracks which 223 // we named before the introduction of classification/dimension system. 224 struct LegacyCharArrayName { 225 template <size_t N> LegacyCharArrayNameLegacyCharArrayName226 explicit LegacyCharArrayName(const char (&_name)[N]) { 227 static_assert(N > 0 && N <= 512); 228 base::StringCopy(name, _name, N); 229 } 230 template <size_t N> LegacyCharArrayNameLegacyCharArrayName231 explicit LegacyCharArrayName(const base::StackString<N>& _name) { 232 static_assert(N > 0 && N <= 512); 233 base::StringCopy(name, _name.c_str(), N); 234 } 235 char name[512]; 236 }; 237 using TrackName = std:: 238 variant<AutoName, LegacyStringIdName, LegacyCharArrayName>; 239 CreateDimensionsBuilder()240 DimensionsBuilder CreateDimensionsBuilder() { 241 return DimensionsBuilder(this); 242 } 243 244 // Interns a thread track into the storage. 245 TrackId InternThreadTrack(UniqueTid, const TrackName& = AutoName()); 246 247 // Interns a process track into the storage. 248 TrackId InternProcessTrack(tracks::TrackClassification, 249 UniquePid, 250 const TrackName& = AutoName()); 251 252 // Interns a global track keyed by track type + CPU into the storage. 253 TrackId InternCpuTrack(tracks::TrackClassification, 254 uint32_t cpu, 255 const TrackName& = AutoName()); 256 257 // Everything below this point are legacy functions and should no longer be 258 // used. 259 260 TrackId LegacyInternLegacyChromeAsyncTrack(StringId name, 261 uint32_t upid, 262 int64_t trace_id, 263 bool trace_id_is_process_scoped, 264 StringId source_scope); 265 266 TrackId LegacyInternGpuTrack(const tables::GpuTrackTable::Row&); 267 268 private: 269 friend class AsyncTrackSetTracker; 270 friend class TrackEventTracker; 271 272 struct TrackMapKey { 273 tracks::TrackClassification classification; 274 std::optional<Dimensions> dimensions; 275 276 bool operator==(const TrackMapKey& k) const { 277 return std::tie(classification, dimensions) == 278 std::tie(k.classification, k.dimensions); 279 } 280 }; 281 282 struct MapHasher { operatorMapHasher283 size_t operator()(const TrackMapKey& l) const { 284 perfetto::base::Hasher hasher; 285 hasher.Update(static_cast<uint32_t>(l.classification)); 286 hasher.Update(l.dimensions ? l.dimensions->arg_set_id : -1ll); 287 return hasher.digest(); 288 } 289 }; 290 291 TrackId CreateTrack(tracks::TrackClassification, 292 std::optional<Dimensions>, 293 const TrackName&); 294 295 TrackId CreateThreadTrack(tracks::TrackClassification, 296 UniqueTid, 297 const TrackName&); 298 299 TrackId CreateProcessTrack(tracks::TrackClassification, 300 UniquePid, 301 std::optional<Dimensions>, 302 const TrackName&); 303 304 StringId StringIdFromTrackName(tracks::TrackClassification classification, 305 const TrackTracker::TrackName& name); 306 307 TrackId AddTrack(const tracks::BlueprintBase&, 308 StringId, 309 StringId, 310 GlobalArgsTracker::CompactArg*, 311 uint32_t, 312 const SetArgsCallback&); 313 314 template <size_t i, typename TupleDimensions> DimensionsToArgs(const TupleDimensions & dimensions,const tracks::DimensionBlueprintBase * dimensions_schema,GlobalArgsTracker::CompactArg * a)315 void DimensionsToArgs(const TupleDimensions& dimensions, 316 const tracks::DimensionBlueprintBase* dimensions_schema, 317 GlobalArgsTracker::CompactArg* a) { 318 static constexpr size_t kTupleSize = std::tuple_size_v<TupleDimensions>; 319 if constexpr (i < kTupleSize) { 320 using elem_t = std::tuple_element_t<i, TupleDimensions>; 321 if constexpr (std::is_same_v<elem_t, uint32_t>) { 322 if (dimensions_schema[i].is_cpu) { 323 MarkCpuValid(std::get<i>(dimensions)); 324 } 325 a[i].value = Variadic::Integer(std::get<i>(dimensions)); 326 } else if constexpr (std::is_integral_v<elem_t>) { 327 a[i].value = Variadic::Integer(std::get<i>(dimensions)); 328 } else { 329 static_assert(std::is_same_v<elem_t, base::StringView>, 330 "Unknown type for dimension"); 331 a[i].value = Variadic::String( 332 context_->storage->InternString(std::get<i>(dimensions))); 333 } 334 DimensionsToArgs<i + 1>(dimensions, dimensions_schema, a); 335 } 336 } 337 338 void MarkCpuValid(uint32_t cpu); 339 SingleDimension(StringId key,const Variadic & val)340 Dimensions SingleDimension(StringId key, const Variadic& val) { 341 std::array args{GlobalArgsTracker::CompactArg{key, key, val}}; 342 return Dimensions{ 343 context_->global_args_tracker->AddArgSet(args.data(), 0, 1)}; 344 } 345 346 base::FlatHashMap<TrackMapKey, TrackId, MapHasher> tracks_; 347 base::FlatHashMap<uint64_t, TrackId, base::AlreadyHashed<uint64_t>> 348 tracks_new_; 349 350 const StringId source_key_; 351 const StringId trace_id_key_; 352 const StringId trace_id_is_process_scoped_key_; 353 const StringId source_scope_key_; 354 const StringId category_key_; 355 const StringId scope_id_; 356 const StringId cookie_id_; 357 358 const StringId fuchsia_source_; 359 const StringId chrome_source_; 360 361 const StringId utid_id_; 362 const StringId upid_id_; 363 const StringId cpu_id_; 364 const StringId uid_id_; 365 const StringId gpu_id_; 366 const StringId name_id_; 367 368 TraceProcessorContext* const context_; 369 ArgsTracker args_tracker_; 370 }; 371 372 } // namespace perfetto::trace_processor 373 374 #endif // SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_TRACK_TRACKER_H_ 375