utils.rs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. // Copyright 2019-2023 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. use anyhow::Result;
  5. use serde::{Deserialize, Serialize};
  6. use serde_json::Value;
  7. use std::{
  8. collections::HashMap,
  9. fs,
  10. io::{BufRead, BufReader},
  11. path::PathBuf,
  12. process::{Command, Output, Stdio},
  13. };
  14. #[derive(Default, Clone, Serialize, Deserialize, Debug)]
  15. pub struct BenchResult {
  16. pub created_at: String,
  17. pub sha1: String,
  18. pub exec_time: HashMap<String, HashMap<String, f64>>,
  19. pub binary_size: HashMap<String, u64>,
  20. pub max_memory: HashMap<String, u64>,
  21. pub thread_count: HashMap<String, u64>,
  22. pub syscall_count: HashMap<String, u64>,
  23. pub cargo_deps: HashMap<String, usize>,
  24. }
  25. #[allow(dead_code)]
  26. #[derive(Debug, Clone, Serialize)]
  27. pub struct StraceOutput {
  28. pub percent_time: f64,
  29. pub seconds: f64,
  30. pub usecs_per_call: Option<u64>,
  31. pub calls: u64,
  32. pub errors: u64,
  33. }
  34. pub fn get_target() -> &'static str {
  35. #[cfg(target_os = "macos")]
  36. return "x86_64-apple-darwin";
  37. #[cfg(target_os = "linux")]
  38. return "x86_64-unknown-linux-gnu";
  39. #[cfg(target_os = "windows")]
  40. unimplemented!();
  41. }
  42. pub fn target_dir() -> PathBuf {
  43. bench_root_path()
  44. .join("tests")
  45. .join("target")
  46. .join(get_target())
  47. .join("release")
  48. }
  49. pub fn bench_root_path() -> PathBuf {
  50. PathBuf::from(env!("CARGO_MANIFEST_DIR"))
  51. }
  52. #[allow(dead_code)]
  53. pub fn home_path() -> PathBuf {
  54. #[cfg(any(target_os = "macos", target_os = "linux"))]
  55. return PathBuf::from(env!("HOME"));
  56. #[cfg(any(target_os = "windows"))]
  57. return PathBuf::from(env!("HOMEPATH"));
  58. }
  59. #[allow(dead_code)]
  60. pub fn tauri_root_path() -> PathBuf {
  61. bench_root_path()
  62. .parent()
  63. .unwrap()
  64. .parent()
  65. .unwrap()
  66. .to_path_buf()
  67. }
  68. #[allow(dead_code)]
  69. pub fn run_collect(cmd: &[&str]) -> (String, String) {
  70. let mut process_builder = Command::new(cmd[0]);
  71. process_builder
  72. .args(&cmd[1..])
  73. .stdin(Stdio::piped())
  74. .stdout(Stdio::piped())
  75. .stderr(Stdio::piped());
  76. let prog = process_builder.spawn().expect("failed to spawn script");
  77. let Output {
  78. stdout,
  79. stderr,
  80. status,
  81. } = prog.wait_with_output().expect("failed to wait on child");
  82. let stdout = String::from_utf8(stdout).unwrap();
  83. let stderr = String::from_utf8(stderr).unwrap();
  84. if !status.success() {
  85. eprintln!("stdout: <<<{}>>>", stdout);
  86. eprintln!("stderr: <<<{}>>>", stderr);
  87. panic!("Unexpected exit code: {:?}", status.code());
  88. }
  89. (stdout, stderr)
  90. }
  91. #[allow(dead_code)]
  92. pub fn parse_max_mem(file_path: &str) -> Option<u64> {
  93. let file = fs::File::open(file_path).unwrap();
  94. let output = BufReader::new(file);
  95. let mut highest: u64 = 0;
  96. // MEM 203.437500 1621617192.4123
  97. for line in output.lines().flatten() {
  98. // split line by space
  99. let split = line.split(' ').collect::<Vec<_>>();
  100. if split.len() == 3 {
  101. // mprof generate result in MB
  102. let current_bytes = str::parse::<f64>(split[1]).unwrap() as u64 * 1024 * 1024;
  103. if current_bytes > highest {
  104. highest = current_bytes;
  105. }
  106. }
  107. }
  108. fs::remove_file(file_path).unwrap();
  109. if highest > 0 {
  110. return Some(highest);
  111. }
  112. None
  113. }
  114. #[allow(dead_code)]
  115. pub fn parse_strace_output(output: &str) -> HashMap<String, StraceOutput> {
  116. let mut summary = HashMap::new();
  117. let mut lines = output
  118. .lines()
  119. .filter(|line| !line.is_empty() && !line.contains("detached ..."));
  120. let count = lines.clone().count();
  121. if count < 4 {
  122. return summary;
  123. }
  124. let total_line = lines.next_back().unwrap();
  125. lines.next_back(); // Drop separator
  126. let data_lines = lines.skip(2);
  127. for line in data_lines {
  128. let syscall_fields = line.split_whitespace().collect::<Vec<_>>();
  129. let len = syscall_fields.len();
  130. let syscall_name = syscall_fields.last().unwrap();
  131. if (5..=6).contains(&len) {
  132. summary.insert(
  133. syscall_name.to_string(),
  134. StraceOutput {
  135. percent_time: str::parse::<f64>(syscall_fields[0]).unwrap(),
  136. seconds: str::parse::<f64>(syscall_fields[1]).unwrap(),
  137. usecs_per_call: Some(str::parse::<u64>(syscall_fields[2]).unwrap()),
  138. calls: str::parse::<u64>(syscall_fields[3]).unwrap(),
  139. errors: if syscall_fields.len() < 6 {
  140. 0
  141. } else {
  142. str::parse::<u64>(syscall_fields[4]).unwrap()
  143. },
  144. },
  145. );
  146. }
  147. }
  148. let total_fields = total_line.split_whitespace().collect::<Vec<_>>();
  149. summary.insert(
  150. "total".to_string(),
  151. match total_fields.len() {
  152. // Old format, has no usecs/call
  153. 5 => StraceOutput {
  154. percent_time: str::parse::<f64>(total_fields[0]).unwrap(),
  155. seconds: str::parse::<f64>(total_fields[1]).unwrap(),
  156. usecs_per_call: None,
  157. calls: str::parse::<u64>(total_fields[2]).unwrap(),
  158. errors: str::parse::<u64>(total_fields[3]).unwrap(),
  159. },
  160. 6 => StraceOutput {
  161. percent_time: str::parse::<f64>(total_fields[0]).unwrap(),
  162. seconds: str::parse::<f64>(total_fields[1]).unwrap(),
  163. usecs_per_call: Some(str::parse::<u64>(total_fields[2]).unwrap()),
  164. calls: str::parse::<u64>(total_fields[3]).unwrap(),
  165. errors: str::parse::<u64>(total_fields[4]).unwrap(),
  166. },
  167. _ => panic!("Unexpected total field count: {}", total_fields.len()),
  168. },
  169. );
  170. summary
  171. }
  172. #[allow(dead_code)]
  173. pub fn run(cmd: &[&str]) {
  174. let mut process_builder = Command::new(cmd[0]);
  175. process_builder.args(&cmd[1..]).stdin(Stdio::piped());
  176. let mut prog = process_builder.spawn().expect("failed to spawn script");
  177. let status = prog.wait().expect("failed to wait on child");
  178. if !status.success() {
  179. panic!("Unexpected exit code: {:?}", status.code());
  180. }
  181. }
  182. #[allow(dead_code)]
  183. pub fn read_json(filename: &str) -> Result<Value> {
  184. let f = fs::File::open(filename)?;
  185. Ok(serde_json::from_reader(f)?)
  186. }
  187. #[allow(dead_code)]
  188. pub fn write_json(filename: &str, value: &Value) -> Result<()> {
  189. let f = fs::File::create(filename)?;
  190. serde_json::to_writer(f, value)?;
  191. Ok(())
  192. }
  193. #[allow(dead_code)]
  194. pub fn download_file(url: &str, filename: PathBuf) {
  195. if !url.starts_with("http:") && !url.starts_with("https:") {
  196. fs::copy(url, filename).unwrap();
  197. return;
  198. }
  199. // Downloading with curl this saves us from adding
  200. // a Rust HTTP client dependency.
  201. println!("Downloading {}", url);
  202. let status = Command::new("curl")
  203. .arg("-L")
  204. .arg("-s")
  205. .arg("-o")
  206. .arg(&filename)
  207. .arg(url)
  208. .status()
  209. .unwrap();
  210. assert!(status.success());
  211. assert!(filename.exists());
  212. }