From ae184cc350a9ed93a8f590192634fe8f9355deda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Flemstr=C3=B6m?= Date: Mon, 18 Nov 2024 16:06:38 +0100 Subject: [PATCH] layer: Introduce explicit "sdk" feature that can be disabled (#14) --- .github/workflows/rust.yml | 10 ++ Cargo.lock | 19 ++++ Cargo.toml | 1 + crates/layer/Cargo.toml | 5 +- crates/layer/src/debug_annotations.rs | 4 + crates/layer/src/init.rs | 4 + crates/layer/src/lib.rs | 3 + crates/layer/src/native_layer.rs | 151 ++++++++++++++++++-------- 8 files changed, 149 insertions(+), 48 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 005d512..16d68b3 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -20,6 +20,16 @@ jobs: run: cargo build --all - name: Run tests run: cargo test --all + build-no-default-features: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Protoc + uses: arduino/setup-protoc@v3 + - name: Build + run: cargo build --all --no-default-features + - name: Run tests + run: cargo test --all --no-default-features build-tokio: runs-on: ubuntu-latest steps: diff --git a/Cargo.lock b/Cargo.lock index 98c24a3..c6d0c13 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -88,6 +88,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.38" @@ -419,6 +425,18 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -922,6 +940,7 @@ dependencies = [ "anyhow", "bytes", "cxx", + "nix", "prost", "serde_yaml", "thiserror", diff --git a/Cargo.toml b/Cargo.toml index 426b2fb..4119319 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ bytes = "1.8.0" cxx = { version = "1.0.129", features = ["c++17"] } cxx-build = { version = "1.0.129", features = ["parallel"] } futures = "0.3.31" +nix = "0.29.0" pbjson = "0.6.0" pbjson-build = "0.6.2" pbjson-types = "0.6.0" diff --git a/crates/layer/Cargo.toml b/crates/layer/Cargo.toml index c3ed9fe..2dc71b3 100644 --- a/crates/layer/Cargo.toml +++ b/crates/layer/Cargo.toml @@ -9,6 +9,7 @@ edition.workspace = true [dependencies] bytes.workspace = true cxx.workspace = true +nix = { workspace = true, features = ["time"] } prost.workspace = true thiserror.workspace = true thread-id.workspace = true @@ -16,7 +17,7 @@ thread_local.workspace = true tokio = { workspace = true, optional = true } tracing.workspace = true tracing-perfetto-sdk-schema.workspace = true -tracing-perfetto-sdk-sys.workspace = true +tracing-perfetto-sdk-sys = { workspace = true, optional = true } tracing-subscriber.workspace = true [dev-dependencies] @@ -26,8 +27,10 @@ tokio = { workspace = true, features = ["macros", "time", "rt-multi-thread"] } tracing-appender.workspace = true [features] +default = ["sdk"] tokio = ["dep:tokio"] serde = ["tracing-perfetto-sdk-schema/serde"] +sdk = ["dep:tracing-perfetto-sdk-sys"] [[example]] name = "native-layer" diff --git a/crates/layer/src/debug_annotations.rs b/crates/layer/src/debug_annotations.rs index 401085d..2a39f50 100644 --- a/crates/layer/src/debug_annotations.rs +++ b/crates/layer/src/debug_annotations.rs @@ -7,11 +7,13 @@ use schema::debug_annotation; use tracing::field; use tracing_perfetto_sdk_schema as schema; use tracing_perfetto_sdk_schema::track_event; +#[cfg(feature = "sdk")] use tracing_perfetto_sdk_sys::ffi; const COUNTER_FIELD_PREFIX: &str = "counter."; const SUPPRESS_EVENT_FIELD: &str = "perfetto.suppress_event"; +#[cfg(feature = "sdk")] #[derive(Default)] pub struct FFIDebugAnnotations { counters: Vec, @@ -42,6 +44,7 @@ pub enum CounterValue { Int(i64), } +#[cfg(feature = "sdk")] impl FFIDebugAnnotations { pub fn as_ffi(&self) -> ffi::DebugAnnotations { ffi::DebugAnnotations { @@ -57,6 +60,7 @@ impl FFIDebugAnnotations { } } +#[cfg(feature = "sdk")] impl field::Visit for FFIDebugAnnotations { fn record_f64(&mut self, field: &field::Field, value: f64) { if !populate_counter(&mut self.counters, field, value) { diff --git a/crates/layer/src/init.rs b/crates/layer/src/init.rs index c6a7323..6248dbc 100644 --- a/crates/layer/src/init.rs +++ b/crates/layer/src/init.rs @@ -1,6 +1,9 @@ +#[cfg(feature = "sdk")] use tracing_perfetto_sdk_sys::ffi; +#[allow(unused_variables)] pub fn global_init(enable_in_process_backend: bool, enable_system_backend: bool) { + #[cfg(feature = "sdk")] ffi::perfetto_global_init( log_callback, enable_in_process_backend, @@ -8,6 +11,7 @@ pub fn global_init(enable_in_process_backend: bool, enable_system_backend: bool) ); } +#[cfg(feature = "sdk")] fn log_callback(level: ffi::LogLev, line: i32, filename: &str, message: &str) { match level { ffi::LogLev::Debug => { diff --git a/crates/layer/src/lib.rs b/crates/layer/src/lib.rs index a30ebd7..7ca3b0b 100644 --- a/crates/layer/src/lib.rs +++ b/crates/layer/src/lib.rs @@ -171,6 +171,7 @@ // Internal modules: mod debug_annotations; +#[cfg(feature = "sdk")] mod ffi_utils; mod ids; mod init; @@ -179,10 +180,12 @@ mod init; pub mod error; pub mod flavor; pub mod native_layer; +#[cfg(feature = "sdk")] pub mod sdk_layer; // Convenience re-exports: pub use error::Error; pub use flavor::Flavor; pub use native_layer::NativeLayer; +#[cfg(feature = "sdk")] pub use sdk_layer::SdkLayer; diff --git a/crates/layer/src/native_layer.rs b/crates/layer/src/native_layer.rs index 3a454c7..665c85e 100644 --- a/crates/layer/src/native_layer.rs +++ b/crates/layer/src/native_layer.rs @@ -1,6 +1,6 @@ //! The main tracing layer and related utils exposed by this crate. use std::sync::atomic; -use std::{borrow, cell, env, process, sync, thread, time}; +use std::{borrow, cell, env, marker, process, sync, thread, time}; use prost::encoding; #[cfg(feature = "tokio")] @@ -10,10 +10,13 @@ use tracing_perfetto_sdk_schema as schema; use tracing_perfetto_sdk_schema::{ counter_descriptor, trace_packet, track_descriptor, track_event, }; +#[cfg(feature = "sdk")] use tracing_perfetto_sdk_sys::ffi; use tracing_subscriber::{fmt, layer, registry}; -use crate::{debug_annotations, error, ffi_utils, flavor, ids, init}; +use crate::{debug_annotations, error, flavor, ids, init}; +#[cfg(feature = "sdk")] +use crate::ffi_utils; /// A layer to be used with `tracing-subscriber` that natively writes the /// Perfetto trace packets in Rust code, but also polls the Perfetto SDK for @@ -30,6 +33,7 @@ where /// A builder for [`NativeLayer`]; use [`NativeLayer::from_config`] or /// [`NativeLayer::from_config_bytes`] to create a new instance. pub struct Builder<'c, W> { + #[cfg(feature = "sdk")] config_bytes: borrow::Cow<'c, [u8]>, writer: W, drop_flush_timeout: time::Duration, @@ -42,6 +46,7 @@ pub struct Builder<'c, W> { discard_tracing_data: bool, enable_in_process: bool, enable_system: bool, + _phantom: marker::PhantomData<&'c ()>, } #[derive(Default)] @@ -59,6 +64,7 @@ where W: for<'w> fmt::MakeWriter<'w>, { // Mutex is held during start, stop, flush, and poll + #[cfg(feature = "sdk")] ffi_session: sync::Arc>>>, writer: sync::Arc, drop_flush_timeout: time::Duration, @@ -123,33 +129,37 @@ where // Shared global initialization for all layers init::global_init(builder.enable_in_process, builder.enable_system); + let writer = sync::Arc::new(builder.writer); + // We send the config to the C++ code as encoded bytes, because it would be too // annoying to have some sort of shared proto struct between the Rust // and C++ worlds - let mut ffi_session = ffi::new_tracing_session(builder.config_bytes.as_ref(), -1)?; - ffi_session.pin_mut().start(); - let ffi_session = sync::Arc::new(sync::Mutex::new(Some(ffi_session))); - - let writer = sync::Arc::new(builder.writer); + #[cfg(feature = "sdk")] + let ffi_session = { + let mut ffi_session = ffi::new_tracing_session(builder.config_bytes.as_ref(), -1)?; + ffi_session.pin_mut().start(); + let ffi_session = sync::Arc::new(sync::Mutex::new(Some(ffi_session))); + + let thread_ffi_session = sync::Arc::clone(&ffi_session); + let thread_writer = sync::Arc::clone(&writer); + + thread::Builder::new() + .name("tracing-perfetto-poller".to_owned()) + .spawn(move || { + background_poller_thread( + thread_ffi_session, + thread_writer, + builder.background_flush_timeout, + builder.background_poll_timeout, + builder.background_poll_interval, + ) + })?; + ffi_session + }; let drop_flush_timeout = builder.drop_flush_timeout; let drop_poll_timeout = builder.drop_poll_timeout; - let thread_ffi_session = sync::Arc::clone(&ffi_session); - let thread_writer = sync::Arc::clone(&writer); - - thread::Builder::new() - .name("tracing-perfetto-poller".to_owned()) - .spawn(move || { - background_poller_thread( - thread_ffi_session, - thread_writer, - builder.background_flush_timeout, - builder.background_poll_timeout, - builder.background_poll_interval, - ) - })?; - let force_flavor = builder.force_flavor; let delay_slice_begin = builder.delay_slice_begin; let discard_tracing_data = builder.discard_tracing_data; @@ -164,6 +174,7 @@ where let thread_local_ctxs = thread_local::ThreadLocal::new(); let inner = sync::Arc::new(Inner { + #[cfg(feature = "sdk")] ffi_session, writer, drop_flush_timeout, @@ -255,8 +266,8 @@ where debug_annotations: debug_annotations::ProtoDebugAnnotations, ) { let packet = self.create_slice_begin_track_event_packet( - ffi::trace_time_ns(), - ffi::trace_clock_id(), + trace_time_ns(), + trace_clock_id(), meta, track_uuid, sequence_id, @@ -300,8 +311,8 @@ where } let packet = self.create_slice_end_track_event_packet( - ffi::trace_time_ns(), - ffi::trace_clock_id(), + trace_time_ns(), + trace_clock_id(), meta, track_uuid, sequence_id, @@ -319,8 +330,8 @@ where sequence_id: ids::SequenceId, ) { let packet = self.create_event_track_event_packet( - ffi::trace_time_ns(), - ffi::trace_clock_id(), + trace_time_ns(), + trace_clock_id(), meta, debug_annotations, track_uuid, @@ -332,8 +343,8 @@ where fn report_counters(&self, meta: &tracing::Metadata, counters: Vec) { if !counters.is_empty() { - let timestamp_ns = ffi::trace_time_ns(); - let timestamp_clock_id = ffi::trace_clock_id(); + let timestamp_ns = trace_time_ns(); + let timestamp_clock_id = trace_clock_id(); self.ensure_counters_known(meta, &counters); for counter in counters { let packet = self.create_counter_track_event_packet( @@ -767,8 +778,8 @@ where if flavor == flavor::Flavor::Async { if self.inner.delay_slice_begin { span.extensions_mut().insert(DelayedSliceBegin { - timestamp_ns: ffi::trace_time_ns(), - timestamp_clock_id: ffi::trace_clock_id(), + timestamp_ns: trace_time_ns(), + timestamp_clock_id: trace_clock_id(), meta, track_uuid, sequence_id, @@ -876,8 +887,10 @@ impl<'c, W> Builder<'c, W> where W: for<'w> fmt::MakeWriter<'w> + Send + Sync + 'static, { + #[allow(unused_variables)] fn new(config_bytes: borrow::Cow<'c, [u8]>, writer: W) -> Self { Self { + #[cfg(feature = "sdk")] config_bytes, writer, drop_flush_timeout: time::Duration::from_millis(100), @@ -890,6 +903,7 @@ where discard_tracing_data: false, enable_in_process: true, enable_system: false, + _phantom: marker::PhantomData, } } @@ -983,30 +997,38 @@ impl Inner where W: for<'w> fmt::MakeWriter<'w>, { + #[allow(unused_variables)] fn flush( &self, flush_timeout: time::Duration, poll_timeout: time::Duration, ) -> error::Result<()> { - use std::io::Write as _; - let data = ffi_utils::with_session_lock(&*self.ffi_session, |session| { - ffi_utils::do_flush(session, flush_timeout)?; - let data = ffi_utils::do_poll_traces(session, poll_timeout)?; - Ok(data) - })?; - self.writer.make_writer().write_all(&*data.data)?; + + #[cfg(feature = "sdk")] { + use std::io::Write as _; + + let data = ffi_utils::with_session_lock(&*self.ffi_session, |session| { + ffi_utils::do_flush(session, flush_timeout)?; + let data = ffi_utils::do_poll_traces(session, poll_timeout)?; + Ok(data) + })?; + self.writer.make_writer().write_all(&*data.data)?; + } + Ok(()) } fn stop(&self) -> error::Result<()> { - // Can't use ffi_utils::with_session_lock here because we want to take the - // session object - let mut session = self - .ffi_session - .lock() - .map_err(|_| error::Error::PoisonedMutex)?; - if let Some(mut session) = session.take() { - session.pin_mut().stop(); + #[cfg(feature = "sdk")] { + // Can't use ffi_utils::with_session_lock here because we want to take the + // session object + let mut session = self + .ffi_session + .lock() + .map_err(|_| error::Error::PoisonedMutex)?; + if let Some(mut session) = session.take() { + session.pin_mut().stop(); + } } Ok(()) } @@ -1022,6 +1044,41 @@ where } } +#[cfg(not(feature="sdk"))] +static HAS_BOOTTIME: sync::LazyLock = sync::LazyLock::new(|| { + nix::time::clock_gettime(nix::time::ClockId::CLOCK_BOOTTIME).is_ok() +}); + +#[cfg(feature="sdk")] +fn trace_time_ns() -> u64 { + ffi::trace_time_ns() +} + +#[cfg(not(feature="sdk"))] +fn trace_time_ns() -> u64 { + use nix::time; + if *HAS_BOOTTIME { + std::time::Duration::from(time::clock_gettime(time::ClockId::CLOCK_BOOTTIME).unwrap()).as_nanos() as u64 + } else { + std::time::Duration::from(time::clock_gettime(time::ClockId::CLOCK_MONOTONIC).unwrap()).as_nanos() as u64 + } +} + +#[cfg(feature="sdk")] +fn trace_clock_id() -> u32 { + ffi::trace_clock_id() +} + +#[cfg(not(feature="sdk"))] +fn trace_clock_id() -> u32 { + if *HAS_BOOTTIME { + schema::BuiltinClock::Boottime as u32 + } else { + schema::BuiltinClock::Monotonic as u32 + } +} + +#[cfg(feature = "sdk")] fn background_poller_thread( ffi_session: sync::Arc>>>, writer: sync::Arc,