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