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
// signpost/src/lib.rs

#[cfg(target_os = "macos")]
const DBG_APPS: u32 = 33;
#[cfg(target_os = "macos")]
const DBG_FUNC_START: u32 = 1;
#[cfg(target_os = "macos")]
const DBG_FUNC_END: u32 = 2;
#[cfg(target_os = "macos")]
const DBG_FUNC_NONE: u32 = 0;
#[cfg(target_os = "macos")]
const DBG_MACH_CHUD: u32 = 0x0a;
#[cfg(target_os = "macos")]
const KDBG_CLASS_OFFSET: u32 = 24;
#[cfg(target_os = "macos")]
const KDBG_SUBCLASS_OFFSET: u32 = 16;
#[cfg(target_os = "macos")]
const KDBG_CODE_OFFSET: u32 = 2;

/// When passed as the last argument to a trace, and when "color using last
/// argument" is checked in Instruments' "Points of Interest" options sidebar,
/// controls the color a trace is rendered with.
///
/// ```
/// signpost::start(42, &[0, 0, 0, signpost::Color::Blue as usize]);
/// // Do stuff...
/// signpost::end(42, &[0, 0, 0, signpost::Color::Blue as usize]);
/// ```
#[cfg(target_os="macos")]
pub enum Color {
    Blue = 0,
    Green = 1,
    Purple = 2,
    Orange = 3,
    Red = 4,
}

#[cfg(target_os = "macos")]
fn appsdbg_code(subclass: u32, code: u32) -> u32 {
    kdbg_code(DBG_APPS, subclass, code)
}

#[cfg(target_os = "macos")]
fn kdbg_code(class: u32, subclass: u32, code: u32) -> u32 {
    kdbg_eventid(class, subclass, code)
}

#[cfg(target_os = "macos")]
fn kdbg_eventid(class: u32, subclass: u32, code: u32) -> u32 {
    ((class & 0xff) << KDBG_CLASS_OFFSET) | ((subclass & 0xff) << KDBG_SUBCLASS_OFFSET) |
        ((code & 0x3fff) << KDBG_CODE_OFFSET)
}

#[cfg(target_os = "macos")]
pub fn start(code: u32, args: &[usize; 4]) {
    unsafe {
        kdebug_trace(appsdbg_code(DBG_MACH_CHUD, code) | DBG_FUNC_START,
                     args[0],
                     args[1],
                     args[2],
                     args[3],
                     0)
    }
}

#[cfg(target_os = "macos")]
pub fn end(code: u32, args: &[usize; 4]) {
    unsafe {
        kdebug_trace(appsdbg_code(DBG_MACH_CHUD, code) | DBG_FUNC_END,
                     args[0],
                     args[1],
                     args[2],
                     args[3],
                     0)
    }
}

#[cfg(target_os = "macos")]
pub fn trace(code: u32, args: &[usize; 4]) {
    unsafe {
        kdebug_trace(appsdbg_code(DBG_MACH_CHUD, code) | DBG_FUNC_NONE,
                     args[0],
                     args[1],
                     args[2],
                     args[3],
                     0)
    }
}

#[cfg(not(target_os = "macos"))]
pub fn start(code: u32, args: &[usize; 4]) {}
#[cfg(not(target_os = "macos"))]
pub fn end(code: u32, args: &[usize; 4]) {}
#[cfg(not(target_os = "macos"))]
pub fn trace(code: u32, args: &[usize; 4]) {}

pub fn trace_function<R, F>(code: u32, args: &[usize; 4], func: F) -> R where F: FnOnce() -> R {
    start(code, args);
    let result = func();
    end(code, args);
    result
}

/// An RAII class to automatically add start and end traces on creation and drop
/// respectively.
pub struct AutoTrace<'a> {
    code: u32,
    args: &'a [usize; 4],
}

impl<'a> AutoTrace<'a> {
    pub fn new(code: u32, args: &'a [usize; 4]) -> AutoTrace<'a> {
        start(code, args);
        AutoTrace {
            code: code,
            args: args,
        }
    }
}

impl<'a> Drop for AutoTrace<'a> {
    fn drop(&mut self) {
        end(self.code, self.args);
    }
}

#[cfg(target_os = "macos")]
extern {
    fn kdebug_trace(code: u32, arg0: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize);
}