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 <tuple> 25 #include <type_traits> 26 #include <utility> 27 28 #include "perfetto/base/compiler.h" 29 #include "perfetto/ext/base/flat_hash_map.h" 30 #include "perfetto/ext/base/hash.h" 31 #include "perfetto/ext/base/string_view.h" 32 #include "perfetto/public/compiler.h" 33 #include "src/trace_processor/importers/common/args_tracker.h" 34 #include "src/trace_processor/importers/common/global_args_tracker.h" 35 #include "src/trace_processor/importers/common/tracks.h" 36 #include "src/trace_processor/importers/common/tracks_common.h" 37 #include "src/trace_processor/importers/common/tracks_internal.h" 38 #include "src/trace_processor/storage/trace_storage.h" 39 #include "src/trace_processor/tables/track_tables_py.h" 40 #include "src/trace_processor/types/trace_processor_context.h" 41 #include "src/trace_processor/types/variadic.h" 42 43 namespace perfetto::trace_processor { 44 45 // Tracks and stores tracks based on track types, ids and scopes. 46 class TrackTracker { 47 public: 48 using SetArgsCallback = std::function<void(ArgsTracker::BoundInserter&)>; 49 50 explicit TrackTracker(TraceProcessorContext*); 51 52 // Given a blueprint (i.e. the schema of a track), and the dimensions checks 53 // whether the track has been seen before and if so, returns the id of the 54 // seen track. 55 // 56 // If the track was *not* seen before, creates an entry in the track table 57 // and returns the id. 58 // 59 // Usage (for slice tracks): 60 // ``` 61 // void ParseMySpecialThreadScopedSlice(UniqueTid utid, ...(other args)) { 62 // static constexpr auto kBlueprint = tracks::SliceBlueprint( 63 // // The type of the track. 64 // "my_special_thread_scoped_slice", 65 // // The dimensions of the track. Can be >1 if the track is broken down 66 // // by multiple fields. 67 // tracks::DimensionBlueprints(tracks::kThreadDimension) 68 // ); 69 // TrackId track_id = track_tracker_->InternTrack( 70 // kBlueprint, tracks::Dimensions(utid)); 71 // 72 // ... add slices using SliceTracker 73 // } 74 // ``` 75 // 76 // Usage (for counter tracks): 77 // ``` 78 // void ParseMySpecialCustomScopedCounter(uint32_t custom_scope, 79 // ... other args) { 80 // static constexpr auto kBlueprint = tracks::CounterBlueprint( 81 // // The type of the track. 82 // "my_special_custom_scoped_counter", 83 // // The dimensions of the track. Can be >1 if the track is broken down 84 // // by multiple fields. 85 // tracks::DimensionBlueprints( 86 // tracks::UnitDimensionBlueprint("custom_scope")) 87 // ); 88 // TrackId track_id = track_tracker_->InternTrack( 89 // kBlueprint, tracks::Dimensions(custom_scope)); 90 // 91 // ... add counters using EventTracker 92 // } 93 // ``` 94 // 95 // Note: when using this function, always try and check the blueprints in 96 // `tracks_common.h` to see if there is a blueprint there which already does 97 // what you need. 98 template <typename BlueprintT> 99 PERFETTO_ALWAYS_INLINE TrackId InternTrack( 100 const BlueprintT& bp, 101 const typename BlueprintT::dimensions_t& dims = {}, 102 const typename BlueprintT::name_t& name = tracks::BlueprintName(), 103 const SetArgsCallback& args = {}, 104 const typename BlueprintT::unit_t& unit = tracks::BlueprintUnit()) { 105 return InternTrackInner(bp, dims, name, args, unit).first; 106 } 107 108 // Wrapper function for `InternTrack` in cases where you want the "main" 109 // slice track for the thread. 110 // 111 // This function should be used in situations where the thread cannot be 112 // executing anything else while the slice is active. It should *not* be used 113 // in cases where the function could overlap; use InternTrack directly with a 114 // custom blueprint. InternThreadTrack(UniqueTid utid)115 TrackId InternThreadTrack(UniqueTid utid) { 116 static constexpr auto kBlueprint = tracks::SliceBlueprint( 117 "thread_execution", 118 tracks::DimensionBlueprints(tracks::kThreadDimensionBlueprint)); 119 return InternTrack(kBlueprint, tracks::Dimensions(utid)); 120 } 121 122 // Wrapper function for `InternTrack` for legacy "async" style tracks which 123 // is supported by the Chrome JSON format and other derivative formats 124 // (e.g. Fuchsia). 125 // 126 // WARNING: this function should *not* be used by any users not explicitly 127 // approved and discussed with a trace processor maintainer. 128 TrackId InternLegacyAsyncTrack(StringId name, 129 uint32_t upid, 130 int64_t trace_id, 131 bool trace_id_is_process_scoped, 132 StringId source_scope); 133 134 private: 135 friend class TrackCompressor; 136 friend class TrackEventTracker; 137 138 TrackId AddTrack(const tracks::BlueprintBase&, 139 StringId, 140 StringId, 141 GlobalArgsTracker::CompactArg*, 142 uint32_t, 143 const SetArgsCallback&); 144 145 template <typename BlueprintT> 146 PERFETTO_ALWAYS_INLINE std::pair<TrackId, bool> InternTrackInner( 147 const BlueprintT& bp, 148 const typename BlueprintT::dimensions_t& dims = {}, 149 const typename BlueprintT::name_t& name = tracks::BlueprintName(), 150 const SetArgsCallback& args = {}, 151 const typename BlueprintT::unit_t& unit = tracks::BlueprintUnit()) { 152 uint64_t hash = tracks::HashFromBlueprintAndDimensions(bp, dims); 153 auto [it, inserted] = tracks_.Insert(hash, {}); 154 if (inserted) { 155 std::array<GlobalArgsTracker::CompactArg, 8> a; 156 DimensionsToArgs<0>(dims, bp.dimension_blueprints.data(), a.data()); 157 StringId n; 158 using NBT = tracks::NameBlueprintT; 159 using name_blueprint_t = typename BlueprintT::name_blueprint_t; 160 if constexpr (std::is_same_v<NBT::Auto, name_blueprint_t>) { 161 n = kNullStringId; 162 } else if constexpr (std::is_same_v<NBT::Static, name_blueprint_t>) { 163 n = context_->storage->InternString(bp.name_blueprint.name); 164 } else if constexpr (std::is_base_of_v<NBT::FnBase, name_blueprint_t>) { 165 n = context_->storage->InternString( 166 std::apply(bp.name_blueprint.fn, dims).string_view()); 167 } else { 168 static_assert(std::is_same_v<NBT::Dynamic, name_blueprint_t>); 169 n = name; 170 } 171 using UBT = tracks::UnitBlueprintT; 172 using unit_blueprint_t = typename BlueprintT::unit_blueprint_t; 173 StringId u; 174 if constexpr (std::is_same_v<UBT::Unknown, unit_blueprint_t>) { 175 u = kNullStringId; 176 } else if constexpr (std::is_same_v<UBT::Static, unit_blueprint_t>) { 177 u = context_->storage->InternString(bp.unit_blueprint.name); 178 } else { 179 static_assert(std::is_same_v<UBT::Dynamic, unit_blueprint_t>); 180 u = unit; 181 } 182 // GCC warns about the variables being unused even they are in certain 183 // constexpr branches above. Just use them here to suppress the warning. 184 base::ignore_result(name, unit); 185 static constexpr uint32_t kDimensionCount = 186 std::tuple_size_v<typename BlueprintT::dimensions_t>; 187 *it = AddTrack(bp, n, u, a.data(), kDimensionCount, args); 188 } 189 return std::make_pair(*it, inserted); 190 } 191 192 template <size_t i, typename TupleDimensions> DimensionsToArgs(const TupleDimensions & dimensions,const tracks::DimensionBlueprintBase * dimensions_schema,GlobalArgsTracker::CompactArg * a)193 void DimensionsToArgs(const TupleDimensions& dimensions, 194 const tracks::DimensionBlueprintBase* dimensions_schema, 195 GlobalArgsTracker::CompactArg* a) { 196 static constexpr size_t kTupleSize = std::tuple_size_v<TupleDimensions>; 197 if constexpr (i < kTupleSize) { 198 using elem_t = std::tuple_element_t<i, TupleDimensions>; 199 if constexpr (std::is_same_v<elem_t, uint32_t>) { 200 a[i].value = Variadic::Integer(std::get<i>(dimensions)); 201 } else if constexpr (std::is_integral_v<elem_t>) { 202 a[i].value = Variadic::Integer(std::get<i>(dimensions)); 203 } else { 204 static_assert(std::is_same_v<elem_t, base::StringView>, 205 "Unknown type for dimension"); 206 a[i].value = Variadic::String( 207 context_->storage->InternString(std::get<i>(dimensions))); 208 } 209 DimensionsToArgs<i + 1>(dimensions, dimensions_schema, a); 210 } 211 // Required for GCC to not complain. 212 base::ignore_result(dimensions_schema); 213 } 214 215 base::FlatHashMap<uint64_t, TrackId, base::AlreadyHashed<uint64_t>> tracks_; 216 217 const StringId source_key_; 218 const StringId trace_id_key_; 219 const StringId trace_id_is_process_scoped_key_; 220 const StringId upid_; 221 const StringId source_scope_key_; 222 const StringId chrome_source_; 223 224 TraceProcessorContext* const context_; 225 ArgsTracker args_tracker_; 226 }; 227 228 } // namespace perfetto::trace_processor 229 230 #endif // SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_TRACK_TRACKER_H_ 231