• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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