fuchsia_trace_observer/lib.rs
1// Copyright 2023 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.
4use fuchsia_trace::{trace_state, TraceState};
5use zx::AsHandleRef;
6
7/// A waiter that is notified with the tracing state changes.
8///
9/// # Examples
10///
11/// ```
12/// use fuchsia_trace_observer::TraceObserver;
13///
14/// let observer = TraceObserver::new();
15/// while let Ok(state) = observer.on_state_changed().await {
16/// println!("New state: {:?}", state);
17/// }
18/// ```
19pub struct TraceObserver {
20 event: zx::Event,
21}
22
23impl Drop for TraceObserver {
24 fn drop(&mut self) {
25 unsafe { sys::trace_unregister_observer(self.event.raw_handle()) }
26 }
27}
28
29impl TraceObserver {
30 /// Creates a new TraceObserver which is ready to be waited on and notified.
31 ///
32 /// # Examples
33 ///
34 /// ```
35 /// use fuchsia_trace_observer::TraceObserver;
36 ///
37 /// let observer = TraceObserver::new();
38 /// ```
39 pub fn new() -> TraceObserver {
40 let event = zx::Event::create();
41 unsafe { sys::trace_register_observer(event.raw_handle()) };
42 Self { event }
43 }
44
45 /// Asynchronously wait for the trace state to change, returning the updated state on success.
46 ///
47 /// # Examples
48 ///
49 /// ```
50 /// use fuchsia_trace_observer::TraceObserver;
51 ///
52 /// let observer = TraceObserver::new();
53 /// while let Ok(state) = observer.on_state_changed().await {
54 /// println!("New state: {:?}", state);
55 /// }
56 /// ```
57 pub async fn on_state_changed(&self) -> Result<TraceState, zx::Status> {
58 let _signals = fuchsia_async::OnSignalsRef::new(
59 self.event.as_handle_ref(),
60 zx::Signals::EVENT_SIGNALED,
61 )
62 .await?;
63 // Careful here, we need to clear the signal from the handle before we read the trace
64 // state.
65 //
66 // When stopping, the engine does the operations
67 // 1) set trace state (STOPPING)
68 // 2) signal handle
69 // 3) set trace state (STOPPED)
70 // 4) signal handle
71 //
72 // We do the operations:
73 // a) clear signal
74 // b) read state
75 //
76 // If we do them in the opposite order, the following bug could occur:
77 // 1) set trace state (STOPPING)
78 // 2) signal handle
79 // a) read state (STOPPING)
80 // 3) set trace state (STOPPED)
81 // 4) signal handle
82 // b) clear signal
83 //
84 // In this case, we would return STOPPING and never check for STOPPED
85 self.event.signal_handle(zx::Signals::EVENT_SIGNALED, zx::Signals::NONE)?;
86 let state = trace_state();
87 unsafe { sys::trace_notify_observer_updated(self.event.raw_handle()) };
88 Ok(state)
89 }
90}
91
92/// Start a thread that calls the provided callback when trace state changes.
93pub fn start_trace_observer(callback: extern "C" fn()) {
94 unsafe { sys::start_trace_observer_rust(callback) }
95}
96
97mod sys {
98 #![allow(non_camel_case_types)]
99 use zx::sys::zx_handle_t;
100 // From librust-trace-observer.so
101 extern "C" {
102 pub fn start_trace_observer_rust(callback: unsafe extern "C" fn());
103 pub fn trace_register_observer(event: zx_handle_t);
104 pub fn trace_notify_observer_updated(event: zx_handle_t);
105 pub fn trace_unregister_observer(event: zx_handle_t);
106 }
107}