-
Notifications
You must be signed in to change notification settings - Fork 0
/
debug.rs
159 lines (155 loc) · 3.94 KB
/
debug.rs
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/// Macros for low level debugging when developing against I2C devices.
/// Create a debugging scope on the provided context.
///
/// A debugging scope causes any subsequent [`text!`] or [`byte!`] calls to be
/// printed with additional padding and automatically ends once the current code
/// scope ends.
///
/// Scopes can be nested, where the inner scope will further pad any output.
///
/// The provided context (`ctx`) can be any struct that has two fields
/// accessible to these macros:
/// 1. `debug_enabled: bool`
/// 2. `debug_depth: Rc<Cell<usize>>`
///
/// Example:
/// ```
/// debug::scope!(ctx, "entering scope 1");
/// debug::text!(ctx, "message 1")
/// debug::scope!(ctx, "entering scope 2");
/// debug::text!(ctx, "message 2")
/// {
/// debug::scope!(ctx, "entering scope 3");
/// debug::text!(ctx, "message 3")
/// } // scope 3 ends
/// debug::text!("message 4")
/// ```
/// Prints:
/// ```text
/// entering scope 1
/// message 1
/// entering scope 2
/// message 2
/// entering scope 3
/// message 3
/// message 4
/// ```
///
/// The nesting ability of scopes becomes really useful when composing I2C
/// operations; example:
/// ```
/// fn start() {
/// debug::scope!("start()");
/// let byte = 1;
/// debug::byte!(byte, "write start byte", byte)
/// device.write(byte);
/// }
///
/// fn stop() {
/// debug::scope!("stop()");
/// let byte = 0;
/// debug::byte!(byte, "write stop byte", byte)
/// device.write(byte);
/// }
///
/// fn restart() {
/// debug::scope!("restart()");
/// stop();
/// start();
/// }
///
/// restart();
/// // ...
/// stop();
/// ```
///
/// Would result in the output:
/// ```text
/// restart()
/// stop()
/// 00000000 write stop byte
/// start()
/// 00000001 write start byte
/// stop()
/// 00000000 write stop byte
/// ```
#[macro_export]
macro_rules! scope {
($ctx:ident, $fmt:expr $(, $arg:expr)* $(,)?) => {
// _unused is dropped at end of scope where this macro is called, which
// decrements debug depth.
let _unused = if $ctx.debug_enabled {
let mut cur_depth = $ctx.debug_depth.lock().unwrap();
let padding = " ".repeat(*cur_depth);
println!("{}{} {{", padding, format!($fmt $(, $arg)*));
*cur_depth += 1;
// lock releases after return
// debug_depth ArcMutex needs to be cloned since guard() takes ownership
// of the arg. Code in block below is executed at the end of the code
// scope where the `scope` macro is called.
Some(scopeguard::guard($ctx.debug_depth.clone(), |depth| {
let mut cur_depth = depth.lock().unwrap();
if *cur_depth > 0 {
*cur_depth -= 1;
}
println!("{}}}", " ".repeat(*cur_depth));
}))
} else {
None
};
};
}
pub use scope;
/// Print a formated message at current debug depth.
///
/// Example:
/// ```
/// debug::text!(ctx, "1. top-level debug text");
/// debug::scope!(ctx, "2. in-scope");
/// debug::text!(ctx, "2a. in-scope debug text");
/// ```
///
/// Prints:
/// ```text
/// 1. top-level debug text
/// 2. in-scope
/// 2a. in-scope debug text
/// ```
#[macro_export]
macro_rules! text {
($ctx:expr, $fmt:expr $(, $arg:expr)* $(,)?) => {
if $ctx.debug_enabled {
let padding = " ".repeat(*$ctx.debug_depth.lock().unwrap());
println!("{}{}", padding, format!($fmt $(, $arg)*));
};
};
}
pub use text;
/// Print the binary representation for the given byte, along with a formatted
/// description at the current debug depth.
///
/// Example:
/// ```
/// let value = 0b0010_1010;
/// debug::byte(value, "is binary for {}", value);
/// ```
/// Prints:
/// ```text
/// 00101010 is binary for 42
/// ```
#[macro_export]
macro_rules! byte {
($ctx:expr, $value:expr, $($description:tt)*) => {
if $ctx.debug_enabled {
let value: u8 = $value;
let formatted_string = format!($($description)*);
debug::text!(
$ctx,
"{:08b} {}",
value,
formatted_string,
);
}
};
}
pub use byte;