1 #ifndef INCLUDE_PERFETTO_TRACING_INTERNAL_DATA_SOURCE_TYPE_H_ 2 #define INCLUDE_PERFETTO_TRACING_INTERNAL_DATA_SOURCE_TYPE_H_ 3 4 #include "perfetto/base/build_config.h" 5 #include "perfetto/base/export.h" 6 #include "perfetto/tracing/core/forward_decls.h" 7 #include "perfetto/tracing/internal/data_source_internal.h" 8 #include "perfetto/tracing/internal/tracing_muxer.h" 9 10 namespace perfetto { 11 namespace internal { 12 13 // Represents a data source type (not an instance). 14 // 15 // All the static state of a DataSource<T> lives here (including 16 // DataSourceStaticState). 17 // 18 // The C shared library API wrapper cannot use DataSource<T>, because it needs 19 // to create new data source types at runtime, so it uses this directly. 20 // 21 // The main reason why this intermediate class exist is to decouple the 22 // DataSourceStaticState from the specific DataSource<T>. The C API cannot 23 // dynamically create template instances and it needs a way to decouple those at 24 // runtime. 25 class PERFETTO_EXPORT_COMPONENT DataSourceType { 26 public: 27 // Function pointer type used to create custom per instance thread local 28 // state. 29 using CreateCustomTlsFn = 30 DataSourceInstanceThreadLocalState::ObjectWithDeleter (*)( 31 DataSourceInstanceThreadLocalState* tls_inst, 32 uint32_t instance_index, 33 void* user_arg); 34 // Function pointer type used to create custom per instance thread local 35 // incremental state (which might be cleared periodically by the tracing 36 // service). 37 using CreateIncrementalStateFn = 38 DataSourceInstanceThreadLocalState::ObjectWithDeleter (*)( 39 DataSourceInstanceThreadLocalState* tls_inst, 40 uint32_t instance_index, 41 void* user_arg); 42 43 // Registers the data source type with the central tracing muxer. 44 // * `descriptor` is the data source protobuf descriptor. 45 // * `factory` is a std::function used to create instances of the data source 46 // type. 47 // * `buffer_exhausted_policy` specifies what to do when the shared memory 48 // buffer runs out of chunks. 49 // * `create_custom_tls_fn` and `create_incremental_state_fn` are function 50 // pointers called to create custom state. They will receive `user_arg` as 51 // an extra param. Register(const DataSourceDescriptor & descriptor,TracingMuxer::DataSourceFactory factory,internal::DataSourceParams params,BufferExhaustedPolicy buffer_exhausted_policy,CreateCustomTlsFn create_custom_tls_fn,CreateIncrementalStateFn create_incremental_state_fn,void * user_arg)52 bool Register(const DataSourceDescriptor& descriptor, 53 TracingMuxer::DataSourceFactory factory, 54 internal::DataSourceParams params, 55 BufferExhaustedPolicy buffer_exhausted_policy, 56 CreateCustomTlsFn create_custom_tls_fn, 57 CreateIncrementalStateFn create_incremental_state_fn, 58 void* user_arg) { 59 buffer_exhausted_policy_ = buffer_exhausted_policy; 60 create_custom_tls_fn_ = create_custom_tls_fn; 61 create_incremental_state_fn_ = create_incremental_state_fn; 62 user_arg_ = user_arg; 63 auto* tracing_impl = TracingMuxer::Get(); 64 return tracing_impl->RegisterDataSource(descriptor, factory, params, 65 &state_); 66 } 67 68 // Updates the data source type descriptor. UpdateDescriptor(const DataSourceDescriptor & descriptor)69 void UpdateDescriptor(const DataSourceDescriptor& descriptor) { 70 auto* tracing_impl = TracingMuxer::Get(); 71 tracing_impl->UpdateDataSourceDescriptor(descriptor, &state_); 72 } 73 74 // The beginning of a trace point. 75 // 76 // `tls_state` must point to a thread local variable that caches a pointer to 77 // an internal per data source type thread local state. 78 // 79 // `instances` must point to a copy of the current active instances for the 80 // data source type. 81 // 82 // `DataSourceTraits` can be used to customize the thread local storage used 83 // for the data source type. 84 // 85 // `TracePointTraits` and `trace_point_data` are customization point for 86 // getting the active instances bitmap. 87 // 88 // If this returns false, the trace point must be skipped. 89 template <typename DataSourceTraits, typename TracePointTraits> TracePrologue(DataSourceThreadLocalState ** tls_state,uint32_t * instances,typename TracePointTraits::TracePointData trace_point_data)90 bool TracePrologue( 91 DataSourceThreadLocalState** tls_state, 92 uint32_t* instances, 93 typename TracePointTraits::TracePointData trace_point_data) { 94 // See tracing_muxer.h for the structure of the TLS. 95 if (PERFETTO_UNLIKELY(!*tls_state)) { 96 *tls_state = GetOrCreateDataSourceTLS<DataSourceTraits>(); 97 // If the TLS hasn't been obtained yet, it's possible that this thread 98 // hasn't observed the initialization of global state like the muxer yet. 99 // To ensure that the thread "sees" the effects of such initialization, 100 // we have to reload |instances| with an acquire fence, ensuring that any 101 // initialization performed before instances was updated is visible 102 // in this thread. 103 *instances &= TracePointTraits::GetActiveInstances(trace_point_data) 104 ->load(std::memory_order_acquire); 105 if (!*instances) 106 return false; 107 } 108 auto* tracing_impl = TracingMuxer::Get(); 109 110 // Avoid re-entering the trace point recursively. 111 if (PERFETTO_UNLIKELY((*tls_state)->root_tls->is_in_trace_point)) 112 return false; 113 114 (*tls_state)->root_tls->is_in_trace_point = true; 115 116 // TracingTLS::generation is a global monotonic counter that is incremented 117 // every time a tracing session is stopped. We use that as a signal to force 118 // a slow-path garbage collection of all the trace writers for the current 119 // thread and to destroy the ones that belong to tracing sessions that have 120 // ended. This is to avoid having too many TraceWriter instances alive, each 121 // holding onto one chunk of the shared memory buffer. 122 // Rationale why memory_order_relaxed should be fine: 123 // - The TraceWriter object that we use is always constructed and destructed 124 // on the current thread. There is no risk of accessing a half-initialized 125 // TraceWriter (which would be really bad). 126 // - In the worst case, in the case of a race on the generation check, we 127 // might end up using a TraceWriter for the same data source that belongs 128 // to a stopped session. This is not really wrong, as we don't give any 129 // guarantee on the global atomicity of the stop. In the worst case the 130 // service will reject the data commit if this arrives too late. 131 132 if (PERFETTO_UNLIKELY( 133 (*tls_state)->root_tls->generation != 134 tracing_impl->generation(std::memory_order_relaxed))) { 135 // Will update root_tls->generation. 136 tracing_impl->DestroyStoppedTraceWritersForCurrentThread(); 137 } 138 139 return true; 140 } 141 142 // Must be called at the ending of a trace point that was not skipped. TraceEpilogue(DataSourceThreadLocalState * tls_state)143 void TraceEpilogue(DataSourceThreadLocalState* tls_state) { 144 tls_state->root_tls->is_in_trace_point = false; 145 } 146 147 struct InstancesIterator { 148 // A bitmap of the currenly active instances. 149 uint32_t cached_instances; 150 // The current instance index. 151 uint32_t i; 152 // The current instance. If this is `nullptr`, the iteration is over. 153 DataSourceInstanceThreadLocalState* instance; 154 }; 155 156 // Returns an iterator to the active instances of this data source type. 157 // 158 // `cached_instances` is a copy of the bitmap of the active instances for this 159 // data source type (usually just a copy of ValidInstances(), but can be 160 // customized). 161 // 162 // `tls_state` is the thread local pointer obtained from TracePrologue. 163 // 164 // `TracePointTraits` and `trace_point_data` are customization point for 165 // getting the active instances bitmap. 166 template <typename TracePointTraits> BeginIteration(uint32_t cached_instances,DataSourceThreadLocalState * tls_state,typename TracePointTraits::TracePointData trace_point_data)167 InstancesIterator BeginIteration( 168 uint32_t cached_instances, 169 DataSourceThreadLocalState* tls_state, 170 typename TracePointTraits::TracePointData trace_point_data) 171 PERFETTO_ALWAYS_INLINE { 172 InstancesIterator it{}; 173 it.cached_instances = cached_instances; 174 FirstActiveInstance<TracePointTraits>(&it, tls_state, trace_point_data); 175 return it; 176 } 177 178 // Advances `*iterator` to point to the next active instance of this data 179 // source type. 180 // 181 // `tls_state` is the thread local pointer obtained from TracePrologue. 182 // 183 // `TracePointTraits` and `trace_point_data` are customization point for 184 // getting the active instances bitmap. 185 template <typename TracePointTraits> NextIteration(InstancesIterator * iterator,DataSourceThreadLocalState * tls_state,typename TracePointTraits::TracePointData trace_point_data)186 void NextIteration(InstancesIterator* iterator, 187 DataSourceThreadLocalState* tls_state, 188 typename TracePointTraits::TracePointData trace_point_data) 189 PERFETTO_ALWAYS_INLINE { 190 iterator->i++; 191 FirstActiveInstance<TracePointTraits>(iterator, tls_state, 192 trace_point_data); 193 } 194 GetIncrementalState(internal::DataSourceInstanceThreadLocalState * tls_inst,uint32_t instance_index)195 void* GetIncrementalState( 196 internal::DataSourceInstanceThreadLocalState* tls_inst, 197 uint32_t instance_index) { 198 // Recreate incremental state data if it has been reset by the service. 199 if (tls_inst->incremental_state_generation != 200 static_state()->incremental_state_generation.load( 201 std::memory_order_relaxed)) { 202 tls_inst->incremental_state.reset(); 203 CreateIncrementalState(tls_inst, instance_index); 204 } 205 return tls_inst->incremental_state.get(); 206 } 207 valid_instances()208 std::atomic<uint32_t>* valid_instances() { return &state_.valid_instances; } 209 static_state()210 DataSourceStaticState* static_state() { return &state_; } 211 212 private: CreateIncrementalState(internal::DataSourceInstanceThreadLocalState * tls_inst,uint32_t instance_index)213 void CreateIncrementalState( 214 internal::DataSourceInstanceThreadLocalState* tls_inst, 215 uint32_t instance_index) { 216 PERFETTO_DCHECK(create_incremental_state_fn_ != nullptr); 217 tls_inst->incremental_state = 218 create_incremental_state_fn_(tls_inst, instance_index, user_arg_); 219 tls_inst->incremental_state_generation = 220 static_state()->incremental_state_generation.load( 221 std::memory_order_relaxed); 222 } 223 224 void PopulateTlsInst(DataSourceInstanceThreadLocalState* tls_inst, 225 DataSourceState* instance_state, 226 uint32_t instance_index); 227 228 // Advances `*iterator` to the first active instance whose index is greater or 229 // equal than `iterator->i`. 230 template <typename TracePointTraits> FirstActiveInstance(InstancesIterator * iterator,DataSourceThreadLocalState * tls_state,typename TracePointTraits::TracePointData trace_point_data)231 void FirstActiveInstance( 232 InstancesIterator* iterator, 233 DataSourceThreadLocalState* tls_state, 234 typename TracePointTraits::TracePointData trace_point_data) { 235 iterator->instance = nullptr; 236 for (; iterator->i < kMaxDataSourceInstances; iterator->i++) { 237 DataSourceState* instance_state = 238 state_.TryGetCached(iterator->cached_instances, iterator->i); 239 if (!instance_state) 240 continue; 241 // Even if we passed the check above, the DataSourceInstance might be 242 // still destroyed concurrently while this code runs. The code below is 243 // designed to deal with such race, as follows: 244 // - We don't access the user-defined data source instance state. The only 245 // bits of state we use are |backend_id| and |buffer_id|. 246 // - Beyond those two integers, we access only the TraceWriter here. The 247 // TraceWriter is always safe because it lives on the TLS. 248 // - |instance_state| is backed by static storage, so the pointer is 249 // always valid, even after the data source instance is destroyed. 250 // - In the case of a race-on-destruction, we'll still see the latest 251 // backend_id and buffer_id and in the worst case keep trying writing 252 // into the tracing shared memory buffer after stopped. But this isn't 253 // really any worse than the case of the stop IPC being delayed by the 254 // kernel scheduler. The tracing service is robust against data commit 255 // attemps made after tracing is stopped. 256 // There is a theoretical race that would case the wrong behavior w.r.t 257 // writing data in the wrong buffer, but it's so rare that we ignore it: 258 // if the data source is stopped and started kMaxDataSourceInstances 259 // times (so that the same id is recycled) while we are in this function, 260 // we might end up reusing the old data source's backend_id and buffer_id 261 // for the new one, because we don't see the generation change past this 262 // point. But stopping and starting tracing (even once) takes so much 263 // handshaking to make this extremely unrealistic. 264 265 auto& tls_inst = tls_state->per_instance[iterator->i]; 266 if (PERFETTO_UNLIKELY(!tls_inst.trace_writer)) { 267 // Here we need an acquire barrier, which matches the release-store made 268 // by TracingMuxerImpl::SetupDataSource(), to ensure that the backend_id 269 // and buffer_id are consistent. 270 iterator->cached_instances &= 271 TracePointTraits::GetActiveInstances(trace_point_data) 272 ->load(std::memory_order_acquire); 273 instance_state = 274 state_.TryGetCached(iterator->cached_instances, iterator->i); 275 if (!instance_state || !instance_state->trace_lambda_enabled.load( 276 std::memory_order_relaxed)) 277 continue; 278 PopulateTlsInst(&tls_inst, instance_state, iterator->i); 279 } 280 iterator->instance = &tls_inst; 281 break; 282 } 283 } 284 285 // Note that the returned object is one per-thread per-data-source-type, NOT 286 // per data-source *instance*. 287 template <typename DataSourceTraits> GetOrCreateDataSourceTLS()288 DataSourceThreadLocalState* GetOrCreateDataSourceTLS() { 289 #if PERFETTO_BUILDFLAG(PERFETTO_OS_IOS) 290 PERFETTO_FATAL("Data source TLS not supported on iOS, see b/158814068"); 291 #endif 292 auto* tracing_impl = TracingMuxer::Get(); 293 TracingTLS* root_tls = tracing_impl->GetOrCreateTracingTLS(); 294 DataSourceThreadLocalState* ds_tls = 295 DataSourceTraits::GetDataSourceTLS(&state_, root_tls); 296 // We keep re-initializing as the initialization is idempotent and not worth 297 // the code for extra checks. Also, ds_tls->static_state might point to 298 // another data source if ResetForTesting() has been used. 299 ds_tls->static_state = &state_; 300 assert(!ds_tls->root_tls || ds_tls->root_tls == root_tls); 301 ds_tls->root_tls = root_tls; 302 return ds_tls; 303 } 304 305 DataSourceStaticState state_; 306 BufferExhaustedPolicy buffer_exhausted_policy_{}; 307 CreateCustomTlsFn create_custom_tls_fn_ = nullptr; 308 CreateIncrementalStateFn create_incremental_state_fn_ = nullptr; 309 // User defined pointer that carries extra content for the fn_ callbacks 310 // above. Only used in the C shared library. 311 void* user_arg_ = nullptr; 312 }; 313 314 } // namespace internal 315 } // namespace perfetto 316 317 #endif // INCLUDE_PERFETTO_TRACING_INTERNAL_DATA_SOURCE_TYPE_H_ 318