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