use std::collections::HashSet;
use clap::{ArgAction, Parser, ValueEnum};
use anyhow::Result;
pub use commands::ProfilerCommand;
use joule_profiler_core::config::{Command, Config, ProfileConfig};
use crate::output::{
displayer::Displayer,
formats::{
OutputFormat, csv::CsvOutput, json::JsonOutput, output_format, terminal::TerminalOutput,
},
};
mod commands;
mod logging;
mod output;
#[allow(clippy::struct_excessive_bools)]
#[derive(Parser, Debug)]
#[command(name = "joule-profiler")]
#[command(
version,
about = "Measure program metrics from various sources like RAPL"
)]
pub struct CliArgs {
#[arg(short = 'v', long = "verbose", action = ArgAction::Count)]
pub verbose: u8,
#[arg(long = "rapl-path")]
pub rapl_path: Option<String>,
#[arg(short = 's', long = "sockets")]
pub sockets: Option<String>,
#[arg(long, conflicts_with = "csv")]
pub json: bool,
#[arg(long, conflicts_with = "json")]
pub csv: bool,
#[arg(short = 'o', long = "output-file")]
pub output_file: Option<String>,
#[arg(long)]
pub gpu: bool,
#[arg(long)]
pub perf: bool,
#[arg(long = "rapl-backend", value_enum, default_value_t = RaplBackend::Perf)]
pub rapl_backend: RaplBackend,
#[command(subcommand)]
pub command: ProfilerCommand,
}
impl CliArgs {
pub fn from_args() -> Self {
Self::parse()
}
}
impl From<CliArgs> for Config {
fn from(cli_args: CliArgs) -> Self {
let command = match cli_args.command {
ProfilerCommand::Profile(profile_args) => Command::Profile(ProfileConfig {
stdout_file: profile_args.stdout_file,
cmd: profile_args.cmd,
token_pattern: profile_args.token_pattern,
use_root: profile_args.use_root,
}),
ProfilerCommand::ListSensors => Command::ListSensors,
};
Config {
command,
rapl_path: cli_args.rapl_path,
}
}
}
#[derive(Clone, Debug, ValueEnum)]
pub enum RaplBackend {
Perf,
Powercap,
}
pub fn output_format_to_displayer(cli: &CliArgs) -> Result<Box<dyn Displayer>> {
let output_format = output_format(cli.json, cli.csv);
let output_file = cli.output_file.clone();
let displayer = match output_format {
OutputFormat::Terminal => TerminalOutput.into(),
OutputFormat::Json => JsonOutput::new(output_file)?.into(),
OutputFormat::Csv => CsvOutput::try_new(output_file)?.into(),
};
Ok(displayer)
}
pub fn init_logging(verbose: u8) {
logging::init_logging(verbose);
}
pub fn parse_sockets_spec(sockets_spec: Option<&str>) -> Option<HashSet<u32>> {
sockets_spec.map(|s| {
s.split(',')
.filter_map(|x| x.trim().parse::<u32>().ok())
.collect()
})
}