mirror of
https://github.com/tauri-apps/benchmark_electron.git
synced 2026-01-31 00:35:24 +01:00
wip
This commit is contained in:
311
bench/main.rs
311
bench/main.rs
@@ -5,221 +5,222 @@
|
||||
use anyhow::Result;
|
||||
use serde::Serialize;
|
||||
use serde_json::Value;
|
||||
use utils::root_path;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
env, fs,
|
||||
path::Path,
|
||||
process::{Command, Stdio},
|
||||
collections::HashMap,
|
||||
env, fs,
|
||||
path::Path,
|
||||
process::{Command, Stdio},
|
||||
};
|
||||
use utils::root_path;
|
||||
|
||||
mod utils;
|
||||
|
||||
fn read_json(filename: &str) -> Result<Value> {
|
||||
let f = fs::File::open(filename)?;
|
||||
Ok(serde_json::from_reader(f)?)
|
||||
let f = fs::File::open(filename)?;
|
||||
Ok(serde_json::from_reader(f)?)
|
||||
}
|
||||
|
||||
fn write_json(filename: &str, value: &Value) -> Result<()> {
|
||||
let f = fs::File::create(filename)?;
|
||||
serde_json::to_writer(f, value)?;
|
||||
Ok(())
|
||||
let f = fs::File::create(filename)?;
|
||||
serde_json::to_writer(f, value)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// The list of the examples of the benchmark name, arguments and return code
|
||||
const EXEC_TIME_BENCHMARKS: &[(&str, &str, Option<i32>)] = &[
|
||||
("hello_world", "hello_world/out/startup-electron-linux-x64/startup-electron", None)
|
||||
];
|
||||
const EXEC_TIME_BENCHMARKS: &[(&str, &str, Option<i32>)] = &[(
|
||||
"hello_world",
|
||||
"hello_world/out/startup-electron-linux-x64/startup-electron",
|
||||
None,
|
||||
)];
|
||||
|
||||
fn run_strace_benchmarks(new_data: &mut BenchResult) -> Result<()> {
|
||||
use std::io::Read;
|
||||
use std::io::Read;
|
||||
|
||||
let mut thread_count = HashMap::<String, u64>::new();
|
||||
let mut syscall_count = HashMap::<String, u64>::new();
|
||||
let mut thread_count = HashMap::<String, u64>::new();
|
||||
let mut syscall_count = HashMap::<String, u64>::new();
|
||||
|
||||
for (name, example_exe, _) in EXEC_TIME_BENCHMARKS {
|
||||
let mut file = tempfile::NamedTempFile::new()?;
|
||||
for (name, example_exe, _) in EXEC_TIME_BENCHMARKS {
|
||||
let mut file = tempfile::NamedTempFile::new()?;
|
||||
|
||||
Command::new("strace")
|
||||
.args(&[
|
||||
"-c",
|
||||
"-f",
|
||||
"-o",
|
||||
file.path().to_str().unwrap(),
|
||||
utils::root_path().join(example_exe).to_str().unwrap(),
|
||||
])
|
||||
.stdout(Stdio::inherit())
|
||||
.spawn()?
|
||||
.wait()?;
|
||||
Command::new("strace")
|
||||
.args(&[
|
||||
"-c",
|
||||
"-f",
|
||||
"-o",
|
||||
file.path().to_str().unwrap(),
|
||||
utils::root_path().join(example_exe).to_str().unwrap(),
|
||||
])
|
||||
.stdout(Stdio::inherit())
|
||||
.spawn()?
|
||||
.wait()?;
|
||||
|
||||
let mut output = String::new();
|
||||
file.as_file_mut().read_to_string(&mut output)?;
|
||||
let mut output = String::new();
|
||||
file.as_file_mut().read_to_string(&mut output)?;
|
||||
|
||||
let strace_result = utils::parse_strace_output(&output);
|
||||
let clone = strace_result.get("clone").map(|d| d.calls).unwrap_or(0) + 1;
|
||||
let total = strace_result.get("total").unwrap().calls;
|
||||
thread_count.insert(name.to_string(), clone);
|
||||
syscall_count.insert(name.to_string(), total);
|
||||
}
|
||||
let strace_result = utils::parse_strace_output(&output);
|
||||
let clone = strace_result.get("clone").map(|d| d.calls).unwrap_or(0) + 1;
|
||||
let total = strace_result.get("total").unwrap().calls;
|
||||
thread_count.insert(name.to_string(), clone);
|
||||
syscall_count.insert(name.to_string(), total);
|
||||
}
|
||||
|
||||
new_data.thread_count = thread_count;
|
||||
new_data.syscall_count = syscall_count;
|
||||
new_data.thread_count = thread_count;
|
||||
new_data.syscall_count = syscall_count;
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_max_mem_benchmark() -> Result<HashMap<String, u64>> {
|
||||
let mut results = HashMap::<String, u64>::new();
|
||||
let mut results = HashMap::<String, u64>::new();
|
||||
|
||||
for (name, example_exe, return_code) in EXEC_TIME_BENCHMARKS {
|
||||
let proc = Command::new("time")
|
||||
.args(&["-v", utils::root_path().join(example_exe).to_str().unwrap()])
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()?;
|
||||
for (name, example_exe, return_code) in EXEC_TIME_BENCHMARKS {
|
||||
let proc = Command::new("time")
|
||||
.args(&["-v", utils::root_path().join(example_exe).to_str().unwrap()])
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()?;
|
||||
|
||||
let proc_result = proc.wait_with_output()?;
|
||||
if let Some(code) = return_code {
|
||||
assert_eq!(proc_result.status.code().unwrap(), *code);
|
||||
let proc_result = proc.wait_with_output()?;
|
||||
if let Some(code) = return_code {
|
||||
assert_eq!(proc_result.status.code().unwrap(), *code);
|
||||
}
|
||||
let out = String::from_utf8(proc_result.stderr)?;
|
||||
|
||||
results.insert(name.to_string(), utils::parse_max_mem(&out).unwrap());
|
||||
}
|
||||
let out = String::from_utf8(proc_result.stderr)?;
|
||||
|
||||
results.insert(name.to_string(), utils::parse_max_mem(&out).unwrap());
|
||||
}
|
||||
|
||||
Ok(results)
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
fn get_binary_sizes() -> Result<HashMap<String, u64>> {
|
||||
let mut sizes = HashMap::<String, u64>::new();
|
||||
// add size for all EXEC_TIME_BENCHMARKS
|
||||
for (name, example_exe, _) in EXEC_TIME_BENCHMARKS {
|
||||
let meta = std::fs::metadata(example_exe).unwrap();
|
||||
sizes.insert(name.to_string(), meta.len());
|
||||
}
|
||||
let mut sizes = HashMap::<String, u64>::new();
|
||||
// add size for all EXEC_TIME_BENCHMARKS
|
||||
for (name, example_exe, _) in EXEC_TIME_BENCHMARKS {
|
||||
let meta = std::fs::metadata(example_exe).unwrap();
|
||||
sizes.insert(name.to_string(), meta.len());
|
||||
}
|
||||
|
||||
Ok(sizes)
|
||||
Ok(sizes)
|
||||
}
|
||||
|
||||
fn cargo_deps() -> usize {
|
||||
let cargo_lock = utils::root_path().join("Cargo.lock");
|
||||
let mut count = 0;
|
||||
let file = std::fs::File::open(cargo_lock).unwrap();
|
||||
use std::io::BufRead;
|
||||
for line in std::io::BufReader::new(file).lines() {
|
||||
if line.unwrap().starts_with("[[package]]") {
|
||||
count += 1
|
||||
let cargo_lock = utils::root_path().join("Cargo.lock");
|
||||
let mut count = 0;
|
||||
let file = std::fs::File::open(cargo_lock).unwrap();
|
||||
use std::io::BufRead;
|
||||
for line in std::io::BufReader::new(file).lines() {
|
||||
if line.unwrap().starts_with("[[package]]") {
|
||||
count += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
println!("cargo_deps {}", count);
|
||||
assert!(count > 10); // Sanity check.
|
||||
count
|
||||
println!("cargo_deps {}", count);
|
||||
assert!(count > 10); // Sanity check.
|
||||
count
|
||||
}
|
||||
|
||||
const RESULT_KEYS: &[&str] = &["mean", "stddev", "user", "system", "min", "max"];
|
||||
|
||||
fn run_exec_time() -> Result<HashMap<String, HashMap<String, f64>>> {
|
||||
let benchmark_file = root_path().join("hyperfine_results.json");
|
||||
let benchmark_file = benchmark_file.to_str().unwrap();
|
||||
let benchmark_file = root_path().join("hyperfine_results.json");
|
||||
let benchmark_file = benchmark_file.to_str().unwrap();
|
||||
|
||||
let mut command = [
|
||||
"hyperfine",
|
||||
"--export-json",
|
||||
benchmark_file,
|
||||
"--warmup",
|
||||
"3",
|
||||
]
|
||||
.iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect::<Vec<_>>();
|
||||
let mut command = [
|
||||
"hyperfine",
|
||||
"--export-json",
|
||||
benchmark_file,
|
||||
"--warmup",
|
||||
"3",
|
||||
]
|
||||
.iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for (_, example_exe, _return_code) in EXEC_TIME_BENCHMARKS {
|
||||
command.push(
|
||||
utils::root_path()
|
||||
.join(example_exe)
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
for (_, example_exe, _return_code) in EXEC_TIME_BENCHMARKS {
|
||||
command.push(
|
||||
utils::root_path()
|
||||
.join(example_exe)
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
utils::run(&command.iter().map(|s| s.as_ref()).collect::<Vec<_>>());
|
||||
utils::run(&command.iter().map(|s| s.as_ref()).collect::<Vec<_>>());
|
||||
|
||||
let mut results = HashMap::<String, HashMap<String, f64>>::new();
|
||||
let hyperfine_results = read_json(benchmark_file)?;
|
||||
for ((name, _, _), data) in EXEC_TIME_BENCHMARKS.iter().zip(
|
||||
hyperfine_results
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.get("results")
|
||||
.unwrap()
|
||||
.as_array()
|
||||
.unwrap(),
|
||||
) {
|
||||
let data = data.as_object().unwrap().clone();
|
||||
results.insert(
|
||||
name.to_string(),
|
||||
data
|
||||
.into_iter()
|
||||
.filter(|(key, _)| RESULT_KEYS.contains(&key.as_str()))
|
||||
.map(|(key, val)| (key, val.as_f64().unwrap()))
|
||||
.collect(),
|
||||
);
|
||||
}
|
||||
let mut results = HashMap::<String, HashMap<String, f64>>::new();
|
||||
let hyperfine_results = read_json(benchmark_file)?;
|
||||
for ((name, _, _), data) in EXEC_TIME_BENCHMARKS.iter().zip(
|
||||
hyperfine_results
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.get("results")
|
||||
.unwrap()
|
||||
.as_array()
|
||||
.unwrap(),
|
||||
) {
|
||||
let data = data.as_object().unwrap().clone();
|
||||
results.insert(
|
||||
name.to_string(),
|
||||
data.into_iter()
|
||||
.filter(|(key, _)| RESULT_KEYS.contains(&key.as_str()))
|
||||
.map(|(key, val)| (key, val.as_f64().unwrap()))
|
||||
.collect(),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(results)
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, Debug)]
|
||||
struct BenchResult {
|
||||
created_at: String,
|
||||
sha1: String,
|
||||
created_at: String,
|
||||
sha1: String,
|
||||
|
||||
exec_time: HashMap<String, HashMap<String, f64>>,
|
||||
binary_size: HashMap<String, u64>,
|
||||
max_memory: HashMap<String, u64>,
|
||||
thread_count: HashMap<String, u64>,
|
||||
syscall_count: HashMap<String, u64>,
|
||||
cargo_deps: usize,
|
||||
exec_time: HashMap<String, HashMap<String, f64>>,
|
||||
binary_size: HashMap<String, u64>,
|
||||
max_memory: HashMap<String, u64>,
|
||||
thread_count: HashMap<String, u64>,
|
||||
syscall_count: HashMap<String, u64>,
|
||||
cargo_deps: usize,
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
if !env::args().any(|s| s == "--bench") {
|
||||
return Ok(());
|
||||
}
|
||||
if !env::args().any(|s| s == "--bench") {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
println!("Starting electron benchmark");
|
||||
println!("Starting electron benchmark");
|
||||
|
||||
env::set_current_dir(&utils::root_path())?;
|
||||
env::set_current_dir(&utils::root_path())?;
|
||||
|
||||
println!("{:?}", &utils::root_path());
|
||||
println!("{:?}", &utils::root_path());
|
||||
|
||||
let mut new_data = BenchResult {
|
||||
created_at: chrono::Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true),
|
||||
sha1: utils::run_collect(&["git", "rev-parse", "HEAD"])
|
||||
.0
|
||||
.trim()
|
||||
.to_string(),
|
||||
exec_time: run_exec_time()?,
|
||||
binary_size: get_binary_sizes()?,
|
||||
cargo_deps: cargo_deps(),
|
||||
..Default::default()
|
||||
};
|
||||
let mut new_data = BenchResult {
|
||||
created_at: chrono::Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true),
|
||||
sha1: utils::run_collect(&["git", "rev-parse", "HEAD"])
|
||||
.0
|
||||
.trim()
|
||||
.to_string(),
|
||||
exec_time: run_exec_time()?,
|
||||
binary_size: get_binary_sizes()?,
|
||||
cargo_deps: cargo_deps(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if cfg!(target_os = "linux") {
|
||||
run_strace_benchmarks(&mut new_data)?;
|
||||
new_data.max_memory = run_max_mem_benchmark()?;
|
||||
}
|
||||
if cfg!(target_os = "linux") {
|
||||
run_strace_benchmarks(&mut new_data)?;
|
||||
new_data.max_memory = run_max_mem_benchmark()?;
|
||||
}
|
||||
|
||||
println!("===== <BENCHMARK RESULTS>");
|
||||
serde_json::to_writer_pretty(std::io::stdout(), &new_data)?;
|
||||
println!("\n===== </BENCHMARK RESULTS>");
|
||||
println!("===== <BENCHMARK RESULTS>");
|
||||
serde_json::to_writer_pretty(std::io::stdout(), &new_data)?;
|
||||
println!("\n===== </BENCHMARK RESULTS>");
|
||||
|
||||
if let Some(filename) = root_path().join("bench.json").to_str() {
|
||||
write_json(filename, &serde_json::to_value(&new_data)?)?;
|
||||
} else {
|
||||
eprintln!("Cannot write bench.json, path is invalid");
|
||||
}
|
||||
if let Some(filename) = root_path().join("bench.json").to_str() {
|
||||
write_json(filename, &serde_json::to_value(&new_data)?)?;
|
||||
} else {
|
||||
eprintln!("Cannot write bench.json, path is invalid");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -7,70 +7,70 @@ use serde_json::Value;
|
||||
use std::{collections::HashMap, fs::File, io::BufReader, path::PathBuf};
|
||||
|
||||
fn target_dir() -> PathBuf {
|
||||
root_dir().join("target").join("release")
|
||||
root_dir().join("target").join("release")
|
||||
}
|
||||
|
||||
fn root_dir() -> PathBuf {
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.parent()
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.to_path_buf()
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.parent()
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.to_path_buf()
|
||||
}
|
||||
|
||||
fn write_json(filename: &PathBuf, value: &Value) {
|
||||
let f = File::create(filename).expect("Unable to create file");
|
||||
serde_json::to_writer(f, value).expect("Unable to write json");
|
||||
let f = File::create(filename).expect("Unable to create file");
|
||||
serde_json::to_writer(f, value).expect("Unable to write json");
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
struct BenchResult {
|
||||
created_at: String,
|
||||
sha1: String,
|
||||
created_at: String,
|
||||
sha1: String,
|
||||
|
||||
exec_time: HashMap<String, HashMap<String, f64>>,
|
||||
binary_size: HashMap<String, u64>,
|
||||
max_memory: HashMap<String, u64>,
|
||||
thread_count: HashMap<String, u64>,
|
||||
syscall_count: HashMap<String, u64>,
|
||||
cargo_deps: usize,
|
||||
exec_time: HashMap<String, HashMap<String, f64>>,
|
||||
binary_size: HashMap<String, u64>,
|
||||
max_memory: HashMap<String, u64>,
|
||||
thread_count: HashMap<String, u64>,
|
||||
syscall_count: HashMap<String, u64>,
|
||||
cargo_deps: usize,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let wry_data = root_dir().join("gh-pages").join("wry-data.json");
|
||||
let wry_recent = root_dir().join("gh-pages").join("wry-recent.json");
|
||||
let electron_data = root_dir().join("gh-pages").join("electron-data.json");
|
||||
let electron_recent = root_dir().join("gh-pages").join("electron-recent.json");
|
||||
|
||||
// current data
|
||||
let current_data_buffer = BufReader::new(
|
||||
File::open(target_dir().join("bench.json")).expect("Unable to read current data file"),
|
||||
);
|
||||
let current_data: BenchResult =
|
||||
serde_json::from_reader(current_data_buffer).expect("Unable to read current data buffer");
|
||||
// current data
|
||||
let current_data_buffer = BufReader::new(
|
||||
File::open(target_dir().join("bench.json")).expect("Unable to read current data file"),
|
||||
);
|
||||
let current_data: BenchResult =
|
||||
serde_json::from_reader(current_data_buffer).expect("Unable to read current data buffer");
|
||||
|
||||
// all data's
|
||||
let all_data_buffer =
|
||||
BufReader::new(File::open(&wry_data).expect("Unable to read all data file"));
|
||||
let mut all_data: Vec<BenchResult> =
|
||||
serde_json::from_reader(all_data_buffer).expect("Unable to read all data buffer");
|
||||
// all data's
|
||||
let all_data_buffer =
|
||||
BufReader::new(File::open(&electron_data).expect("Unable to read all data file"));
|
||||
let mut all_data: Vec<BenchResult> =
|
||||
serde_json::from_reader(all_data_buffer).expect("Unable to read all data buffer");
|
||||
|
||||
// add current data to alls data
|
||||
all_data.push(current_data);
|
||||
// add current data to alls data
|
||||
all_data.push(current_data);
|
||||
|
||||
// use only latest 20 elements from alls data
|
||||
let recent: Vec<BenchResult>;
|
||||
if all_data.len() > 20 {
|
||||
recent = all_data[all_data.len() - 20..].to_vec();
|
||||
} else {
|
||||
recent = all_data.clone();
|
||||
}
|
||||
// use only latest 20 elements from alls data
|
||||
let recent: Vec<BenchResult>;
|
||||
if all_data.len() > 20 {
|
||||
recent = all_data[all_data.len() - 20..].to_vec();
|
||||
} else {
|
||||
recent = all_data.clone();
|
||||
}
|
||||
|
||||
write_json(
|
||||
&wry_data,
|
||||
&serde_json::to_value(&all_data).expect("Unable to build final json (alls)"),
|
||||
);
|
||||
write_json(
|
||||
&wry_recent,
|
||||
&serde_json::to_value(&recent).expect("Unable to build final json (recent)"),
|
||||
);
|
||||
write_json(
|
||||
&electron_data,
|
||||
&serde_json::to_value(&all_data).expect("Unable to build final json (alls)"),
|
||||
);
|
||||
write_json(
|
||||
&electron_recent,
|
||||
&serde_json::to_value(&recent).expect("Unable to build final json (recent)"),
|
||||
);
|
||||
}
|
||||
|
||||
186
bench/utils.rs
186
bench/utils.rs
@@ -4,123 +4,123 @@
|
||||
|
||||
use serde::Serialize;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
path::PathBuf,
|
||||
process::{Command, Output, Stdio},
|
||||
collections::HashMap,
|
||||
path::PathBuf,
|
||||
process::{Command, Output, Stdio},
|
||||
};
|
||||
|
||||
pub fn root_path() -> PathBuf {
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
}
|
||||
|
||||
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)
|
||||
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)
|
||||
}
|
||||
|
||||
pub fn parse_max_mem(output: &str) -> Option<u64> {
|
||||
// Takes the output from "time -v" as input and extracts the 'maximum
|
||||
// resident set size' and returns it in bytes.
|
||||
for line in output.lines() {
|
||||
if line
|
||||
.to_lowercase()
|
||||
.contains("maximum resident set size (kbytes)")
|
||||
{
|
||||
let value = line.split(": ").nth(1).unwrap();
|
||||
return Some(str::parse::<u64>(value).unwrap() * 1024);
|
||||
// Takes the output from "time -v" as input and extracts the 'maximum
|
||||
// resident set size' and returns it in bytes.
|
||||
for line in output.lines() {
|
||||
if line
|
||||
.to_lowercase()
|
||||
.contains("maximum resident set size (kbytes)")
|
||||
{
|
||||
let value = line.split(": ").nth(1).unwrap();
|
||||
return Some(str::parse::<u64>(value).unwrap() * 1024);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
None
|
||||
}
|
||||
|
||||
#[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 percent_time: f64,
|
||||
pub seconds: f64,
|
||||
pub usecs_per_call: Option<u64>,
|
||||
pub calls: u64,
|
||||
pub errors: u64,
|
||||
}
|
||||
|
||||
pub fn parse_strace_output(output: &str) -> HashMap<String, StraceOutput> {
|
||||
let mut summary = HashMap::new();
|
||||
let mut summary = HashMap::new();
|
||||
|
||||
let mut lines = output
|
||||
.lines()
|
||||
.filter(|line| !line.is_empty() && !line.contains("detached ..."));
|
||||
let count = lines.clone().count();
|
||||
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()
|
||||
},
|
||||
},
|
||||
);
|
||||
if count < 4 {
|
||||
return summary;
|
||||
}
|
||||
}
|
||||
|
||||
let total_fields = total_line.split_whitespace().collect::<Vec<_>>();
|
||||
summary.insert(
|
||||
"total".to_string(),
|
||||
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(),
|
||||
},
|
||||
);
|
||||
let total_line = lines.next_back().unwrap();
|
||||
lines.next_back(); // Drop separator
|
||||
let data_lines = lines.skip(2);
|
||||
|
||||
summary
|
||||
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(),
|
||||
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(),
|
||||
},
|
||||
);
|
||||
|
||||
summary
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,10 @@ const createWindow = () => {
|
||||
height: 600,
|
||||
});
|
||||
mainWindow.webContents.on("dom-ready", () => {
|
||||
app.quit();
|
||||
// without interval it panic?
|
||||
setInterval(() => {
|
||||
app.quit();
|
||||
}, 1);
|
||||
});
|
||||
mainWindow.loadFile(path.join(__dirname, "index.html"));
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user