xref: /relibc/ralloc/src/log.rs (revision 2cb61efbaa30da912d4f1015c8b2b361005533f0)
1 //! Allocator logging.
2 //!
3 //! This allows for detailed logging for `ralloc`.
4 
5 /// Log to the appropriate source.
6 ///
7 /// The first argument defines the log level, the rest of the arguments are just `write!`-like
8 /// formatters.
9 #[macro_export]
10 macro_rules! log {
11     (INTERNAL, $( $x:tt )*) => {
12         log!(@["INTERNAL: ", 1], $( $x )*);
13     };
14     (DEBUG, $( $x:tt )*) => {
15         log!(@["DEBUG:    ", 2], $( $x )*);
16     };
17     (CALL, $( $x:tt )*) => {
18         log!(@["CALL:     ", 3], $( $x )*);
19     };
20     (NOTE, $( $x:tt )*) => {
21         log!(@["NOTE:     ", 5], $( $x )*);
22     };
23     (WARNING, $( $x:tt )*) => {
24         log!(@["WARNING:  ", 5], $( $x )*);
25     };
26     (ERROR, $( $x:tt )*) => {
27         log!(@["ERROR:    ", 6], $( $x )*);
28     };
29     (@[$kind:expr, $lv:expr], $( $arg:expr ),*) => {
30         #[cfg(feature = "log")]
31         {
32             use core::fmt::Write;
33 
34             use log::internal::{LogWriter, level};
35 
36             // Set the level.
37             if level($lv) {
38                 // Print the pool state.
39                 let mut log = LogWriter::new();
40                 // Print the log message.
41                 let _ = write!(log, $kind);
42                 let _ = write!(log, $( $arg ),*);
43                 let _ = writeln!(log, " (at {}:{})", file!(), line!());
44             }
45         }
46     };
47 }
48 
49 /// Log with bookkeeper data to the appropriate source.
50 ///
51 /// The first argument this takes is of the form `pool;cursor`, which is used to print the
52 /// block pools state. `cursor` is what the operation "revolves around" to give a sense of
53 /// position.
54 ///
55 /// If the `;cursor` part is left out, no cursor will be printed.
56 ///
57 /// The rest of the arguments are just normal formatters.
58 ///
59 /// This logs to level 2.
60 #[macro_export]
61 macro_rules! bk_log {
62     ($pool:expr, $( $arg:expr ),*) => {
63         bk_log!($pool;(), $( $arg ),*);
64     };
65     ($bk:expr;$cur:expr, $( $arg:expr ),*) => {
66         #[cfg(feature = "log")]
67         {
68             use log::internal::{IntoCursor, BlockLogger};
69 
70             log!(INTERNAL, "({:2}) {:10?} : {}", $bk.id, BlockLogger {
71                 cur: $cur.clone().into_cursor(),
72                 blocks: &$bk.pool,
73             }, format_args!($( $arg ),*));
74         }
75     };
76 }
77 
78 /// Make a runtime assertion.
79 ///
80 /// The only way it differs from the one provided by `libcore` is the panicking strategy, which
81 /// allows for aborting, non-allocating panics when running the tests.
82 #[macro_export]
83 #[cfg(feature = "write")]
84 macro_rules! assert {
85     ($e:expr) => {
86         assert!($e, "No description.");
87     };
88     ($e:expr, $( $arg:expr ),*) => {{
89         use core::intrinsics;
90 
91         if !$e {
92             log!(ERROR, $( $arg ),*);
93 
94             #[allow(unused_unsafe)]
95             unsafe { intrinsics::abort() }
96         }
97     }}
98 }
99 
100 /// Make a runtime assertion in debug mode.
101 ///
102 /// The only way it differs from the one provided by `libcore` is the panicking strategy, which
103 /// allows for aborting, non-allocating panics when running the tests.
104 #[cfg(feature = "write")]
105 #[macro_export]
106 macro_rules! debug_assert {
107     // We force the programmer to provide explanation of their assertion.
108     ($first:expr, $( $arg:tt )*) => {{
109         if cfg!(debug_assertions) {
110             assert!($first, $( $arg )*);
111         }
112     }}
113 }
114 
115 /// Make a runtime equality assertion in debug mode.
116 ///
117 /// The only way it differs from the one provided by `libcore` is the panicking strategy, which
118 /// allows for aborting, non-allocating panics when running the tests.
119 #[cfg(feature = "write")]
120 #[macro_export]
121 macro_rules! assert_eq {
122     ($left:expr, $right:expr) => ({
123         // We evaluate _once_.
124         let left = &$left;
125         let right = &$right;
126 
127         assert!(left == right, "(left: '{:?}', right: '{:?}')", left, right)
128     })
129 }
130 
131 /// Top-secret module.
132 #[cfg(feature = "log")]
133 pub mod internal {
134     use prelude::*;
135 
136     use core::fmt;
137     use core::cell::Cell;
138     use core::ops::Range;
139 
140     use shim::config;
141 
142     use sync;
143 
144     /// The log lock.
145     ///
146     /// This lock is used to avoid bungling and intertwining the log.
147     #[cfg(not(feature = "no_log_lock"))]
148     pub static LOG_LOCK: Mutex<()> = Mutex::new(());
149 
150     /// A log writer.
151     ///
152     /// This writes to the shim logger.
153     pub struct LogWriter {
154         /// The inner lock.
155         #[cfg(not(feature = "no_log_lock"))]
156         _lock: sync::MutexGuard<'static, ()>,
157     }
158 
159     impl LogWriter {
160         /// Standard error output.
161         pub fn new() -> LogWriter {
162             #[cfg(feature = "no_log_lock")]
163             {
164                 LogWriter {}
165             }
166 
167             #[cfg(not(feature = "no_log_lock"))]
168             LogWriter {
169                 _lock: LOG_LOCK.lock(),
170             }
171         }
172     }
173 
174     impl fmt::Write for LogWriter {
175         fn write_str(&mut self, s: &str) -> fmt::Result {
176             if config::log(s) == !0 { Err(fmt::Error) } else { Ok(()) }
177         }
178     }
179 
180     /// A "cursor".
181     ///
182     /// Cursors represents a block or an interval in the log output. This trait is implemented for
183     /// various types that can represent a cursor.
184     pub trait Cursor {
185         /// Iteration at n.
186         ///
187         /// This is called in the logging loop. The cursor should then write, what it needs, to the
188         /// formatter if the underlying condition is true.
189         ///
190         /// For example, a plain position cursor will write `"|"` when `n == self.pos`.
191         // TODO: Use an iterator instead.
192         fn at(&self, f: &mut fmt::Formatter, n: usize) -> fmt::Result;
193 
194         /// The after hook.
195         ///
196         /// This is runned when the loop is over. The aim is to e.g. catch up if the cursor wasn't
197         /// printed (i.e. is out of range).
198         fn after(&self, f: &mut fmt::Formatter) -> fmt::Result;
199     }
200 
201     /// Types that can be converted into a cursor.
202     pub trait IntoCursor {
203         /// The end result.
204         type Cursor: Cursor;
205 
206         /// Convert this value into its equivalent cursor.
207         fn into_cursor(self) -> Self::Cursor;
208     }
209 
210     /// A single-point cursor.
211     pub struct UniCursor {
212         /// The position where this cursor will be placed.
213         pos: usize,
214         /// Is this cursor printed?
215         ///
216         /// This is used for the after hook.
217         is_printed: Cell<bool>,
218     }
219 
220     impl Cursor for UniCursor {
221         fn at(&self, f: &mut fmt::Formatter, n: usize) -> fmt::Result {
222             if self.pos == n {
223                 self.is_printed.set(true);
224                 write!(f, "|")?;
225             }
226 
227             Ok(())
228         }
229 
230         fn after(&self, f: &mut fmt::Formatter) -> fmt::Result {
231             if !self.is_printed.get() {
232                 write!(f, "…|")?;
233             }
234 
235             Ok(())
236         }
237     }
238 
239     impl IntoCursor for usize {
240         type Cursor = UniCursor;
241 
242         fn into_cursor(self) -> UniCursor {
243             UniCursor {
244                 pos: self,
245                 is_printed: Cell::new(false),
246             }
247         }
248     }
249 
250     impl Cursor for () {
251         fn at(&self, _: &mut fmt::Formatter, _: usize) -> fmt::Result { Ok(()) }
252 
253         fn after(&self, _: &mut fmt::Formatter) -> fmt::Result { Ok(()) }
254     }
255 
256     impl IntoCursor for () {
257         type Cursor = ();
258 
259         fn into_cursor(self) -> () {
260             ()
261         }
262     }
263 
264     /// A interval/range cursor.
265     ///
266     /// The start of the range is marked by `[` and the end by `]`.
267     pub struct RangeCursor {
268         /// The range of this cursor.
269         range: Range<usize>,
270     }
271 
272     impl Cursor for RangeCursor {
273         fn at(&self, f: &mut fmt::Formatter, n: usize) -> fmt::Result {
274             if self.range.start == n {
275                 write!(f, "[")?;
276             } else if self.range.end == n {
277                 write!(f, "]")?;
278             }
279 
280             Ok(())
281         }
282 
283         fn after(&self, _: &mut fmt::Formatter) -> fmt::Result { Ok(()) }
284     }
285 
286     impl IntoCursor for Range<usize> {
287         type Cursor = RangeCursor;
288 
289         fn into_cursor(self) -> RangeCursor {
290             RangeCursor {
291                 range: self,
292             }
293         }
294     }
295 
296     /// A "block logger".
297     ///
298     /// This intend to show the structure of a block pool. The syntax used is like:
299     ///
300     /// ```
301     /// xxx__|xx_
302     /// ```
303     ///
304     /// where `x` denotes an non-empty block. `_` denotes an empty block, with `|` representing the
305     /// cursor.
306     pub struct BlockLogger<'a, T> {
307         /// The cursor.
308         ///
309         /// This is where the `|` will be printed.
310         pub cur: T,
311         /// The blocks.
312         pub blocks: &'a [Block],
313     }
314 
315     impl<'a, T: Cursor> fmt::Debug for BlockLogger<'a, T> {
316         fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
317             // TODO: Handle alignment etc.
318 
319             for (n, i) in self.blocks.iter().enumerate() {
320                 self.cur.at(f, n)?;
321 
322                 if i.is_empty() {
323                     // Empty block.
324                     write!(f, "_")?;
325                 } else {
326                     // Non-empty block.
327                     write!(f, "x")?;
328                 }
329             }
330 
331             self.cur.after(f)?;
332 
333             Ok(())
334         }
335     }
336 
337     /// Check if this log level is enabled.
338     #[allow(absurd_extreme_comparisons)]
339     #[inline]
340     pub fn level(lv: u8) -> bool {
341         lv >= config::MIN_LOG_LEVEL
342     }
343 }
344