1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
//! This module and its submodules implement a type-safe wrapper around Rust types such that any
//! suitable type can be passed as a parameter to the C probing libraries with a minimum of
//! overhead.
//!
//! In order to be able to use a type as a probe argument, that type `T` must implement
//! `ProbeArgType<T>`, and there must be an implementation of `ProbeArgWrapper` suitable for that
//! type.
//!
//! This library provides implementations for all of the following:
//!
//! * All integer types from `u8/i8` to `u64/i64`
//! * `bool` (passed as an `i32` `1` means `true` and `0` means `false`)
//! * String references `&str`
//! * C-style string references `&CStr`
//! * `Option<T>` for any `T` which is itself a supported probe argument type and implements `Copy`
//! * Any pointer type, which is passed as either a 32- or 64-bit unsigned int depending upon
//! architecture
//!
//!
use std::fmt::Debug;
use strum_macros::{Display, EnumString, IntoStaticStr};

pub mod bool;
pub mod cstring;
pub mod int;
pub mod native;
pub mod option;
pub mod pointer;
pub mod refs;
pub mod string;

pub use self::bool::*;
pub use cstring::*;
pub use int::*;
pub use native::*;
pub use option::*;
pub use pointer::*;
pub use refs::*;
pub use string::*;

#[derive(Display, Debug, Clone, PartialEq, Hash, Eq, IntoStaticStr, EnumString)]
pub enum CType {
    #[strum(serialize = "void")]
    NoArg,
    #[strum(serialize = "void*")]
    VoidPtr,
    #[strum(serialize = "char*")]
    CharPtr,
    #[strum(serialize = "unsigned char*")]
    UCharPtr,
    #[strum(serialize = "char")]
    Char,
    #[strum(serialize = "unsigned char")]
    UChar,
    #[strum(serialize = "short")]
    Short,
    #[strum(serialize = "unsigned short")]
    UShort,
    #[strum(serialize = "int")]
    Int,
    #[strum(serialize = "unsigned int")]
    UInt,
    #[strum(serialize = "long")]
    Long,
    #[strum(serialize = "unsigned long")]
    ULong,
    #[strum(serialize = "long long")]
    LongLong,
    #[strum(serialize = "unsigned long long")]
    ULongLong,
    #[strum(serialize = "size_t")]
    SizeT,
    #[strum(serialize = "ssize_t")]
    SSizeT,
}

/// Marker trait which decorates only those std::os::raw types which correspond to C types
/// supported by the C probing APIs.  Due to limitations of Rust's type system, this trait is split
/// into two parts: `ProbeArgNativeTypeInfo` which has no type parameter, and
/// `ProbeArgNativeType`which extends `ProbeArgNativeTypeInfo`, takes a type parameter `T` and
/// therefore adds the `get_default_value()` method.
pub trait ProbeArgNativeTypeInfo {
    fn get_c_type() -> CType;

    fn get_c_type_str() -> &'static str {
        // The #[strum...] attr contains the string representation of the C type for each member
        Self::get_c_type().into()
    }

    fn get_rust_type_str() -> &'static str;
}

/// The other half of `ProbeArgNativeTypeInfo`, which takes a type parameter and thus adds
/// `get_default_value`.
pub trait ProbeArgNativeType<T>: ProbeArgNativeTypeInfo {
    fn get_default_value() -> T;
}

/// This trait is defined on any type which is supported as an argument to a probe.
///
/// In general only scalar integer types are supported directly, though pointers can be passed
/// as u64 values and the tracing code is then responsible for knowing what to do with the pointer
/// (for example, treat it as a null-terminated UTF-8 string, or a pointer to a certain structure, etc).
pub trait ProbeArgType<T> {
    type WrapperType: ProbeArgWrapper;

    fn wrap(arg: T) -> Self::WrapperType;
}

/// This trait, a companion to ProbeArgType<T>, wraps a supported type and on demand converts it to its equivalent C type.
/// For scalar types that are directly supported there is no overhead to this wrapping, but many more complicated types, including
/// Rust string types, need additional logic to produce a NULL-terminated byte array.
pub trait ProbeArgWrapper: Debug
where
    //How's this for a type restriction: it says the CType must be one we've marked as a native
    //type
    <Self as ProbeArgWrapper>::CType: ProbeArgNativeType<<Self as ProbeArgWrapper>::CType>,
{
    type CType: ProbeArgNativeTypeInfo;

    /// Convert the probe argument from it's Rust type to one compatible with the native
    /// tracing library infrastructure.
    fn as_c_type(&self) -> Self::CType;

    /// This is ugly but unavoidable.  The underlying C type for an Opt<T> is the same C type as T.
    /// We will use the default value for T to indicate a value of None.  That will have to be good enough.
    fn default_c_value() -> Self::CType {
        <Self::CType as ProbeArgNativeType<Self::CType>>::get_default_value()
    }
}

/// Helper function to wrap a probe arg in its correspondong wrapper without contorting one's fingers typing angle brackets
pub fn wrap<T: ProbeArgType<T>>(arg: T) -> <T as ProbeArgType<T>>::WrapperType {
    <T as ProbeArgType<T>>::wrap(arg)
}