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 namespace internal {
87 class TrackEventCategoryRegistry;
88
89 class PERFETTO_EXPORT_COMPONENT BaseTrackEventInternedDataIndex {
90 public:
91 virtual ~BaseTrackEventInternedDataIndex();
92
93 #if PERFETTO_DCHECK_IS_ON()
94 const char* type_id_ = nullptr;
95 const void* add_function_ptr_ = nullptr;
96 #endif // PERFETTO_DCHECK_IS_ON()
97 };
98
99 struct TrackEventTlsState {
100 template <typename TraceContext>
101 explicit TrackEventTlsState(const TraceContext& trace_context);
102 bool enable_thread_time_sampling = false;
103 bool filter_debug_annotations = false;
104 bool filter_dynamic_event_names = false;
105 uint64_t timestamp_unit_multiplier = 1;
106 uint32_t default_clock;
107 };
108
109 struct TrackEventIncrementalState {
110 static constexpr size_t kMaxInternedDataFields = 32;
111
112 // Packet-sequence-scoped clock that encodes nanosecond timestamps in the
113 // domain of the clock returned by GetClockId() as delta values - see
114 // Clock::is_incremental in perfetto/trace/clock_snapshot.proto.
115 // Default unit: nanoseconds.
116 static constexpr uint32_t kClockIdIncremental = 64;
117
118 // Packet-sequence-scoped clock that encodes timestamps in the domain of the
119 // clock returned by GetClockId() with custom unit_multiplier.
120 // Default unit: nanoseconds.
121 static constexpr uint32_t kClockIdAbsolute = 65;
122
123 bool was_cleared = true;
124
125 // A heap-allocated message for storing newly seen interned data while we are
126 // in the middle of writing a track event. When a track event wants to write
127 // new interned data into the trace, it is first serialized into this message
128 // and then flushed to the real trace in EventContext when the packet ends.
129 // The message is cached here as a part of incremental state so that we can
130 // reuse the underlying buffer allocation for subsequently written interned
131 // data.
132 protozero::HeapBuffered<protos::pbzero::InternedData>
133 serialized_interned_data;
134
135 // In-memory indices for looking up interned data ids.
136 // For each intern-able field (up to a max of 32) we keep a dictionary of
137 // field-value -> interning-key. Depending on the type we either keep the full
138 // value or a hash of it (See track_event_interned_data_index.h)
139 using InternedDataIndex =
140 std::pair</* interned_data.proto field number */ size_t,
141 std::unique_ptr<BaseTrackEventInternedDataIndex>>;
142 std::array<InternedDataIndex, kMaxInternedDataFields> interned_data_indices =
143 {};
144
145 // Track uuids for which we have written descriptors into the trace. If a
146 // trace event uses a track which is not in this set, we'll write out a
147 // descriptor for it.
148 base::FlatSet<uint64_t> seen_tracks;
149
150 // Dynamically registered category names that have been encountered during
151 // this tracing session. The value in the map indicates whether the category
152 // is enabled or disabled.
153 std::unordered_map<std::string, bool> dynamic_categories;
154
155 // The latest reference timestamp that was used in a TracePacket or in a
156 // ClockSnapshot. The increment between this timestamp and the current trace
157 // time (GetTimeNs) is a value in kClockIdIncremental's domain.
158 uint64_t last_timestamp_ns = 0;
159
160 // The latest known counter values that was used in a TracePacket for each
161 // counter track. The key (uint64_t) is the uuid of counter track.
162 // The value is used for delta encoding of counter values.
163 std::unordered_map<uint64_t, int64_t> last_counter_value_per_track;
164 int64_t last_thread_time_ns = 0;
165 };
166
167 // The backend portion of the track event trace point implemention. Outlined to
168 // a separate .cc file so it can be shared by different track event category
169 // namespaces.
170 class PERFETTO_EXPORT_COMPONENT TrackEventInternal {
171 public:
172 static bool Initialize(
173 const TrackEventCategoryRegistry&,
174 bool (*register_data_source)(const DataSourceDescriptor&));
175
176 static bool AddSessionObserver(const TrackEventCategoryRegistry&,
177 TrackEventSessionObserver*);
178 static void RemoveSessionObserver(const TrackEventCategoryRegistry&,
179 TrackEventSessionObserver*);
180
181 static void EnableTracing(const TrackEventCategoryRegistry& registry,
182 const protos::gen::TrackEventConfig& config,
183 const DataSourceBase::SetupArgs&);
184 static void OnStart(const TrackEventCategoryRegistry&,
185 const DataSourceBase::StartArgs&);
186 static void OnStop(const TrackEventCategoryRegistry&,
187 const DataSourceBase::StopArgs&);
188 static void DisableTracing(const TrackEventCategoryRegistry& registry,
189 uint32_t internal_instance_index);
190 static void WillClearIncrementalState(
191 const TrackEventCategoryRegistry&,
192 const DataSourceBase::ClearIncrementalStateArgs&);
193
194 static bool IsCategoryEnabled(const TrackEventCategoryRegistry& registry,
195 const protos::gen::TrackEventConfig& config,
196 const Category& category);
197
198 static void WriteEventName(perfetto::DynamicString event_name,
199 perfetto::EventContext& event_ctx,
200 const TrackEventTlsState&);
201
202 static void WriteEventName(perfetto::StaticString event_name,
203 perfetto::EventContext& event_ctx,
204 const TrackEventTlsState&);
205
206 static perfetto::EventContext WriteEvent(
207 TraceWriterBase*,
208 TrackEventIncrementalState*,
209 const TrackEventTlsState& tls_state,
210 const Category* category,
211 perfetto::protos::pbzero::TrackEvent::Type,
212 const TraceTimestamp& timestamp,
213 bool on_current_thread_track);
214
ResetIncrementalStateIfRequired(TraceWriterBase * trace_writer,TrackEventIncrementalState * incr_state,const TrackEventTlsState & tls_state,const TraceTimestamp & timestamp)215 static void ResetIncrementalStateIfRequired(
216 TraceWriterBase* trace_writer,
217 TrackEventIncrementalState* incr_state,
218 const TrackEventTlsState& tls_state,
219 const TraceTimestamp& timestamp) {
220 if (incr_state->was_cleared) {
221 incr_state->was_cleared = false;
222 ResetIncrementalState(trace_writer, incr_state, tls_state, timestamp);
223 }
224 }
225
226 // TODO(altimin): Remove this method once Chrome uses
227 // EventContext::AddDebugAnnotation directly.
228 template <typename NameType, typename ValueType>
AddDebugAnnotation(perfetto::EventContext * event_ctx,NameType && name,ValueType && value)229 static void AddDebugAnnotation(perfetto::EventContext* event_ctx,
230 NameType&& name,
231 ValueType&& value) {
232 auto annotation =
233 AddDebugAnnotation(event_ctx, std::forward<NameType>(name));
234 WriteIntoTracedValue(
235 internal::CreateTracedValueFromProto(annotation, event_ctx),
236 std::forward<ValueType>(value));
237 }
238
239 // If the given track hasn't been seen by the trace writer yet, write a
240 // descriptor for it into the trace. Doesn't take a lock unless the track
241 // descriptor is new.
242 template <typename TrackType>
WriteTrackDescriptorIfNeeded(const TrackType & track,TraceWriterBase * trace_writer,TrackEventIncrementalState * incr_state,const TrackEventTlsState & tls_state,const TraceTimestamp & timestamp)243 static void WriteTrackDescriptorIfNeeded(
244 const TrackType& track,
245 TraceWriterBase* trace_writer,
246 TrackEventIncrementalState* incr_state,
247 const TrackEventTlsState& tls_state,
248 const TraceTimestamp& timestamp) {
249 auto it_and_inserted = incr_state->seen_tracks.insert(track.uuid);
250 if (PERFETTO_LIKELY(!it_and_inserted.second))
251 return;
252 WriteTrackDescriptor(track, trace_writer, incr_state, tls_state, timestamp);
253 }
254
255 // Unconditionally write a track descriptor into the trace.
256 template <typename TrackType>
WriteTrackDescriptor(const TrackType & track,TraceWriterBase * trace_writer,TrackEventIncrementalState * incr_state,const TrackEventTlsState & tls_state,const TraceTimestamp & timestamp)257 static void WriteTrackDescriptor(const TrackType& track,
258 TraceWriterBase* trace_writer,
259 TrackEventIncrementalState* incr_state,
260 const TrackEventTlsState& tls_state,
261 const TraceTimestamp& timestamp) {
262 ResetIncrementalStateIfRequired(trace_writer, incr_state, tls_state,
263 timestamp);
264 TrackRegistry::Get()->SerializeTrack(
265 track, NewTracePacket(trace_writer, incr_state, tls_state, timestamp));
266 }
267
268 // Get the current time in nanoseconds in the trace clock timebase.
269 static uint64_t GetTimeNs();
270
271 static TraceTimestamp GetTraceTime();
272
GetClockId()273 static inline protos::pbzero::BuiltinClock GetClockId() { return clock_; }
SetClockId(protos::pbzero::BuiltinClock clock)274 static inline void SetClockId(protos::pbzero::BuiltinClock clock) {
275 clock_ = clock;
276 }
277
GetDisallowMergingWithSystemTracks()278 static inline bool GetDisallowMergingWithSystemTracks() {
279 return disallow_merging_with_system_tracks_;
280 }
SetDisallowMergingWithSystemTracks(bool disallow_merging_with_system_tracks)281 static inline void SetDisallowMergingWithSystemTracks(
282 bool disallow_merging_with_system_tracks) {
283 disallow_merging_with_system_tracks_ = disallow_merging_with_system_tracks;
284 }
285
286 static int GetSessionCount();
287
288 // Represents the default track for the calling thread.
289 static const Track kDefaultTrack;
290
291 private:
292 static void ResetIncrementalState(TraceWriterBase* trace_writer,
293 TrackEventIncrementalState* incr_state,
294 const TrackEventTlsState& tls_state,
295 const TraceTimestamp& timestamp);
296
297 static protozero::MessageHandle<protos::pbzero::TracePacket> NewTracePacket(
298 TraceWriterBase*,
299 TrackEventIncrementalState*,
300 const TrackEventTlsState& tls_state,
301 TraceTimestamp,
302 uint32_t seq_flags =
303 protos::pbzero::TracePacket::SEQ_NEEDS_INCREMENTAL_STATE);
304
305 static protos::pbzero::DebugAnnotation* AddDebugAnnotation(
306 perfetto::EventContext*,
307 const char* name);
308
309 static protos::pbzero::DebugAnnotation* AddDebugAnnotation(
310 perfetto::EventContext*,
311 perfetto::DynamicString name);
312
313 static std::atomic<int> session_count_;
314
315 static protos::pbzero::BuiltinClock clock_;
316 static bool disallow_merging_with_system_tracks_;
317 };
318
319 template <typename TraceContext>
TrackEventTlsState(const TraceContext & trace_context)320 TrackEventTlsState::TrackEventTlsState(const TraceContext& trace_context) {
321 auto locked_ds = trace_context.GetDataSourceLocked();
322 bool disable_incremental_timestamps = false;
323 if (locked_ds.valid()) {
324 const auto& config = locked_ds->GetConfig();
325 disable_incremental_timestamps = config.disable_incremental_timestamps();
326 filter_debug_annotations = config.filter_debug_annotations();
327 filter_dynamic_event_names = config.filter_dynamic_event_names();
328 enable_thread_time_sampling = config.enable_thread_time_sampling();
329 if (config.has_timestamp_unit_multiplier()) {
330 timestamp_unit_multiplier = config.timestamp_unit_multiplier();
331 }
332 }
333 if (disable_incremental_timestamps) {
334 if (timestamp_unit_multiplier == 1) {
335 default_clock = static_cast<uint32_t>(TrackEventInternal::GetClockId());
336 } else {
337 default_clock = TrackEventIncrementalState::kClockIdAbsolute;
338 }
339 } else {
340 default_clock = TrackEventIncrementalState::kClockIdIncremental;
341 }
342 }
343
344 } // namespace internal
345 } // namespace perfetto
346
347 #endif // INCLUDE_PERFETTO_TRACING_INTERNAL_TRACK_EVENT_INTERNAL_H_
348