// 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) -> &'static str { std::any::type_name::() } // 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> = RefCell::new(Box::new(DefaultMocker{}))); /// Sets the global mock object. fn set_mocker(mocker: Box) { 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: 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(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(); } }