1 // Copyright 2017 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 //
6 // The ABI-stable entry points used by trace instrumentation libraries.
7 //
8 // Functions used by process-wide trace instrumentation to query the state
9 // of the trace engine and acquire the engine's trace context.
10 //
11 // The engine's trace context is initialized when the trace engine is started
12 // and is destroyed when the trace engine completely stops after all references
13 // have been released.
14 //
15 // Acquiring a reference to the engine's trace context is optimized for speed
16 // to be fail-fast and lock-free. This helps to ensure that trace
17 // instrumentation has negligible performance impact when tracing is disabled
18 // (on the order of nanoseconds) and only a small impact when tracing is enabled
19 // (on the order of tens to hundreds of nanoseconds depending on the complexity
20 // of the trace records being written).
21 //
22 // Client code shouldn't be using these APIs directly.
23 // See <trace/event.h> for instrumentation macros.
24 //
25
26 #pragma once
27
28 #include <stdbool.h>
29 #include <stdint.h>
30
31 #include <zircon/compiler.h>
32
33 #include <trace-engine/context.h>
34
35 __BEGIN_CDECLS
36
37 // Returns a new unique 64-bit unsigned integer (within this process).
38 // Each invocation returns a new unique non-zero value.
39 //
40 // Useful for generating unique correlation ids for async and flow events.
41 //
42 // This function is thread-safe and lock-free.
43 uint64_t trace_generate_nonce(void);
44
45 // Describes the state of the trace engine.
46 typedef enum {
47 // Trace instrumentation is inactive.
48 // Any data written into the trace buffer will be discarded.
49 TRACE_STOPPED = 0,
50 // Trace instrumentation is active.
51 TRACE_STARTED = 1,
52 // Trace instrumentation is active but is in the process of shutting down.
53 // Tracing will stop once all references to the trace buffer have been released.
54 TRACE_STOPPING = 2,
55 } trace_state_t;
56
57 // Gets the current state of the trace engine.
58 //
59 // This function is thread-safe.
60 trace_state_t trace_state(void);
61
62 // Returns true if tracing is enabled (started or stopping but not stopped).
63 //
64 // This function is thread-safe and lock-free.
trace_is_enabled(void)65 static inline bool trace_is_enabled(void) {
66 return trace_state() != TRACE_STOPPED;
67 }
68
69 // Returns true if tracing of the specified category has been enabled (which
70 // implies that |trace_is_enabled()| is also true).
71 //
72 // Use |trace_acquire_context_for_category()| if you intend to immediately
73 // write a record into the trace buffer after checking the category.
74 //
75 // |category_literal| must be a null-terminated static string constant.
76 //
77 // This function is thread-safe.
78 bool trace_is_category_enabled(const char* category_literal);
79
80 // Acquires a reference to the trace engine's context.
81 // Must be balanced by a call to |trace_release_context()| when the result is non-NULL.
82 //
83 // This function is optimized to return quickly when tracing is not enabled.
84 //
85 // Trace engine shutdown is deferred until all references to the trace context
86 // have been released, therefore it is important for clients to promptly
87 // release their reference to the trace context once they have finished
88 // writing records into the trace buffer.
89 // It is also important to release the context promptly to maintain proper
90 // operation in streaming mode: The buffer can't be saved until all writers
91 // have released their context.
92 //
93 // Returns a valid trace context if tracing is enabled.
94 // Returns NULL otherwise.
95 //
96 // This function is thread-safe, fail-fast, and lock-free.
97 trace_context_t* trace_acquire_context(void);
98
99 // Acquires a reference to the trace engine's context, only if the specified
100 // category is enabled. Must be balanced by a call to |trace_release_context()|
101 // when the result is non-NULL.
102 //
103 // This function is optimized to return quickly when tracing is not enabled.
104 //
105 // Trace engine shutdown is deferred until all references to the trace context
106 // have been released, therefore it is important for clients to promptly
107 // release their reference to the trace context once they have finished
108 // writing records into the trace buffer.
109 // It is also important to release the context promptly to maintain proper
110 // operation in streaming mode: The buffer can't be saved until all writers
111 // have released their context.
112 //
113 // This function is equivalent to calling |trace_acquire_context()| to acquire
114 // the engine's context, then calling |trace_context_register_category_literal()|
115 // to check whether the specified category is enabled and register it in the
116 // string table. It releases the context and returns NULL if the category
117 // is not enabled.
118 //
119 // |category_literal| must be a null-terminated static string constant.
120 // |out_ref| points to where the registered string reference should be returned.
121 //
122 // Returns a valid trace context if tracing is enabled for the specified category.
123 // Returns NULL otherwise.
124 //
125 // This function is thread-safe and lock-free.
126 trace_context_t* trace_acquire_context_for_category(const char* category_literal,
127 trace_string_ref_t* out_ref);
128
129 // Opaque type that is used to cache category enabled/disabled state.
130 // ["opaque" in the sense that client code must not touch it]
131 // The term "site" is used because it's relatively unique and because this type
132 // is generally used to record category state at TRACE_<event>() call sites.
133 typedef uintptr_t trace_site_state_t;
134 typedef struct {
135 // "state" is intentionally non-descript
136 trace_site_state_t state;
137 } trace_site_t;
138
139 // Same as |trace_acquire_context_for_category()| except includes an extra
140 // parameter to allow for caching of the category lookup.
141 //
142 // |category_literal| must be a null-terminated static string constant.
143 // |site_ptr| must point to a variable of static storage duration initialized
144 // to zero. A static local variable at the call site of recording a trace
145 // event is the normal solution. The caller must not touch the memory pointed
146 // to by this value, it is for the sole use of the trace engine.
147 // |out_ref| points to where the registered string reference should be returned.
148 //
149 // Returns a valid trace context if tracing is enabled for the specified category.
150 // Returns NULL otherwise.
151 //
152 // This function is thread-safe and lock-free.
153 trace_context_t* trace_acquire_context_for_category_cached(
154 const char* category_literal, trace_site_t* site_ptr,
155 trace_string_ref_t* out_ref);
156
157 // Flush the cache built up by calls to
158 // |trace_acquire_context_for_category_cached()|.
159 //
160 // The trace engine maintains this cache, but there is one case where it
161 // needs help: When a DSO containing cache state is unloaded; that is the
162 // |site_ptr| argument to a call to
163 // |trace_acquire_context_for_category_cached()| points into the soon to be
164 // unloaded DSO.
165 // This is normally not a problem as |dlclose()| is basically a nop.
166 // However, should a DSO get physically unloaded then this function must be
167 // called before the DSO is unloaded. The actual unloading procedure must be:
168 // 1) Stop execution in the DSO.
169 // 2) Stop tracing.
170 // 3) Call |trace_engine_flush_category_cache()|.
171 // 4) Unload DSO.
172 // (1,2) can be done in either order.
173 //
174 // Returns ZX_OK on success.
175 // Returns ZX_ERR_BAD_STATE if the engine is not stopped.
176 //
177 // This function is thread-safe.
178 zx_status_t trace_engine_flush_category_cache(void);
179
180 // Releases a reference to the trace engine's context.
181 // Must balance a prior successful call to |trace_acquire_context()|
182 // or |trace_acquire_context_for_category()|.
183 //
184 // |context| must be a valid trace context reference.
185 //
186 // This function is thread-safe, never-fail, and lock-free.
187 void trace_release_context(trace_context_t* context);
188
189 // Acquires a reference to the trace engine's context, for prolonged use.
190 // This cannot be used to acquire the context for the purposes of writing to
191 // the trace buffer. Instead, this is intended for uses like the ktrace
192 // provider where it wishes to hold a copy of the context for the duration of
193 // the trace.
194 // Must be balanced by a call to |trace_release_prolonged_context()| when the
195 // result is non-NULL.
196 //
197 // This function is optimized to return quickly when tracing is not enabled.
198 //
199 // Trace engine shutdown is deferred until all references to the trace context
200 // have been released, therefore it is important for clients to promptly
201 // release their reference to the trace context once they have finished with
202 // it.
203 //
204 // Returns a valid trace context if tracing is enabled.
205 // Returns NULL otherwise.
206 //
207 // This function is thread-safe, fail-fast, and lock-free.
208 trace_prolonged_context_t* trace_acquire_prolonged_context(void);
209
210 // Releases a reference to the trace engine's prolonged context.
211 // Must balance a prior successful call to |trace_acquire_prolonged_context()|.
212 //
213 // |context| must be a valid trace context reference.
214 //
215 // This function is thread-safe, never-fail, and lock-free.
216 void trace_release_prolonged_context(trace_prolonged_context_t* context);
217
218 // Registers an event handle which the trace engine will signal when the
219 // trace state or set of enabled categories changes.
220 //
221 // Trace observers can use this mechanism to activate custom instrumentation
222 // mechanisms and write collected information into the trace buffer in response
223 // to state changes.
224 //
225 // The protocol works like this:
226 //
227 // 1. The trace observer creates an event object (using |zx_event_create()| or
228 // equivalent) then calls |trace_register_observer()| to register itself.
229 // 2. The trace observer queries the current trace state and set of enabled categories.
230 // 3. If tracing is enabled, the trace observer configures itself to collect data
231 // and write trace records relevant to the set of enabled categories.
232 // 4. When the trace state and/or set of enabled categories changes, the trace engine
233 // sets the |ZX_EVENT_SIGNALED| signal bit of each |event| associated with
234 // currently registered observers.
235 // 5. In response to observing the |ZX_EVENT_SIGNALED| signal, the trace observer
236 // first clears the |ZX_EVENT_SIGNALED| bit (using |zx_object_signal()| or equivalent)
237 // then adjusts its behavior as in step 2 and 3 above, and then calls
238 // trace_notify_observer_updated().
239 // 6. When no longer interested in receiving events, the trace observer calls
240 // |trace_unregister_observer()| to unregister itself then closes the event handle.
241 //
242 // Returns |ZX_OK| if successful.
243 // Returns |ZX_ERR_INVALID_ARGS| if the event was already registered.
244 zx_status_t trace_register_observer(zx_handle_t event);
245
246 // Unregisters the observer event handle previously registered with
247 // |trace_register_observer|.
248 //
249 // Returns |ZX_OK| if successful.
250 // Returns |ZX_ERR_NOT_FOUND| if the event was not previously registered.
251 zx_status_t trace_unregister_observer(zx_handle_t event);
252
253 // Callback to notify the engine that the observer has finished processing
254 // all state changes.
255 void trace_notify_observer_updated(zx_handle_t event);
256
257 __END_CDECLS
258
259 #ifdef __cplusplus
260
261 #include <fbl/macros.h>
262
263 namespace trace {
264
265 // Holds and retains ownership of a trace context.
266 // Releases the context automatically when destroyed.
267 class TraceContext final {
268 public:
TraceContext()269 TraceContext()
270 : context_(nullptr) {}
271
TraceContext(trace_context_t * context)272 TraceContext(trace_context_t* context)
273 : context_(context) {}
274
TraceContext(TraceContext && other)275 TraceContext(TraceContext&& other)
276 : context_(other.context_) {
277 other.context_ = nullptr;
278 }
279
~TraceContext()280 ~TraceContext() {
281 Release();
282 }
283
284 // Gets the trace context, or null if there is none.
get()285 trace_context_t* get() const { return context_; }
286
287 // Returns true if the holder contains a valid context.
288 explicit operator bool() const { return context_ != nullptr; }
289
290 // Acquires a reference to the trace engine's context.
Acquire()291 static TraceContext Acquire() {
292 return TraceContext(trace_acquire_context());
293 }
294
295 // Acquires a reference to the trace engine's context, only if the specified
296 // category is enabled.
AcquireForCategory(const char * category_literal,trace_string_ref_t * out_ref)297 static TraceContext AcquireForCategory(const char* category_literal,
298 trace_string_ref_t* out_ref) {
299 return TraceContext(trace_acquire_context_for_category(
300 category_literal, out_ref));
301 }
302
303 // Releases the trace context.
Release()304 void Release() {
305 if (context_) {
306 trace_release_context(context_);
307 context_ = nullptr;
308 }
309 }
310
311 TraceContext& operator=(TraceContext&& other) {
312 Release();
313 context_ = other.context_;
314 other.context_ = nullptr;
315 return *this;
316 }
317
318 private:
319 trace_context_t* context_;
320
321 DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(TraceContext);
322 };
323
324 // Holds and retains ownership of a prolonged trace context.
325 // Releases the context automatically when destroyed.
326 class TraceProlongedContext final {
327 public:
TraceProlongedContext()328 TraceProlongedContext()
329 : context_(nullptr) {}
330
TraceProlongedContext(trace_prolonged_context_t * context)331 TraceProlongedContext(trace_prolonged_context_t* context)
332 : context_(context) {}
333
TraceProlongedContext(TraceProlongedContext && other)334 TraceProlongedContext(TraceProlongedContext&& other)
335 : context_(other.context_) {
336 other.context_ = nullptr;
337 }
338
~TraceProlongedContext()339 ~TraceProlongedContext() {
340 Release();
341 }
342
343 // Gets the trace context, or null if there is none.
get()344 trace_prolonged_context_t* get() const { return context_; }
345
346 // Returns true if the holder contains a valid context.
347 explicit operator bool() const { return context_ != nullptr; }
348
349 // Acquires a reference to the trace engine's context.
Acquire()350 static TraceProlongedContext Acquire() {
351 return TraceProlongedContext(trace_acquire_prolonged_context());
352 }
353
354 // Releases the trace context.
Release()355 void Release() {
356 if (context_) {
357 trace_release_prolonged_context(context_);
358 context_ = nullptr;
359 }
360 }
361
362 TraceProlongedContext& operator=(TraceProlongedContext&& other) {
363 Release();
364 context_ = other.context_;
365 other.context_ = nullptr;
366 return *this;
367 }
368
369 private:
370 trace_prolonged_context_t* context_;
371
372 DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(TraceProlongedContext);
373 };
374
375 } // namespace trace
376
377 #endif // __cplusplus
378