xref: /aosp_15_r20/external/perfetto/src/trace_processor/importers/common/track_tracker.h (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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