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}