diff --git a/Cargo.toml b/Cargo.toml index b765587a..f54419d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,6 +80,11 @@ name = "inferno-collapse-vtune" path = "src/bin/collapse-vtune.rs" required-features = ["cli"] +[[bin]] +name = "inferno-collapse-pmc" +path = "src/bin/collapse-pmc.rs" +required-features = ["cli"] + [[bin]] name = "inferno-collapse-guess" path = "src/bin/collapse-guess.rs" diff --git a/benches/collapse.rs b/benches/collapse.rs index 716accd9..60f00b8c 100644 --- a/benches/collapse.rs +++ b/benches/collapse.rs @@ -2,13 +2,14 @@ use std::fs::File; use std::io::{self, Read}; use criterion::*; -use inferno::collapse::{dtrace, perf, sample, Collapse}; +use inferno::collapse::{dtrace, perf, pmc, sample, Collapse}; use lazy_static::lazy_static; use libflate::gzip::Decoder; const INFILE_DTRACE: &str = "flamegraph/example-dtrace-stacks.txt"; const INFILE_PERF: &str = "flamegraph/example-perf-stacks.txt.gz"; const INFILE_SAMPLE: &str = "tests/data/collapse-sample/large.txt.gz"; +const INFILE_PMC: &str = "tests/data/collapse-pmc/large.txt.gz"; const SAMPLE_SIZE: usize = 100; lazy_static! { @@ -96,7 +97,8 @@ macro_rules! benchmark_multi { benchmark_multi!(dtrace, "dtrace", INFILE_DTRACE); benchmark_multi!(perf, "perf", INFILE_PERF); benchmark_single!(sample, "sample", INFILE_SAMPLE); +benchmark_multi!(pmc, "pmc", INFILE_PMC); -criterion_group!(benches, dtrace, perf, sample); +criterion_group!(benches, dtrace, perf, pmc, sample); criterion_main!(benches); diff --git a/compare.sh b/compare.sh index ddabc208..f5ed5430 100755 --- a/compare.sh +++ b/compare.sh @@ -28,3 +28,12 @@ zcat < tests/data/collapse-sample/large.txt.gz > "$f" echo "==> sample <==" hyperfine --warmup 20 -m 50 "$BIN/inferno-collapse-sample $f" "./flamegraph/stackcollapse-sample.awk $f" rm "$f" + +echo +echo +cargo build --release --bin inferno-collapse-pmc +f=tests/data/collapse-pmc/large.txt +zcat < tests/data/collapse-pmc/large.txt.gz > "$f" +echo "==> pmc <==" +hyperfine --warmup 20 -m 50 "$BIN/inferno-collapse-pmc $f" "./flamegraph/stackcollapse-pmc.pl $f" +rm "$f" diff --git a/src/bin/collapse-pmc.rs b/src/bin/collapse-pmc.rs new file mode 100644 index 00000000..39a0eef6 --- /dev/null +++ b/src/bin/collapse-pmc.rs @@ -0,0 +1,86 @@ +use std::io; +use std::path::PathBuf; + +use env_logger::Env; +use inferno::collapse::pmc::{Folder, Options}; +use inferno::collapse::{Collapse, DEFAULT_NTHREADS}; +use lazy_static::lazy_static; +use structopt::StructOpt; + +lazy_static! { + static ref NTHREADS: String = format!("{}", *DEFAULT_NTHREADS); +} + +#[derive(Debug, StructOpt)] +#[structopt( + name = "inferno-collapse-pmc", + about, + after_help = "\ +[1] pmcstat must be used in callchain mode (-G). + For example: + To capture, use: + pmcstat -S unhalted-cycles -O pmc.out + To convert to callchain, you can use: + pmcstat -R pmc.out -z16 -G pmc.graph + Then collapse all stacks to flamegraph format + inferno-collapse-pmc pmc.graph +" +)] +struct Opt { + // ************* // + // *** FLAGS *** // + // ************* // + /// Silence all log output + #[structopt(short = "q", long = "quiet")] + quiet: bool, + + /// Verbose logging mode (-v, -vv, -vvv) + #[structopt(short = "v", long = "verbose", parse(from_occurrences))] + verbose: usize, + + // *************** // + // *** OPTIONS *** // + // *************** // + /// Number of threads to use + #[structopt( + short = "n", + long = "nthreads", + default_value = &NTHREADS, + value_name = "UINT" + )] + nthreads: usize, + + // ************ // + // *** ARGS *** // + // ************ // + #[structopt(value_name = "PATH")] + /// Pmcstat -G output file, or STDIN if not specified + infile: Option, +} + +impl Opt { + fn into_parts(self) -> (Option, Options) { + let mut options = Options::default(); + options.nthreads = self.nthreads; + (self.infile, options) + } +} + +fn main() -> io::Result<()> { + let opt = Opt::from_args(); + + // Initialize logger + if !opt.quiet { + env_logger::Builder::from_env(Env::default().default_filter_or(match opt.verbose { + 0 => "warn", + 1 => "info", + 2 => "debug", + _ => "trace", + })) + .format_timestamp(None) + .init(); + } + + let (infile, options) = opt.into_parts(); + Folder::from(options).collapse_file_to_stdout(infile.as_ref()) +} diff --git a/src/collapse/guess.rs b/src/collapse/guess.rs index c28526e9..06981c63 100644 --- a/src/collapse/guess.rs +++ b/src/collapse/guess.rs @@ -3,7 +3,7 @@ use std::io::{self, Cursor}; use log::{error, info}; -use crate::collapse::{self, dtrace, perf, sample, vtune, Collapse}; +use crate::collapse::{self, dtrace, perf, pmc, sample, vtune, Collapse}; const LINES_PER_ITERATION: usize = 10; @@ -69,10 +69,17 @@ impl Collapse for Folder { }; let mut sample = sample::Folder::default(); let mut vtune = vtune::Folder::default(); + let mut pmc = { + let options = pmc::Options { + nthreads: self.opt.nthreads, + ..Default::default() + }; + pmc::Folder::from(options) + }; // Each Collapse impl gets its own flag in this array. // It gets set to true when the impl has been ruled out. - let mut not_applicable = [false; 4]; + let mut not_applicable = [false; 5]; let mut buffer = String::new(); loop { @@ -106,6 +113,7 @@ impl Collapse for Folder { try_collapse_impl!(dtrace, 1); try_collapse_impl!(sample, 2); try_collapse_impl!(vtune, 3); + try_collapse_impl!(pmc, 4); if eof { break; diff --git a/src/collapse/mod.rs b/src/collapse/mod.rs index 57d065fa..a4e2675c 100644 --- a/src/collapse/mod.rs +++ b/src/collapse/mod.rs @@ -35,6 +35,13 @@ pub mod sample; /// [crate-level documentation]: ../../index.html pub mod vtune; +/// Stack collapsing for the output of [`pmcstat`](https://www.freebsd.org/cgi/man.cgi?query=pmcstat&sektion=8) in callchain mode (-G). +/// +/// See the [crate-level documentation] for details. +/// +/// [crate-level documentation]: ../../index.html +pub mod pmc; + // DEFAULT_NTHREADS is public because we use it in the help text of the binaries, // but it doesn't need to be exposed to library users, hence #[doc(hidden)]. #[doc(hidden)] diff --git a/src/collapse/pmc.rs b/src/collapse/pmc.rs new file mode 100644 index 00000000..44fff835 --- /dev/null +++ b/src/collapse/pmc.rs @@ -0,0 +1,463 @@ +use std::collections::VecDeque; +use std::io::{self, BufRead}; + +use crate::collapse::common::{self, CollapsePrivate, Occurrences}; + +mod logging { + use log::warn; + + pub(super) fn weird_stack_line(line: &str) { + warn!("Weird stack line: {}", line); + } +} + +/// `pmcstat` folder configuration options. +#[derive(Clone, Debug)] +#[non_exhaustive] +pub struct Options { + /// The number of threads to use. + /// + /// Default is the number of logical cores on your machine. + pub nthreads: usize, +} + +impl Default for Options { + fn default() -> Self { + Self { + nthreads: *common::DEFAULT_NTHREADS, + } + } +} + +/// A stack collapser for the output of `pmcstat -G` (callchain mode). +/// +/// To construct one, either use `pmc::Folder::default()` or create an [`Options`] and use +/// `pmc::Folder::from(options)`. +pub struct Folder { + // State... + /// The number of stacks per job to send to the threadpool. + nstacks_per_job: usize, + + /// Function entries on the stack in this entry thus far. + stack: VecDeque, + + /// Number of leading spaces (i.e. stack depth) found on last stack line + indent: Option, + + /// The called count found on last processed stack line + count: Option, + + // Options... + opt: Options, +} + +impl From for Folder { + fn from(mut opt: Options) -> Self { + if opt.nthreads == 0 { + opt.nthreads = 1; + } + Self { + nstacks_per_job: common::DEFAULT_NSTACKS_PER_JOB, + stack: VecDeque::default(), + indent: None, + count: None, + opt, + } + } +} + +impl Default for Folder { + fn default() -> Self { + Options::default().into() + } +} + +impl CollapsePrivate for Folder { + fn pre_process(&mut self, _reader: &mut R, _occurrences: &mut Occurrences) -> io::Result<()> + where + R: io::BufRead, + { + // We do not need any special pre-processing + Ok(()) + } + + fn collapse_single_threaded( + &mut self, + mut reader: R, + occurrences: &mut Occurrences, + ) -> io::Result<()> + where + R: io::BufRead, + { + // While there are still stacks left to process, process them... + let mut line_buffer = Vec::new(); + while !self.process_single_stack(&mut line_buffer, &mut reader, occurrences)? {} + + // Reset state... + self.stack.clear(); + self.indent = None; + self.count = None; + Ok(()) + } + + /// Determine if this format corresponds to the input data. + fn is_applicable(&mut self, input: &str) -> Option { + // First line is always of the form + // @ [ bool { + line.is_empty() + } + + fn clone_and_reset_stack_context(&self) -> Self { + Self { + nstacks_per_job: self.nstacks_per_job, + stack: VecDeque::default(), + indent: None, + count: None, + opt: self.opt.clone(), + } + } + + fn nstacks_per_job(&self) -> usize { + self.nstacks_per_job + } + + fn set_nstacks_per_job(&mut self, n: usize) { + self.nstacks_per_job = n; + } + + fn nthreads(&self) -> usize { + self.opt.nthreads + } + + fn set_nthreads(&mut self, n: usize) { + self.opt.nthreads = n; + } +} + +impl Folder { + /// Processes a stack. On success, returns `true` if at end of data; `false` otherwise. + fn process_single_stack( + &mut self, + line_buffer: &mut Vec, + reader: &mut R, + occurrences: &mut Occurrences, + ) -> io::Result + where + R: io::BufRead, + { + loop { + line_buffer.clear(); + if reader.read_until(0x0A, line_buffer)? == 0 { + if !self.stack.is_empty() { + self.after_stack(occurrences); + } + return Ok(true); + } + let line = String::from_utf8_lossy(line_buffer); + if line.starts_with('@') { + continue; + } + let line = line.trim_end(); + if line.is_empty() { + self.after_stack(occurrences); + return Ok(false); + } else { + self.on_stack_line(line, occurrences); + } + } + } + + /// Parse a stack line and extract and validate some fields + // We extract: + // - the size of the leading spaces + // - the percentage value (not used for now but just in case it is needed later) + // - the count between [ ] + // - the function name + // + // We ignore the module part "@ xxx" which is optionnaly present at the end of the line + // + // Ex: + // 08.91% [1318] acpi_cpu_c1 @ /boot/kernel/kernel + // 100.0% [1318] acpi_cpu_idle + // 100.0% [1318] cpu_idle_acpi + fn stack_line_parts(line: &str) -> Option<(usize, &str, usize, &str)> { + // count leading spaces + let indent = line.chars().position(|c| !c.is_whitespace()).unwrap_or(0); + + // Ex: " 54.00% [27] kern_clock_nanosleep" + let mut words = line[indent..].split_whitespace(); // TODO check performance vs multiple splitn() + + let percent = words.next(); + let count = words.next(); + let function = words.next(); + + match (percent, count, function) { + (Some(percent), Some(count), Some(function)) => { + // minimal validation '0%' -> 'x.y%' -> '100.0%' + let lpercent = percent.len(); + if lpercent >= 2 && percent.ends_with('%') { + // minimal validation '[numbers]' + let lcount = count.len(); + if lcount >= 3 && count.starts_with('[') && count.ends_with(']') { + if let Ok(count) = count[1..lcount - 1].parse() { + return Some((indent, &percent[..lpercent - 1], count, function)); + } + } + } + } + (_, _, _) => { + println!("not enough words, ignoring line"); + } + } + + None + } + + // we have a stack line that shows one stack entry from the preceeding event, like: + // + // 08.91% [1318] acpi_cpu_c1 @ /boot/kernel/kernel + // 100.0% [1318] acpi_cpu_idle + // 100.0% [1318] cpu_idle_acpi + // 100.0% [1318] cpu_idle + // 100.0% [1318] sched_idletd + // 100.0% [1318] fork_exit + fn on_stack_line(&mut self, line: &str, occurrences: &mut Occurrences) { + let parts = Self::stack_line_parts(line); + match parts { + Some((indent, _, count, function)) => { + // detect shared stacks, i.e. stacks that share some elements + // for example: + // + // 01.17% [173] randomdev_encrypt @ /boot/kernel/kernel + // 95.95% [166] random_fortuna_read + // 100.0% [166] read_random_uio + // 100.0% [166] devfs_read_f + // 100.0% [166] kern_readv + // 100.0% [166] sys_read + // 100.0% [166] amd64_syscall + // 04.05% [7] read_random_uio + // 100.0% [7] devfs_read_f + // 100.0% [7] kern_readv + // 100.0% [7] sys_read + // 100.0% [7] amd64_syscall + // + // Or (a more complex one) + // + // 00.31% [2] 0xf4ae3 @ /lib/libc.so.7 + // 50.00% [1] 0x53c2f @ /usr/lib/libprivatessh.so.5 + // 50.00% [1] 0x3b25deb @ /usr/bin/clang-cpp + // 100.0% [1] 0x48c282e + // 100.0% [1] 0x48c73c8 + if self.indent.is_some() && indent <= self.indent.unwrap_or(0) { + // allocate a string that is long enough to hold the entire stack string + let mut stack_str = + String::with_capacity(self.stack.iter().fold(0, |a, s| a + s.len() + 1)); + + // add the stack entries + let mut first = true; + for e in self.stack.iter() { + if !first { + stack_str.push(';'); + } else { + first = false; + } + stack_str.push_str(e); + } + + // count it! + assert!(self.count.is_some()); + occurrences.insert_or_add( + stack_str, + self.count.expect("count not found on previous line"), + ); + + // pop as many element as needed to prepare for the next shared stack + self.stack.drain(..self.stack.len() - indent); + } + + self.indent = Some(indent); + self.count = Some(count); + + // TODO annotate kernel functions with a `_[k]` suffix. + // TODO filter raw addresses + // TODO demangle C++ / Rust symbols + self.stack.push_front(function.to_string()); + } + None => { + logging::weird_stack_line(line); + } + } + } + + fn after_stack(&mut self, occurrences: &mut Occurrences) { + // end of stack, so emit stack entry + if !self.stack.is_empty() { + // allocate a string that is long enough to hold the entire stack string + let mut stack_str = + String::with_capacity(self.stack.iter().fold(0, |a, s| a + s.len() + 1)); + + // add the stack entries (if any) + let mut first = true; + for e in self.stack.drain(..) { + if !first { + stack_str.push(';'); + } else { + first = false; + } + stack_str.push_str(&e); + } + + // count it! + assert!(self.count.is_some()); + occurrences.insert_or_add( + stack_str, + self.count.expect("count not found on previous line"), + ); + } + + // reset for next stack + self.stack.clear(); + self.indent = None; + self.count = None; + } +} + +#[cfg(test)] +mod tests { + use std::fs; + use std::io::Read; + use std::path::PathBuf; + + use lazy_static::lazy_static; + use pretty_assertions::assert_eq; + use rand::prelude::*; + + use super::*; + use crate::collapse::common; + use crate::collapse::Collapse; + + lazy_static! { + static ref INPUT: Vec = { + [ + "./tests/data/collapse-pmc/simple.txt", + "./tests/data/collapse-pmc/shared.txt", + "./tests/data/collapse-pmc/shared2.txt", + "./tests/data/collapse-pmc/dd.txt", + "./tests/data/collapse-pmc/iperf3.txt", + // "./tests/data/collapse-pmc/large.txt.gz", + ] + .iter() + .map(PathBuf::from) + .collect::>() + }; + } + + #[test] + fn test_collapse_multi_pmc() -> io::Result<()> { + let mut folder = Folder::default(); + common::testing::test_collapse_multi(&mut folder, &INPUT) + } + + #[test] + fn test_collapse_multi_pmc_simple() -> io::Result<()> { + let path = "./tests/data/collapse-pmc/simple.txt"; + let mut file = fs::File::open(path)?; + let mut bytes = Vec::new(); + file.read_to_end(&mut bytes)?; + let mut folder = Folder::default(); + ::collapse(&mut folder, &bytes[..], io::sink()) + } + + /// Varies the nstacks_per_job parameter and outputs the 10 fastests configurations by file. + /// + /// Command: `cargo test bench_nstacks_pmc --release -- --ignored --nocapture` + #[test] + #[ignore] + fn bench_nstacks_pmc() -> io::Result<()> { + let mut folder = Folder::default(); + common::testing::bench_nstacks(&mut folder, &INPUT) + } + + #[test] + #[ignore] + /// Fuzz test the multithreaded collapser. + /// + /// Command: `cargo test fuzz_collapse_pmc --release -- --ignored --nocapture` + fn fuzz_collapse_pmc() -> io::Result<()> { + let seed = thread_rng().gen::(); + println!("Random seed: {}", seed); + let mut rng = SmallRng::seed_from_u64(seed); + + let mut buf_actual = Vec::new(); + let mut buf_expected = Vec::new(); + let mut count = 0; + + let inputs = common::testing::read_inputs(&INPUT)?; + + loop { + let nstacks_per_job = rng.gen_range(1..=500); + let options = Options { + nthreads: rng.gen_range(2..=32), + }; + + for (path, input) in inputs.iter() { + buf_actual.clear(); + buf_expected.clear(); + + let mut folder = { + let mut options = options.clone(); + options.nthreads = 1; + Folder::from(options) + }; + folder.nstacks_per_job = nstacks_per_job; + ::collapse(&mut folder, &input[..], &mut buf_expected)?; + let expected = std::str::from_utf8(&buf_expected[..]).unwrap(); + + let mut folder = Folder::from(options.clone()); + folder.nstacks_per_job = nstacks_per_job; + ::collapse(&mut folder, &input[..], &mut buf_actual)?; + let actual = std::str::from_utf8(&buf_actual[..]).unwrap(); + + if actual != expected { + eprintln!( + "Failed on file: {}\noptions: {:#?}\n", + path.display(), + options + ); + assert_eq!(actual, expected); + } + } + + count += 1; + if count % 10 == 0 { + println!("Successfully ran {} fuzz tests.", count); + } + } + } +} diff --git a/tests/collapse-guess.rs b/tests/collapse-guess.rs index c545d525..5c50be8e 100644 --- a/tests/collapse-guess.rs +++ b/tests/collapse-guess.rs @@ -85,6 +85,13 @@ fn collapse_guess_vtune() { test_collapse_guess(test_file, result_file, false).unwrap() } +#[test] +fn collapse_guess_pmc() { + let test_file = "./tests/data/collapse-pmc/dd.txt"; + let result_file = "./tests/data/collapse-pmc/results/dd-collapsed.txt"; + test_collapse_guess(test_file, result_file, false).unwrap() +} + #[test] fn collapse_guess_unknown_format_should_log_error() { test_collapse_guess_logs( diff --git a/tests/collapse-pmc.rs b/tests/collapse-pmc.rs new file mode 100644 index 00000000..91a00bde --- /dev/null +++ b/tests/collapse-pmc.rs @@ -0,0 +1,184 @@ +mod common; + +use std::fs::File; +use std::io::{self, BufReader, Cursor}; +use std::path::Path; +use std::process::{Command, Stdio}; + +use assert_cmd::cargo::CommandCargoExt; +use inferno::collapse::pmc::{Folder, Options}; +use log::Level; +use pretty_assertions::assert_eq; +use testing_logger::CapturedLog; + +fn test_collapse_pmc( + test_file: &str, + expected_file: &str, + options: Options, + strip_quotes: bool, +) -> io::Result<()> { + for &n in &[1, 2] { + let mut options = options.clone(); + options.nthreads = n; + common::test_collapse( + Folder::from(options), + test_file, + expected_file, + strip_quotes, + )?; + } + Ok(()) +} + +fn test_collapse_pmc_logs_with_options(input_file: &str, asserter: F, mut options: Options) +where + F: Fn(&Vec), +{ + // We must run log tests in a single thread to play nicely with `testing_logger`. + options.nthreads = 1; + common::test_collapse_logs(Folder::from(options), input_file, asserter); +} + +fn test_collapse_pmc_logs(input_file: &str, asserter: F) +where + F: Fn(&Vec), +{ + test_collapse_pmc_logs_with_options(input_file, asserter, Options::default()); +} + +// Create tests for test files in $dir. The test files are used as input +// and the results are compared to result files in the results sub directory. +// The test and result file names are derived from $name. +macro_rules! collapse_pmc_tests_inner { + ($($name:ident),*; $dir:expr; $results_dir:expr; $strip_prefix:expr, $strip_quotes:expr) => { + $( + #[test] + #[allow(non_snake_case)] + fn $name() { + let mut test_name = stringify!($name); + if test_name.starts_with($strip_prefix) { + test_name = &test_name[$strip_prefix.len()..]; + } + let test_file_stem = test_name.replace("_", "-"); + + let test_file = format!("{}.txt", test_file_stem); + let result_file = format!("{}-collapsed.txt", test_file_stem); + + let test_path = Path::new($dir); + let results_path = Path::new($results_dir); + + test_collapse_pmc( + test_path.join(test_file).to_str().unwrap(), + results_path.join(result_file).to_str().unwrap(), + Options::default(), + $strip_quotes + ).unwrap() + } + )* + } +} + +macro_rules! collapse_pmc_tests { + ($($name:ident),*) => { + collapse_pmc_tests_inner!($($name),*; "./tests/data/collapse-pmc"; "./tests/data/collapse-pmc/results"; "collapse_pmc_", false); + } +} + +collapse_pmc_tests! { + collapse_pmc_simple, + collapse_pmc_shared, + collapse_pmc_shared2, + collapse_pmc_dd, + collapse_pmc_iperf3 +} + +#[test] +fn collapse_pmc_should_warn_about_weird_input_lines_bad_percent() { + test_collapse_pmc_logs( + "./tests/data/collapse-pmc/weird-stack-line-bad-percent.txt", + |captured_logs| { + let nwarnings = captured_logs + .iter() + .filter(|log| { + log.body.starts_with("Weird stack line: ") && log.level == Level::Warn + }) + .count(); + assert_eq!( + nwarnings, 1, + "bad lines warning logged {} times, but should be logged exactly once", + nwarnings + ); + }, + ); +} + +#[test] +fn collapse_pmc_should_warn_about_weird_input_lines_bad_count() { + // bad count + test_collapse_pmc_logs( + "./tests/data/collapse-pmc/weird-stack-line-bad-count.txt", + |captured_logs| { + let nwarnings = captured_logs + .iter() + .filter(|log| { + log.body.starts_with("Weird stack line: ") && log.level == Level::Warn + }) + .count(); + assert_eq!( + nwarnings, 1, + "bad lines warning logged {} times, but should be logged exactly once", + nwarnings + ); + }, + ); +} + +#[test] +fn collapse_pmc_should_warn_about_weird_input_lines_no_function_name() { + // no funcname + test_collapse_pmc_logs( + "./tests/data/collapse-pmc/weird-stack-line-no-function-name.txt", + |captured_logs| { + let nwarnings = captured_logs + .iter() + .filter(|log| { + log.body.starts_with("Weird stack line: ") && log.level == Level::Warn + }) + .count(); + assert_eq!( + nwarnings, 1, + "bad lines warning logged {} times, but should be logged exactly once", + nwarnings + ); + }, + ); +} + +#[test] +fn collapse_pmc_cli() { + let input_file = "./tests/data/collapse-pmc/dd.txt"; + let expected_file = "./tests/data/collapse-pmc/results/dd-collapsed.txt"; + + // Test with file passed in + let output = Command::cargo_bin("inferno-collapse-pmc") + .unwrap() + .arg(input_file) + .output() + .expect("failed to execute process"); + let expected = BufReader::new(File::open(expected_file).unwrap()); + common::compare_results(Cursor::new(output.stdout), expected, expected_file, true); + + // Test with STDIN + let mut child = Command::cargo_bin("inferno-collapse-pmc") + .unwrap() + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + .expect("Failed to spawn child process"); + let mut input = BufReader::new(File::open(input_file).unwrap()); + let stdin = child.stdin.as_mut().expect("Failed to open stdin"); + io::copy(&mut input, stdin).unwrap(); + let output = child.wait_with_output().expect("Failed to read stdout"); + let expected = BufReader::new(File::open(expected_file).unwrap()); + common::compare_results(Cursor::new(output.stdout), expected, expected_file, true); +} diff --git a/tests/data/collapse-pmc/dd.txt b/tests/data/collapse-pmc/dd.txt new file mode 100644 index 00000000..f9181205 --- /dev/null +++ b/tests/data/collapse-pmc/dd.txt @@ -0,0 +1,209 @@ +@ CLOCK.HARD [2 samples] + +100.0% [2] spinlock_exit @ /boot/kernel/kernel + 50.00% [1] sleepq_timedwait + 100.0% [1] _sleep + 100.0% [1] pmclog_loop @ /boot/kernel/hwpmc.ko + 100.0% [1] fork_exit @ /boot/kernel/kernel + 50.00% [1] cpu_activeclock + 100.0% [1] cpu_idle + 100.0% [1] sched_idletd + 100.0% [1] fork_exit + +@ CLOCK.HARD [1 samples] + +100.0% [1] spinlock_exit @ /boot/kernel/kernel + 100.0% [1] cpu_activeclock + 100.0% [1] cpu_idle + 100.0% [1] sched_idletd + 100.0% [1] fork_exit + +@ CLOCK.HARD [251 samples] + +89.64% [225] randomdev_keystream @ /boot/kernel/kernel + 100.0% [225] random_fortuna_genbytes + 100.0% [225] random_fortuna_read + 100.0% [225] read_random_uio + 100.0% [225] devfs_read_f + 100.0% [225] dofileread + 100.0% [225] sys_read + 100.0% [225] amd64_syscall + +02.79% [7] copyout_nosmap_std @ /boot/kernel/kernel + 100.0% [7] uiomove_faultflag + 100.0% [7] read_random_uio + 100.0% [7] devfs_read_f + 100.0% [7] dofileread + 100.0% [7] sys_read + 100.0% [7] amd64_syscall + +02.39% [6] memset_std @ /boot/kernel/kernel + 100.0% [6] explicit_bzero + 100.0% [6] zfree + 100.0% [6] read_random_uio + 100.0% [6] devfs_read_f + 100.0% [6] dofileread + 100.0% [6] sys_read + 100.0% [6] amd64_syscall + +01.59% [4] spinlock_exit @ /boot/kernel/kernel + 100.0% [4] pmap_update_pde + 50.00% [2] pmap_promote_pde + 100.0% [2] pmap_enter + 100.0% [2] kmem_back_domain + 100.0% [2] kmem_malloc_domainset + 100.0% [2] malloc_large + 100.0% [2] malloc + 100.0% [2] read_random_uio + 100.0% [2] devfs_read_f + 100.0% [2] dofileread + 100.0% [2] sys_read + 100.0% [2] amd64_syscall + 50.00% [2] pmap_demote_pde_locked + 100.0% [2] pmap_remove + 100.0% [2] _kmem_unback + 100.0% [2] kmem_free + 100.0% [2] zfree + 100.0% [2] read_random_uio + 100.0% [2] devfs_read_f + 100.0% [2] dofileread + 100.0% [2] sys_read + 100.0% [2] amd64_syscall + +01.20% [3] vm_radix_insert @ /boot/kernel/kernel + 100.0% [3] vm_page_alloc_domain_after + 100.0% [3] kmem_back_domain + 100.0% [3] kmem_malloc_domainset + 100.0% [3] malloc_large + 100.0% [3] malloc + 100.0% [3] read_random_uio + 100.0% [3] devfs_read_f + 100.0% [3] dofileread + 100.0% [3] sys_read + 100.0% [3] amd64_syscall + +00.80% [2] 0xb768a @ /lib/libc.so.7 + 100.0% [2] 0x2042e0 @ /bin/dd + +00.40% [1] vm_reserv_free_page @ /boot/kernel/kernel + 100.0% [1] vm_page_free_prep + 100.0% [1] vm_page_free_toq + 100.0% [1] _kmem_unback + 100.0% [1] kmem_free + 100.0% [1] zfree + 100.0% [1] read_random_uio + 100.0% [1] devfs_read_f + 100.0% [1] dofileread + 100.0% [1] sys_read + 100.0% [1] amd64_syscall + +00.40% [1] zfree @ /boot/kernel/kernel + 100.0% [1] read_random_uio + 100.0% [1] devfs_read_f + 100.0% [1] dofileread + 100.0% [1] sys_read + 100.0% [1] amd64_syscall + +00.40% [1] vm_page_free_prep @ /boot/kernel/kernel + 100.0% [1] vm_page_free_toq + 100.0% [1] _kmem_unback + 100.0% [1] kmem_free + 100.0% [1] zfree + 100.0% [1] read_random_uio + 100.0% [1] devfs_read_f + 100.0% [1] dofileread + 100.0% [1] sys_read + 100.0% [1] amd64_syscall + +00.40% [1] vm_domain_allocate @ /boot/kernel/kernel + 100.0% [1] vm_reserv_alloc_page + 100.0% [1] vm_page_alloc_domain_after + 100.0% [1] kmem_back_domain + 100.0% [1] kmem_malloc_domainset + 100.0% [1] malloc_large + 100.0% [1] malloc + 100.0% [1] read_random_uio + 100.0% [1] devfs_read_f + 100.0% [1] dofileread + 100.0% [1] sys_read + 100.0% [1] amd64_syscall + +@ � [66 samples] + +87.88% [58] randomdev_keystream @ /boot/kernel/kernel + 100.0% [58] random_fortuna_genbytes + 100.0% [58] random_fortuna_read + 100.0% [58] read_random_uio + 100.0% [58] devfs_read_f + 100.0% [58] dofileread + 100.0% [58] sys_read + 100.0% [58] amd64_syscall + +06.06% [4] spinlock_exit @ /boot/kernel/kernel + 75.00% [3] pmap_update_pde + 66.67% [2] pmap_promote_pde + 100.0% [2] pmap_enter + 100.0% [2] kmem_back_domain + 100.0% [2] kmem_malloc_domainset + 100.0% [2] malloc_large + 100.0% [2] malloc + 100.0% [2] read_random_uio + 100.0% [2] devfs_read_f + 100.0% [2] dofileread + 100.0% [2] sys_read + 100.0% [2] amd64_syscall + 33.33% [1] pmap_demote_pde_locked + 100.0% [1] pmap_remove + 100.0% [1] _kmem_unback + 100.0% [1] kmem_free + 100.0% [1] zfree + 100.0% [1] read_random_uio + 100.0% [1] devfs_read_f + 100.0% [1] dofileread + 100.0% [1] sys_read + 100.0% [1] amd64_syscall + 25.00% [1] cpu_activeclock + 100.0% [1] cpu_idle + 100.0% [1] sched_idletd + 100.0% [1] fork_exit + +01.52% [1] copyout_nosmap_std @ /boot/kernel/kernel + 100.0% [1] uiomove_faultflag + 100.0% [1] read_random_uio + 100.0% [1] devfs_read_f + 100.0% [1] dofileread + 100.0% [1] sys_read + 100.0% [1] amd64_syscall + +01.52% [1] vm_reserv_free_page @ /boot/kernel/kernel + 100.0% [1] vm_page_free_prep + 100.0% [1] vm_page_free_toq + 100.0% [1] _kmem_unback + 100.0% [1] kmem_free + 100.0% [1] zfree + 100.0% [1] read_random_uio + 100.0% [1] devfs_read_f + 100.0% [1] dofileread + 100.0% [1] sys_read + 100.0% [1] amd64_syscall + +01.52% [1] memset_std @ /boot/kernel/kernel + 100.0% [1] explicit_bzero + 100.0% [1] zfree + 100.0% [1] read_random_uio + 100.0% [1] devfs_read_f + 100.0% [1] dofileread + 100.0% [1] sys_read + 100.0% [1] amd64_syscall + +01.52% [1] vm_page_free_prep @ /boot/kernel/kernel + 100.0% [1] vm_page_free_toq + 100.0% [1] _kmem_unback + 100.0% [1] kmem_free + 100.0% [1] zfree + 100.0% [1] read_random_uio + 100.0% [1] devfs_read_f + 100.0% [1] dofileread + 100.0% [1] sys_read + 100.0% [1] amd64_syscall + diff --git a/tests/data/collapse-pmc/iperf3.txt b/tests/data/collapse-pmc/iperf3.txt new file mode 100644 index 00000000..43653d86 --- /dev/null +++ b/tests/data/collapse-pmc/iperf3.txt @@ -0,0 +1,192 @@ +@ CLOCK.HARD [118 samples] + +50.00% [59] copyout_nosmap_std @ /boot/kernel/kernel + 100.0% [59] uiomove_faultflag + 100.0% [59] soreceive_generic + 100.0% [59] soreceive + 100.0% [59] dofileread + 100.0% [59] sys_read + 100.0% [59] amd64_syscall + +11.86% [14] soreceive_generic @ /boot/kernel/kernel + 100.0% [14] soreceive + 100.0% [14] dofileread + 100.0% [14] sys_read + 100.0% [14] amd64_syscall + +11.86% [14] acpi_timer_get_timecount @ /boot/kernel/kernel + 85.71% [12] nanouptime + 100.0% [12] kern_clock_gettime + 100.0% [12] sys_clock_gettime + 100.0% [12] amd64_syscall + 14.29% [2] binuptime + 100.0% [2] cpu_idleclock + 100.0% [2] cpu_idle + 100.0% [2] sched_idletd + 100.0% [2] fork_exit + +03.39% [4] uma_zfree_arg @ /boot/kernel/kernel + 100.0% [4] mb_free_ext + 100.0% [4] m_free + 100.0% [4] soreceive_generic + 100.0% [4] soreceive + 100.0% [4] dofileread + 100.0% [4] sys_read + 100.0% [4] amd64_syscall + +01.69% [2] 0xb768a @ /lib/libc.so.7 + 100.0% [2] cJSON_Version @ /usr/local/lib/libiperf.so.0.0.0 + 100.0% [2] 0x202238 @ /usr/local/bin/iperf3 + 100.0% [2] 0x20210f + 100.0% [2] 0x201ee0 + +01.69% [2] tcp_output @ /boot/kernel/kernel + 100.0% [2] tcp_usr_rcvd + 100.0% [2] soreceive_generic + 100.0% [2] soreceive + 100.0% [2] dofileread + 100.0% [2] sys_read + 100.0% [2] amd64_syscall + +01.69% [2] uiomove_faultflag @ /boot/kernel/kernel + 100.0% [2] soreceive_generic + 100.0% [2] soreceive + 100.0% [2] dofileread + 100.0% [2] sys_read + 100.0% [2] amd64_syscall + +01.69% [2] Xfast_syscall @ /boot/kernel/kernel + +01.69% [2] pmap_kextract @ /boot/kernel/kernel + 100.0% [2] uma_zfree_arg + 100.0% [2] mb_free_ext + 100.0% [2] m_free + 100.0% [2] soreceive_generic + 100.0% [2] soreceive + 100.0% [2] dofileread + 100.0% [2] sys_read + 100.0% [2] amd64_syscall + +00.85% [1] virtqueue_dequeue @ /boot/kernel/kernel + 100.0% [1] vtnet_rxq_eof + 100.0% [1] vtnet_rx_vq_process + 100.0% [1] ithread_loop + 100.0% [1] fork_exit + +00.85% [1] _cv_timedwait_sig_sbt @ /boot/kernel/kernel + 100.0% [1] seltdwait + 100.0% [1] kern_select + 100.0% [1] sys_select + 100.0% [1] amd64_syscall + +00.85% [1] vtpci_legacy_notify_vq @ /boot/kernel/kernel + 100.0% [1] virtqueue_notify + 100.0% [1] vtnet_txq_notify + 100.0% [1] vtnet_txq_mq_start_locked + 100.0% [1] vtnet_txq_mq_start + 100.0% [1] ether_output_frame + 100.0% [1] ether_output + 100.0% [1] ip_output_send + 100.0% [1] ip_output + 100.0% [1] tcp_output + 100.0% [1] tcp_do_segment + 100.0% [1] tcp_input_with_port + 100.0% [1] tcp_input + 100.0% [1] ip_input + 100.0% [1] netisr_dispatch_src + 100.0% [1] ether_demux + 100.0% [1] ether_nh_input + 100.0% [1] netisr_dispatch_src + 100.0% [1] ether_input + 100.0% [1] vtnet_rxq_eof + 100.0% [1] vtnet_rx_vq_process + 100.0% [1] ithread_loop + 100.0% [1] fork_exit + +00.85% [1] acpi_cpu_c1 @ /boot/kernel/kernel + 100.0% [1] acpi_cpu_idle + 100.0% [1] cpu_idle_acpi + 100.0% [1] cpu_idle + 100.0% [1] sched_idletd + 100.0% [1] fork_exit + +00.85% [1] kern_select @ /boot/kernel/kernel + 100.0% [1] sys_select + 100.0% [1] amd64_syscall + +00.85% [1] sbfree @ /boot/kernel/kernel + 100.0% [1] soreceive_generic + 100.0% [1] soreceive + 100.0% [1] dofileread + 100.0% [1] sys_read + 100.0% [1] amd64_syscall + +00.85% [1] fget_only_user @ /boot/kernel/kernel + 100.0% [1] kern_select + 100.0% [1] sys_select + 100.0% [1] amd64_syscall + +00.85% [1] selfdfree @ /boot/kernel/kernel + 100.0% [1] kern_select + 100.0% [1] sys_select + 100.0% [1] amd64_syscall + +00.85% [1] PHYS_TO_VM_PAGE @ /boot/kernel/kernel + 100.0% [1] free + 100.0% [1] kern_select + 100.0% [1] sys_select + 100.0% [1] amd64_syscall + +00.85% [1] m_free @ /boot/kernel/kernel + 100.0% [1] soreceive_generic + 100.0% [1] soreceive + 100.0% [1] dofileread + 100.0% [1] sys_read + 100.0% [1] amd64_syscall + +00.85% [1] tcp_usr_rcvd @ /boot/kernel/kernel + 100.0% [1] soreceive_generic + 100.0% [1] soreceive + 100.0% [1] dofileread + 100.0% [1] sys_read + 100.0% [1] amd64_syscall + +00.85% [1] 0xb44f1 @ /lib/libc.so.7 + 100.0% [1] cJSON_Version @ /usr/local/lib/libiperf.so.0.0.0 + 100.0% [1] 0x202238 @ /usr/local/bin/iperf3 + 100.0% [1] 0x20210f + 100.0% [1] 0x201ee0 + +00.85% [1] maybe_yield @ /boot/kernel/kernel + 100.0% [1] uiomove_faultflag + 100.0% [1] soreceive_generic + 100.0% [1] soreceive + 100.0% [1] dofileread + 100.0% [1] sys_read + 100.0% [1] amd64_syscall + +00.85% [1] spinlock_exit @ /boot/kernel/kernel + 100.0% [1] cpu_idleclock + 100.0% [1] cpu_idle + 100.0% [1] sched_idletd + 100.0% [1] fork_exit + +00.85% [1] 0xf126 @ /lib/libthr.so.3 + 100.0% [1] cJSON_Version @ /usr/local/lib/libiperf.so.0.0.0 + 100.0% [1] 0x202238 @ /usr/local/bin/iperf3 + 100.0% [1] 0x20210f + 100.0% [1] 0x201ee0 + +00.85% [1] mb_free_ext @ /boot/kernel/kernel + 100.0% [1] m_free + 100.0% [1] soreceive_generic + 100.0% [1] soreceive + 100.0% [1] dofileread + 100.0% [1] sys_read + 100.0% [1] amd64_syscall + +00.85% [1] sopoll_generic @ /boot/kernel/kernel + 100.0% [1] kern_select + 100.0% [1] sys_select + 100.0% [1] amd64_syscall + diff --git a/tests/data/collapse-pmc/large.txt.gz b/tests/data/collapse-pmc/large.txt.gz new file mode 100644 index 00000000..fac94dc3 Binary files /dev/null and b/tests/data/collapse-pmc/large.txt.gz differ diff --git a/tests/data/collapse-pmc/results/dd-collapsed.txt b/tests/data/collapse-pmc/results/dd-collapsed.txt new file mode 100644 index 00000000..16aa4b37 --- /dev/null +++ b/tests/data/collapse-pmc/results/dd-collapsed.txt @@ -0,0 +1,13 @@ +0x2042e0;0xb768a 2 +amd64_syscall;sys_read;dofileread;devfs_read_f;read_random_uio;malloc;malloc_large;kmem_malloc_domainset;kmem_back_domain;pmap_enter;pmap_promote_pde;pmap_update_pde;spinlock_exit 4 +amd64_syscall;sys_read;dofileread;devfs_read_f;read_random_uio;malloc;malloc_large;kmem_malloc_domainset;kmem_back_domain;vm_page_alloc_domain_after;vm_radix_insert 3 +amd64_syscall;sys_read;dofileread;devfs_read_f;read_random_uio;malloc;malloc_large;kmem_malloc_domainset;kmem_back_domain;vm_page_alloc_domain_after;vm_reserv_alloc_page;vm_domain_allocate 1 +amd64_syscall;sys_read;dofileread;devfs_read_f;read_random_uio;random_fortuna_read;random_fortuna_genbytes;randomdev_keystream 283 +amd64_syscall;sys_read;dofileread;devfs_read_f;read_random_uio;uiomove_faultflag;copyout_nosmap_std 8 +amd64_syscall;sys_read;dofileread;devfs_read_f;read_random_uio;zfree 1 +amd64_syscall;sys_read;dofileread;devfs_read_f;read_random_uio;zfree;explicit_bzero;memset_std 7 +amd64_syscall;sys_read;dofileread;devfs_read_f;read_random_uio;zfree;kmem_free;_kmem_unback;pmap_remove;pmap_demote_pde_locked;pmap_update_pde;spinlock_exit 3 +amd64_syscall;sys_read;dofileread;devfs_read_f;read_random_uio;zfree;kmem_free;_kmem_unback;vm_page_free_toq;vm_page_free_prep 2 +amd64_syscall;sys_read;dofileread;devfs_read_f;read_random_uio;zfree;kmem_free;_kmem_unback;vm_page_free_toq;vm_page_free_prep;vm_reserv_free_page 2 +fork_exit;pmclog_loop;_sleep;sleepq_timedwait;spinlock_exit 1 +fork_exit;sched_idletd;cpu_idle;cpu_activeclock;spinlock_exit 3 diff --git a/tests/data/collapse-pmc/results/iperf3-collapsed.txt b/tests/data/collapse-pmc/results/iperf3-collapsed.txt new file mode 100644 index 00000000..c0fe2730 --- /dev/null +++ b/tests/data/collapse-pmc/results/iperf3-collapsed.txt @@ -0,0 +1,27 @@ +0x201ee0;0x20210f;0x202238;cJSON_Version;0xb44f1 1 +0x201ee0;0x20210f;0x202238;cJSON_Version;0xb768a 2 +0x201ee0;0x20210f;0x202238;cJSON_Version;0xf126 1 +Xfast_syscall 2 +amd64_syscall;sys_clock_gettime;kern_clock_gettime;nanouptime;acpi_timer_get_timecount 12 +amd64_syscall;sys_read;dofileread;soreceive;soreceive_generic 14 +amd64_syscall;sys_read;dofileread;soreceive;soreceive_generic;m_free 1 +amd64_syscall;sys_read;dofileread;soreceive;soreceive_generic;m_free;mb_free_ext 1 +amd64_syscall;sys_read;dofileread;soreceive;soreceive_generic;m_free;mb_free_ext;uma_zfree_arg 4 +amd64_syscall;sys_read;dofileread;soreceive;soreceive_generic;m_free;mb_free_ext;uma_zfree_arg;pmap_kextract 2 +amd64_syscall;sys_read;dofileread;soreceive;soreceive_generic;sbfree 1 +amd64_syscall;sys_read;dofileread;soreceive;soreceive_generic;tcp_usr_rcvd 1 +amd64_syscall;sys_read;dofileread;soreceive;soreceive_generic;tcp_usr_rcvd;tcp_output 2 +amd64_syscall;sys_read;dofileread;soreceive;soreceive_generic;uiomove_faultflag 2 +amd64_syscall;sys_read;dofileread;soreceive;soreceive_generic;uiomove_faultflag;copyout_nosmap_std 59 +amd64_syscall;sys_read;dofileread;soreceive;soreceive_generic;uiomove_faultflag;maybe_yield 1 +amd64_syscall;sys_select;kern_select 1 +amd64_syscall;sys_select;kern_select;fget_only_user 1 +amd64_syscall;sys_select;kern_select;free;PHYS_TO_VM_PAGE 1 +amd64_syscall;sys_select;kern_select;selfdfree 1 +amd64_syscall;sys_select;kern_select;seltdwait;_cv_timedwait_sig_sbt 1 +amd64_syscall;sys_select;kern_select;sopoll_generic 1 +fork_exit;ithread_loop;vtnet_rx_vq_process;vtnet_rxq_eof;ether_input;netisr_dispatch_src;ether_nh_input;ether_demux;netisr_dispatch_src;ip_input;tcp_input;tcp_input_with_port;tcp_do_segment;tcp_output;ip_output;ip_output_send;ether_output;ether_output_frame;vtnet_txq_mq_start;vtnet_txq_mq_start_locked;vtnet_txq_notify;virtqueue_notify;vtpci_legacy_notify_vq 1 +fork_exit;ithread_loop;vtnet_rx_vq_process;vtnet_rxq_eof;virtqueue_dequeue 1 +fork_exit;sched_idletd;cpu_idle;cpu_idle_acpi;acpi_cpu_idle;acpi_cpu_c1 1 +fork_exit;sched_idletd;cpu_idle;cpu_idleclock;binuptime;acpi_timer_get_timecount 2 +fork_exit;sched_idletd;cpu_idle;cpu_idleclock;spinlock_exit 1 diff --git a/tests/data/collapse-pmc/results/shared-collapsed.txt b/tests/data/collapse-pmc/results/shared-collapsed.txt new file mode 100644 index 00000000..72edfb4a --- /dev/null +++ b/tests/data/collapse-pmc/results/shared-collapsed.txt @@ -0,0 +1,8 @@ +amd64_syscall;__umtx_op_wait_uint_private;do_wait;kern_clock_gettime;nanouptime;acpi_timer_get_timecount 3 +amd64_syscall;sys_gettimeofday;microtime;acpi_timer_get_timecount 6 +amd64_syscall;sys_nanosleep;kern_clock_nanosleep;binuptime;acpi_timer_get_timecount 27 +amd64_syscall;sys_poll;kern_poll;binuptime;acpi_timer_get_timecount 5 +amd64_syscall;sys_read;kern_readv;devfs_read_f;read_random_uio;random_fortuna_read;randomdev_encrypt 166 +amd64_syscall;sys_read;kern_readv;devfs_read_f;read_random_uio;randomdev_encrypt 7 +fork_exit;md_kthread;g_io_deliver;binuptime;acpi_timer_get_timecount 1 +fork_exit;sched_idletd;cpu_idle;cpu_idleclock;binuptime;acpi_timer_get_timecount 17 diff --git a/tests/data/collapse-pmc/results/shared2-collapsed.txt b/tests/data/collapse-pmc/results/shared2-collapsed.txt new file mode 100644 index 00000000..3d76dcb0 --- /dev/null +++ b/tests/data/collapse-pmc/results/shared2-collapsed.txt @@ -0,0 +1,2 @@ +0x18a70a0;0x18b40bb;0x1fb0b7c;0x1f9da7c;0x1f9d548;0x1fc843e;0x3ac91aa;0x1fc8c57;0x18b457c;0x18a79e4;0x21e869f;0x20cbe89;0x2152576;0x250c163;0x21f4d45;0x1c0cc27;0x3854a2a;0x35c1b3e;0x385448a;0x48c73c8;0x48c282e;0x3b25deb;0xf4ae3 1 +0x53c2f;0xf4ae3 1 diff --git a/tests/data/collapse-pmc/results/simple-collapsed.txt b/tests/data/collapse-pmc/results/simple-collapsed.txt new file mode 100644 index 00000000..99eb7c7e --- /dev/null +++ b/tests/data/collapse-pmc/results/simple-collapsed.txt @@ -0,0 +1,2 @@ +amd64_syscall;sys_read;kern_readv;devfs_read_f;read_random_uio;random_fortuna_read;rijndael_blockEncrypt;aes_ecb_encrypt 1676 +fork_exit;sched_idletd;cpu_idle;cpu_idle_acpi;acpi_cpu_idle;acpi_cpu_c1 260401 diff --git a/tests/data/collapse-pmc/shared.txt b/tests/data/collapse-pmc/shared.txt new file mode 100644 index 00000000..2e49dddf --- /dev/null +++ b/tests/data/collapse-pmc/shared.txt @@ -0,0 +1,39 @@ +@ CLOCK.HARD [302186 samples] + +01.17% [173] randomdev_encrypt @ /boot/kernel/kernel + 95.95% [166] random_fortuna_read + 100.0% [166] read_random_uio + 100.0% [166] devfs_read_f + 100.0% [166] kern_readv + 100.0% [166] sys_read + 100.0% [166] amd64_syscall + 04.05% [7] read_random_uio + 100.0% [7] devfs_read_f + 100.0% [7] kern_readv + 100.0% [7] sys_read + 100.0% [7] amd64_syscall + +00.40% [59] acpi_timer_get_timecount @ /boot/kernel/kernel + 84.75% [50] binuptime + 54.00% [27] kern_clock_nanosleep + 100.0% [27] sys_nanosleep + 100.0% [27] amd64_syscall + 34.00% [17] cpu_idleclock + 100.0% [17] cpu_idle + 100.0% [17] sched_idletd + 100.0% [17] fork_exit + 10.00% [5] kern_poll + 100.0% [5] sys_poll + 100.0% [5] amd64_syscall + 02.00% [1] g_io_deliver + 100.0% [1] md_kthread + 100.0% [1] fork_exit + 10.17% [6] microtime + 100.0% [6] sys_gettimeofday + 100.0% [6] amd64_syscall + 05.08% [3] nanouptime + 100.0% [3] kern_clock_gettime + 100.0% [3] do_wait + 100.0% [3] __umtx_op_wait_uint_private + 100.0% [3] amd64_syscall + diff --git a/tests/data/collapse-pmc/shared2.txt b/tests/data/collapse-pmc/shared2.txt new file mode 100644 index 00000000..04d71b80 --- /dev/null +++ b/tests/data/collapse-pmc/shared2.txt @@ -0,0 +1,26 @@ +@ CLOCK.HARD [302186 samples] + +00.31% [2] 0xf4ae3 @ /lib/libc.so.7 + 50.00% [1] 0x53c2f @ /usr/lib/libprivatessh.so.5 + 50.00% [1] 0x3b25deb @ /usr/bin/clang-cpp + 100.0% [1] 0x48c282e + 100.0% [1] 0x48c73c8 + 100.0% [1] 0x385448a + 100.0% [1] 0x35c1b3e + 100.0% [1] 0x3854a2a + 100.0% [1] 0x1c0cc27 + 100.0% [1] 0x21f4d45 + 100.0% [1] 0x250c163 + 100.0% [1] 0x2152576 + 100.0% [1] 0x20cbe89 + 100.0% [1] 0x21e869f + 100.0% [1] 0x18a79e4 + 100.0% [1] 0x18b457c + 100.0% [1] 0x1fc8c57 + 100.0% [1] 0x3ac91aa + 100.0% [1] 0x1fc843e + 100.0% [1] 0x1f9d548 + 100.0% [1] 0x1f9da7c + 100.0% [1] 0x1fb0b7c + 100.0% [1] 0x18b40bb + 100.0% [1] 0x18a70a0 diff --git a/tests/data/collapse-pmc/simple.txt b/tests/data/collapse-pmc/simple.txt new file mode 100644 index 00000000..dbe82252 --- /dev/null +++ b/tests/data/collapse-pmc/simple.txt @@ -0,0 +1,18 @@ +@ CLOCK.HARD [302186 samples] + +86.17% [260401] acpi_cpu_c1 @ /boot/kernel/kernel + 100.0% [260401] acpi_cpu_idle + 100.0% [260401] cpu_idle_acpi + 100.0% [260401] cpu_idle + 100.0% [260401] sched_idletd + 100.0% [260401] fork_exit + +00.55% [1676] aes_ecb_encrypt @ /boot/kernel/kernel + 100.0% [1676] rijndael_blockEncrypt + 100.0% [1676] random_fortuna_read + 100.0% [1676] read_random_uio + 100.0% [1676] devfs_read_f + 100.0% [1676] kern_readv + 100.0% [1676] sys_read + 100.0% [1676] amd64_syscall + diff --git a/tests/data/collapse-pmc/weird-stack-line-bad-count.txt b/tests/data/collapse-pmc/weird-stack-line-bad-count.txt new file mode 100644 index 00000000..d594b748 --- /dev/null +++ b/tests/data/collapse-pmc/weird-stack-line-bad-count.txt @@ -0,0 +1,5 @@ +@ CLOCK.HARD [302186 samples] + +86.17% [260401] acpi_cpu_c1 @ /boot/kernel/kernel + 100.0% [] acpi_cpu_idle + diff --git a/tests/data/collapse-pmc/weird-stack-line-bad-percent.txt b/tests/data/collapse-pmc/weird-stack-line-bad-percent.txt new file mode 100644 index 00000000..ce24a7b7 --- /dev/null +++ b/tests/data/collapse-pmc/weird-stack-line-bad-percent.txt @@ -0,0 +1,5 @@ +@ CLOCK.HARD [302186 samples] + +86.17% [260401] acpi_cpu_c1 @ /boot/kernel/kernel + % [260401] acpi_cpu_idle + diff --git a/tests/data/collapse-pmc/weird-stack-line-no-function-name.txt b/tests/data/collapse-pmc/weird-stack-line-no-function-name.txt new file mode 100644 index 00000000..e63e7ed8 --- /dev/null +++ b/tests/data/collapse-pmc/weird-stack-line-no-function-name.txt @@ -0,0 +1,5 @@ +@ CLOCK.HARD [302186 samples] + +86.17% [260401] acpi_cpu_c1 @ /boot/kernel/kernel + 100.0% [260401] +