xref: /relibc/ralloc/src/log.rs (revision 9cef1dfa322dd22404c3e9ab33874296568bdcc9)
1 //! Allocator logging.
2 //!
3 //! This allows for detailed logging for `ralloc`.
4 
5 /// Log to the appropriate source.
6 ///
7 /// The first argument this takes is of the form `pool;cursor`, which is used to print the
8 /// block pools state. `cursor` is what the operation "revolves around" to give a sense of
9 /// position.
10 ///
11 /// If the `;cursor` part is left out, no cursor will be printed.
12 ///
13 /// The rest of the arguments are just normal formatters.
14 #[macro_export]
15 macro_rules! log {
16     ($pool:expr, $( $arg:expr ),*) => {
17         log!($pool;(), $( $arg ),*);
18     };
19     ($bk:expr;$cur:expr, $( $arg:expr ),*) => {
20         #[cfg(feature = "log")]
21         {
22             use core::fmt::Write;
23 
24             use {write, log};
25             use log::internal::IntoCursor;
26 
27             // To avoid cluttering the lines, we acquire a lock.
28             let _lock = write::LINE_LOCK.lock();
29 
30             // Print the pool state.
31             let mut log = write::Writer::new();
32             let _ = write!(log, "({:2})   {:10?} : ", $bk.id, log::internal::BlockLogger {
33                 cur: $cur.clone().into_cursor(),
34                 blocks: &$bk.pool,
35             });
36 
37             // Print the log message.
38             let _ = write!(log, $( $arg ),*);
39             let _ = writeln!(log, " (at {}:{})", file!(), line!());
40         }
41     };
42 }
43 
44 /// Top secret place-holding module.
45 #[macro_use]
46 #[cfg(feature = "log")]
47 pub mod internal {
48     use prelude::*;
49 
50     use core::fmt;
51 
52     use core::cell::Cell;
53     use core::ops::Range;
54 
55     /// A "cursor".
56     ///
57     /// Cursors represents a block or an interval in the log output. This trait is implemented for
58     /// various types that can represent a cursor.
59     pub trait Cursor {
60         /// Iteration at n.
61         ///
62         /// This is called in the logging loop. The cursor should then write, what it needs, to the
63         /// formatter if the underlying condition is true.
64         ///
65         /// For example, a plain position cursor will write `"|"` when `n == self.pos`.
66         // TODO use an iterator instead.
67         fn at(&self, f: &mut fmt::Formatter, n: usize) -> fmt::Result;
68 
69         /// The after hook.
70         ///
71         /// This is runned when the loop is over. The aim is to e.g. catch up if the cursor wasn't
72         /// printed (i.e. is out of range).
73         fn after(&self, f: &mut fmt::Formatter) -> fmt::Result;
74     }
75 
76     /// Types that can be converted into a cursor.
77     pub trait IntoCursor {
78         /// The end result.
79         type Cursor: Cursor;
80 
81         /// Convert this value into its equivalent cursor.
82         fn into_cursor(self) -> Self::Cursor;
83     }
84 
85     /// A single-point cursor.
86     pub struct UniCursor {
87         /// The position where this cursor will be placed.
88         pos: usize,
89         /// Is this cursor printed?
90         ///
91         /// This is used for the after hook.
92         is_printed: Cell<bool>,
93     }
94 
95     impl Cursor for UniCursor {
96         fn at(&self, f: &mut fmt::Formatter, n: usize) -> fmt::Result {
97             if self.pos == n {
98                 self.is_printed.set(true);
99                 write!(f, "|")?;
100             }
101 
102             Ok(())
103         }
104 
105         fn after(&self, f: &mut fmt::Formatter) -> fmt::Result {
106             if !self.is_printed.get() {
107                 write!(f, "…|")?;
108             }
109 
110             Ok(())
111         }
112     }
113 
114     impl IntoCursor for usize {
115         type Cursor = UniCursor;
116 
117         fn into_cursor(self) -> UniCursor {
118             UniCursor {
119                 pos: self,
120                 is_printed: Cell::new(false),
121             }
122         }
123     }
124 
125     impl Cursor for () {
126         fn at(&self, _: &mut fmt::Formatter, _: usize) -> fmt::Result { Ok(()) }
127 
128         fn after(&self, _: &mut fmt::Formatter) -> fmt::Result { Ok(()) }
129     }
130 
131     impl IntoCursor for () {
132         type Cursor = ();
133 
134         fn into_cursor(self) -> () {
135             ()
136         }
137     }
138 
139     /// A interval/range cursor.
140     ///
141     /// The start of the range is marked by `[` and the end by `]`.
142     pub struct RangeCursor {
143         /// The range of this cursor.
144         range: Range<usize>,
145     }
146 
147     impl Cursor for RangeCursor {
148         fn at(&self, f: &mut fmt::Formatter, n: usize) -> fmt::Result {
149             if self.range.start == n {
150                 write!(f, "[")?;
151             } else if self.range.end == n {
152                 write!(f, "]")?;
153             }
154 
155             Ok(())
156         }
157 
158         fn after(&self, _: &mut fmt::Formatter) -> fmt::Result { Ok(()) }
159     }
160 
161     impl IntoCursor for Range<usize> {
162         type Cursor = RangeCursor;
163 
164         fn into_cursor(self) -> RangeCursor {
165             RangeCursor {
166                 range: self,
167             }
168         }
169     }
170 
171     /// A "block logger".
172     ///
173     /// This intend to show the structure of a block pool. The syntax used is like:
174     ///
175     /// ```
176     /// xxx__|xx_
177     /// ```
178     ///
179     /// where `x` denotes an non-empty block. `_` denotes an empty block, with `|` representing the
180     /// cursor.
181     pub struct BlockLogger<'a, T> {
182         /// The cursor.
183         ///
184         /// This is where the `|` will be printed.
185         pub cur: T,
186         /// The blocks.
187         pub blocks: &'a [Block],
188     }
189 
190     impl<'a, T: Cursor> fmt::Debug for BlockLogger<'a, T> {
191         fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
192             // TODO handle alignment etc.
193 
194             for (n, i) in self.blocks.iter().enumerate() {
195                 self.cur.at(f, n)?;
196 
197                 if i.is_empty() {
198                     // Empty block.
199                     write!(f, "_")?;
200                 } else {
201                     // Non-empty block.
202                     write!(f, "x")?;
203                 }
204             }
205 
206             self.cur.after(f)?;
207 
208             Ok(())
209         }
210     }
211 }
212