blob: 13ecffb37790500d94fa28dbdf968d00381f70be [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use fuchsia_trace::TraceFutureExt;
use std::future::{Future, poll_fn};
use std::sync::{Barrier, Condvar, Mutex};
use std::task::Poll;
use {fuchsia_async as fasync, fuchsia_trace as trace, fuchsia_trace_observer as trace_observer};
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_trace_enabled() -> bool {
return trace::is_enabled();
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_category_disabled_cstr() -> bool {
return trace::category_enabled(c"-disabled");
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_category_disabled_str() -> bool {
return trace::category_enabled("-disabled");
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_category_enabled_cstr() -> bool {
return trace::category_enabled(c"+enabled");
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_category_enabled_str() -> bool {
return trace::category_enabled("+enabled");
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_counter_macro_cstr() {
trace::counter!(c"+enabled", c"name", 42, "arg" => 10);
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_counter_macro_str() {
trace::counter!("+enabled", "name", 42, "arg" => 10);
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_counter_macro_str_and_string() {
trace::counter!("+enabled", "name".to_string(), 42, "arg" => 10);
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_instant_macro_cstr() {
trace::instant!(c"+enabled", c"name", trace::Scope::Process, "arg" => 10);
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_instant_macro_str() {
trace::instant!("+enabled", "name", trace::Scope::Process, "arg" => 10);
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_instant_macro_str_and_string() {
trace::instant!("+enabled", "name".to_string(), trace::Scope::Process, "arg" => 10);
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_duration_macro_cstr() {
trace::duration!(c"+enabled", c"name", "x" => 5, "y" => 10);
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_duration_macro_str() {
trace::duration!("+enabled", "name", "x" => 5, "y" => 10);
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_duration_macro_str_and_string() {
trace::duration!("+enabled", "name".to_string(), "x" => 5, "y" => 10);
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_duration_macro_with_scope() {
// N.B. The ordering here is intentional. The duration! macro emits a trace
// event when the scoped object is dropped. From an output perspective,
// that means we are looking to see that the instant event occurs first.
trace::duration!(c"+enabled", c"name", "x" => 5, "y" => 10);
trace::instant!(c"+enabled", c"name", trace::Scope::Process, "arg" => 10);
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_duration_begin_end_macros_cstr() {
trace::duration_begin!(c"+enabled", c"name", "x" => 5);
trace::instant!(c"+enabled", c"name", trace::Scope::Process, "arg" => 10);
trace::duration_end!(c"+enabled", c"name", "y" => "foo");
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_duration_begin_end_macros_str() {
trace::duration_begin!("+enabled", "name", "x" => 5);
trace::instant!("+enabled", "name", trace::Scope::Process, "arg" => 10);
trace::duration_end!("+enabled", "name", "y" => "foo");
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_duration_begin_end_macros_str_and_string() {
let name = "name".to_string();
trace::duration_begin!("+enabled", &name, "x" => 5);
trace::instant!("+enabled", &name, trace::Scope::Process, "arg" => 10);
trace::duration_end!("+enabled", &name, "y" => "foo");
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_blob_macro_cstr() {
trace::blob!(c"+enabled", c"name", "blob contents".as_bytes().to_vec().as_slice(), "x" => 5);
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_blob_macro_str() {
trace::blob!("+enabled", "name", "blob contents".as_bytes().to_vec().as_slice(), "x" => 5);
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_blob_macro_str_and_string() {
trace::blob!("+enabled", "name".to_string(), "blob contents".as_bytes().to_vec().as_slice(), "x" => 5);
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_flow_begin_step_end_macros_cstr() {
trace::flow_begin!(c"+enabled", c"name", 123.into(), "x" => 5);
trace::flow_step!(c"+enabled", c"step", 123.into(), "z" => 42);
trace::flow_end!(c"+enabled", c"name", 123.into(), "y" => "foo");
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_flow_begin_step_end_macros_str() {
trace::flow_begin!("+enabled", "name", 123.into(), "x" => 5);
trace::flow_step!("+enabled", "step", 123.into(), "z" => 42);
trace::flow_end!("+enabled", "name", 123.into(), "y" => "foo");
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_flow_begin_step_end_macros_str_and_string() {
let name = "name".to_string();
trace::flow_begin!("+enabled", &name, 123.into(), "x" => 5);
trace::flow_step!("+enabled", "step".to_string(), 123.into(), "z" => 42);
trace::flow_end!("+enabled", &name, 123.into(), "y" => "foo");
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_arglimit() {
trace::duration!(c"+enabled", c"name",
"1" => 1,
"2" => 2,
"3" => 3,
"4" => 4,
"5" => 5,
"6" => 6,
"7" => 7,
"8" => 8,
"9" => 9,
"10" => 10,
"11" => 11,
"12" => 12,
"13" => 13,
"14" => 14,
"15" => 15
);
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_async_event_with_scope() {
// N.B. The ordering here is intentional. The async_enter! macro emits a trace event when the
// scoped object is instantiated and when it is dropped. From an output perspective, that means
// we are looking to see that the instant event occurs sandwiched between the two.
let _guard = trace::async_enter!(1.into(), c"+enabled", c"name", "x" => 5, "y" => 10);
trace::instant!(c"+enabled", c"name", trace::Scope::Process, "arg" => 10);
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_alert_cstr() {
trace::alert!(c"+enabled", c"alert_name");
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_alert_str() {
trace::alert!("+enabled", "alert_name");
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_alert_str_and_string() {
let name = "alert_name".to_string();
trace::alert!("+enabled", &name);
}
fn run_future(fut: impl Future<Output = ()>) {
fasync::TestExecutor::new().run_singlethreaded(fut);
}
fn multi_poll_future(mut ready_after: u64) -> impl Future<Output = ()> {
poll_fn(move |cx| {
if ready_after > 0 {
ready_after -= 1;
cx.waker().clone().wake();
Poll::Pending
} else {
Poll::Ready(())
}
})
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_trace_future_enabled_cstr() {
// The future is immediately ready. There will be a single duration event around the poll call.
// No flow events will be generated.
run_future(multi_poll_future(0).trace(trace::trace_future_args!(
c"+enabled",
c"name",
3.into()
)));
// The future will return pending once followed by ready. There will be 2 duration events. The
// 1st duration will contain a flow begin, and the 2nd duration will contain a flow end.
run_future(multi_poll_future(1).trace(trace::trace_future_args!(
c"+enabled",
c"name",
4.into()
)));
// The future will return pending twice followed by ready. There will be 3 duration events. The
// 1st duration will contain a flow begin, the 2nd duration will contain a flow step, and the
// 3rd duration will contain a flow end.
run_future(multi_poll_future(2).trace(trace::trace_future_args!(
c"+enabled",
c"name",
5.into()
)));
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_trace_future_enabled_str() {
// The future is immediately ready. There will be a single duration event around the poll call.
// No flow events will be generated.
run_future(multi_poll_future(0).trace(trace::trace_future_args!("+enabled", "name", 3.into())));
// The future will return pending once followed by ready. There will be 2 duration events. The
// 1st duration will contain a flow begin, and the 2nd duration will contain a flow end.
run_future(multi_poll_future(1).trace(trace::trace_future_args!("+enabled", "name", 4.into())));
// The future will return pending twice followed by ready. There will be 3 duration events. The
// 1st duration will contain a flow begin, the 2nd duration will contain a flow step, and the
// 3rd duration will contain a flow end.
run_future(multi_poll_future(2).trace(trace::trace_future_args!("+enabled", "name", 5.into())));
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_trace_future_enabled_str_and_string() {
let name = "name".to_string();
// The future is immediately ready. There will be a single duration event around the poll call.
// No flow events will be generated.
run_future(multi_poll_future(0).trace(trace::trace_future_args!("+enabled", &name, 3.into())));
// The future will return pending once followed by ready. There will be 2 duration events. The
// 1st duration will contain a flow begin, and the 2nd duration will contain a flow end.
run_future(multi_poll_future(1).trace(trace::trace_future_args!("+enabled", &name, 4.into())));
// The future will return pending twice followed by ready. There will be 3 duration events. The
// 1st duration will contain a flow begin, the 2nd duration will contain a flow step, and the
// 3rd duration will contain a flow end.
run_future(multi_poll_future(2).trace(trace::trace_future_args!("+enabled", &name, 5.into())));
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_trace_future_enabled_with_arg() {
run_future(multi_poll_future(1).trace(trace::trace_future_args!(
c"+enabled",
c"name",
3.into(),
"arg" => 10,
)));
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_trace_future_disabled() {
run_future(multi_poll_future(1).trace(trace::trace_future_args!(
c"-disabled",
c"name",
3.into(),
)));
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_test_trace_future_disabled_with_arg() {
#[allow(unreachable_code)]
run_future(multi_poll_future(1).trace(trace::trace_future_args!(
c"-disabled",
c"name",
3.into(),
"arg" => {
panic!("arg should not be evaluated");
()
}
)));
}
static STATE_CV: Condvar = Condvar::new();
static TRACE_STATE: Mutex<fuchsia_trace::TraceState> =
Mutex::new(fuchsia_trace::TraceState::Stopped);
#[unsafe(no_mangle)]
pub extern "C" fn rs_check_trace_state() -> u32 {
*TRACE_STATE.lock().unwrap() as u32
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_wait_trace_state_is(expected: u32) {
let mut state = TRACE_STATE.lock().unwrap();
while *state as u32 != expected {
state = STATE_CV.wait(state).unwrap();
}
}
#[unsafe(no_mangle)]
pub extern "C" fn rs_setup_trace_observer() {
static BARRIER: Barrier = Barrier::new(2);
std::thread::spawn(|| {
let mut executor = fasync::LocalExecutor::default();
executor.run_singlethreaded(async move {
let observer = trace_observer::TraceObserver::new();
BARRIER.wait();
while let Ok(new_state) = observer.on_state_changed().await {
let mut state = TRACE_STATE.lock().unwrap();
*state = new_state;
STATE_CV.notify_all();
}
});
});
// Ensure that we've registered our trace_observer in the spawned thread before continuing on
// and potentially starting the trace session.
BARRIER.wait();
}