#![cfg_attr(
feature = "cargo-clippy",
allow(
clippy::just_underscores_and_digits, // Used in the stats code
clippy::transmute_ptr_to_ptr, // Used in the stats code
)
)]
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate log;
#[macro_use]
mod macros_private;
#[cfg_attr(feature = "cargo-clippy", allow(clippy::too_many_arguments))]
#[macro_use]
mod plot;
mod analysis;
mod bench_target;
mod compile;
mod config;
mod connection;
mod estimate;
mod format;
mod html;
mod kde;
mod message_formats;
mod model;
mod report;
mod stats;
mod value_formatter;
use crate::config::{OutputFormat, PlottingBackend, SelfConfig, TextColor};
use crate::connection::{AxisScale, PlotConfiguration};
use crate::plot::Plotter;
use crate::report::{Report, ReportContext};
use anyhow::Error;
use lazy_static::lazy_static;
lazy_static! {
static ref DEBUG_ENABLED: bool = std::env::var_os("CRITERION_DEBUG").is_some();
}
fn debug_enabled() -> bool {
*DEBUG_ENABLED
}
fn configure_log() {
use simplelog::*;
let filter = if debug_enabled() {
LevelFilter::max()
} else {
LevelFilter::Warn
};
TermLogger::init(
filter,
Default::default(),
TerminalMode::Stderr,
ColorChoice::Never,
)
.unwrap();
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
configure_log();
let configuration = config::configure()?;
let self_config = &configuration.self_config;
let compile::CompiledBenchmarks {
targets,
library_paths,
} = compile::compile(self_config.debug_build, &configuration.cargo_args)?;
let mut run_model = model::Model::load(
self_config.criterion_home.clone(),
"main".into(),
self_config.history_id.clone(),
self_config.history_description.clone(),
);
let cli_report = configure_cli_output(self_config);
let bencher_report = crate::report::BencherReport;
let html_report = get_plotter(self_config)?.map(|plotter| crate::html::Html::new(plotter));
let machine_report = message_formats::create_machine_report(self_config);
let mut reports: Vec<&dyn crate::report::Report> = Vec::new();
match self_config.output_format {
OutputFormat::Bencher => reports.push(&bencher_report),
OutputFormat::Criterion | OutputFormat::Quiet | OutputFormat::Verbose => {
reports.push(&cli_report)
}
}
if let Some(html_report) = &html_report {
reports.push(html_report);
}
if let Some(machine_report) = &machine_report {
reports.push(machine_report);
}
let reports = crate::report::Reports::new(reports);
if self_config.do_run {
for bench in targets {
info!("Executing {} - {:?}", bench.name, bench.executable);
let err = bench.execute(
&self_config.criterion_home,
&configuration.additional_args,
&library_paths,
&reports,
&mut run_model,
self_config.message_format.is_some(),
);
if let Err(err) = err {
if self_config.do_fail_fast {
return Err(err.into());
} else {
error!(
"Failed to execute benchmark target {}:\n{}",
bench.name, err
);
}
}
}
let final_context = ReportContext {
output_directory: self_config.criterion_home.join("reports"),
plot_config: PlotConfiguration {
summary_scale: AxisScale::Linear,
},
};
reports.final_summary(&final_context, &run_model);
}
Ok(())
}
fn configure_cli_output(self_config: &crate::config::SelfConfig) -> crate::report::CliReport {
let stderr_isatty = atty::is(atty::Stream::Stderr);
let mut enable_text_overwrite = stderr_isatty && !debug_enabled();
let enable_text_coloring = match self_config.text_color {
TextColor::Auto => stderr_isatty,
TextColor::Never => {
enable_text_overwrite = false;
false
}
TextColor::Always => true,
};
let show_differences = match self_config.output_format {
OutputFormat::Criterion | OutputFormat::Verbose => true,
OutputFormat::Quiet | OutputFormat::Bencher => false,
};
let verbose = match self_config.output_format {
OutputFormat::Verbose => true,
OutputFormat::Criterion | OutputFormat::Quiet | OutputFormat::Bencher => debug_enabled(),
};
crate::report::CliReport::new(
enable_text_overwrite,
enable_text_coloring,
show_differences,
verbose,
)
}
#[cfg(feature = "gnuplot_backend")]
fn gnuplot_plotter(config: &SelfConfig) -> Result<Box<dyn Plotter>, Error> {
match criterion_plot::version() {
Ok(_) => {
let generator = crate::plot::PlotGenerator {
backend: crate::plot::Gnuplot::new(&config.colors),
};
Ok(Box::new(generator))
},
Err(_) => Err(anyhow::anyhow!("Gnuplot is not available. To continue, either install Gnuplot or allow cargo-criterion to fall back to using plotters.")),
}
}
#[cfg(not(feature = "gnuplot_backend"))]
fn gnuplot_plotter(_: &SelfConfig) -> Result<Box<dyn Plotter>, Error> {
anyhow::bail!("Gnuplot backend is disabled. To use gnuplot backend, install cargo-criterion with the 'gnuplot_backend' feature enabled")
}
#[cfg(feature = "plotters_backend")]
fn plotters_plotter(config: &SelfConfig) -> Result<Box<dyn Plotter>, Error> {
let generator = crate::plot::PlotGenerator {
backend: crate::plot::PlottersBackend::new(&config.colors),
};
Ok(Box::new(generator))
}
#[cfg(not(feature = "plotters_backend"))]
fn plotters_plotter(_: &SelfConfig) -> Result<Box<dyn Plotter>, Error> {
anyhow::bail!("Plotters backend is disabled. To use plotters backend, install cargo-criterion with the 'plotters_backend' feature enabled")
}
#[cfg(any(feature = "gnuplot_backend", feature = "plotters_backend"))]
fn get_plotter(config: &SelfConfig) -> Result<Option<Box<dyn Plotter>>, Error> {
match config.plotting_backend {
PlottingBackend::Gnuplot => gnuplot_plotter(config).map(Some),
PlottingBackend::Plotters => plotters_plotter(config).map(Some),
PlottingBackend::Auto => gnuplot_plotter(config)
.or_else(|_| plotters_plotter(config))
.map(Some),
PlottingBackend::Disabled => Ok(None),
}
}
#[cfg(not(any(feature = "gnuplot_backend", feature = "plotters_backend")))]
fn get_plotter(config: &SelfConfig) -> Result<Option<Box<dyn Plotter>>, Error> {
match config.plotting_backend {
PlottingBackend::Disabled => Ok(None),
_ => anyhow::bail!("No plotting backend is available. At least one of the 'gnuplot_backend' or 'plotters_backend' features must be included.")
}
}
trait DurationExt {
fn to_nanos(&self) -> u64;
}
const NANOS_PER_SEC: u64 = 1_000_000_000;
impl DurationExt for std::time::Duration {
fn to_nanos(&self) -> u64 {
self.as_secs() * NANOS_PER_SEC + u64::from(self.subsec_nanos())
}
}