123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244 |
- // Copyright 2019-2023 Tauri Programme within The Commons Conservancy
- // SPDX-License-Identifier: Apache-2.0
- // SPDX-License-Identifier: MIT
- use anyhow::Result;
- use serde::{Deserialize, Serialize};
- use serde_json::Value;
- use std::{
- collections::HashMap,
- fs,
- io::{BufRead, BufReader},
- path::PathBuf,
- process::{Command, Output, Stdio},
- };
- #[derive(Default, Clone, Serialize, Deserialize, Debug)]
- pub struct BenchResult {
- pub created_at: String,
- pub sha1: String,
- pub exec_time: HashMap<String, HashMap<String, f64>>,
- pub binary_size: HashMap<String, u64>,
- pub max_memory: HashMap<String, u64>,
- pub thread_count: HashMap<String, u64>,
- pub syscall_count: HashMap<String, u64>,
- pub cargo_deps: HashMap<String, usize>,
- }
- #[allow(dead_code)]
- #[derive(Debug, Clone, Serialize)]
- pub struct StraceOutput {
- pub percent_time: f64,
- pub seconds: f64,
- pub usecs_per_call: Option<u64>,
- pub calls: u64,
- pub errors: u64,
- }
- pub fn get_target() -> &'static str {
- #[cfg(target_os = "macos")]
- return if cfg!(target_arch = "aarch64") {
- "aarch64-apple-darwin"
- } else {
- "x86_64-apple-darwin"
- };
- #[cfg(target_os = "linux")]
- return "x86_64-unknown-linux-gnu";
- #[cfg(target_os = "windows")]
- unimplemented!();
- }
- pub fn target_dir() -> PathBuf {
- bench_root_path()
- .join("tests")
- .join("target")
- .join(get_target())
- .join("release")
- }
- pub fn bench_root_path() -> PathBuf {
- PathBuf::from(env!("CARGO_MANIFEST_DIR"))
- }
- #[allow(dead_code)]
- pub fn home_path() -> PathBuf {
- #[cfg(any(target_os = "macos", target_os = "linux"))]
- return PathBuf::from(env!("HOME"));
- #[cfg(any(target_os = "windows"))]
- return PathBuf::from(env!("HOMEPATH"));
- }
- #[allow(dead_code)]
- pub fn tauri_root_path() -> PathBuf {
- bench_root_path()
- .parent()
- .unwrap()
- .parent()
- .unwrap()
- .to_path_buf()
- }
- #[allow(dead_code)]
- pub fn run_collect(cmd: &[&str]) -> (String, String) {
- let mut process_builder = Command::new(cmd[0]);
- process_builder
- .args(&cmd[1..])
- .stdin(Stdio::piped())
- .stdout(Stdio::piped())
- .stderr(Stdio::piped());
- let prog = process_builder.spawn().expect("failed to spawn script");
- let Output {
- stdout,
- stderr,
- status,
- } = prog.wait_with_output().expect("failed to wait on child");
- let stdout = String::from_utf8(stdout).unwrap();
- let stderr = String::from_utf8(stderr).unwrap();
- if !status.success() {
- eprintln!("stdout: <<<{}>>>", stdout);
- eprintln!("stderr: <<<{}>>>", stderr);
- panic!("Unexpected exit code: {:?}", status.code());
- }
- (stdout, stderr)
- }
- #[allow(dead_code)]
- pub fn parse_max_mem(file_path: &str) -> Option<u64> {
- let file = fs::File::open(file_path).unwrap();
- let output = BufReader::new(file);
- let mut highest: u64 = 0;
- // MEM 203.437500 1621617192.4123
- for line in output.lines().flatten() {
- // split line by space
- let split = line.split(' ').collect::<Vec<_>>();
- if split.len() == 3 {
- // mprof generate result in MB
- let current_bytes = str::parse::<f64>(split[1]).unwrap() as u64 * 1024 * 1024;
- if current_bytes > highest {
- highest = current_bytes;
- }
- }
- }
- fs::remove_file(file_path).unwrap();
- if highest > 0 {
- return Some(highest);
- }
- None
- }
- #[allow(dead_code)]
- pub fn parse_strace_output(output: &str) -> HashMap<String, StraceOutput> {
- let mut summary = HashMap::new();
- let mut lines = output
- .lines()
- .filter(|line| !line.is_empty() && !line.contains("detached ..."));
- let count = lines.clone().count();
- if count < 4 {
- return summary;
- }
- let total_line = lines.next_back().unwrap();
- lines.next_back(); // Drop separator
- let data_lines = lines.skip(2);
- for line in data_lines {
- let syscall_fields = line.split_whitespace().collect::<Vec<_>>();
- let len = syscall_fields.len();
- let syscall_name = syscall_fields.last().unwrap();
- if (5..=6).contains(&len) {
- summary.insert(
- syscall_name.to_string(),
- StraceOutput {
- percent_time: str::parse::<f64>(syscall_fields[0]).unwrap(),
- seconds: str::parse::<f64>(syscall_fields[1]).unwrap(),
- usecs_per_call: Some(str::parse::<u64>(syscall_fields[2]).unwrap()),
- calls: str::parse::<u64>(syscall_fields[3]).unwrap(),
- errors: if syscall_fields.len() < 6 {
- 0
- } else {
- str::parse::<u64>(syscall_fields[4]).unwrap()
- },
- },
- );
- }
- }
- let total_fields = total_line.split_whitespace().collect::<Vec<_>>();
- summary.insert(
- "total".to_string(),
- match total_fields.len() {
- // Old format, has no usecs/call
- 5 => StraceOutput {
- percent_time: str::parse::<f64>(total_fields[0]).unwrap(),
- seconds: str::parse::<f64>(total_fields[1]).unwrap(),
- usecs_per_call: None,
- calls: str::parse::<u64>(total_fields[2]).unwrap(),
- errors: str::parse::<u64>(total_fields[3]).unwrap(),
- },
- 6 => StraceOutput {
- percent_time: str::parse::<f64>(total_fields[0]).unwrap(),
- seconds: str::parse::<f64>(total_fields[1]).unwrap(),
- usecs_per_call: Some(str::parse::<u64>(total_fields[2]).unwrap()),
- calls: str::parse::<u64>(total_fields[3]).unwrap(),
- errors: str::parse::<u64>(total_fields[4]).unwrap(),
- },
- _ => panic!("Unexpected total field count: {}", total_fields.len()),
- },
- );
- summary
- }
- #[allow(dead_code)]
- pub fn run(cmd: &[&str]) {
- let mut process_builder = Command::new(cmd[0]);
- process_builder.args(&cmd[1..]).stdin(Stdio::piped());
- let mut prog = process_builder.spawn().expect("failed to spawn script");
- let status = prog.wait().expect("failed to wait on child");
- if !status.success() {
- panic!("Unexpected exit code: {:?}", status.code());
- }
- }
- #[allow(dead_code)]
- pub fn read_json(filename: &str) -> Result<Value> {
- let f = fs::File::open(filename)?;
- Ok(serde_json::from_reader(f)?)
- }
- #[allow(dead_code)]
- pub fn write_json(filename: &str, value: &Value) -> Result<()> {
- let f = fs::File::create(filename)?;
- serde_json::to_writer(f, value)?;
- Ok(())
- }
- #[allow(dead_code)]
- pub fn download_file(url: &str, filename: PathBuf) {
- if !url.starts_with("http:") && !url.starts_with("https:") {
- fs::copy(url, filename).unwrap();
- return;
- }
- // Downloading with curl this saves us from adding
- // a Rust HTTP client dependency.
- println!("Downloading {}", url);
- let status = Command::new("curl")
- .arg("-L")
- .arg("-s")
- .arg("-o")
- .arg(&filename)
- .arg(url)
- .status()
- .unwrap();
- assert!(status.success());
- assert!(filename.exists());
- }
|