use std::ffi::{CStr, CString};
use std::marker::PhantomData;
#[cfg(feature = "api-19")]
pub mod api_19;
#[doc(hidden)]
pub trait FinishBehavior {
fn finish(&self);
}
#[derive(Clone, Copy, Default)]
#[doc(hidden)]
pub struct DefaultFinishBehavior;
impl FinishBehavior for DefaultFinishBehavior {
fn finish(&self) {
finish_trace();
}
}
#[cfg(all(feature = "api-19", target_env = "ohos"))]
#[derive(Clone, Copy)]
#[doc(hidden)]
pub struct ExFinishBehavior(api_19::HiTraceOutputLevel);
#[cfg(all(feature = "api-19", target_env = "ohos"))]
impl FinishBehavior for ExFinishBehavior {
fn finish(&self) {
finish_trace_ex(self.0);
}
}
pub fn start_trace<T: AsRef<CStr>>(name: &T) {
start_trace_cstr(name.as_ref())
}
#[cfg(all(
feature = "api-19",
target_env = "ohos",
not(feature = "max_level_off")
))]
pub fn start_trace_ex<T: AsRef<CStr>, U: AsRef<CStr>>(
level: api_19::HiTraceOutputLevel,
name: &T,
custom_args: &U,
) {
start_trace_ex_cstr(level, name.as_ref(), custom_args.as_ref())
}
#[cfg(all(target_env = "ohos", not(feature = "max_level_off")))]
fn start_trace_cstr(name: &CStr) {
unsafe {
hitrace_sys::OH_HiTrace_StartTrace(name.as_ptr());
}
}
#[cfg(all(
feature = "api-19",
target_env = "ohos",
not(feature = "max_level_off")
))]
fn start_trace_ex_cstr(level: api_19::HiTraceOutputLevel, name: &CStr, custom_args: &CStr) {
unsafe {
hitrace_sys::OH_HiTrace_StartTraceEx(level.into(), name.as_ptr(), custom_args.as_ptr());
}
}
#[cfg(any(not(target_env = "ohos"), feature = "max_level_off"))]
fn start_trace_cstr(_: &CStr) {}
pub fn finish_trace() {
#[cfg(all(target_env = "ohos", not(feature = "max_level_off")))]
fn finish_trace_() {
unsafe {
hitrace_sys::OH_HiTrace_FinishTrace();
}
}
#[cfg(any(not(target_env = "ohos"), feature = "max_level_off"))]
fn finish_trace_() {}
finish_trace_()
}
#[cfg(all(feature = "api-19", target_env = "ohos"))]
pub fn finish_trace_ex(level: api_19::HiTraceOutputLevel) {
finish_trace_ex_(level)
}
#[cfg(all(
feature = "api-19",
target_env = "ohos",
not(feature = "max_level_off")
))]
fn finish_trace_ex_(level: api_19::HiTraceOutputLevel) {
unsafe {
hitrace_sys::OH_HiTrace_FinishTraceEx(level.into());
}
}
#[cfg(any(not(target_env = "ohos"), feature = "max_level_off"))]
#[cfg(all(feature = "api-19", target_env = "ohos"))]
fn finish_trace_ex_(_: api_19::HiTraceOutputLevel) {}
#[cfg(all(target_env = "ohos", not(feature = "max_level_off")))]
fn trace_metric_cstr(name: &CStr, count: i64) {
unsafe {
hitrace_sys::OH_HiTrace_CountTrace(name.as_ptr(), count);
}
}
#[cfg(any(not(target_env = "ohos"), feature = "max_level_off"))]
fn trace_metric_cstr(_: &CStr, _: i64) {}
fn trace_metric<T: AsRef<CStr>, C: Into<i64>>(name: &T, count: C) {
trace_metric_cstr(name.as_ref(), count.into());
}
pub fn trace_metric_str<C: Into<i64>>(name: &str, count: C) {
let c_string = CString::new(name).expect("Failed to convert to CString");
trace_metric(&c_string, count.into());
}
pub fn trace_metric_saturating<T: AsRef<CStr>>(name: &T, count: impl SaturatingIntoI64) {
trace_metric_cstr(name.as_ref(), count.saturating_into());
}
pub fn trace_metric_saturating_str(name: &str, count: impl SaturatingIntoI64) {
let c_string = CString::new(name).expect("Failed to convert to CString");
trace_metric_saturating(&c_string, count);
}
pub trait SaturatingIntoI64 {
fn saturating_into(self) -> i64;
}
impl SaturatingIntoI64 for u64 {
fn saturating_into(self) -> i64 {
self.min(i64::MAX as u64) as i64
}
}
impl SaturatingIntoI64 for i128 {
fn saturating_into(self) -> i64 {
if self > i64::MAX as i128 {
i64::MAX
} else if self < i64::MIN as i128 {
i64::MIN
} else {
self as i64
}
}
}
impl SaturatingIntoI64 for u128 {
fn saturating_into(self) -> i64 {
self.min(i64::MAX as u128) as i64
}
}
impl SaturatingIntoI64 for usize {
fn saturating_into(self) -> i64 {
self.min(i64::MAX as usize) as i64
}
}
impl SaturatingIntoI64 for isize {
fn saturating_into(self) -> i64 {
if self > i64::MAX as isize {
i64::MAX
} else if self < i64::MIN as isize {
i64::MIN
} else {
self as i64
}
}
}
pub struct ScopedTrace<F: FinishBehavior = DefaultFinishBehavior> {
finish_behavior: F,
phantom_data: PhantomData<*mut u8>,
}
impl<F: FinishBehavior> ScopedTrace<F> {
fn new(finish_behavior: F) -> Self {
Self {
finish_behavior,
phantom_data: PhantomData,
}
}
}
impl ScopedTrace<DefaultFinishBehavior> {
#[must_use]
pub fn start_trace<T: AsRef<CStr>>(name: &T) -> Self {
start_trace(name);
Self::new(DefaultFinishBehavior)
}
#[must_use]
pub fn start_trace_str(name: &str) -> Self {
Self::start_trace(&CString::new(name).expect("Contained null-byte"))
}
#[doc(hidden)]
pub unsafe fn _start_trace_str_with_null(name_with_null: &str) -> Self {
#[cfg(any(not(target_env = "ohos"), feature = "max_level_off"))]
let _ = name_with_null;
#[cfg(all(target_env = "ohos", not(feature = "max_level_off")))]
unsafe {
hitrace_sys::OH_HiTrace_StartTrace(name_with_null.as_ptr().cast::<core::ffi::c_char>());
}
Self::new(DefaultFinishBehavior)
}
}
#[cfg(all(feature = "api-19", target_env = "ohos"))]
impl ScopedTrace<ExFinishBehavior> {
#[must_use]
pub fn start_trace_ex<T: AsRef<CStr>, U: AsRef<CStr>>(
level: api_19::HiTraceOutputLevel,
name: &T,
custom_args: &U,
) -> Self {
start_trace_ex(level, name, custom_args);
Self::new(ExFinishBehavior(level))
}
#[must_use]
pub fn start_trace_ex_str(
level: api_19::HiTraceOutputLevel,
name: &str,
custom_args: &str,
) -> Self {
Self::start_trace_ex(
level,
&CString::new(name).expect("Contained null-byte"),
&CString::new(custom_args).expect("Contained null-byte"),
)
}
}
impl<F: FinishBehavior> Drop for ScopedTrace<F> {
fn drop(&mut self) {
self.finish_behavior.finish();
}
}
#[cfg(test)]
mod test {
use static_assertions::assert_not_impl_any;
use crate::ScopedTrace;
assert_not_impl_any!(ScopedTrace: Send, Sync);
}