| // Copyright (C) 2023 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| //! ATrace instrumentation methods from cutils. |
| |
| use std::ffi::CString; |
| |
| #[cfg(not(test))] |
| use cutils_trace_bindgen as trace_bind; |
| |
| // Wrap tags into a mod to allow missing docs. |
| // We have to use the mod for this because Rust won't apply the attribute to the bitflags macro |
| // invocation. |
| pub use self::tags::*; |
| pub mod tags { |
| // Tag constants are not documented in libcutils, so we don't document them here. |
| #![allow(missing_docs)] |
| |
| use bitflags::bitflags; |
| use static_assertions::const_assert_eq; |
| |
| bitflags! { |
| /// The trace tag is used to filter tracing in userland to avoid some of the runtime cost of |
| /// tracing when it is not desired. |
| /// |
| /// Using `AtraceTag::Always` will result in the tracing always being enabled - this should |
| /// ONLY be done for debug code, as userland tracing has a performance cost even when the |
| /// trace is not being recorded. `AtraceTag::Never` will result in the tracing always being |
| /// disabled. |
| /// |
| /// `AtraceTag::Hal` should be bitwise ORed with the relevant tags for tracing |
| /// within a hardware module. For example a camera hardware module would use |
| /// `AtraceTag::Camera | AtraceTag::Hal`. |
| /// |
| /// Source of truth is `system/core/libcutils/include/cutils/trace.h`. |
| #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] |
| pub struct AtraceTag: u64 { |
| const Never = cutils_trace_bindgen::ATRACE_TAG_NEVER as u64; |
| const Always = cutils_trace_bindgen::ATRACE_TAG_ALWAYS as u64; |
| const Graphics = cutils_trace_bindgen::ATRACE_TAG_GRAPHICS as u64; |
| const Input = cutils_trace_bindgen::ATRACE_TAG_INPUT as u64; |
| const View = cutils_trace_bindgen::ATRACE_TAG_VIEW as u64; |
| const Webview = cutils_trace_bindgen::ATRACE_TAG_WEBVIEW as u64; |
| const WindowManager = cutils_trace_bindgen::ATRACE_TAG_WINDOW_MANAGER as u64; |
| const ActivityManager = cutils_trace_bindgen::ATRACE_TAG_ACTIVITY_MANAGER as u64; |
| const SyncManager = cutils_trace_bindgen::ATRACE_TAG_SYNC_MANAGER as u64; |
| const Audio = cutils_trace_bindgen::ATRACE_TAG_AUDIO as u64; |
| const Video = cutils_trace_bindgen::ATRACE_TAG_VIDEO as u64; |
| const Camera = cutils_trace_bindgen::ATRACE_TAG_CAMERA as u64; |
| const Hal = cutils_trace_bindgen::ATRACE_TAG_HAL as u64; |
| const App = cutils_trace_bindgen::ATRACE_TAG_APP as u64; |
| const Resources = cutils_trace_bindgen::ATRACE_TAG_RESOURCES as u64; |
| const Dalvik = cutils_trace_bindgen::ATRACE_TAG_DALVIK as u64; |
| const Rs = cutils_trace_bindgen::ATRACE_TAG_RS as u64; |
| const Bionic = cutils_trace_bindgen::ATRACE_TAG_BIONIC as u64; |
| const Power = cutils_trace_bindgen::ATRACE_TAG_POWER as u64; |
| const PackageManager = cutils_trace_bindgen::ATRACE_TAG_PACKAGE_MANAGER as u64; |
| const SystemServer = cutils_trace_bindgen::ATRACE_TAG_SYSTEM_SERVER as u64; |
| const Database = cutils_trace_bindgen::ATRACE_TAG_DATABASE as u64; |
| const Network = cutils_trace_bindgen::ATRACE_TAG_NETWORK as u64; |
| const Adb = cutils_trace_bindgen::ATRACE_TAG_ADB as u64; |
| const Vibrator = cutils_trace_bindgen::ATRACE_TAG_VIBRATOR as u64; |
| const Aidl = cutils_trace_bindgen::ATRACE_TAG_AIDL as u64; |
| const Nnapi = cutils_trace_bindgen::ATRACE_TAG_NNAPI as u64; |
| const Rro = cutils_trace_bindgen::ATRACE_TAG_RRO as u64; |
| const Thermal = cutils_trace_bindgen::ATRACE_TAG_THERMAL as u64; |
| const Last = cutils_trace_bindgen::ATRACE_TAG_LAST as u64; |
| const NotReady = cutils_trace_bindgen::ATRACE_TAG_NOT_READY as u64; |
| const ValidMask = cutils_trace_bindgen::ATRACE_TAG_VALID_MASK as u64; |
| } |
| } |
| |
| // Assertion to keep tags in sync. If it fails, it means there are new tags added to |
| // cutils/trace.h. Add them to the tags above and update the assertion. |
| const_assert_eq!(AtraceTag::Thermal.bits(), cutils_trace_bindgen::ATRACE_TAG_LAST as u64); |
| } |
| |
| /// RAII guard to close an event with tag. |
| pub struct ScopedEvent { |
| tag: AtraceTag, |
| } |
| |
| impl Drop for ScopedEvent { |
| fn drop(&mut self) { |
| atrace_end(self.tag); |
| } |
| } |
| |
| /// Begins an event via `atrace_begin` and returns a guard that calls `atrace_end` when dropped. |
| pub fn begin_scoped_event(tag: AtraceTag, name: &str) -> ScopedEvent { |
| atrace_begin(tag, name); |
| ScopedEvent { tag } |
| } |
| |
| /// Creates a scoped event with the current method name. |
| #[macro_export] |
| macro_rules! trace_method { |
| {$tag:expr} => { |
| let mut _atrace_trace_method_name: &'static str = ""; |
| { |
| // Declares function f inside current function. |
| fn f() {} |
| fn type_name_of<T>(_: T) -> &'static str { |
| std::any::type_name::<T>() |
| } |
| // type name of f is struct_or_crate_name::calling_function_name::f |
| let name = type_name_of(f); |
| // Remove the third to last character ("::f") |
| _atrace_trace_method_name = &name[..name.len() - 3]; |
| } |
| let _atrace_trace_method_guard = atrace::begin_scoped_event($tag, _atrace_trace_method_name); |
| }; |
| } |
| |
| /// Set whether tracing is enabled for the current process. This is used to prevent tracing within |
| /// the Zygote process. |
| pub fn atrace_set_tracing_enabled(enabled: bool) { |
| // SAFETY: No pointers are transferred. |
| unsafe { |
| trace_bind::atrace_set_tracing_enabled(enabled); |
| } |
| } |
| |
| /// `atrace_init` readies the process for tracing by opening the trace_marker file. |
| /// Calling any trace function causes this to be run, so calling it is optional. |
| /// This can be explicitly run to avoid setup delay on first trace function. |
| pub fn atrace_init() { |
| // SAFETY: Call with no arguments. |
| unsafe { |
| trace_bind::atrace_init(); |
| } |
| } |
| |
| /// Returns enabled tags as a bitmask. |
| /// |
| /// The tag mask is converted into an `AtraceTag`, keeping flags that do not correspond to a tag. |
| pub fn atrace_get_enabled_tags() -> AtraceTag { |
| // SAFETY: Call with no arguments that returns a 64-bit int. |
| unsafe { AtraceTag::from_bits_retain(trace_bind::atrace_get_enabled_tags()) } |
| } |
| |
| /// Test if a given tag is currently enabled. |
| /// |
| /// It can be used as a guard condition around more expensive trace calculations. |
| pub fn atrace_is_tag_enabled(tag: AtraceTag) -> bool { |
| // SAFETY: No pointers are transferred. |
| unsafe { trace_bind::atrace_is_tag_enabled_wrap(tag.bits()) != 0 } |
| } |
| |
| /// Trace the beginning of a context. `name` is used to identify the context. |
| /// |
| /// This is often used to time function execution. |
| pub fn atrace_begin(tag: AtraceTag, name: &str) { |
| if !atrace_is_tag_enabled(tag) { |
| return; |
| } |
| |
| let name_cstr = CString::new(name.as_bytes()).expect("CString::new failed"); |
| // SAFETY: The function does not accept the pointer ownership, only reads its contents. |
| // The passed string is guaranteed to be null-terminated by CString. |
| unsafe { |
| trace_bind::atrace_begin_wrap(tag.bits(), name_cstr.as_ptr()); |
| } |
| } |
| |
| /// Trace the end of a context. |
| /// |
| /// This should match up (and occur after) a corresponding `atrace_begin`. |
| pub fn atrace_end(tag: AtraceTag) { |
| // SAFETY: No pointers are transferred. |
| unsafe { |
| trace_bind::atrace_end_wrap(tag.bits()); |
| } |
| } |
| |
| /// Trace the beginning of an asynchronous event. Unlike `atrace_begin`/`atrace_end` contexts, |
| /// asynchronous events do not need to be nested. |
| /// |
| /// The name describes the event, and the cookie provides a unique identifier for distinguishing |
| /// simultaneous events. |
| /// |
| /// The name and cookie used to begin an event must be used to end it. |
| pub fn atrace_async_begin(tag: AtraceTag, name: &str, cookie: i32) { |
| if !atrace_is_tag_enabled(tag) { |
| return; |
| } |
| |
| let name_cstr = CString::new(name.as_bytes()).expect("CString::new failed"); |
| // SAFETY: The function does not accept the pointer ownership, only reads its contents. |
| // The passed string is guaranteed to be null-terminated by CString. |
| unsafe { |
| trace_bind::atrace_async_begin_wrap(tag.bits(), name_cstr.as_ptr(), cookie); |
| } |
| } |
| |
| /// Trace the end of an asynchronous event. |
| /// |
| /// This should have a corresponding `atrace_async_begin`. |
| pub fn atrace_async_end(tag: AtraceTag, name: &str, cookie: i32) { |
| if !atrace_is_tag_enabled(tag) { |
| return; |
| } |
| |
| let name_cstr = CString::new(name.as_bytes()).expect("CString::new failed"); |
| // SAFETY: The function does not accept the pointer ownership, only reads its contents. |
| // The passed string is guaranteed to be null-terminated by CString. |
| unsafe { |
| trace_bind::atrace_async_end_wrap(tag.bits(), name_cstr.as_ptr(), cookie); |
| } |
| } |
| |
| /// Trace the beginning of an asynchronous event. |
| /// |
| /// In addition to the name and a cookie as in `atrace_async_begin`/`atrace_async_end`, a track name |
| /// argument is provided, which is the name of the row where this async event should be recorded. |
| /// |
| /// The track name and cookie used to begin an event must be used to end it. |
| /// |
| /// The cookie here must be unique on the track_name level, not the name level. |
| pub fn atrace_async_for_track_begin(tag: AtraceTag, track_name: &str, name: &str, cookie: i32) { |
| if !atrace_is_tag_enabled(tag) { |
| return; |
| } |
| |
| let name_cstr = CString::new(name.as_bytes()).expect("CString::new failed"); |
| let track_name_cstr = CString::new(track_name.as_bytes()).expect("CString::new failed"); |
| // SAFETY: The function does not accept the pointer ownership, only reads its contents. |
| // The passed strings are guaranteed to be null-terminated by CString. |
| unsafe { |
| trace_bind::atrace_async_for_track_begin_wrap( |
| tag.bits(), |
| track_name_cstr.as_ptr(), |
| name_cstr.as_ptr(), |
| cookie, |
| ); |
| } |
| } |
| |
| /// Trace the end of an asynchronous event. |
| /// |
| /// This should correspond to a previous `atrace_async_for_track_begin`. |
| pub fn atrace_async_for_track_end(tag: AtraceTag, track_name: &str, cookie: i32) { |
| if !atrace_is_tag_enabled(tag) { |
| return; |
| } |
| |
| let track_name_cstr = CString::new(track_name.as_bytes()).expect("CString::new failed"); |
| // SAFETY: The function does not accept the pointer ownership, only reads its contents. |
| // The passed string is guaranteed to be null-terminated by CString. |
| unsafe { |
| trace_bind::atrace_async_for_track_end_wrap(tag.bits(), track_name_cstr.as_ptr(), cookie); |
| } |
| } |
| |
| /// Trace an instantaneous context. `name` is used to identify the context. |
| /// |
| /// An "instant" is an event with no defined duration. Visually is displayed like a single marker |
| /// in the timeline (rather than a span, in the case of begin/end events). |
| /// |
| /// By default, instant events are added into a dedicated track that has the same name of the event. |
| /// Use `atrace_instant_for_track` to put different instant events into the same timeline track/row. |
| pub fn atrace_instant(tag: AtraceTag, name: &str) { |
| if !atrace_is_tag_enabled(tag) { |
| return; |
| } |
| |
| let name_cstr = CString::new(name.as_bytes()).expect("CString::new failed"); |
| // SAFETY: The function does not accept the pointer ownership, only reads its contents. |
| // The passed string is guaranteed to be null-terminated by CString. |
| unsafe { |
| trace_bind::atrace_instant_wrap(tag.bits(), name_cstr.as_ptr()); |
| } |
| } |
| |
| /// Trace an instantaneous context. `name` is used to identify the context. `track_name` is the name |
| /// of the row where the event should be recorded. |
| /// |
| /// An "instant" is an event with no defined duration. Visually is displayed like a single marker |
| /// in the timeline (rather than a span, in the case of begin/end events). |
| pub fn atrace_instant_for_track(tag: AtraceTag, track_name: &str, name: &str) { |
| if !atrace_is_tag_enabled(tag) { |
| return; |
| } |
| |
| let name_cstr = CString::new(name.as_bytes()).expect("CString::new failed"); |
| let track_name_cstr = CString::new(track_name.as_bytes()).expect("CString::new failed"); |
| // SAFETY: The function does not accept the pointer ownership, only reads its contents. |
| // The passed string is guaranteed to be null-terminated by CString. |
| unsafe { |
| trace_bind::atrace_instant_for_track_wrap( |
| tag.bits(), |
| track_name_cstr.as_ptr(), |
| name_cstr.as_ptr(), |
| ); |
| } |
| } |
| |
| /// Traces an integer counter value. `name` is used to identify the counter. |
| /// |
| /// This can be used to track how a value changes over time. |
| pub fn atrace_int(tag: AtraceTag, name: &str, value: i32) { |
| if !atrace_is_tag_enabled(tag) { |
| return; |
| } |
| |
| let name_cstr = CString::new(name.as_bytes()).expect("CString::new failed"); |
| // SAFETY: The function does not accept the pointer ownership, only reads its contents. |
| // The passed string is guaranteed to be null-terminated by CString. |
| unsafe { |
| trace_bind::atrace_int_wrap(tag.bits(), name_cstr.as_ptr(), value); |
| } |
| } |
| |
| /// Traces a 64-bit integer counter value. `name` is used to identify the counter. |
| /// |
| /// This can be used to track how a value changes over time. |
| pub fn atrace_int64(tag: AtraceTag, name: &str, value: i64) { |
| if !atrace_is_tag_enabled(tag) { |
| return; |
| } |
| |
| let name_cstr = CString::new(name.as_bytes()).expect("CString::new failed"); |
| // SAFETY: The function does not accept the pointer ownership, only reads its contents. |
| // The passed string is guaranteed to be null-terminated by CString. |
| unsafe { |
| trace_bind::atrace_int64_wrap(tag.bits(), name_cstr.as_ptr(), value); |
| } |
| } |
| |
| #[cfg(test)] |
| use self::tests::mock_atrace as trace_bind; |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| |
| use std::ffi::CStr; |
| use std::os::raw::c_char; |
| |
| /// Utilities to mock ATrace bindings. |
| /// |
| /// Normally, for behavior-driven testing we focus on the outcomes of the functions rather than |
| /// calls into bindings. However, since the purpose of the library is to forward data into |
| /// the underlying implementation (which we assume to be correct), that's what we test. |
| pub mod mock_atrace { |
| use std::cell::RefCell; |
| use std::os::raw::c_char; |
| |
| /// Contains logic to check binding calls. |
| /// Implement this trait in the test with mocking logic and checks in implemented functions. |
| /// Default implementations panic. |
| pub trait ATraceMocker { |
| fn atrace_set_tracing_enabled(&mut self, _enabled: bool) { |
| panic!("Unexpected call"); |
| } |
| fn atrace_init(&mut self) { |
| panic!("Unexpected call"); |
| } |
| fn atrace_get_enabled_tags(&mut self) -> u64 { |
| panic!("Unexpected call"); |
| } |
| fn atrace_is_tag_enabled_wrap(&mut self, _tag: u64) -> u64 { |
| panic!("Unexpected call"); |
| } |
| fn atrace_begin_wrap(&mut self, _tag: u64, _name: *const c_char) { |
| panic!("Unexpected call"); |
| } |
| fn atrace_end_wrap(&mut self, _tag: u64) { |
| panic!("Unexpected call"); |
| } |
| fn atrace_async_begin_wrap(&mut self, _tag: u64, _name: *const c_char, _cookie: i32) { |
| panic!("Unexpected call"); |
| } |
| fn atrace_async_end_wrap(&mut self, _tag: u64, _name: *const c_char, _cookie: i32) { |
| panic!("Unexpected call"); |
| } |
| fn atrace_async_for_track_begin_wrap( |
| &mut self, |
| _tag: u64, |
| _track_name: *const c_char, |
| _name: *const c_char, |
| _cookie: i32, |
| ) { |
| panic!("Unexpected call"); |
| } |
| fn atrace_async_for_track_end_wrap( |
| &mut self, |
| _tag: u64, |
| _track_name: *const c_char, |
| _cookie: i32, |
| ) { |
| panic!("Unexpected call"); |
| } |
| fn atrace_instant_wrap(&mut self, _tag: u64, _name: *const c_char) { |
| panic!("Unexpected call"); |
| } |
| fn atrace_instant_for_track_wrap( |
| &mut self, |
| _tag: u64, |
| _track_name: *const c_char, |
| _name: *const c_char, |
| ) { |
| panic!("Unexpected call"); |
| } |
| fn atrace_int_wrap(&mut self, _tag: u64, _name: *const c_char, _value: i32) { |
| panic!("Unexpected call"); |
| } |
| fn atrace_int64_wrap(&mut self, _tag: u64, _name: *const c_char, _value: i64) { |
| panic!("Unexpected call"); |
| } |
| |
| /// This method should contain checks to be performed at the end of the test. |
| fn finish(&self) {} |
| } |
| |
| struct DefaultMocker; |
| impl ATraceMocker for DefaultMocker {} |
| |
| // Global mock object is thread-local, so that the tests can run safely in parallel. |
| thread_local!(static MOCKER: RefCell<Box<dyn ATraceMocker>> = RefCell::new(Box::new(DefaultMocker{}))); |
| |
| /// Sets the global mock object. |
| fn set_mocker(mocker: Box<dyn ATraceMocker>) { |
| MOCKER.with(|m| *m.borrow_mut() = mocker) |
| } |
| |
| /// Calls the passed method `f` with a mutable reference to the global mock object. |
| /// Example: |
| /// ``` |
| /// with_mocker(|mocker| mocker.atrace_begin_wrap(tag, name)) |
| /// ``` |
| fn with_mocker<F, R>(f: F) -> R |
| where |
| F: FnOnce(&mut dyn ATraceMocker) -> R, |
| { |
| MOCKER.with(|m| f(m.borrow_mut().as_mut())) |
| } |
| |
| /// Finish the test and perform final checks in the mocker. |
| /// Calls `finish()` on the global mocker. |
| /// |
| /// Needs to be called manually at the end of each test that uses mocks. |
| /// |
| /// May panic, so it can not be called in `drop()` methods, |
| /// since it may result in double panic. |
| pub fn mocker_finish() { |
| with_mocker(|m| m.finish()) |
| } |
| |
| /// RAII guard that resets the mock to the default implementation. |
| pub struct MockerGuard; |
| impl Drop for MockerGuard { |
| fn drop(&mut self) { |
| set_mocker(Box::new(DefaultMocker {})); |
| } |
| } |
| |
| /// Sets the mock object for the duration of the scope. |
| /// |
| /// Returns a RAII guard that resets the mock back to default on destruction. |
| pub fn set_scoped_mocker<T: ATraceMocker + 'static>(m: T) -> MockerGuard { |
| set_mocker(Box::new(m)); |
| MockerGuard {} |
| } |
| |
| // Wrapped functions that forward calls into mocker. |
| // The functions are marked as unsafe to match the binding interface, won't compile otherwise. |
| // The mocker methods themselves are not marked as unsafe. |
| |
| pub unsafe fn atrace_set_tracing_enabled(enabled: bool) { |
| with_mocker(|m| m.atrace_set_tracing_enabled(enabled)) |
| } |
| pub unsafe fn atrace_init() { |
| with_mocker(|m| m.atrace_init()) |
| } |
| pub unsafe fn atrace_get_enabled_tags() -> u64 { |
| with_mocker(|m| m.atrace_get_enabled_tags()) |
| } |
| pub unsafe fn atrace_is_tag_enabled_wrap(tag: u64) -> u64 { |
| with_mocker(|m| m.atrace_is_tag_enabled_wrap(tag)) |
| } |
| pub unsafe fn atrace_begin_wrap(tag: u64, name: *const c_char) { |
| with_mocker(|m| m.atrace_begin_wrap(tag, name)) |
| } |
| pub unsafe fn atrace_end_wrap(tag: u64) { |
| with_mocker(|m| m.atrace_end_wrap(tag)) |
| } |
| pub unsafe fn atrace_async_begin_wrap(tag: u64, name: *const c_char, cookie: i32) { |
| with_mocker(|m| m.atrace_async_begin_wrap(tag, name, cookie)) |
| } |
| pub unsafe fn atrace_async_end_wrap(tag: u64, name: *const c_char, cookie: i32) { |
| with_mocker(|m| m.atrace_async_end_wrap(tag, name, cookie)) |
| } |
| pub unsafe fn atrace_async_for_track_begin_wrap( |
| tag: u64, |
| track_name: *const c_char, |
| name: *const c_char, |
| cookie: i32, |
| ) { |
| with_mocker(|m| m.atrace_async_for_track_begin_wrap(tag, track_name, name, cookie)) |
| } |
| pub unsafe fn atrace_async_for_track_end_wrap( |
| tag: u64, |
| track_name: *const c_char, |
| cookie: i32, |
| ) { |
| with_mocker(|m| m.atrace_async_for_track_end_wrap(tag, track_name, cookie)) |
| } |
| pub unsafe fn atrace_instant_wrap(tag: u64, name: *const c_char) { |
| with_mocker(|m| m.atrace_instant_wrap(tag, name)) |
| } |
| pub unsafe fn atrace_instant_for_track_wrap( |
| tag: u64, |
| track_name: *const c_char, |
| name: *const c_char, |
| ) { |
| with_mocker(|m| m.atrace_instant_for_track_wrap(tag, track_name, name)) |
| } |
| pub unsafe fn atrace_int_wrap(tag: u64, name: *const c_char, value: i32) { |
| with_mocker(|m| m.atrace_int_wrap(tag, name, value)) |
| } |
| pub unsafe fn atrace_int64_wrap(tag: u64, name: *const c_char, value: i64) { |
| with_mocker(|m| m.atrace_int64_wrap(tag, name, value)) |
| } |
| } |
| |
| #[test] |
| fn forwards_set_tracing_enabled() { |
| #[derive(Default)] |
| struct CallCheck { |
| set_tracing_enabled_count: u32, |
| } |
| |
| impl mock_atrace::ATraceMocker for CallCheck { |
| fn atrace_set_tracing_enabled(&mut self, enabled: bool) { |
| self.set_tracing_enabled_count += 1; |
| assert!(self.set_tracing_enabled_count < 2); |
| assert!(enabled); |
| } |
| |
| fn finish(&self) { |
| assert_eq!(self.set_tracing_enabled_count, 1); |
| } |
| } |
| |
| let _guard = mock_atrace::set_scoped_mocker(CallCheck::default()); |
| |
| atrace_set_tracing_enabled(true); |
| |
| mock_atrace::mocker_finish(); |
| } |
| |
| #[test] |
| fn forwards_atrace_init() { |
| #[derive(Default)] |
| struct CallCheck { |
| init_count: u32, |
| } |
| |
| impl mock_atrace::ATraceMocker for CallCheck { |
| fn atrace_init(&mut self) { |
| self.init_count += 1; |
| assert!(self.init_count < 2); |
| } |
| |
| fn finish(&self) { |
| assert_eq!(self.init_count, 1); |
| } |
| } |
| |
| let _guard = mock_atrace::set_scoped_mocker(CallCheck::default()); |
| |
| atrace_init(); |
| |
| mock_atrace::mocker_finish(); |
| } |
| |
| #[test] |
| fn forwards_atrace_get_enabled_tags() { |
| #[derive(Default)] |
| struct CallCheck { |
| get_enabled_tags_count: u32, |
| } |
| |
| impl mock_atrace::ATraceMocker for CallCheck { |
| fn atrace_get_enabled_tags(&mut self) -> u64 { |
| self.get_enabled_tags_count += 1; |
| assert!(self.get_enabled_tags_count < 2); |
| (cutils_trace_bindgen::ATRACE_TAG_HAL | cutils_trace_bindgen::ATRACE_TAG_GRAPHICS) |
| as u64 |
| } |
| |
| fn finish(&self) { |
| assert_eq!(self.get_enabled_tags_count, 1); |
| } |
| } |
| |
| let _guard = mock_atrace::set_scoped_mocker(CallCheck::default()); |
| |
| let res = atrace_get_enabled_tags(); |
| assert_eq!(res, AtraceTag::Hal | AtraceTag::Graphics); |
| |
| mock_atrace::mocker_finish(); |
| } |
| |
| #[test] |
| fn forwards_trace_begin() { |
| #[derive(Default)] |
| struct CallCheck { |
| begin_count: u32, |
| } |
| |
| impl mock_atrace::ATraceMocker for CallCheck { |
| fn atrace_is_tag_enabled_wrap(&mut self, _tag: u64) -> u64 { |
| 1 |
| } |
| fn atrace_begin_wrap(&mut self, tag: u64, name: *const c_char) { |
| self.begin_count += 1; |
| assert!(self.begin_count < 2); |
| assert_eq!(tag, cutils_trace_bindgen::ATRACE_TAG_APP as u64); |
| // SAFETY: If the code under test is correct, the pointer is guaranteed to satisfy |
| // the requirements of `CStr::from_ptr`. If the code is not correct, this section is |
| // unsafe and will hopefully fail the test. |
| unsafe { |
| assert_eq!(CStr::from_ptr(name).to_str().expect("to_str failed"), "Test Name"); |
| } |
| } |
| |
| fn finish(&self) { |
| assert_eq!(self.begin_count, 1); |
| } |
| } |
| |
| let _guard = mock_atrace::set_scoped_mocker(CallCheck::default()); |
| |
| atrace_begin(AtraceTag::App, "Test Name"); |
| |
| mock_atrace::mocker_finish(); |
| } |
| |
| #[test] |
| fn trace_begin_not_called_with_disabled_tag() { |
| #[derive(Default)] |
| struct CallCheck { |
| is_tag_enabled_count: u32, |
| } |
| |
| impl mock_atrace::ATraceMocker for CallCheck { |
| fn atrace_is_tag_enabled_wrap(&mut self, _tag: u64) -> u64 { |
| self.is_tag_enabled_count += 1; |
| assert!(self.is_tag_enabled_count < 2); |
| 0 |
| } |
| fn atrace_begin_wrap(&mut self, _tag: u64, _name: *const c_char) { |
| panic!("Begin should not be called with disabled tag.") |
| } |
| |
| fn finish(&self) { |
| assert_eq!(self.is_tag_enabled_count, 1); |
| } |
| } |
| |
| let _guard = mock_atrace::set_scoped_mocker(CallCheck::default()); |
| |
| atrace_begin(AtraceTag::App, "Ignore me"); |
| |
| mock_atrace::mocker_finish(); |
| } |
| |
| #[test] |
| fn forwards_trace_end() { |
| #[derive(Default)] |
| struct CallCheck { |
| end_count: u32, |
| } |
| |
| impl mock_atrace::ATraceMocker for CallCheck { |
| fn atrace_end_wrap(&mut self, tag: u64) { |
| self.end_count += 1; |
| assert!(self.end_count < 2); |
| assert_eq!(tag, cutils_trace_bindgen::ATRACE_TAG_APP as u64); |
| } |
| |
| fn finish(&self) { |
| assert_eq!(self.end_count, 1); |
| } |
| } |
| |
| let _guard = mock_atrace::set_scoped_mocker(CallCheck::default()); |
| |
| atrace_end(AtraceTag::App); |
| |
| mock_atrace::mocker_finish(); |
| } |
| |
| #[test] |
| fn can_combine_tags() { |
| #[derive(Default)] |
| struct CallCheck { |
| begin_count: u32, |
| } |
| |
| impl mock_atrace::ATraceMocker for CallCheck { |
| fn atrace_is_tag_enabled_wrap(&mut self, _tag: u64) -> u64 { |
| 1 |
| } |
| fn atrace_begin_wrap(&mut self, tag: u64, _name: *const c_char) { |
| self.begin_count += 1; |
| assert!(self.begin_count < 2); |
| assert_eq!( |
| tag, |
| (cutils_trace_bindgen::ATRACE_TAG_HAL | cutils_trace_bindgen::ATRACE_TAG_CAMERA) |
| as u64 |
| ); |
| } |
| |
| fn finish(&self) { |
| assert_eq!(self.begin_count, 1); |
| } |
| } |
| |
| let _guard = mock_atrace::set_scoped_mocker(CallCheck::default()); |
| |
| atrace_begin(AtraceTag::Hal | AtraceTag::Camera, "foo"); |
| |
| mock_atrace::mocker_finish(); |
| } |
| |
| #[test] |
| fn forwards_is_tag_enabled() { |
| #[derive(Default)] |
| struct CallCheck { |
| is_tag_enabled_count: u32, |
| } |
| |
| impl mock_atrace::ATraceMocker for CallCheck { |
| fn atrace_is_tag_enabled_wrap(&mut self, tag: u64) -> u64 { |
| self.is_tag_enabled_count += 1; |
| assert!(self.is_tag_enabled_count < 2); |
| assert_eq!(tag, cutils_trace_bindgen::ATRACE_TAG_ADB as u64); |
| 1 |
| } |
| |
| fn finish(&self) { |
| assert_eq!(self.is_tag_enabled_count, 1); |
| } |
| } |
| |
| let _guard = mock_atrace::set_scoped_mocker(CallCheck::default()); |
| |
| let res = atrace_is_tag_enabled(AtraceTag::Adb); |
| assert!(res); |
| |
| mock_atrace::mocker_finish(); |
| } |
| |
| #[test] |
| fn forwards_async_begin() { |
| #[derive(Default)] |
| struct CallCheck { |
| async_begin_count: u32, |
| } |
| |
| impl mock_atrace::ATraceMocker for CallCheck { |
| fn atrace_is_tag_enabled_wrap(&mut self, _tag: u64) -> u64 { |
| 1 |
| } |
| fn atrace_async_begin_wrap(&mut self, tag: u64, name: *const c_char, cookie: i32) { |
| self.async_begin_count += 1; |
| assert!(self.async_begin_count < 2); |
| assert_eq!(tag, cutils_trace_bindgen::ATRACE_TAG_APP as u64); |
| // SAFETY: If the code under test is correct, the pointer is guaranteed to satisfy |
| // the requirements of `CStr::from_ptr`. If the code is not correct, this section is |
| // unsafe and will hopefully fail the test. |
| unsafe { |
| assert_eq!(CStr::from_ptr(name).to_str().expect("to_str failed"), "Test Name"); |
| } |
| assert_eq!(cookie, 123); |
| } |
| |
| fn finish(&self) { |
| assert_eq!(self.async_begin_count, 1); |
| } |
| } |
| |
| let _guard = mock_atrace::set_scoped_mocker(CallCheck::default()); |
| |
| atrace_async_begin(AtraceTag::App, "Test Name", 123); |
| |
| mock_atrace::mocker_finish(); |
| } |
| |
| #[test] |
| fn forwards_async_end() { |
| #[derive(Default)] |
| struct CallCheck { |
| async_end_count: u32, |
| } |
| |
| impl mock_atrace::ATraceMocker for CallCheck { |
| fn atrace_is_tag_enabled_wrap(&mut self, _tag: u64) -> u64 { |
| 1 |
| } |
| fn atrace_async_end_wrap(&mut self, tag: u64, name: *const c_char, cookie: i32) { |
| self.async_end_count += 1; |
| assert!(self.async_end_count < 2); |
| assert_eq!(tag, cutils_trace_bindgen::ATRACE_TAG_APP as u64); |
| // SAFETY: If the code under test is correct, the pointer is guaranteed to satisfy |
| // the requirements of `CStr::from_ptr`. If the code is not correct, this section is |
| // unsafe and will hopefully fail the test. |
| unsafe { |
| assert_eq!(CStr::from_ptr(name).to_str().expect("to_str failed"), "Test Name"); |
| } |
| assert_eq!(cookie, 123); |
| } |
| |
| fn finish(&self) { |
| assert_eq!(self.async_end_count, 1); |
| } |
| } |
| |
| let _guard = mock_atrace::set_scoped_mocker(CallCheck::default()); |
| |
| atrace_async_end(AtraceTag::App, "Test Name", 123); |
| |
| mock_atrace::mocker_finish(); |
| } |
| |
| #[test] |
| fn forwards_async_for_track_begin() { |
| #[derive(Default)] |
| struct CallCheck { |
| async_for_track_begin_count: u32, |
| } |
| |
| impl mock_atrace::ATraceMocker for CallCheck { |
| fn atrace_is_tag_enabled_wrap(&mut self, _tag: u64) -> u64 { |
| 1 |
| } |
| fn atrace_async_for_track_begin_wrap( |
| &mut self, |
| tag: u64, |
| track_name: *const c_char, |
| name: *const c_char, |
| cookie: i32, |
| ) { |
| self.async_for_track_begin_count += 1; |
| assert!(self.async_for_track_begin_count < 2); |
| assert_eq!(tag, cutils_trace_bindgen::ATRACE_TAG_APP as u64); |
| // SAFETY: If the code under test is correct, the pointer is guaranteed to satisfy |
| // the requirements of `CStr::from_ptr`. If the code is not correct, this section is |
| // unsafe and will hopefully fail the test. |
| unsafe { |
| assert_eq!( |
| CStr::from_ptr(track_name).to_str().expect("to_str failed"), |
| "Track" |
| ); |
| assert_eq!(CStr::from_ptr(name).to_str().expect("to_str failed"), "Test Name"); |
| } |
| assert_eq!(cookie, 123); |
| } |
| |
| fn finish(&self) { |
| assert_eq!(self.async_for_track_begin_count, 1); |
| } |
| } |
| |
| let _guard = mock_atrace::set_scoped_mocker(CallCheck::default()); |
| |
| atrace_async_for_track_begin(AtraceTag::App, "Track", "Test Name", 123); |
| |
| mock_atrace::mocker_finish(); |
| } |
| |
| #[test] |
| fn forwards_async_for_track_end() { |
| #[derive(Default)] |
| struct CallCheck { |
| async_for_track_end_count: u32, |
| } |
| |
| impl mock_atrace::ATraceMocker for CallCheck { |
| fn atrace_is_tag_enabled_wrap(&mut self, _tag: u64) -> u64 { |
| 1 |
| } |
| fn atrace_async_for_track_end_wrap( |
| &mut self, |
| tag: u64, |
| track_name: *const c_char, |
| cookie: i32, |
| ) { |
| self.async_for_track_end_count += 1; |
| assert!(self.async_for_track_end_count < 2); |
| assert_eq!(tag, cutils_trace_bindgen::ATRACE_TAG_APP as u64); |
| // SAFETY: If the code under test is correct, the pointer is guaranteed to satisfy |
| // the requirements of `CStr::from_ptr`. If the code is not correct, this section is |
| // unsafe and will hopefully fail the test. |
| unsafe { |
| assert_eq!( |
| CStr::from_ptr(track_name).to_str().expect("to_str failed"), |
| "Track" |
| ); |
| } |
| assert_eq!(cookie, 123); |
| } |
| |
| fn finish(&self) { |
| assert_eq!(self.async_for_track_end_count, 1); |
| } |
| } |
| |
| let _guard = mock_atrace::set_scoped_mocker(CallCheck::default()); |
| |
| atrace_async_for_track_end(AtraceTag::App, "Track", 123); |
| |
| mock_atrace::mocker_finish(); |
| } |
| |
| #[test] |
| fn forwards_trace_instant() { |
| #[derive(Default)] |
| struct CallCheck { |
| trace_instant_count: u32, |
| } |
| |
| impl mock_atrace::ATraceMocker for CallCheck { |
| fn atrace_is_tag_enabled_wrap(&mut self, _tag: u64) -> u64 { |
| 1 |
| } |
| fn atrace_instant_wrap(&mut self, tag: u64, name: *const c_char) { |
| self.trace_instant_count += 1; |
| assert!(self.trace_instant_count < 2); |
| assert_eq!(tag, cutils_trace_bindgen::ATRACE_TAG_APP as u64); |
| // SAFETY: If the code under test is correct, the pointer is guaranteed to satisfy |
| // the requirements of `CStr::from_ptr`. If the code is not correct, this section is |
| // unsafe and will hopefully fail the test. |
| unsafe { |
| assert_eq!(CStr::from_ptr(name).to_str().expect("to_str failed"), "Test Name"); |
| } |
| } |
| |
| fn finish(&self) { |
| assert_eq!(self.trace_instant_count, 1); |
| } |
| } |
| |
| let _guard = mock_atrace::set_scoped_mocker(CallCheck::default()); |
| |
| atrace_instant(AtraceTag::App, "Test Name"); |
| |
| mock_atrace::mocker_finish(); |
| } |
| |
| #[test] |
| fn forwards_trace_instant_for_track() { |
| #[derive(Default)] |
| struct CallCheck { |
| trace_instant_for_track_count: u32, |
| } |
| |
| impl mock_atrace::ATraceMocker for CallCheck { |
| fn atrace_is_tag_enabled_wrap(&mut self, _tag: u64) -> u64 { |
| 1 |
| } |
| fn atrace_instant_for_track_wrap( |
| &mut self, |
| tag: u64, |
| track_name: *const c_char, |
| name: *const c_char, |
| ) { |
| self.trace_instant_for_track_count += 1; |
| assert!(self.trace_instant_for_track_count < 2); |
| assert_eq!(tag, cutils_trace_bindgen::ATRACE_TAG_APP as u64); |
| // SAFETY: If the code under test is correct, the pointer is guaranteed to satisfy |
| // the requirements of `CStr::from_ptr`. If the code is not correct, this section is |
| // unsafe and will hopefully fail the test. |
| unsafe { |
| assert_eq!( |
| CStr::from_ptr(track_name).to_str().expect("to_str failed"), |
| "Track" |
| ); |
| assert_eq!(CStr::from_ptr(name).to_str().expect("to_str failed"), "Test Name"); |
| } |
| } |
| |
| fn finish(&self) { |
| assert_eq!(self.trace_instant_for_track_count, 1); |
| } |
| } |
| |
| let _guard = mock_atrace::set_scoped_mocker(CallCheck::default()); |
| |
| atrace_instant_for_track(AtraceTag::App, "Track", "Test Name"); |
| |
| mock_atrace::mocker_finish(); |
| } |
| |
| #[test] |
| fn forwards_trace_int() { |
| #[derive(Default)] |
| struct CallCheck { |
| trace_int_count: u32, |
| } |
| |
| impl mock_atrace::ATraceMocker for CallCheck { |
| fn atrace_is_tag_enabled_wrap(&mut self, _tag: u64) -> u64 { |
| 1 |
| } |
| fn atrace_int_wrap(&mut self, tag: u64, name: *const c_char, value: i32) { |
| self.trace_int_count += 1; |
| assert!(self.trace_int_count < 2); |
| assert_eq!(tag, cutils_trace_bindgen::ATRACE_TAG_APP as u64); |
| // SAFETY: If the code under test is correct, the pointer is guaranteed to satisfy |
| // the requirements of `CStr::from_ptr`. If the code is not correct, this section is |
| // unsafe and will hopefully fail the test. |
| unsafe { |
| assert_eq!(CStr::from_ptr(name).to_str().expect("to_str failed"), "Test Name"); |
| } |
| assert_eq!(value, 32); |
| } |
| |
| fn finish(&self) { |
| assert_eq!(self.trace_int_count, 1); |
| } |
| } |
| |
| let _guard = mock_atrace::set_scoped_mocker(CallCheck::default()); |
| |
| atrace_int(AtraceTag::App, "Test Name", 32); |
| |
| mock_atrace::mocker_finish(); |
| } |
| |
| #[test] |
| fn forwards_trace_int64() { |
| #[derive(Default)] |
| struct CallCheck { |
| trace_int64_count: u32, |
| } |
| |
| impl mock_atrace::ATraceMocker for CallCheck { |
| fn atrace_is_tag_enabled_wrap(&mut self, _tag: u64) -> u64 { |
| 1 |
| } |
| fn atrace_int64_wrap(&mut self, tag: u64, name: *const c_char, value: i64) { |
| self.trace_int64_count += 1; |
| assert!(self.trace_int64_count < 2); |
| assert_eq!(tag, cutils_trace_bindgen::ATRACE_TAG_APP as u64); |
| // SAFETY: If the code under test is correct, the pointer is guaranteed to satisfy |
| // the requirements of `CStr::from_ptr`. If the code is not correct, this section is |
| // unsafe and will hopefully fail the test. |
| unsafe { |
| assert_eq!(CStr::from_ptr(name).to_str().expect("to_str failed"), "Test Name"); |
| } |
| assert_eq!(value, 64); |
| } |
| |
| fn finish(&self) { |
| assert_eq!(self.trace_int64_count, 1); |
| } |
| } |
| |
| let _guard = mock_atrace::set_scoped_mocker(CallCheck::default()); |
| |
| atrace_int64(AtraceTag::App, "Test Name", 64); |
| |
| mock_atrace::mocker_finish(); |
| } |
| |
| #[test] |
| fn scoped_event_starts_and_ends_in_order() { |
| #[derive(Default)] |
| struct CallCheck { |
| begin_count: u32, |
| end_count: u32, |
| instant_count: u32, |
| } |
| |
| impl mock_atrace::ATraceMocker for CallCheck { |
| fn atrace_is_tag_enabled_wrap(&mut self, _tag: u64) -> u64 { |
| 1 |
| } |
| |
| fn atrace_begin_wrap(&mut self, tag: u64, name: *const c_char) { |
| assert_eq!(self.end_count, 0); |
| assert_eq!(self.instant_count, 0); |
| |
| self.begin_count += 1; |
| assert!(self.begin_count < 2); |
| assert_eq!(tag, cutils_trace_bindgen::ATRACE_TAG_APP as u64); |
| // SAFETY: If the code under test is correct, the pointer is guaranteed to satisfy |
| // the requirements of `CStr::from_ptr`. If the code is not correct, this section is |
| // unsafe and will hopefully fail the test. |
| unsafe { |
| assert_eq!( |
| CStr::from_ptr(name).to_str().expect("to_str failed"), |
| "Scoped Event" |
| ); |
| } |
| } |
| |
| fn atrace_instant_wrap(&mut self, _tag: u64, _name: *const c_char) { |
| // We don't care about the contents of the event, we only use it to check begin/end ordering. |
| assert_eq!(self.begin_count, 1); |
| assert_eq!(self.end_count, 0); |
| |
| self.instant_count += 1; |
| assert!(self.instant_count < 2); |
| } |
| |
| fn atrace_end_wrap(&mut self, tag: u64) { |
| assert_eq!(self.begin_count, 1); |
| assert_eq!(self.instant_count, 1); |
| |
| self.end_count += 1; |
| assert!(self.end_count < 2); |
| assert_eq!(tag, cutils_trace_bindgen::ATRACE_TAG_APP as u64); |
| } |
| |
| fn finish(&self) { |
| assert_eq!(self.begin_count, 1); |
| assert_eq!(self.end_count, 1); |
| assert_eq!(self.instant_count, 1); |
| } |
| } |
| |
| let _guard = mock_atrace::set_scoped_mocker(CallCheck::default()); |
| |
| { |
| let _event_guard = begin_scoped_event(AtraceTag::App, "Scoped Event"); |
| atrace_instant(AtraceTag::App, "Instant event called within scoped event"); |
| } |
| |
| mock_atrace::mocker_finish(); |
| } |
| |
| // Need to have this alias to make the macro work, since it calls atrace::begin_scoped_event. |
| use crate as atrace; |
| fn traced_method_for_test() { |
| trace_method!(AtraceTag::App); |
| atrace_instant(AtraceTag::App, "Instant event called within method"); |
| } |
| |
| #[test] |
| fn method_trace_starts_and_ends_in_order() { |
| #[derive(Default)] |
| struct CallCheck { |
| begin_count: u32, |
| end_count: u32, |
| instant_count: u32, |
| } |
| |
| impl mock_atrace::ATraceMocker for CallCheck { |
| fn atrace_is_tag_enabled_wrap(&mut self, _tag: u64) -> u64 { |
| 1 |
| } |
| |
| fn atrace_begin_wrap(&mut self, tag: u64, name: *const c_char) { |
| assert_eq!(self.end_count, 0); |
| assert_eq!(self.instant_count, 0); |
| |
| self.begin_count += 1; |
| assert!(self.begin_count < 2); |
| assert_eq!(tag, cutils_trace_bindgen::ATRACE_TAG_APP as u64); |
| // SAFETY: If the code under test is correct, the pointer is guaranteed to satisfy |
| // the requirements of `CStr::from_ptr`. If the code is not correct, this section is |
| // unsafe and will hopefully fail the test. |
| unsafe { |
| assert_eq!( |
| CStr::from_ptr(name).to_str().expect("to_str failed"), |
| "lib::tests::traced_method_for_test" |
| ); |
| } |
| } |
| |
| fn atrace_instant_wrap(&mut self, _tag: u64, _name: *const c_char) { |
| // We don't care about the contents of the event, we only use it to check begin/end ordering. |
| assert_eq!(self.begin_count, 1); |
| assert_eq!(self.end_count, 0); |
| |
| self.instant_count += 1; |
| assert!(self.instant_count < 2); |
| } |
| |
| fn atrace_end_wrap(&mut self, tag: u64) { |
| assert_eq!(self.begin_count, 1); |
| assert_eq!(self.instant_count, 1); |
| |
| self.end_count += 1; |
| assert!(self.end_count < 2); |
| assert_eq!(tag, cutils_trace_bindgen::ATRACE_TAG_APP as u64); |
| } |
| |
| fn finish(&self) { |
| assert_eq!(self.begin_count, 1); |
| assert_eq!(self.end_count, 1); |
| assert_eq!(self.instant_count, 1); |
| } |
| } |
| |
| let _guard = mock_atrace::set_scoped_mocker(CallCheck::default()); |
| |
| traced_method_for_test(); |
| |
| mock_atrace::mocker_finish(); |
| } |
| } |