wrk_api_bench/
plot.rs

1use std::{
2    io::Write,
3    path::{Path, PathBuf},
4    process::{Command, Stdio},
5};
6
7use tempfile::NamedTempFile;
8
9use crate::{wrk::Benchmarks, Result, WrkError};
10
11#[derive(Debug, Clone)]
12pub struct Gnuplot {
13    title: String,
14    output: PathBuf,
15}
16
17impl Gnuplot {
18    pub fn new(title: &str, output: &Path) -> Self {
19        Self {
20            title: title.to_string(),
21            output: output.to_path_buf(),
22        }
23    }
24
25    pub fn plot(&self, benchmarks: &Benchmarks) -> Result<()> {
26        if benchmarks.len() < 2 {
27            return Err(WrkError::Plot(format!(
28                "There are {} availble datapoints. Unable to plot history with less than 2 datapoints",
29                benchmarks.len()
30            )));
31        }
32        let dates: Vec<_> = benchmarks
33            .iter()
34            .map(|b| b.date().format("%Y-%m-%d-%H:%M:%S").to_string())
35            .collect();
36        let serie: Vec<_> = benchmarks.iter().map(|b| *b.requests_sec() as u64).collect();
37        let min_x = dates.iter().min().unwrap();
38        let max_x = dates.iter().max().unwrap();
39        let min_y = *serie.iter().min().unwrap_or(&0) as f64;
40        let min_y = (min_y - (min_y * 0.15)) as u64;
41        let max_y = *serie.iter().max().unwrap_or(&1000) as f64;
42        let max_y = (max_y + (max_y * 0.15)) as u64;
43        let mut data_file = NamedTempFile::new()?;
44        for (i, b) in benchmarks.iter().enumerate() {
45            data_file.write_all(format!("{} {}\n", dates[i], b.requests_sec()).as_bytes())?;
46        }
47        let gnuplot = format!(
48            r#"set xdata time
49set timefmt "%Y-%m-%d-%H:%M:%S"
50set format x "%m/%y/%d %H:%M:%S"
51set xrange ["{}":"{}"]
52set yrange [{}:{}]
53set key off
54set xtics rotate by -45
55set title "{}"
56set terminal png
57set output "{}"
58plot "{}" using 1:2 with linespoints linetype 6 linewidth 2"#,
59            min_x,
60            max_x,
61            min_y,
62            max_y,
63            self.title,
64            self.output.display(),
65            data_file.path().display()
66        );
67        let mut child = Command::new("gnuplot").stdin(Stdio::piped()).spawn()?;
68        if let Some(mut stdin) = child.stdin.take() {
69            stdin.write_all(gnuplot.as_ref())?;
70        }
71        let status = child.wait()?;
72        if status.success() {
73            Ok(())
74        } else {
75            let err = WrkError::Plot(format!(
76                "Error plotting file {} which is kept for debug",
77                data_file.path().display()
78            ));
79            data_file.keep()?;
80            Err(err)
81        }
82    }
83}