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