hitrace/
lib.rs

1//! HiTrace
2//!
3//! Safe bindings for the [`HiTrace`] tracing system on OpenHarmony.
4//! This crate does nothing if not compiled for OpenHarmony (`target_env = ohos`).
5//!
6//!
7//! # Usage
8//!
9//! `HiTrace` allows tracing Spans in a synchronous and stack based fashion.
10//!
11//! ## Examples
12//!
13//! ##
14//! ```
15//! # fn OH_HiTrace_StartTrace(_: * const core::ffi::c_char) {}
16//! # fn OH_HiTrace_FinishTrace() {}
17//! # fn step1() {}
18//! # fn step2() {}
19//! # use hitrace::{start_trace, finish_trace};
20//! # use std::ffi::CString;
21//! fn load_website() {
22//!     start_trace(&c"step1");
23//!     step1();
24//!     finish_trace();
25//!     start_trace(&CString::new("step2").unwrap());
26//!     step2();
27//!     finish_trace();
28//! }
29//! start_trace(&c"LoadingWebsite");
30//! load_website();
31//! finish_trace();
32//! ```
33//!
34//!
35//! [`HiTrace`]: <https://gitee.com/openharmony/hiviewdfx_hitrace>
36
37use std::ffi::{CStr, CString};
38use std::marker::PhantomData;
39
40pub fn start_trace<T: AsRef<CStr>>(name: &T) {
41    start_trace_cstr(name.as_ref())
42}
43
44#[cfg(all(target_env = "ohos", not(feature = "max_level_off")))]
45fn start_trace_cstr(name: &CStr) {
46    // SAFETY: We have a valid CStr, which is copied by `OH_HiTrace_StartTrace`.
47    unsafe {
48        hitrace_sys::OH_HiTrace_StartTrace(name.as_ptr());
49    }
50}
51
52#[cfg(any(not(target_env = "ohos"), feature = "max_level_off"))]
53fn start_trace_cstr(_: &CStr) {}
54
55/// Finishes the most recently started trace span
56pub fn finish_trace() {
57    #[cfg(all(target_env = "ohos", not(feature = "max_level_off")))]
58    fn finish_trace_() {
59        // Todo: We should check in the OpenHarmony code to make sure that
60        // `OH_HiTrace_FinishTrace` does not cause Memory Safety issues, if called
61        // without a corresponding previous `OH_HiTrace_StartTrace`.
62        unsafe {
63            hitrace_sys::OH_HiTrace_FinishTrace();
64        }
65    }
66
67    #[cfg(any(not(target_env = "ohos"), feature = "max_level_off"))]
68    fn finish_trace_() {}
69
70    finish_trace_()
71}
72
73pub struct ScopedTrace {
74    // Remove Send / Sync, since the trace needs to be finished on the same thread.
75    phantom_data: PhantomData<*mut u8>,
76}
77
78impl ScopedTrace {
79    /// Starts a new ScopedTrace, which ends when the returned object is dropped.
80    ///
81    /// Keep in mind the general limitations of HiTrace, where a call to
82    /// finish_trace will end the span of the most recently started trace.
83    /// Users should try not to mix `ScopedTrace` with manual calls to `finish_trace()`,
84    /// and should avoid passing the `ScopedTrace` object around.
85    #[must_use]
86    pub fn start_trace<T: AsRef<CStr>>(name: &T) -> Self {
87        start_trace(name);
88        Self {
89            phantom_data: PhantomData,
90        }
91    }
92
93    /// Like `start_trace()` but accepts a `&str`.
94    ///
95    /// # Panic
96    ///
97    /// Panics if the provided name can't be converted into a CString.
98    #[must_use]
99    pub fn start_trace_str(name: &str) -> Self {
100        Self::start_trace(&CString::new(name).expect("Contained null-byte"))
101    }
102
103    // A hidden function, which should only be used by hitrace-macro,
104    // until `c""` syntax is available.
105    #[doc(hidden)]
106    pub unsafe fn _start_trace_str_with_null(name_with_null: &str) -> Self {
107        #[cfg(any(not(target_env = "ohos"), feature = "max_level_off"))]
108        let _ = name_with_null;
109        // SAFETY: The User promises that the `str` slice is a valid null-terminated C-style string.
110        #[cfg(all(target_env = "ohos", not(feature = "max_level_off")))]
111        unsafe {
112            hitrace_sys::OH_HiTrace_StartTrace(name_with_null.as_ptr());
113        }
114        Self {
115            phantom_data: PhantomData,
116        }
117    }
118}
119
120impl Drop for ScopedTrace {
121    fn drop(&mut self) {
122        finish_trace()
123    }
124}
125
126#[cfg(test)]
127mod test {
128    use static_assertions::assert_not_impl_any;
129
130    use crate::ScopedTrace;
131
132    assert_not_impl_any!(ScopedTrace: Send, Sync);
133}