From e457efc4d695014a2a8d1fd81c002fec8322ed4f Mon Sep 17 00:00:00 2001 From: Guillaume Endignoux Date: Thu, 25 Jun 2020 15:46:08 +0200 Subject: [PATCH] Add a heap visualization tool. --- run_desktop_tests.sh | 2 + tools/heapviz/Cargo.toml | 12 ++++++ tools/heapviz/src/main.rs | 90 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+) create mode 100644 tools/heapviz/Cargo.toml create mode 100644 tools/heapviz/src/main.rs diff --git a/run_desktop_tests.sh b/run_desktop_tests.sh index 07b652e..8bcd2d7 100755 --- a/run_desktop_tests.sh +++ b/run_desktop_tests.sh @@ -26,6 +26,8 @@ cd ../.. echo "Building sha256sum tool..." cargo build --manifest-path third_party/tock/tools/sha256sum/Cargo.toml +echo "Checking that heapviz tool builds properly..." +cargo build --manifest-path tools/heapviz/Cargo.toml echo "Checking that CTAP2 builds properly..." cargo check --release --target=thumbv7em-none-eabi diff --git a/tools/heapviz/Cargo.toml b/tools/heapviz/Cargo.toml new file mode 100644 index 0000000..e173d17 --- /dev/null +++ b/tools/heapviz/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "heapviz" +version = "0.1.0" +authors = [ + "Guillaume Endignoux ", +] +license = "Apache-2.0" +edition = "2018" + +[dependencies] +ncurses = "5.99.0" +regex = "1" diff --git a/tools/heapviz/src/main.rs b/tools/heapviz/src/main.rs new file mode 100644 index 0000000..82e07dd --- /dev/null +++ b/tools/heapviz/src/main.rs @@ -0,0 +1,90 @@ +use regex::Regex; +use std::io::{BufRead, Write}; +use std::thread::sleep; +use std::time::Duration; + +struct Event { + is_alloc: bool, + start: usize, + len: usize, +} + +fn main() { + let re = Regex::new(r"^(alloc|dealloc)\[(\d+), \d+\] = 0x([0-9a-f]+) \(\d+ ptrs, \d+ bytes\)$") + .unwrap(); + + let mut events = Vec::new(); + for line in std::io::stdin().lock().lines() { + if let Some(caps) = re.captures(&line.unwrap()) { + let typ = caps.get(1).unwrap().as_str(); + let len = caps.get(2).unwrap().as_str().parse::().unwrap(); + let start = usize::from_str_radix(&caps.get(3).unwrap().as_str(), 16).unwrap(); + events.push(Event { + is_alloc: typ == "alloc", + start, + len, + }); + } + } + + let count_alloc = events.iter().filter(|e| e.is_alloc).count(); + let count_dealloc = events.len() - count_alloc; + let start = events.iter().map(|e| e.start).min().unwrap_or(0); + let end = events.iter().map(|e| e.start + e.len).max().unwrap_or(0); + let mut usage = 0; + let peak = events + .iter() + .map(|e| { + if e.is_alloc { + usage += e.len; + } else { + usage -= e.len; + } + usage + }) + .max() + .unwrap_or(0); + let len = end - start; + println!( + "Observed {} allocations and {} deallocations", + count_alloc, count_dealloc + ); + println!("Start address: {:08x}", start); + println!("End address: {:08x}", end); + println!("Peak usage: {0} = {0:08x} bytes", peak); + println!("Peak consumption: {0} = {0:08x} bytes", len); + println!("Fragmentation overhead: {0} = {0:08x} bytes", len - peak); + + print!("\nLoading visualization"); + for _ in 0..30 { + print!("."); + std::io::stdout().flush().unwrap(); + sleep(Duration::from_millis(50)); + } + println!(); + + let window = ncurses::initscr(); + ncurses::cbreak(); + ncurses::noecho(); + ncurses::intrflush(window, false); + ncurses::curs_set(ncurses::CURSOR_VISIBILITY::CURSOR_INVISIBLE); + + let width = ncurses::getmaxx(window) as usize; + + for e in events.iter() { + let position = e.start - start; + ncurses::wmove(window, (position / width) as i32, (position % width) as i32); + + let mut s = Vec::with_capacity(e.len); + if e.is_alloc { + s.resize(e.len, b'#'); + } else { + s.resize(e.len, b'.'); + } + ncurses::addstr(std::str::from_utf8(s.as_slice()).unwrap()); + ncurses::refresh(); + sleep(Duration::from_millis(50)); + } + + ncurses::endwin(); +}