xref: /relibc/ralloc/src/log.rs (revision 5a42b783d46b59c625e781942fbd5bf048a4ac89)
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::fmt;
143     use core::cell::Cell;
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 { Err(fmt::Error) } else { Ok(()) }
183         }
184     }
185 
186     /// A "cursor".
187     ///
188     /// Cursors represents a block or an interval in the log output. This trait is implemented for
189     /// various types that can represent a cursor.
190     pub trait Cursor {
191         /// Iteration at n.
192         ///
193         /// This is called in the logging loop. The cursor should then write, what it needs, to the
194         /// formatter if the underlying condition is true.
195         ///
196         /// For example, a plain position cursor will write `"|"` when `n == self.pos`.
197         // TODO: Use an iterator instead.
198         fn at(&self, f: &mut fmt::Formatter, n: usize) -> fmt::Result;
199 
200         /// The after hook.
201         ///
202         /// This is runned when the loop is over. The aim is to e.g. catch up if the cursor wasn't
203         /// printed (i.e. is out of range).
204         fn after(&self, f: &mut fmt::Formatter) -> fmt::Result;
205     }
206 
207     /// Types that can be converted into a cursor.
208     pub trait IntoCursor {
209         /// The end result.
210         type Cursor: Cursor;
211 
212         /// Convert this value into its equivalent cursor.
213         fn into_cursor(self) -> Self::Cursor;
214     }
215 
216     /// A single-point cursor.
217     pub struct UniCursor {
218         /// The position where this cursor will be placed.
219         pos: usize,
220         /// Is this cursor printed?
221         ///
222         /// This is used for the after hook.
223         is_printed: Cell<bool>,
224     }
225 
226     impl Cursor for UniCursor {
227         fn at(&self, f: &mut fmt::Formatter, n: usize) -> fmt::Result {
228             if self.pos == n {
229                 self.is_printed.set(true);
230                 write!(f, "|")?;
231             }
232 
233             Ok(())
234         }
235 
236         fn after(&self, f: &mut fmt::Formatter) -> fmt::Result {
237             if !self.is_printed.get() {
238                 write!(f, "…|")?;
239             }
240 
241             Ok(())
242         }
243     }
244 
245     impl IntoCursor for usize {
246         type Cursor = UniCursor;
247 
248         fn into_cursor(self) -> UniCursor {
249             UniCursor {
250                 pos: self,
251                 is_printed: Cell::new(false),
252             }
253         }
254     }
255 
256     impl Cursor for () {
257         fn at(&self, _: &mut fmt::Formatter, _: usize) -> fmt::Result { Ok(()) }
258 
259         fn after(&self, _: &mut fmt::Formatter) -> fmt::Result { Ok(()) }
260     }
261 
262     impl IntoCursor for () {
263         type Cursor = ();
264 
265         fn into_cursor(self) -> () {
266             ()
267         }
268     }
269 
270     /// A interval/range cursor.
271     ///
272     /// The start of the range is marked by `[` and the end by `]`.
273     pub struct RangeCursor {
274         /// The range of this cursor.
275         range: Range<usize>,
276     }
277 
278     impl Cursor for RangeCursor {
279         fn at(&self, f: &mut fmt::Formatter, n: usize) -> fmt::Result {
280             if self.range.start == n {
281                 write!(f, "[")?;
282             } else if self.range.end == n {
283                 write!(f, "]")?;
284             }
285 
286             Ok(())
287         }
288 
289         fn after(&self, _: &mut fmt::Formatter) -> fmt::Result { Ok(()) }
290     }
291 
292     impl IntoCursor for Range<usize> {
293         type Cursor = RangeCursor;
294 
295         fn into_cursor(self) -> RangeCursor {
296             RangeCursor {
297                 range: self,
298             }
299         }
300     }
301 
302     /// A "block logger".
303     ///
304     /// This intend to show the structure of a block pool. The syntax used is like:
305     ///
306     /// ```
307     /// xxx__|xx_
308     /// ```
309     ///
310     /// where `x` denotes an non-empty block. `_` denotes an empty block, with `|` representing the
311     /// cursor.
312     pub struct BlockLogger<'a, T> {
313         /// The cursor.
314         ///
315         /// This is where the `|` will be printed.
316         pub cur: T,
317         /// The blocks.
318         pub blocks: &'a [Block],
319     }
320 
321     impl<'a, T: Cursor> fmt::Debug for BlockLogger<'a, T> {
322         fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
323             // TODO: Handle alignment etc.
324 
325             for (n, i) in self.blocks.iter().enumerate() {
326                 self.cur.at(f, n)?;
327 
328                 if i.is_empty() {
329                     // Empty block.
330                     write!(f, "_")?;
331                 } else {
332                     // Non-empty block.
333                     write!(f, "x")?;
334                 }
335             }
336 
337             self.cur.after(f)?;
338 
339             Ok(())
340         }
341     }
342 
343     /// Check if this log level is enabled.
344     #[allow(absurd_extreme_comparisons)]
345     #[inline]
346     pub fn level(lv: u8) -> bool {
347         lv >= config::MIN_LOG_LEVEL
348     }
349 }
350