xref: /relibc/ralloc/src/log.rs (revision 14e275d8145880b7dca823efefc27a47a3d6e9ef)
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 {
96                 // LAST AUDIT: 2016-08-21 (Ticki).
97 
98                 // Right now there is no safe interface exposed for this, but it is safe no matter
99                 // what.
100                 intrinsics::abort();
101             }
102         }
103     }}
104 }
105 
106 /// Make a runtime assertion in debug mode.
107 ///
108 /// The only way it differs from the one provided by `libcore` is the panicking strategy, which
109 /// allows for aborting, non-allocating panics when running the tests.
110 #[cfg(feature = "write")]
111 #[macro_export]
112 macro_rules! debug_assert {
113     // We force the programmer to provide explanation of their assertion.
114     ($first:expr, $( $arg:tt )*) => {{
115         if cfg!(debug_assertions) {
116             assert!($first, $( $arg )*);
117         }
118     }}
119 }
120 
121 /// Make a runtime equality assertion in debug mode.
122 ///
123 /// The only way it differs from the one provided by `libcore` is the panicking strategy, which
124 /// allows for aborting, non-allocating panics when running the tests.
125 #[cfg(feature = "write")]
126 #[macro_export]
127 macro_rules! assert_eq {
128     ($left:expr, $right:expr) => {{
129         // We evaluate _once_.
130         let left = &$left;
131         let right = &$right;
132 
133         assert!(left == right, "(left: '{:?}', right: '{:?}')", left, right)
134     }};
135 }
136 
137 /// Top-secret module.
138 #[cfg(feature = "log")]
139 pub mod internal {
140     use prelude::*;
141 
142     use core::cell::Cell;
143     use core::fmt;
144     use core::ops::Range;
145 
146     use shim::config;
147 
148     use sync;
149 
150     /// The log lock.
151     ///
152     /// This lock is used to avoid bungling and intertwining the log.
153     #[cfg(not(feature = "no_log_lock"))]
154     pub static LOG_LOCK: Mutex<()> = Mutex::new(());
155 
156     /// A log writer.
157     ///
158     /// This writes to the shim logger.
159     pub struct LogWriter {
160         /// The inner lock.
161         #[cfg(not(feature = "no_log_lock"))]
162         _lock: sync::MutexGuard<'static, ()>,
163     }
164 
165     impl LogWriter {
166         /// Standard error output.
167         pub fn new() -> LogWriter {
168             #[cfg(feature = "no_log_lock")]
169             {
170                 LogWriter {}
171             }
172 
173             #[cfg(not(feature = "no_log_lock"))]
174             LogWriter {
175                 _lock: LOG_LOCK.lock(),
176             }
177         }
178     }
179 
180     impl fmt::Write for LogWriter {
181         fn write_str(&mut self, s: &str) -> fmt::Result {
182             if config::log(s) == !0 {
183                 Err(fmt::Error)
184             } else {
185                 Ok(())
186             }
187         }
188     }
189 
190     /// A "cursor".
191     ///
192     /// Cursors represents a block or an interval in the log output. This trait is implemented for
193     /// various types that can represent a cursor.
194     pub trait Cursor {
195         /// Iteration at n.
196         ///
197         /// This is called in the logging loop. The cursor should then write, what it needs, to the
198         /// formatter if the underlying condition is true.
199         ///
200         /// For example, a plain position cursor will write `"|"` when `n == self.pos`.
201         // TODO: Use an iterator instead.
202         fn at(&self, f: &mut fmt::Formatter, n: usize) -> fmt::Result;
203 
204         /// The after hook.
205         ///
206         /// This is runned when the loop is over. The aim is to e.g. catch up if the cursor wasn't
207         /// printed (i.e. is out of range).
208         fn after(&self, f: &mut fmt::Formatter) -> fmt::Result;
209     }
210 
211     /// Types that can be converted into a cursor.
212     pub trait IntoCursor {
213         /// The end result.
214         type Cursor: Cursor;
215 
216         /// Convert this value into its equivalent cursor.
217         fn into_cursor(self) -> Self::Cursor;
218     }
219 
220     /// A single-point cursor.
221     pub struct UniCursor {
222         /// The position where this cursor will be placed.
223         pos: usize,
224         /// Is this cursor printed?
225         ///
226         /// This is used for the after hook.
227         is_printed: Cell<bool>,
228     }
229 
230     impl Cursor for UniCursor {
231         fn at(&self, f: &mut fmt::Formatter, n: usize) -> fmt::Result {
232             if self.pos == n {
233                 self.is_printed.set(true);
234                 write!(f, "|")?;
235             }
236 
237             Ok(())
238         }
239 
240         fn after(&self, f: &mut fmt::Formatter) -> fmt::Result {
241             if !self.is_printed.get() {
242                 write!(f, "…|")?;
243             }
244 
245             Ok(())
246         }
247     }
248 
249     impl IntoCursor for usize {
250         type Cursor = UniCursor;
251 
252         fn into_cursor(self) -> UniCursor {
253             UniCursor {
254                 pos: self,
255                 is_printed: Cell::new(false),
256             }
257         }
258     }
259 
260     impl Cursor for () {
261         fn at(&self, _: &mut fmt::Formatter, _: usize) -> fmt::Result {
262             Ok(())
263         }
264 
265         fn after(&self, _: &mut fmt::Formatter) -> fmt::Result {
266             Ok(())
267         }
268     }
269 
270     impl IntoCursor for () {
271         type Cursor = ();
272 
273         fn into_cursor(self) -> () {
274             ()
275         }
276     }
277 
278     /// A interval/range cursor.
279     ///
280     /// The start of the range is marked by `[` and the end by `]`.
281     pub struct RangeCursor {
282         /// The range of this cursor.
283         range: Range<usize>,
284     }
285 
286     impl Cursor for RangeCursor {
287         fn at(&self, f: &mut fmt::Formatter, n: usize) -> fmt::Result {
288             if self.range.start == n {
289                 write!(f, "[")?;
290             } else if self.range.end == n {
291                 write!(f, "]")?;
292             }
293 
294             Ok(())
295         }
296 
297         fn after(&self, _: &mut fmt::Formatter) -> fmt::Result {
298             Ok(())
299         }
300     }
301 
302     impl IntoCursor for Range<usize> {
303         type Cursor = RangeCursor;
304 
305         fn into_cursor(self) -> RangeCursor {
306             RangeCursor { range: self }
307         }
308     }
309 
310     /// A "block logger".
311     ///
312     /// This intend to show the structure of a block pool. The syntax used is like:
313     ///
314     /// ```
315     /// xxx__|xx_
316     /// ```
317     ///
318     /// where `x` denotes an non-empty block. `_` denotes an empty block, with `|` representing the
319     /// cursor.
320     pub struct BlockLogger<'a, T> {
321         /// The cursor.
322         ///
323         /// This is where the `|` will be printed.
324         pub cur: T,
325         /// The blocks.
326         pub blocks: &'a [Block],
327     }
328 
329     impl<'a, T: Cursor> fmt::Debug for BlockLogger<'a, T> {
330         fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
331             // TODO: Handle alignment etc.
332 
333             for (n, i) in self.blocks.iter().enumerate() {
334                 self.cur.at(f, n)?;
335 
336                 if i.is_empty() {
337                     // Empty block.
338                     write!(f, "_")?;
339                 } else {
340                     // Non-empty block.
341                     write!(f, "x")?;
342                 }
343             }
344 
345             self.cur.after(f)?;
346 
347             Ok(())
348         }
349     }
350 
351     /// Check if this log level is enabled.
352     #[inline]
353     pub fn level(lv: u8) -> bool {
354         lv >= config::MIN_LOG_LEVEL
355     }
356 }
357