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 INCLUDE_PERFETTO_TRACING_INTERNAL_TRACK_EVENT_INTERNAL_H_
18 #define INCLUDE_PERFETTO_TRACING_INTERNAL_TRACK_EVENT_INTERNAL_H_
19
20 #include "perfetto/base/flat_set.h"
21 #include "perfetto/protozero/scattered_heap_buffer.h"
22 #include "perfetto/tracing/core/forward_decls.h"
23 #include "perfetto/tracing/data_source.h"
24 #include "perfetto/tracing/debug_annotation.h"
25 #include "perfetto/tracing/trace_writer_base.h"
26 #include "perfetto/tracing/traced_value.h"
27 #include "perfetto/tracing/track.h"
28 #include "protos/perfetto/common/builtin_clock.pbzero.h"
29 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
30 #include "protos/perfetto/trace/track_event/track_event.pbzero.h"
31
32 #include <unordered_map>
33
34 namespace perfetto {
35
36 // Represents a point in time for the clock specified by |clock_id|.
37 struct TraceTimestamp {
38 // Clock IDs have the following semantic:
39 // [1, 63]: Builtin types, see BuiltinClock from
40 // ../common/builtin_clock.proto.
41 // [64, 127]: User-defined clocks. These clocks are sequence-scoped. They
42 // are only valid within the same |trusted_packet_sequence_id|
43 // (i.e. only for TracePacket(s) emitted by the same TraceWriter
44 // that emitted the clock snapshot).
45 // [128, MAX]: Reserved for future use. The idea is to allow global clock
46 // IDs and setting this ID to hash(full_clock_name) & ~127.
47 // Learn more: `clock_snapshot.proto`
48 uint32_t clock_id;
49 uint64_t value;
50 };
51
52 class EventContext;
53 class TrackEventSessionObserver;
54 struct Category;
55 struct TraceTimestamp;
56 namespace protos {
57 namespace gen {
58 class TrackEventConfig;
59 } // namespace gen
60 namespace pbzero {
61 class DebugAnnotation;
62 } // namespace pbzero
63 } // namespace protos
64
65 // A callback interface for observing track event tracing sessions starting and
66 // stopping. See TrackEvent::{Add,Remove}SessionObserver. Note that all methods
67 // will be called on an internal Perfetto thread.
68 class PERFETTO_EXPORT_COMPONENT TrackEventSessionObserver {
69 public:
70 virtual ~TrackEventSessionObserver();
71 // Called when a track event tracing session is configured. Note tracing isn't
72 // active yet, so track events emitted here won't be recorded. See
73 // DataSourceBase::OnSetup.
74 virtual void OnSetup(const DataSourceBase::SetupArgs&);
75 // Called when a track event tracing session is started. It is possible to
76 // emit track events from this callback.
77 virtual void OnStart(const DataSourceBase::StartArgs&);
78 // Called when a track event tracing session is stopped. It is still possible
79 // to emit track events from this callback.
80 virtual void OnStop(const DataSourceBase::StopArgs&);
81 // Called when tracing muxer requests to clear incremental state.
82 virtual void WillClearIncrementalState(
83 const DataSourceBase::ClearIncrementalStateArgs&);
84 };
85
86 // A class that the embedder can store arbitrary data user data per thread.
87 class PERFETTO_EXPORT_COMPONENT TrackEventTlsStateUserData {
88 public:
89 TrackEventTlsStateUserData() = default;
90 // Not clonable.
91 TrackEventTlsStateUserData(const TrackEventTlsStateUserData&) = delete;
92 TrackEventTlsStateUserData& operator=(const TrackEventTlsStateUserData&) =
93 delete;
94
95 virtual ~TrackEventTlsStateUserData();
96 };
97
98 namespace internal {
99 class TrackEventCategoryRegistry;
100
101 class PERFETTO_EXPORT_COMPONENT BaseTrackEventInternedDataIndex {
102 public:
103 virtual ~BaseTrackEventInternedDataIndex();
104
105 #if PERFETTO_DCHECK_IS_ON()
106 const char* type_id_ = nullptr;
107 const void* add_function_ptr_ = nullptr;
108 #endif // PERFETTO_DCHECK_IS_ON()
109 };
110
111 struct TrackEventTlsState {
112 template <typename TraceContext>
113 explicit TrackEventTlsState(const TraceContext& trace_context);
114 bool enable_thread_time_sampling = false;
115 bool filter_debug_annotations = false;
116 bool filter_dynamic_event_names = false;
117 uint64_t timestamp_unit_multiplier = 1;
118 uint32_t default_clock;
119 std::map<const void*, std::unique_ptr<TrackEventTlsStateUserData>> user_data;
120 };
121
122 struct TrackEventIncrementalState {
123 static constexpr size_t kMaxInternedDataFields = 32;
124
125 // Packet-sequence-scoped clock that encodes nanosecond timestamps in the
126 // domain of the clock returned by GetClockId() as delta values - see
127 // Clock::is_incremental in perfetto/trace/clock_snapshot.proto.
128 // Default unit: nanoseconds.
129 static constexpr uint32_t kClockIdIncremental = 64;
130
131 // Packet-sequence-scoped clock that encodes timestamps in the domain of the
132 // clock returned by GetClockId() with custom unit_multiplier.
133 // Default unit: nanoseconds.
134 static constexpr uint32_t kClockIdAbsolute = 65;
135
136 bool was_cleared = true;
137
138 // A heap-allocated message for storing newly seen interned data while we are
139 // in the middle of writing a track event. When a track event wants to write
140 // new interned data into the trace, it is first serialized into this message
141 // and then flushed to the real trace in EventContext when the packet ends.
142 // The message is cached here as a part of incremental state so that we can
143 // reuse the underlying buffer allocation for subsequently written interned
144 // data.
145 protozero::HeapBuffered<protos::pbzero::InternedData>
146 serialized_interned_data;
147
148 // In-memory indices for looking up interned data ids.
149 // For each intern-able field (up to a max of 32) we keep a dictionary of
150 // field-value -> interning-key. Depending on the type we either keep the full
151 // value or a hash of it (See track_event_interned_data_index.h)
152 using InternedDataIndex =
153 std::pair</* interned_data.proto field number */ size_t,
154 std::unique_ptr<BaseTrackEventInternedDataIndex>>;
155 std::array<InternedDataIndex, kMaxInternedDataFields> interned_data_indices =
156 {};
157
158 // Track uuids for which we have written descriptors into the trace. If a
159 // trace event uses a track which is not in this set, we'll write out a
160 // descriptor for it.
161 base::FlatSet<uint64_t> seen_tracks;
162
163 // Dynamically registered category names that have been encountered during
164 // this tracing session. The value in the map indicates whether the category
165 // is enabled or disabled.
166 std::unordered_map<std::string, bool> dynamic_categories;
167
168 // The latest reference timestamp that was used in a TracePacket or in a
169 // ClockSnapshot. The increment between this timestamp and the current trace
170 // time (GetTimeNs) is a value in kClockIdIncremental's domain.
171 uint64_t last_timestamp_ns = 0;
172
173 // The latest known counter values that was used in a TracePacket for each
174 // counter track. The key (uint64_t) is the uuid of counter track.
175 // The value is used for delta encoding of counter values.
176 std::unordered_map<uint64_t, int64_t> last_counter_value_per_track;
177 int64_t last_thread_time_ns = 0;
178 };
179
180 // The backend portion of the track event trace point implemention. Outlined to
181 // a separate .cc file so it can be shared by different track event category
182 // namespaces.
183 class PERFETTO_EXPORT_COMPONENT TrackEventInternal {
184 public:
185 static bool Initialize(
186 const TrackEventCategoryRegistry&,
187 bool (*register_data_source)(const DataSourceDescriptor&));
188
189 static bool AddSessionObserver(const TrackEventCategoryRegistry&,
190 TrackEventSessionObserver*);
191 static void RemoveSessionObserver(const TrackEventCategoryRegistry&,
192 TrackEventSessionObserver*);
193
194 static void EnableTracing(const TrackEventCategoryRegistry& registry,
195 const protos::gen::TrackEventConfig& config,
196 const DataSourceBase::SetupArgs&);
197 static void OnStart(const TrackEventCategoryRegistry&,
198 const DataSourceBase::StartArgs&);
199 static void OnStop(const TrackEventCategoryRegistry&,
200 const DataSourceBase::StopArgs&);
201 static void DisableTracing(const TrackEventCategoryRegistry& registry,
202 uint32_t internal_instance_index);
203 static void WillClearIncrementalState(
204 const TrackEventCategoryRegistry&,
205 const DataSourceBase::ClearIncrementalStateArgs&);
206
207 static bool IsCategoryEnabled(const TrackEventCategoryRegistry& registry,
208 const protos::gen::TrackEventConfig& config,
209 const Category& category);
210
211 static void WriteEventName(perfetto::DynamicString event_name,
212 perfetto::EventContext& event_ctx,
213 const TrackEventTlsState&);
214
215 static void WriteEventName(perfetto::StaticString event_name,
216 perfetto::EventContext& event_ctx,
217 const TrackEventTlsState&);
218
219 static perfetto::EventContext WriteEvent(
220 TraceWriterBase*,
221 TrackEventIncrementalState*,
222 TrackEventTlsState& tls_state,
223 const Category* category,
224 perfetto::protos::pbzero::TrackEvent::Type,
225 const TraceTimestamp& timestamp,
226 bool on_current_thread_track);
227
ResetIncrementalStateIfRequired(TraceWriterBase * trace_writer,TrackEventIncrementalState * incr_state,const TrackEventTlsState & tls_state,const TraceTimestamp & timestamp)228 static void ResetIncrementalStateIfRequired(
229 TraceWriterBase* trace_writer,
230 TrackEventIncrementalState* incr_state,
231 const TrackEventTlsState& tls_state,
232 const TraceTimestamp& timestamp) {
233 if (incr_state->was_cleared) {
234 incr_state->was_cleared = false;
235 ResetIncrementalState(trace_writer, incr_state, tls_state, timestamp);
236 }
237 }
238
239 // TODO(altimin): Remove this method once Chrome uses
240 // EventContext::AddDebugAnnotation directly.
241 template <typename NameType, typename ValueType>
AddDebugAnnotation(perfetto::EventContext * event_ctx,NameType && name,ValueType && value)242 static void AddDebugAnnotation(perfetto::EventContext* event_ctx,
243 NameType&& name,
244 ValueType&& value) {
245 auto annotation =
246 AddDebugAnnotation(event_ctx, std::forward<NameType>(name));
247 WriteIntoTracedValue(
248 internal::CreateTracedValueFromProto(annotation, event_ctx),
249 std::forward<ValueType>(value));
250 }
251
252 // If the given track hasn't been seen by the trace writer yet, write a
253 // descriptor for it into the trace. Doesn't take a lock unless the track
254 // descriptor is new.
255 template <typename TrackType>
WriteTrackDescriptorIfNeeded(const TrackType & track,TraceWriterBase * trace_writer,TrackEventIncrementalState * incr_state,const TrackEventTlsState & tls_state,const TraceTimestamp & timestamp)256 static void WriteTrackDescriptorIfNeeded(
257 const TrackType& track,
258 TraceWriterBase* trace_writer,
259 TrackEventIncrementalState* incr_state,
260 const TrackEventTlsState& tls_state,
261 const TraceTimestamp& timestamp) {
262 uint64_t uuid = track.uuid;
263 if (uuid) {
264 auto it_and_inserted = incr_state->seen_tracks.insert(uuid);
265 if (PERFETTO_LIKELY(!it_and_inserted.second))
266 return;
267 uuid = WriteTrackDescriptor(track, trace_writer, incr_state, tls_state,
268 timestamp);
269 }
270 while (uuid) {
271 auto it_and_inserted = incr_state->seen_tracks.insert(uuid);
272 if (PERFETTO_LIKELY(!it_and_inserted.second))
273 return;
274 std::optional<TrackRegistry::TrackInfo> track_info =
275 TrackRegistry::Get()->FindTrackInfo(uuid);
276 if (!track_info) {
277 return;
278 }
279 TrackRegistry::WriteTrackDescriptor(
280 std::move(track_info->desc),
281 NewTracePacket(trace_writer, incr_state, tls_state, timestamp));
282 uuid = track_info->parent_uuid;
283 }
284 }
285
286 // Unconditionally write a track descriptor into the trace.
287 //
288 // Returns the parent track uuid.
289 template <typename TrackType>
WriteTrackDescriptor(const TrackType & track,TraceWriterBase * trace_writer,TrackEventIncrementalState * incr_state,const TrackEventTlsState & tls_state,const TraceTimestamp & timestamp)290 static uint64_t WriteTrackDescriptor(const TrackType& track,
291 TraceWriterBase* trace_writer,
292 TrackEventIncrementalState* incr_state,
293 const TrackEventTlsState& tls_state,
294 const TraceTimestamp& timestamp) {
295 ResetIncrementalStateIfRequired(trace_writer, incr_state, tls_state,
296 timestamp);
297 return TrackRegistry::Get()->SerializeTrack(
298 track, NewTracePacket(trace_writer, incr_state, tls_state, timestamp));
299 }
300
301 // Get the current time in nanoseconds in the trace clock timebase.
302 static uint64_t GetTimeNs();
303
304 static TraceTimestamp GetTraceTime();
305
GetClockId()306 static inline protos::pbzero::BuiltinClock GetClockId() { return clock_; }
SetClockId(protos::pbzero::BuiltinClock clock)307 static inline void SetClockId(protos::pbzero::BuiltinClock clock) {
308 clock_ = clock;
309 }
310
GetDisallowMergingWithSystemTracks()311 static inline bool GetDisallowMergingWithSystemTracks() {
312 return disallow_merging_with_system_tracks_;
313 }
SetDisallowMergingWithSystemTracks(bool disallow_merging_with_system_tracks)314 static inline void SetDisallowMergingWithSystemTracks(
315 bool disallow_merging_with_system_tracks) {
316 disallow_merging_with_system_tracks_ = disallow_merging_with_system_tracks;
317 }
318
319 static int GetSessionCount();
320
321 // Represents the default track for the calling thread.
322 static const Track kDefaultTrack;
323
324 private:
325 static void ResetIncrementalState(TraceWriterBase* trace_writer,
326 TrackEventIncrementalState* incr_state,
327 const TrackEventTlsState& tls_state,
328 const TraceTimestamp& timestamp);
329
330 static protozero::MessageHandle<protos::pbzero::TracePacket> NewTracePacket(
331 TraceWriterBase*,
332 TrackEventIncrementalState*,
333 const TrackEventTlsState& tls_state,
334 TraceTimestamp,
335 uint32_t seq_flags =
336 protos::pbzero::TracePacket::SEQ_NEEDS_INCREMENTAL_STATE);
337
338 static protos::pbzero::DebugAnnotation* AddDebugAnnotation(
339 perfetto::EventContext*,
340 const char* name);
341
342 static protos::pbzero::DebugAnnotation* AddDebugAnnotation(
343 perfetto::EventContext*,
344 perfetto::DynamicString name);
345
346 static std::atomic<int> session_count_;
347
348 static protos::pbzero::BuiltinClock clock_;
349 static bool disallow_merging_with_system_tracks_;
350 };
351
352 template <typename TraceContext>
TrackEventTlsState(const TraceContext & trace_context)353 TrackEventTlsState::TrackEventTlsState(const TraceContext& trace_context) {
354 auto locked_ds = trace_context.GetDataSourceLocked();
355 bool disable_incremental_timestamps = false;
356 if (locked_ds.valid()) {
357 const auto& config = locked_ds->GetConfig();
358 disable_incremental_timestamps = config.disable_incremental_timestamps();
359 filter_debug_annotations = config.filter_debug_annotations();
360 filter_dynamic_event_names = config.filter_dynamic_event_names();
361 enable_thread_time_sampling = config.enable_thread_time_sampling();
362 if (config.has_timestamp_unit_multiplier()) {
363 timestamp_unit_multiplier = config.timestamp_unit_multiplier();
364 }
365 }
366 if (disable_incremental_timestamps) {
367 if (timestamp_unit_multiplier == 1) {
368 default_clock = static_cast<uint32_t>(TrackEventInternal::GetClockId());
369 } else {
370 default_clock = TrackEventIncrementalState::kClockIdAbsolute;
371 }
372 } else {
373 default_clock = TrackEventIncrementalState::kClockIdIncremental;
374 }
375 }
376
377 } // namespace internal
378 } // namespace perfetto
379
380 #endif // INCLUDE_PERFETTO_TRACING_INTERNAL_TRACK_EVENT_INTERNAL_H_
381