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}