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