Skip to content

Instantly share code, notes, and snippets.

@cuviper
Last active August 29, 2015 14:00
Show Gist options
  • Save cuviper/fd016ad0f4cee4d87914 to your computer and use it in GitHub Desktop.
Save cuviper/fd016ad0f4cee4d87914 to your computer and use it in GitHub Desktop.
Prototype Rust probe!() macro creating SystemTap SDT probes
I'm leaving this gist in place for posterity, but the project now has a home:
https://github.com/cuviper/rust-libprobe
//! SystemTap static probes
//!
//! This is a mechanism for developers to provide static annotations for
//! meaningful points in code, with arguments that indicate some relevant
//! state. Such locations may be probed by SystemTap `process.mark("name")`,
//! and GDB can also locate them with `info probes` and `break -probe name`.
//!
//! The impact on code generation is designed to be minimal: just a single
//! `NOP` placeholder is added inline for instrumentation, and ELF notes
//! contain metadata to name the probe and describe the location of its
//! arguments.
//!
//! # Links:
//!
//! * https://sourceware.org/systemtap/man/stapprobes.3stap.html#lbAO (see `process.mark`)
//! * https://sourceware.org/systemtap/wiki/AddingUserSpaceProbingToApps
//! * https://sourceware.org/systemtap/wiki/UserSpaceProbeImplementation
//! * https://sourceware.org/gdb/onlinedocs/gdb/Static-Probe-Points.html
#![macro_escape]
#![cfg(target_os = "linux")]
/// Define a static probe point
///
/// This annotates a code location with a name and arguments, and compiles
/// in metadata to let debugging tools locate it.
///
/// # Arguments
///
/// * `provider` - An identifier for naming probe groups.
/// * `name` - An identifier for this specific probe.
/// * `arg...` - Optional data to provide with the probe.
///
/// # Example
///
/// ```
/// probe!(foo, begin);
/// for i in range(0, 10000) {
/// probe!(foo, loop, i, i*i);
/// }
/// probe!(foo, end);
/// ```
#[macro_export]
macro_rules! probe(
($provider:ident, $name:ident)
=> (_probe!($provider, $name));
($provider:ident, $name:ident, $($arg:expr),*)
=> (_probe!($provider, $name, $($arg),*));
)
//
// DEVELOPER NOTES
//
// Arguments are currently type-casted as i64, because that directly maps to
// SystemTap's long, no matter the architecture. However, if we could figure
// out types here, they could be notated more specifically, for example an
// argstr of "4@$0 -2@$1" indicates u32 and i16 respectively. Any pointer
// would be fine too, like *c_char, simply 4@ or 8@ for target_word_size.
//
// The macros in sdt.h don't know types either, so they split each argument
// into two asm inputs, roughly:
// asm("[...]"
// ".asciz \"%n[_SDT_S0]@%[_SDT_A0]\""
// "[...]"
// : :
// [_SDT_S0] "n" ((_SDT_ARGSIGNED (x) ? 1 : -1) * (int) sizeof (x)),
// [_SDT_A0] "nor" (x)
// );
// where _SDT_ARGSIGNED is a macro using gcc builtins, so it's still resolved a
// compile time, and %n makes it a raw literal rather than an asm number.
//
// NB: If there were a way to generate that "-8@$0 ..." string, not just the
// type but also the positional index, then we could the duplication below.
// For now, expand to 12 args, the same limit as sys/sdt.h.
//
macro_rules! _probe(
($provider:ident, $name:ident)
=> (sdt_asm!($provider, $name, "",));
($provider:ident, $name:ident, $arg1:expr)
=> (sdt_asm!($provider, $name,
"-8@$0",
$arg1));
($provider:ident, $name:ident, $arg1:expr, $arg2:expr)
=> (sdt_asm!($provider, $name,
"-8@$0 -8@$1",
$arg1, $arg2));
($provider:ident, $name:ident, $arg1:expr, $arg2:expr, $arg3:expr)
=> (sdt_asm!($provider, $name,
"-8@$0 -8@$1 -8@$2",
$arg1, $arg2, $arg3));
($provider:ident, $name:ident, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr)
=> (sdt_asm!($provider, $name,
"-8@$0 -8@$1 -8@$2 -8@$3",
$arg1, $arg2, $arg3, $arg4));
($provider:ident, $name:ident, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $arg5:expr)
=> (sdt_asm!($provider, $name,
"-8@$0 -8@$1 -8@$2 -8@$3 -8@$4",
$arg1, $arg2, $arg3, $arg4, $arg5));
($provider:ident, $name:ident, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $arg5:expr,
$arg6:expr)
=> (sdt_asm!($provider, $name,
"-8@$0 -8@$1 -8@$2 -8@$3 -8@$4 -8@$5",
$arg1, $arg2, $arg3, $arg4, $arg5, $arg6));
($provider:ident, $name:ident, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $arg5:expr,
$arg6:expr, $arg7:expr)
=> (sdt_asm!($provider, $name,
"-8@$0 -8@$1 -8@$2 -8@$3 -8@$4 -8@$5 -8@$6",
$arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7));
($provider:ident, $name:ident, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $arg5:expr,
$arg6:expr, $arg7:expr, $arg8:expr)
=> (sdt_asm!($provider, $name,
"-8@$0 -8@$1 -8@$2 -8@$3 -8@$4 -8@$5 -8@$6 -8@$7",
$arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7, $arg8));
($provider:ident, $name:ident, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $arg5:expr,
$arg6:expr, $arg7:expr, $arg8:expr, $arg9:expr)
=> (sdt_asm!($provider, $name,
"-8@$0 -8@$1 -8@$2 -8@$3 -8@$4 -8@$5 -8@$6 -8@$7 -8@$8",
$arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7, $arg8, $arg9));
($provider:ident, $name:ident, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $arg5:expr,
$arg6:expr, $arg7:expr, $arg8:expr, $arg9:expr, $arg10:expr)
=> (sdt_asm!($provider, $name,
"-8@$0 -8@$1 -8@$2 -8@$3 -8@$4 -8@$5 -8@$6 -8@$7 -8@$8 -8@$9",
$arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7, $arg8, $arg9, $arg10));
($provider:ident, $name:ident, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $arg5:expr,
$arg6:expr, $arg7:expr, $arg8:expr, $arg9:expr, $arg10:expr, $arg11:expr)
=> (sdt_asm!($provider, $name,
"-8@$0 -8@$1 -8@$2 -8@$3 -8@$4 -8@$5 -8@$6 -8@$7 -8@$8 -8@$9 -8@$10",
$arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7, $arg8, $arg9, $arg10, $arg11));
($provider:ident, $name:ident, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $arg5:expr,
$arg6:expr, $arg7:expr, $arg8:expr, $arg9:expr, $arg10:expr, $arg11:expr, $arg12:expr)
=> (sdt_asm!($provider, $name,
"-8@$0 -8@$1 -8@$2 -8@$3 -8@$4 -8@$5 -8@$6 -8@$7 -8@$8 -8@$9 -8@$10 -8@$11",
$arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7, $arg8, $arg9, $arg10, $arg11,
$arg12));
)
#[cfg(target_word_size = "32")]
macro_rules! sdt_asm(
($provider:ident, $name:ident, $argstr:tt, $($arg:expr),*)
=> (unsafe {
_sdt_asm!(".4byte", $provider, $name, $argstr, $(($arg) as i64),*);
}))
#[cfg(target_word_size = "64")]
macro_rules! sdt_asm(
($provider:ident, $name:ident, $argstr:tt, $($arg:expr),*)
=> (unsafe {
_sdt_asm!(".8byte", $provider, $name, $argstr, $(($arg) as i64),*);
}))
// Since we can't #include <sys/sdt.h>, we have to reinvent it...
// but once you take out the C/C++ type handling, there's not a lot to it.
macro_rules! _sdt_asm(
($addr:tt, $provider:ident, $name:ident, $argstr:tt, $($arg:expr),*) => (
asm!(concat!(r#"
990: nop
.pushsection .note.stapsdt,"?","note"
.balign 4
.4byte 992f-991f, 994f-993f, 3
991: .asciz "stapsdt"
992: .balign 4
993: "#, $addr, r#" 990b
"#, $addr, r#" _.stapsdt.base
"#, $addr, r#" 0 // TODO semaphores?
.asciz ""#, stringify!($provider), r#""
.asciz ""#, stringify!($name), r#""
.asciz ""#, $argstr, r#""
994: .balign 4
.popsection
.ifndef _.stapsdt.base
.pushsection .stapsdt.base,"aG","progbits",.stapsdt.base,comdat
.weak _.stapsdt.base
.hidden _.stapsdt.base
_.stapsdt.base: .space 1
.size _.stapsdt.base, 1
.popsection
.endif
"#
)
: // output operands
: // input operands
$("nor"($arg)),*
: // clobbers
: // options
"volatile"
)
))
// A simple example demonstrating the sdt.rs probe! macro.
//
// This defines all of its probes in provider "foo", with
// one for "begin", "loop", and "end".
#![feature(asm, macro_rules)]
mod sdt;
fn main() {
probe!(foo, begin);
for i in range(0, 10000) {
probe!(foo, loop, i, i*i);
}
probe!(foo, end);
}
// Example script to read the probes in sdtex.rs
//
// If you're in the stapdev group, you can run this like:
//
// $ stap sdtex.stp -c ./sdtex
//
// Or if your stap has dyninst enabled, you can run without
// any special group privileges at all:
//
// $ stap --dyninst sdtex.stp -c ./sdtex
global i, i2;
probe process.provider("foo").mark("loop") {
// accumulate both args
i <<< $arg1;
i2 <<< $arg2;
}
// .provider is optional
probe process.mark("begin"), process.mark("end") {
printf("%s:%s\n", $$provider, $$name);
}
// Since we never read the global stats, they'll be automatically printed at
// the end with @count, @min, @max, @sum, @avg.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment