Add CLI parameters and wait for ENTER to start visualization.
This commit is contained in:
@@ -8,5 +8,6 @@ license = "Apache-2.0"
|
|||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
clap = "2.33.1"
|
||||||
ncurses = "5.99.0"
|
ncurses = "5.99.0"
|
||||||
regex = "1"
|
regex = "1"
|
||||||
|
|||||||
@@ -12,11 +12,52 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
use clap::{App, Arg};
|
||||||
use regex::Regex;
|
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::thread::sleep;
|
||||||
use std::time::Duration;
|
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.
|
/// An allocation or deallocation event.
|
||||||
struct Event {
|
struct Event {
|
||||||
/// Whether this even is an allocation (true) or a deallocation (false).
|
/// Whether this even is an allocation (true) or a deallocation (false).
|
||||||
@@ -28,30 +69,32 @@ struct Event {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
/// The following regex matches lines looking like the following from OpenSK's output. Such
|
let config = parse_cli();
|
||||||
/// lines are printed to the console when the `--debug-allocations` feature is enabled in the
|
|
||||||
/// deploy script.
|
// 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)
|
// ```
|
||||||
/// ```
|
// 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
|
// The two integers between square brackets after the (de)alloc keywords represent the length
|
||||||
/// integers within parentheses represent statistics about the total number of allocated blocks
|
// and alignement of the allocated block, respectively. The integer serialized in hexadecimal
|
||||||
/// and the total number of allocated bytes after the (de)allocation operation, respectively.
|
// 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
|
||||||
/// This regex captures three elements, in this order.
|
// and the total number of allocated bytes after the (de)allocation operation, respectively.
|
||||||
/// - The keyword to know whether this operation is an allocation or a deallocation.
|
//
|
||||||
/// - The length of the allocated block.
|
// This regex captures three elements, in this order.
|
||||||
/// - The starting address of the allocated block.
|
// - 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\)$")
|
let re = Regex::new(r"^(alloc|dealloc)\[(\d+), \d+\] = 0x([0-9a-f]+) \(\d+ ptrs, \d+ bytes\)$")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut events = Vec::new();
|
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()) {
|
if let Some(caps) = re.captures(&line.unwrap()) {
|
||||||
let typ = caps.get(1).unwrap().as_str();
|
let typ = caps.get(1).unwrap().as_str();
|
||||||
let len = caps.get(2).unwrap().as_str().parse::<usize>().unwrap();
|
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!("Peak consumption: {0} = {0:08x} bytes", len);
|
||||||
println!("Fragmentation overhead: {0} = {0:08x} bytes", len - peak);
|
println!("Fragmentation overhead: {0} = {0:08x} bytes", len - peak);
|
||||||
|
|
||||||
print!("\nLoading visualization");
|
print!("\nPress ENTER to start the visualization...");
|
||||||
for _ in 0..30 {
|
std::io::stdout().flush().unwrap();
|
||||||
print!(".");
|
// Wait for ENTER, by reading a single byte and discarding it.
|
||||||
std::io::stdout().flush().unwrap();
|
let _ = std::io::stdin().lock().read(&mut [0u8]).unwrap();
|
||||||
sleep(Duration::from_millis(50));
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
|
|
||||||
let window = ncurses::initscr();
|
let window = ncurses::initscr();
|
||||||
ncurses::cbreak();
|
ncurses::cbreak();
|
||||||
@@ -120,7 +160,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
ncurses::addstr(std::str::from_utf8(s.as_slice()).unwrap());
|
ncurses::addstr(std::str::from_utf8(s.as_slice()).unwrap());
|
||||||
ncurses::refresh();
|
ncurses::refresh();
|
||||||
sleep(Duration::from_millis(50));
|
sleep(Duration::from_nanos(1_000_000_000 / config.fps));
|
||||||
}
|
}
|
||||||
|
|
||||||
ncurses::endwin();
|
ncurses::endwin();
|
||||||
|
|||||||
Reference in New Issue
Block a user