Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ exclude = ["/.github/**"]
[lib]
name = "probe"
crate-type = ["rlib"]

[lints.clippy]
new_without_default = "allow"
46 changes: 36 additions & 10 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,14 @@
//!
//! ```rust
//! use probe::probe;
//! fn main() {
//! probe!(foo, begin);
//! let mut total = 0;
//! for i in 0..100 {
//! total += i;
//! probe!(foo, loop, i, total);
//! }
//! assert_eq!(total, 4950);
//! probe!(foo, end);
//! probe!(foo, begin);
//! let mut total = 0;
//! for i in 0..100 {
//! total += i;
//! probe!(foo, loop, i, total);
//! }
//! assert_eq!(total, 4950);
//! probe!(foo, end);
//! ```
//!
//! ## Using probes with SystemTap
Expand Down Expand Up @@ -146,6 +144,34 @@ macro_rules! probe(
/// ```
#[macro_export]
macro_rules! probe_lazy(
(extern $semaphore:path, $provider:ident, $name:ident $(, $arg:expr)* $(,)?)
=> ({
// Type-check the location to have 'static lifetime, so it is safe to
// put it in the stapsdt note. The program cannot really do
// anything with it---the only available method is enabled(); therefore,
// even though not placing it in the .probes section could upset the tracing
// tool, it cannot be used to trigger undefined behavior.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is worth pointing out—here I assume that the tools are robust enough and will either complain or ignore the probe, but not mess themselves up.

let semaphore: &'static $crate::Semaphore = &$semaphore;
let enabled = semaphore.enabled();
if enabled {
$crate::platform_probe_lazy!($semaphore, $provider, $name, $($arg,)*);
}
enabled
});

($provider:ident, $name:ident $(, $arg:expr)* $(,)?)
=> ($crate::platform_probe_lazy!($provider, $name, $($arg,)*));
=> ({
$crate::platform_declare_semaphore!(SEMAPHORE);
$crate::probe_lazy!(extern SEMAPHORE, $provider, $name, $($arg,)*)
});
);

/// A location that represents whether a tracepoint was enabled.
///
/// [`probe_lazy!`] uses a [`Semaphore`] to guard evaluation of
/// arguments. A platform-specific mechanism ensures that [`Semaphore::enabled`]
/// returns `true` when a debugger or tracing tool is attached to the probe.
///
/// Note that, if a platform implementation can't determine that, it might
/// always return `true`.
pub use platform::Semaphore;
31 changes: 26 additions & 5 deletions src/platform/default.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
pub struct Semaphore;

impl Semaphore {
/// Return a `Semaphore` that starts as disabled.
pub const fn new() -> Self {
Self
}

/// Return whether a debugger or tracing tool is attached to a probe
/// that uses this semaphore.
pub fn enabled(&self) -> bool {
false
}
}

#[doc(hidden)]
#[macro_export]
macro_rules! platform_probe(
Expand All @@ -7,14 +22,20 @@ macro_rules! platform_probe(
})
);

#[doc(hidden)]
#[macro_export]
macro_rules! platform_declare_semaphore(
($semaphore:ident) => {
static $semaphore: $crate::Semaphore = $crate::Semaphore::new();
}
);

#[doc(hidden)]
#[macro_export]
macro_rules! platform_probe_lazy(
($provider:ident, $name:ident, $($arg:expr,)*) => ({
($semaphore:path, $provider:ident, $name:ident, $($arg:expr,)*) => ({
// The caller wraps this with what is effectively "if false"
// Expand the arguments so they don't cause unused warnings.
if false {
let _ = ($($arg,)*);
}
false
let _ = ($($arg,)*);
})
);
8 changes: 6 additions & 2 deletions src/platform/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(any(target_os = "linux", target_os = "android", docsrs))]
mod systemtap;
#[cfg(any(target_os = "linux", target_os = "android", docsrs))]
pub use systemtap::*;

#[cfg(not(any(target_os = "linux", target_os = "android")))]
#[cfg(not(any(target_os = "linux", target_os = "android", docsrs)))]
mod default;
#[cfg(not(any(target_os = "linux", target_os = "android", docsrs)))]
pub use default::*;
49 changes: 38 additions & 11 deletions src/platform/systemtap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
//! * <https://sourceware.org/systemtap/wiki/UserSpaceProbeImplementation>
//! * <https://sourceware.org/gdb/onlinedocs/gdb/Static-Probe-Points.html>

use core::cell::UnsafeCell;
use core::ptr;

//
// DEVELOPER NOTES
//
Expand Down Expand Up @@ -55,6 +58,27 @@
// when there's nobody attached to see the probe.
//

#[repr(transparent)]
pub struct Semaphore(UnsafeCell<u16>);

// SAFETY: the UnsafeCell is only ever read as far as Rust is
// concerned; data races require a read and a write.
unsafe impl Sync for Semaphore {}

impl Semaphore {
/// Return a `Semaphore` that starts as disabled.
pub const fn new() -> Self {
Self(UnsafeCell::new(0))
}

/// Return whether a debugger or tracing tool is attached to a probe
/// that uses this semaphore.
#[inline(always)]
pub fn enabled(&self) -> bool {
(unsafe { ptr::read_volatile(self.0.get() as *const _) }) != 0u16
}
}

#[doc(hidden)]
#[macro_export]
macro_rules! platform_probe(
Expand All @@ -65,15 +89,18 @@ macro_rules! platform_probe(

#[doc(hidden)]
#[macro_export]
macro_rules! platform_probe_lazy(
($provider:ident, $name:ident, $($arg:expr,)*) => ({
macro_rules! platform_declare_semaphore(
($semaphore:ident) => {
#[link_section = ".probes"]
static mut SEMAPHORE: u16 = 0;
let enabled = unsafe { ::core::ptr::read_volatile(&SEMAPHORE) } != 0;
if enabled {
$crate::sdt!([sym "{}" SEMAPHORE], $provider, $name, $($arg,)*);
}
enabled
static $semaphore: $crate::Semaphore = $crate::Semaphore::new();
}
);

#[doc(hidden)]
#[macro_export]
macro_rules! platform_probe_lazy(
($semaphore:path, $provider:ident, $name:ident, $($arg:expr,)*) => ({
$crate::sdt!([sym "{}" $semaphore], $provider, $name, $($arg,)*);
})
);

Expand All @@ -82,7 +109,7 @@ macro_rules! platform_probe_lazy(
#[doc(hidden)]
#[macro_export]
macro_rules! sdt(
([sym $symstr:literal $($sym:ident)?],
([sym $symstr:literal $($sym:path)?],
$provider:ident, $name:ident, $($arg:expr,)*
) => (
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
Expand All @@ -94,7 +121,7 @@ macro_rules! sdt(
$provider, $name, $($arg,)*);
);

([sym $symstr:literal $($sym:ident)?, opt $($opt:ident)?],
([sym $symstr:literal $($sym:path)?, opt $($opt:ident)?],
$provider:ident, $name:ident, $($arg1:expr, $($arg:expr,)*)?
) => (
#[cfg(target_pointer_width = "32")]
Expand All @@ -106,7 +133,7 @@ macro_rules! sdt(
$provider, $name, $("-8@{}", $arg1, $(" -8@{}", $arg,)*)?);
);

([sym $symstr:literal $($sym:ident)?, opt $($opt:ident)?, size $size:literal],
([sym $symstr:literal $($sym:path)?, opt $($opt:ident)?, size $size:literal],
$provider:ident, $name:ident, $($argstr:literal, $arg:expr,)*
) => (unsafe {
::core::arch::asm!(concat!(r#"
Expand Down