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,bool no_flush,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 bool no_flush, 57 CreateCustomTlsFn create_custom_tls_fn, 58 CreateIncrementalStateFn create_incremental_state_fn, 59 void* user_arg) { 60 buffer_exhausted_policy_ = buffer_exhausted_policy; 61 create_custom_tls_fn_ = create_custom_tls_fn; 62 create_incremental_state_fn_ = create_incremental_state_fn; 63 user_arg_ = user_arg; 64 auto* tracing_impl = TracingMuxer::Get(); 65 return tracing_impl->RegisterDataSource(descriptor, factory, params, 66 no_flush, &state_); 67 } 68 69 // Updates the data source type descriptor. UpdateDescriptor(const DataSourceDescriptor & descriptor)70 void UpdateDescriptor(const DataSourceDescriptor& descriptor) { 71 auto* tracing_impl = TracingMuxer::Get(); 72 tracing_impl->UpdateDataSourceDescriptor(descriptor, &state_); 73 } 74 75 // The beginning of a trace point. 76 // 77 // `tls_state` must point to a thread local variable that caches a pointer to 78 // an internal per data source type thread local state. 79 // 80 // `instances` must point to a copy of the current active instances for the 81 // data source type. 82 // 83 // `DataSourceTraits` can be used to customize the thread local storage used 84 // for the data source type. 85 // 86 // `TracePointTraits` and `trace_point_data` are customization point for 87 // getting the active instances bitmap. 88 // 89 // If this returns false, the trace point must be skipped. 90 template <typename DataSourceTraits, typename TracePointTraits> TracePrologue(DataSourceThreadLocalState ** tls_state,uint32_t * instances,typename TracePointTraits::TracePointData trace_point_data)91 bool TracePrologue( 92 DataSourceThreadLocalState** tls_state, 93 uint32_t* instances, 94 typename TracePointTraits::TracePointData trace_point_data) { 95 // See tracing_muxer.h for the structure of the TLS. 96 if (PERFETTO_UNLIKELY(!*tls_state)) { 97 *tls_state = GetOrCreateDataSourceTLS<DataSourceTraits>(); 98 // If the TLS hasn't been obtained yet, it's possible that this thread 99 // hasn't observed the initialization of global state like the muxer yet. 100 // To ensure that the thread "sees" the effects of such initialization, 101 // we have to reload |instances| with an acquire fence, ensuring that any 102 // initialization performed before instances was updated is visible 103 // in this thread. 104 *instances &= TracePointTraits::GetActiveInstances(trace_point_data) 105 ->load(std::memory_order_acquire); 106 if (!*instances) 107 return false; 108 } 109 auto* tracing_impl = TracingMuxer::Get(); 110 111 // Avoid re-entering the trace point recursively. 112 if (PERFETTO_UNLIKELY((*tls_state)->root_tls->is_in_trace_point)) 113 return false; 114 115 (*tls_state)->root_tls->is_in_trace_point = true; 116 117 // TracingTLS::generation is a global monotonic counter that is incremented 118 // every time a tracing session is stopped. We use that as a signal to force 119 // a slow-path garbage collection of all the trace writers for the current 120 // thread and to destroy the ones that belong to tracing sessions that have 121 // ended. This is to avoid having too many TraceWriter instances alive, each 122 // holding onto one chunk of the shared memory buffer. 123 // Rationale why memory_order_relaxed should be fine: 124 // - The TraceWriter object that we use is always constructed and destructed 125 // on the current thread. There is no risk of accessing a half-initialized 126 // TraceWriter (which would be really bad). 127 // - In the worst case, in the case of a race on the generation check, we 128 // might end up using a TraceWriter for the same data source that belongs 129 // to a stopped session. This is not really wrong, as we don't give any 130 // guarantee on the global atomicity of the stop. In the worst case the 131 // service will reject the data commit if this arrives too late. 132 133 if (PERFETTO_UNLIKELY( 134 (*tls_state)->root_tls->generation != 135 tracing_impl->generation(std::memory_order_relaxed))) { 136 // Will update root_tls->generation. 137 tracing_impl->DestroyStoppedTraceWritersForCurrentThread(); 138 } 139 140 return true; 141 } 142 143 // Must be called at the ending of a trace point that was not skipped. TraceEpilogue(DataSourceThreadLocalState * tls_state)144 void TraceEpilogue(DataSourceThreadLocalState* tls_state) { 145 tls_state->root_tls->is_in_trace_point = false; 146 } 147 148 struct InstancesIterator { 149 // A bitmap of the currenly active instances. 150 uint32_t cached_instances; 151 // The current instance index. 152 uint32_t i; 153 // The current instance. If this is `nullptr`, the iteration is over. 154 DataSourceInstanceThreadLocalState* instance; 155 }; 156 157 // Returns an iterator to the active instances of this data source type. 158 // 159 // `cached_instances` is a copy of the bitmap of the active instances for this 160 // data source type (usually just a copy of ValidInstances(), but can be 161 // customized). 162 // 163 // `tls_state` is the thread local pointer obtained from TracePrologue. 164 // 165 // `TracePointTraits` and `trace_point_data` are customization point for 166 // getting the active instances bitmap. 167 template <typename TracePointTraits> BeginIteration(uint32_t cached_instances,DataSourceThreadLocalState * tls_state,typename TracePointTraits::TracePointData trace_point_data)168 InstancesIterator BeginIteration( 169 uint32_t cached_instances, 170 DataSourceThreadLocalState* tls_state, 171 typename TracePointTraits::TracePointData trace_point_data) { 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( 187 InstancesIterator* iterator, 188 DataSourceThreadLocalState* tls_state, 189 typename TracePointTraits::TracePointData trace_point_data) { 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()->GetUnsafe(instance_index)->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()->GetUnsafe(instance_index)->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 auto* tracing_impl = TracingMuxer::Get(); 290 TracingTLS* root_tls = tracing_impl->GetOrCreateTracingTLS(); 291 DataSourceThreadLocalState* ds_tls = 292 DataSourceTraits::GetDataSourceTLS(&state_, root_tls); 293 // We keep re-initializing as the initialization is idempotent and not worth 294 // the code for extra checks. Also, ds_tls->static_state might point to 295 // another data source if ResetForTesting() has been used. 296 ds_tls->static_state = &state_; 297 assert(!ds_tls->root_tls || ds_tls->root_tls == root_tls); 298 ds_tls->root_tls = root_tls; 299 return ds_tls; 300 } 301 302 DataSourceStaticState state_; 303 BufferExhaustedPolicy buffer_exhausted_policy_{}; 304 CreateCustomTlsFn create_custom_tls_fn_ = nullptr; 305 CreateIncrementalStateFn create_incremental_state_fn_ = nullptr; 306 // User defined pointer that carries extra content for the fn_ callbacks 307 // above. Only used in the C shared library. 308 void* user_arg_ = nullptr; 309 }; 310 311 } // namespace internal 312 } // namespace perfetto 313 314 #endif // INCLUDE_PERFETTO_TRACING_INTERNAL_DATA_SOURCE_TYPE_H_ 315