use clap::Parser;
use main_error::MainError;
use stak_configuration::DEFAULT_HEAP_SIZE;
use stak_device::StdioDevice;
use stak_file::OsFileSystem;
use stak_process_context::OsProcessContext;
use stak_profiler::{
DurationRecord, ProcedureRecord, StackProfiler, calculate_durations, calculate_flamegraph,
collapse_stacks, read_records, reverse_stacks, write_records,
};
use stak_r7rs::SmallPrimitiveSet;
use stak_time::OsClock;
use stak_vm::Vm;
use std::{
fs::{OpenOptions, read},
io::{BufWriter, stdin, stdout},
path::PathBuf,
};
#[derive(clap::Parser)]
#[command(about, version)]
struct Arguments {
#[command(subcommand)]
command: Command,
}
#[derive(clap::Subcommand)]
enum Command {
Run(RunArguments),
Analyze(AnalyzeArguments),
}
#[derive(clap::Args)]
struct RunArguments {
#[arg(required(true))]
bytecode_file: PathBuf,
#[arg(short = 'p', long = "profile", required(true))]
profile_file: PathBuf,
#[arg(short = 's', long, default_value_t = DEFAULT_HEAP_SIZE)]
heap_size: usize,
}
#[derive(clap::Args)]
struct AnalyzeArguments {
#[command(subcommand)]
command: Analysis,
}
#[derive(clap::Subcommand)]
enum Analysis {
Duration,
StackCollapse,
StackReverse,
Flamegraph,
}
fn main() -> Result<(), MainError> {
match Arguments::parse().command {
Command::Run(arguments) => {
let mut profiler = StackProfiler::new(BufWriter::new(
OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(&arguments.profile_file)?,
));
Vm::new(
vec![Default::default(); arguments.heap_size],
SmallPrimitiveSet::new(
StdioDevice::new(),
OsFileSystem::new(),
OsProcessContext::new().with_argument_skip(1),
OsClock::new(),
),
)?
.with_profiler(&mut profiler)
.run(read(&arguments.bytecode_file)?)?;
}
Command::Analyze(arguments) => {
let reader = stdin().lock();
let writer = BufWriter::new(stdout().lock());
match arguments.command {
Analysis::Duration => write_records(
calculate_durations(read_records::<ProcedureRecord>(reader)),
writer,
)?,
Analysis::StackCollapse => write_records(
collapse_stacks(read_records::<DurationRecord>(reader)),
writer,
)?,
Analysis::StackReverse => write_records(
reverse_stacks(read_records::<DurationRecord>(reader)),
writer,
)?,
Analysis::Flamegraph => calculate_flamegraph(read_records(reader), writer)?,
}
}
}
Ok(())
}