Struct usdt::UniqueId[][src]

pub struct UniqueId { /* fields omitted */ }
Expand description

A unique identifier that can be used to correlate multiple USDT probes together.

It’s a common pattern in DTrace scripts to correlate multiple probes. For example, one can time system calls by storing a timestamp on the syscall:::entry probe and then computing the elapsed time in the syscall:::return probe. This requires some way to “match up” these two probes, to ensure that the elapsed time is correctly attributed to a single system call. Doing so requires an identifier. User code may already have an ID appopriate for this use case, but the UniqueId type may be used when one is not already available. These unique IDs can be used to correlate multiple probes occurring in a section or span of user code.

A probe function may accept a UniqueId, which appears in a D as a u64. The value is guaranteed to be unique, even if multiple threads run the same traced section of code. (See the [notes] for caveats.) The value may be shared between threads by calling clone() on a constructed span – in this case, the cloned object shares the same value, so that a traced span running in multiple threads (or asynchronous tasks) shares the same identifier.

A UniqueId is very cheap to construct. The internal value is “materialized” in two situations:

  • When an enabled probe fires
  • When the value is cloned (e.g., for sharing with another thread)

This minimizes the disabled-probe effect, but still allows sharing a consistent ID in the case of multithreaded work.

Example

#![feature(asm)]
#![cfg_attr(target_os = "macos, feature(asm_sym))]
#[usdt::provider]
mod with_id {
    fn work_started(_: &usdt::UniqueId) {}
    fn halfway_there(_: &usdt::UniqueId, msg: &str) {}
    fn work_completed(_: &usdt::UniqueId, result: u64) {}
}

// Constructing an ID is very cheap.
let id = usdt::UniqueId::new();

// The ID will only be materialized if this probe is enabled.
with_id_work_started!(|| &id);

// If the ID has been materialized above, this simply clone the internal value. If the ID has
// _not_ yet been materialized, say because the `work_started` probe was not enabled, this will
// do so now.
let id2 = id.clone();
let handle = std::thread::spawn(move || {
    for i in 0..10 {
        // Do our work.
        if i == 5 {
            with_id_halfway_there!(|| (&id2, "work is half completed"));
        }
    }
    10
});

let result = handle.join().unwrap();
with_id_work_completed!(|| (&id, result));

Note that this type is not Sync, which means we cannot accidentally share the value between threads. The only way to track the same ID in work spanning threads is to first clone the type, which materializes the internal value. For example, this will fail to compile:

#![feature(asm)]
#![cfg_attr(target_os = "macos, feature(asm_sym))]
#[usdt::provider]
mod with_id {
    fn work_started(_: &usdt::UniqueId) {}
    fn halfway_there(_: &usdt::UniqueId, msg: &str) {}
    fn work_completed(_: &usdt::UniqueId, result: u64) {}
}

let id = usdt::UniqueId::new();
with_id_work_started!(|| &id);
let handle = std::thread::spawn(move || {
    for i in 0..10 {
        // Do our work.
        if i == 5 {
            // Note that we're using `id`, not a clone as the previous example.
            with_id_halfway_there!(|| (&id, "work is half completed"));
        }
    }
    10
});
let result = handle.join().unwrap();
with_id_work_completed!(|| (&id, result));

Notes

In any practical situation, the generated ID is unique. Its value is assigned on the basis of the thread that creates the UniqueId object, plus a monotonic thread-local counter. However, the counter is 32 bits, and so wraps around after about 4 billion unique values. So theoretically, multiple UniqueIds could manifest as the same value to DTrace, if they are exceptionally long-lived or generated very often.

Implementations

Construct a new identifier.

A UniqueId is cheap to create, and is not materialized into an actual value until it’s needed, either by a probe function or during cloneing to share the value between threads.

Trait Implementations

Returns a copy of the value. Read more

Performs copy-assignment from source. Read more

Formats the value using the given formatter. Read more

Auto Trait Implementations

Blanket Implementations

Gets the TypeId of self. Read more

Immutably borrows from an owned value. Read more

Mutably borrows from an owned value. Read more

Performs the conversion.

Performs the conversion.

The resulting type after obtaining ownership.

Creates owned data from borrowed data, usually by cloning. Read more

🔬 This is a nightly-only experimental API. (toowned_clone_into)

recently added

Uses borrowed data to replace owned data, usually by cloning. Read more

The type returned in the event of a conversion error.

Performs the conversion.

The type returned in the event of a conversion error.

Performs the conversion.