Add CLI parameters and wait for ENTER to start visualization.

This commit is contained in:
Guillaume Endignoux
2020-06-26 10:45:53 +02:00
parent 75836d459a
commit 85f759f912
2 changed files with 70 additions and 29 deletions

View File

@@ -8,5 +8,6 @@ license = "Apache-2.0"
edition = "2018"
[dependencies]
clap = "2.33.1"
ncurses = "5.99.0"
regex = "1"

View File

@@ -12,11 +12,52 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use clap::{App, Arg};
use regex::Regex;
use std::io::{BufRead, Write};
use std::fs::File;
use std::io::{BufRead, BufReader, Read, Write};
use std::thread::sleep;
use std::time::Duration;
/// Configuration, built from CLI parameters.
struct Config {
/// Handle to the log file containing allocation operations.
logfile: File,
/// Number of allocation operations to show per second.
fps: u64,
}
fn parse_cli() -> Config {
let matches = App::new("Heap visualizer")
.version("0.1")
.author("Guillaume Endignoux <guillaumee@google.com>")
.about("Tool to visualize heap usage of libtock-rs applications")
.arg(Arg::with_name("logfile")
.short("f")
.long("logfile")
.value_name("FILE")
.help("Log file containing allocation info (deploy OpenSK with --debug-allocations to obtain it)")
.takes_value(true)
.required(true))
.arg(Arg::with_name("fps")
.long("fps")
.value_name("FPS")
.help("Number of allocation operations to show per second")
.takes_value(true)
.default_value("20"))
.get_matches();
let logpath = matches.value_of("logfile").unwrap();
let fps = matches
.value_of("fps")
.unwrap()
.parse::<u64>()
.expect("The --fps parameter must be an integer");
let logfile = File::open(logpath).expect("Couldn't open --logfile for reading");
Config { logfile, fps }
}
/// An allocation or deallocation event.
struct Event {
/// Whether this even is an allocation (true) or a deallocation (false).
@@ -28,30 +69,32 @@ struct Event {
}
fn main() {
/// The following regex matches lines looking like the following from OpenSK's output. Such
/// lines are printed to the console when the `--debug-allocations` feature is enabled in the
/// deploy script.
///
/// ```
/// alloc[256, 1] = 0x2002410c (2 ptrs, 384 bytes)
/// dealloc[256, 1] = 0x2002410c (1 ptrs, 512 bytes)
/// ```
///
/// The two integers between square brackets after the (de)alloc keywords represent the length
/// and alignement of the allocated block, respectively. The integer serialized in hexadecimal
/// after the equal sign represents the starting address of the allocated block. The two
/// integers within parentheses represent statistics about the total number of allocated blocks
/// and the total number of allocated bytes after the (de)allocation operation, respectively.
///
/// This regex captures three elements, in this order.
/// - The keyword to know whether this operation is an allocation or a deallocation.
/// - The length of the allocated block.
/// - The starting address of the allocated block.
let config = parse_cli();
// The following regex matches lines looking like the following from OpenSK's output. Such lines
// are printed to the console when the `--debug-allocations` feature is enabled in the deploy
// script.
//
// ```
// alloc[256, 1] = 0x2002410c (2 ptrs, 384 bytes)
// dealloc[256, 1] = 0x2002410c (1 ptrs, 512 bytes)
// ```
//
// The two integers between square brackets after the (de)alloc keywords represent the length
// and alignement of the allocated block, respectively. The integer serialized in hexadecimal
// after the equal sign represents the starting address of the allocated block. The two
// integers within parentheses represent statistics about the total number of allocated blocks
// and the total number of allocated bytes after the (de)allocation operation, respectively.
//
// This regex captures three elements, in this order.
// - The keyword to know whether this operation is an allocation or a deallocation.
// - The length of the allocated block.
// - The starting address of the allocated block.
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() {
for line in BufReader::new(config.logfile).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::<usize>().unwrap();
@@ -92,13 +135,10 @@ fn main() {
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!();
print!("\nPress ENTER to start the visualization...");
std::io::stdout().flush().unwrap();
// Wait for ENTER, by reading a single byte and discarding it.
let _ = std::io::stdin().lock().read(&mut [0u8]).unwrap();
let window = ncurses::initscr();
ncurses::cbreak();
@@ -120,7 +160,7 @@ fn main() {
}
ncurses::addstr(std::str::from_utf8(s.as_slice()).unwrap());
ncurses::refresh();
sleep(Duration::from_millis(50));
sleep(Duration::from_nanos(1_000_000_000 / config.fps));
}
ncurses::endwin();