浏览代码

Simple visual logging system

ticki 8 年之前
父节点
当前提交
4d3632533d
共有 6 个文件被更改,包括 139 次插入18 次删除
  1. 2 1
      Cargo.toml
  2. 1 1
      src/allocator.rs
  3. 25 7
      src/bookkeeper.rs
  4. 5 3
      src/lib.rs
  5. 96 0
      src/log.rs
  6. 10 6
      src/write.rs

+ 2 - 1
Cargo.toml

@@ -30,7 +30,8 @@ default = ["allocator", "clippy"]
 
 allocator = []
 debug_tools = []
-internals = []
+libc_write = []
+log = ["libc_write"]
 security = []
 unsafe_no_brk_lock = []
 unsafe_no_mutex_lock = []

+ 1 - 1
src/allocator.rs

@@ -98,7 +98,7 @@ impl Allocator {
     ///
     /// In release mode, this is a NOOP.
     pub fn debug_assert_no_leak(&self) {
-        #[cfg(features = "debug_tools")]
+        #[cfg(feature = "debug_tools")]
         self.inner.assert_no_leak();
     }
 }

+ 25 - 7
src/bookkeeper.rs

@@ -74,7 +74,7 @@ pub struct Bookkeeper {
     /// The inner OOM handler.
     oom_handler: fn() -> !,
     /// The number of bytes currently allocated.
-    #[cfg(features = "debug_tools")]
+    #[cfg(feature = "debug_tools")]
     allocated: usize,
 }
 
@@ -83,7 +83,7 @@ impl Bookkeeper {
     ///
     /// This will make no allocations or BRKs.
     #[inline]
-    #[cfg(features = "debug_tools")]
+    #[cfg(feature = "debug_tools")]
     pub const fn new() -> Bookkeeper {
         Bookkeeper {
             pool: Vec::new(),
@@ -94,7 +94,7 @@ impl Bookkeeper {
     }
 
     #[inline]
-    #[cfg(not(features = "debug_tools"))]
+    #[cfg(not(feature = "debug_tools"))]
     pub const fn new() -> Bookkeeper {
         Bookkeeper {
             pool: Vec::new(),
@@ -267,6 +267,9 @@ impl Bookkeeper {
         // Find the index.
         let ind = self.find(&block);
 
+        // Logging.
+        log!(self.pool;ind, "Reallocating {:?} to size {} with align {}.", block, new_size, align);
+
         // "Leave" the allocator.
         let block = self.enter(block);
         // Try to do an inplace reallocation.
@@ -346,6 +349,9 @@ impl Bookkeeper {
     ///
     /// See [`realloc_inplace_ind`](#method.realloc_inplace.html) for more information.
     fn realloc_inplace_ind(&mut self, ind: usize, mut block: Block, new_size: usize) -> Result<Block, Block> {
+        // Logging.
+        log!(self.pool;ind, "Inplace reallocating {:?} to size {}.", block, new_size);
+
         /// Assertions...
         debug_assert!(self.find(&block) == ind, "Block is not inserted at the appropriate index.");
 
@@ -398,6 +404,9 @@ impl Bookkeeper {
     /// See [`free`](#method.free) for more information.
     #[inline]
     fn free_ind(&mut self, ind: usize, mut block: Block) {
+        // Logging.
+        log!(self.pool;ind, "Freeing {:?}.", block);
+
         // Short circuit in case of empty block.
         if block.is_empty() { return; }
 
@@ -411,7 +420,7 @@ impl Bookkeeper {
             let entry = &mut self.pool[ind];
 
             // Make some handy assertions.
-            #[cfg(features = "debug_tools")]
+            #[cfg(feature = "debug_tools")]
             assert!(entry != &mut block, "Double free.");
             debug_assert!(block.is_empty() || entry <= &mut block, "Block merged in the wrong \
                           direction.");
@@ -429,6 +438,9 @@ impl Bookkeeper {
     /// Extend the data segment.
     #[inline]
     fn brk(&self, size: usize, align: usize) -> (Block, Block, Block) {
+        // Logging.
+        log!(self.pool;self.pool.len(), "BRK'ing a block of size, {}, and alignment {}.", size, align);
+
         // Calculate the canonical size (extra space is allocated to limit the number of system calls).
         let brk_size = canonicalize_brk(size).checked_add(align).unwrap_or_else(|| self.oom());
 
@@ -458,6 +470,9 @@ impl Bookkeeper {
     /// This guarantees linearity so that the blocks will be adjacent.
     #[inline]
     fn double_push(&mut self, block_a: Block, block_b: Block) {
+        // Logging.
+        log!(self.pool;self.pool.len(), "Pushing {:?} and {:?}.", block_a, block_b);
+
         // Reserve extra elements.
         let len = self.pool.len();
         self.reserve(len + 2);
@@ -600,6 +615,9 @@ impl Bookkeeper {
     /// The insertion is now completed.
     #[inline]
     fn insert(&mut self, ind: usize, block: Block) {
+        // Logging.
+        log!(self.pool;ind, "Inserting block {:?}.", block);
+
         // Bound check.
         assert!(self.pool.len() >= ind, "Insertion out of bounds.");
 
@@ -663,7 +681,7 @@ impl Bookkeeper {
     #[inline]
     fn leave(&mut self, block: Block) -> Block {
         // Update the number of bytes allocated.
-        #[cfg(features = "debug_tools")]
+        #[cfg(feature = "debug_tools")]
         {
             self.allocated += block.size();
         }
@@ -679,7 +697,7 @@ impl Bookkeeper {
     #[inline]
     fn enter(&mut self, block: Block) -> Block {
         // Update the number of bytes allocated.
-        #[cfg(features = "debug_tools")]
+        #[cfg(feature = "debug_tools")]
         {
             self.allocated -= block.size();
         }
@@ -719,7 +737,7 @@ impl Bookkeeper {
     /// Check for memory leaks.
     ///
     /// This will ake sure that all the allocated blocks have been freed.
-    #[cfg(features = "debug_tools")]
+    #[cfg(feature = "debug_tools")]
     pub fn assert_no_leak(&self) {
         assert!(self.allocated == self.pool.capacity() * mem::size_of::<Block>(), "Not all blocks \
                 freed. Total allocated space is {} ({} free blocks).", self.allocated,

+ 5 - 3
src/lib.rs

@@ -10,12 +10,14 @@
 #![no_std]
 
 #![feature(allocator, const_fn, core_intrinsics, stmt_expr_attributes, drop_types_in_const,
-           nonzero, optin_builtin_traits, type_ascription)]
+           nonzero, optin_builtin_traits, type_ascription, question_mark)]
 #![warn(missing_docs)]
 
-#[cfg(feature = "internals")]
+#[cfg(feature = "libc_write")]
 #[macro_use]
-mod debug;
+mod write;
+#[macro_use]
+mod log;
 
 mod block;
 mod bookkeeper;

+ 96 - 0
src/log.rs

@@ -0,0 +1,96 @@
+//! Allocator logging.
+//!
+//! This allows for detailed logging for `ralloc`.
+
+/// NO-OP.
+#[macro_export]
+#[cfg(not(feature = "log"))]
+macro_rules! log {
+    ($( $arg:tt )*) => {};
+}
+
+/// Top secret place-holding module.
+#[cfg(feature = "log")]
+#[macro_use]
+pub mod internal {
+    use prelude::*;
+
+    use core::fmt;
+
+    /// Log to the appropriate source.
+    ///
+    /// The first argument this takes is of the form `pool;number`, which is used to print the
+    /// block pools state. `number` is what the operation "revolves around" to give a sense of
+    /// position.
+    ///
+    /// The rest of the arguments are just normal formatters.
+    #[macro_export]
+    macro_rules! log {
+        ($pool:expr;$n:expr, $( $arg:expr ),*) => {{
+            use {write, log};
+
+            use core::fmt::Write;
+
+            // Print the pool state.
+            let mut stderr = write::Writer::stderr();
+            let _ = write!(stderr, "{:10?} : ", log::internal::BlockLogger {
+                cur: $n,
+                blocks: &$pool,
+            });
+
+            // Print the log message.
+            let _ = write!(stderr, $( $arg ),*);
+            let _ = writeln!(stderr, " (at {}:{})", file!(), line!());
+        }};
+    }
+
+    /// A "block logger".
+    ///
+    /// This intend to show the structure of a block pool. The syntax used is like:
+    ///
+    /// ```
+    /// xxx__|xx_
+    /// ```
+    ///
+    /// where `x` denotes an non-empty block. `_` denotes an empty block, and `|` is placed on the
+    /// "current block".
+    pub struct BlockLogger<'a> {
+        /// The cursor.
+        ///
+        /// This is where the `|` will be printed.
+        pub cur: usize,
+        /// The blocks.
+        pub blocks: &'a [Block],
+    }
+
+    impl<'a> fmt::Debug for BlockLogger<'a> {
+        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+            // TODO handle alignment etc.
+
+            let mut cursor_set = false;
+
+            for (n, i) in self.blocks.iter().enumerate() {
+                if n == self.cur {
+                    // Write the cursor.
+                    write!(f, "|")?;
+                    cursor_set = true;
+                }
+
+                if i.is_empty() {
+                    // Empty block.
+                    write!(f, "_")?;
+                } else {
+                    // Non-empty block.
+                    write!(f, "x")?;
+                }
+            }
+
+            if !cursor_set {
+                // The cursor isn't set yet, so we place it in the end.
+                write!(f, "|")?;
+            }
+
+            Ok(())
+        }
+    }
+}

+ 10 - 6
src/debug.rs → src/write.rs

@@ -1,4 +1,6 @@
-//! Debugging primitives.
+//! Direct libc-based write for internal debugging.
+//!
+//! This will replace the assertion macros to avoid deadlocks in panics.
 
 use core::fmt;
 
@@ -38,12 +40,13 @@ impl fmt::Write for Writer {
 #[macro_export]
 macro_rules! assert {
     ($e:expr) => {{
-        use debug;
+        use write;
+
         use core::intrinsics;
         use core::fmt::Write;
 
         if !$e {
-            let _ = write!(debug::Writer::stderr(), "assertion failed at {}:{}: {}", file!(),
+            let _ = write!(write::Writer::stderr(), "assertion failed at {}:{}: {}", file!(),
                            line!(), stringify!($e));
 
             #[allow(unused_unsafe)]
@@ -51,14 +54,15 @@ macro_rules! assert {
         }
     }};
     ($e:expr, $( $arg:expr ),*) => {{
-        use debug;
+        use write;
+
         use core::intrinsics;
         use core::fmt::Write;
 
         if !$e {
-            let _ = write!(debug::Writer::stderr(), "assertion failed at {}:{}: `{}` - ", file!(),
+            let _ = write!(write::Writer::stderr(), "assertion failed at {}:{}: `{}` - ", file!(),
                            line!(), stringify!($e));
-            let _ = writeln!(debug::Writer::stderr(), $( $arg ),*);
+            let _ = writeln!(write::Writer::stderr(), $( $arg ),*);
 
             #[allow(unused_unsafe)]
             unsafe { intrinsics::abort() }