Skip to content

Commit adad86a

Browse files
authored
Merge pull request #1781 from Kobzol/measureme-modifications
Simplify handling of self-profile data in `collector`
2 parents c2aa920 + 7dd7436 commit adad86a

File tree

8 files changed

+60
-197
lines changed

8 files changed

+60
-197
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

collector/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ object = "0.32.1"
4141
tabled = { version = "0.14.0" , features = ["ansi-str"]}
4242
humansize = "2.1.3"
4343
regex = "1.7.1"
44+
analyzeme = { git = "https://github.com/rust-lang/measureme", branch = "stable" }
4445

4546
benchlib = { path = "benchlib" }
4647

collector/README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,7 @@ The following options alter the behaviour of the `bench_local` subcommand.
155155
choices are one or more (comma-separated) of `Llvm`, `Cranelift`. The default
156156
is `Llvm`.
157157
- `--self-profile`: use rustc's `-Zself-profile` option to produce
158-
query/function tables in the output. The `measureme` tool must be installed
159-
for this to work.
158+
query/function tables in the output.
160159

161160
`RUST_LOG=debug` can be specified to enable verbose logging, which is useful
162161
for debugging `collector` itself.

collector/collect.sh

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,6 @@ while : ; do
1919
rustup update
2020
cargo +nightly build --release -p collector
2121

22-
# Install measureme tooling
23-
cargo install --git https://github.com/rust-lang/measureme --branch stable flamegraph crox summarize
24-
2522
target/release/collector bench_next $SITE_URL --self-profile --bench-rustc --db $DATABASE
2623
STATUS=$?
2724
echo finished run at `date` with exit code $STATUS

collector/src/bin/collector.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1593,12 +1593,6 @@ fn bench_compile(
15931593
shared.artifact_id, shared.toolchain.triple
15941594
);
15951595

1596-
if config.is_self_profile {
1597-
if let Err(e) = check_measureme_installed() {
1598-
panic!("{}Or omit --self-profile` to opt out\n", e);
1599-
}
1600-
}
1601-
16021596
let bench_rustc = config.bench_rustc;
16031597

16041598
let start = Instant::now();

collector/src/bin/rustc-fake.rs

Lines changed: 2 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
use anyhow::Context;
21
use std::env;
32
use std::ffi::OsString;
43
use std::fs;
5-
use std::path::Path;
64
use std::path::PathBuf;
75
use std::process::Command;
86
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
@@ -450,55 +448,8 @@ fn process_self_profile_output(prof_out_dir: PathBuf, args: &[OsString]) {
450448
.find(|args| args[0] == "--crate-name")
451449
.and_then(|args| args[1].to_str())
452450
.expect("rustc to be invoked with crate name");
453-
let mut prefix = None;
454-
let mut full_path = None;
455-
// We don't know the pid of rustc, and can't easily get it -- we only know the
456-
// `perf` pid. So just blindly look in the directory to hopefully find it.
457-
for entry in fs::read_dir(&prof_out_dir).unwrap() {
458-
let entry = entry.unwrap();
459-
if entry
460-
.file_name()
461-
.to_str()
462-
.map_or(false, |s| s.starts_with(crate_name))
463-
{
464-
if entry.file_name().to_str().unwrap().ends_with("mm_profdata") {
465-
full_path = Some(entry.path());
466-
break;
467-
}
468-
let file = entry.file_name().to_str().unwrap().to_owned();
469-
let new_prefix = Some(file[..file.find('.').unwrap()].to_owned());
470-
assert!(
471-
prefix.is_none() || prefix == new_prefix,
472-
"prefix={:?}, new_prefix={:?}",
473-
prefix,
474-
new_prefix
475-
);
476-
prefix = new_prefix;
477-
}
478-
}
479-
if let Some(profile_data) = full_path {
480-
// measureme 0.8 has a single file
481-
println!("!self-profile-file:{}", profile_data.to_str().unwrap());
482-
let filename = profile_data.file_name().unwrap().to_str().unwrap();
483-
let json = match run_summarize("summarize", &prof_out_dir, filename) {
484-
Ok(s) => s,
485-
Err(e1) => match run_summarize("summarize-9.0", &prof_out_dir, filename) {
486-
Ok(s) => s,
487-
Err(e2) => {
488-
panic!("failed to run summarize and summarize-9.0. Errors:\nsummarize: {:?}\nsummarize-9.0: {:?}", e1, e2);
489-
}
490-
},
491-
};
492-
println!("!self-profile-output:{}", json);
493-
} else {
494-
let prefix = prefix.unwrap_or_else(|| panic!("found prefix {:?}", prof_out_dir));
495-
let json = run_summarize("summarize", &prof_out_dir, &prefix)
496-
.or_else(|_| run_summarize("summarize-0.7", &prof_out_dir, &prefix))
497-
.expect("able to run summarize or summarize-0.7");
498-
println!("!self-profile-dir:{}", prof_out_dir.to_str().unwrap());
499-
println!("!self-profile-prefix:{}", prefix);
500-
println!("!self-profile-output:{}", json);
501-
}
451+
println!("!self-profile-dir:{}", prof_out_dir.to_str().unwrap());
452+
println!("!self-profile-crate:{}", crate_name);
502453
}
503454

504455
#[cfg(windows)]
@@ -546,28 +497,5 @@ fn print_time(dur: Duration) {
546497
);
547498
}
548499

549-
fn run_summarize(name: &str, prof_out_dir: &Path, prefix: &str) -> anyhow::Result<String> {
550-
let mut cmd = Command::new(name);
551-
cmd.current_dir(prof_out_dir);
552-
cmd.arg("summarize").arg("--json");
553-
cmd.arg(prefix);
554-
let status = cmd
555-
.status()
556-
.with_context(|| format!("Command::new({}).status() failed", name))?;
557-
if !status.success() {
558-
anyhow::bail!(
559-
"failed to run {} in {:?} with prefix {:?}",
560-
name,
561-
prof_out_dir,
562-
prefix
563-
)
564-
}
565-
let json = prof_out_dir.join(format!(
566-
"{}.json",
567-
prefix.strip_suffix(".mm_profdata").unwrap_or(prefix)
568-
));
569-
fs::read_to_string(&json).with_context(|| format!("failed to read {:?}", json))
570-
}
571-
572500
#[cfg(windows)]
573501
fn print_memory() {}

collector/src/compile/execute/bencher.rs

Lines changed: 3 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use futures::{future, StreamExt};
1616
use std::collections::VecDeque;
1717
use std::future::Future;
1818
use std::io::Read;
19-
use std::path::{Path, PathBuf};
19+
use std::path::PathBuf;
2020
use std::pin::Pin;
2121
use std::process::Command;
2222
use std::{env, process};
@@ -216,7 +216,8 @@ impl<'a> Processor for BenchProcessor<'a> {
216216
}
217217
Err(
218218
e @ (DeserializeStatError::ParseError { .. }
219-
| DeserializeStatError::XperfError(..)),
219+
| DeserializeStatError::XperfError(..)
220+
| DeserializeStatError::IOError(..)),
220221
) => {
221222
panic!("process_perf_stat_output failed: {:?}", e);
222223
}
@@ -283,44 +284,6 @@ impl SelfProfileS3Upload {
283284
.context("create temporary file")
284285
.unwrap();
285286
let filename = match files {
286-
SelfProfileFiles::Seven {
287-
string_index,
288-
string_data,
289-
events,
290-
} => {
291-
let tarball = snap::write::FrameEncoder::new(Vec::new());
292-
let mut builder = tar::Builder::new(tarball);
293-
builder.mode(tar::HeaderMode::Deterministic);
294-
295-
let append_file = |builder: &mut tar::Builder<_>,
296-
file: &Path,
297-
name: &str|
298-
-> anyhow::Result<()> {
299-
if file.exists() {
300-
// Silently ignore missing files, the new self-profile
301-
// experiment with one file has a different structure.
302-
builder.append_path_with_name(file, name)?;
303-
}
304-
Ok(())
305-
};
306-
307-
append_file(&mut builder, &string_index, "self-profile.string_index")
308-
.expect("append string index");
309-
append_file(&mut builder, &string_data, "self-profile.string_data")
310-
.expect("append string data");
311-
append_file(&mut builder, &events, "self-profile.events").expect("append events");
312-
builder.finish().expect("complete tarball");
313-
std::fs::write(
314-
upload.path(),
315-
builder
316-
.into_inner()
317-
.expect("get")
318-
.into_inner()
319-
.expect("snap success"),
320-
)
321-
.expect("wrote tarball");
322-
format!("self-profile-{}.tar.sz", collection)
323-
}
324287
SelfProfileFiles::Eight { file } => {
325288
let data = std::fs::read(file).expect("read profile data");
326289
let mut data = snap::read::FrameEncoder::new(&data[..]);

collector/src/compile/execute/mod.rs

Lines changed: 52 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,18 @@ use crate::compile::benchmark::BenchmarkName;
88
use crate::toolchain::Toolchain;
99
use crate::utils::fs::EnsureImmutableFile;
1010
use crate::{async_command_output, command_output, utils};
11+
use analyzeme::ArtifactSize;
1112
use anyhow::Context;
1213
use bencher::Bencher;
13-
use database::QueryLabel;
1414
use std::collections::HashMap;
1515
use std::env;
1616
use std::fs;
1717
use std::future::Future;
18+
use std::io::ErrorKind;
1819
use std::path::{Path, PathBuf};
1920
use std::pin::Pin;
2021
use std::process::{self, Command};
2122
use std::str;
22-
use std::time::Duration;
2323

2424
pub mod bencher;
2525
mod etw_parser;
@@ -468,7 +468,7 @@ fn store_artifact_sizes_into_stats(stats: &mut Stats, profile: &SelfProfile) {
468468
for artifact in profile.artifact_sizes.iter() {
469469
stats
470470
.stats
471-
.insert(format!("size:{}", artifact.label), artifact.size as f64);
471+
.insert(format!("size:{}", artifact.label), artifact.value as f64);
472472
}
473473
}
474474

@@ -480,17 +480,12 @@ enum DeserializeStatError {
480480
ParseError(String, #[source] ::std::num::ParseFloatError),
481481
#[error("could not process xperf data")]
482482
XperfError(#[from] anyhow::Error),
483+
#[error("io error")]
484+
IOError(#[from] std::io::Error),
483485
}
484486

485487
enum SelfProfileFiles {
486-
Seven {
487-
string_data: PathBuf,
488-
string_index: PathBuf,
489-
events: PathBuf,
490-
},
491-
Eight {
492-
file: PathBuf,
493-
},
488+
Eight { file: PathBuf },
494489
}
495490

496491
fn process_stat_output(
@@ -499,25 +494,15 @@ fn process_stat_output(
499494
let stdout = String::from_utf8(output.stdout.clone()).expect("utf8 output");
500495
let mut stats = Stats::new();
501496

502-
let mut profile: Option<SelfProfile> = None;
503-
let mut dir: Option<PathBuf> = None;
504-
let mut prefix: Option<String> = None;
505-
let mut file: Option<PathBuf> = None;
497+
let mut self_profile_dir: Option<PathBuf> = None;
498+
let mut self_profile_crate: Option<String> = None;
506499
for line in stdout.lines() {
507-
if let Some(stripped) = line.strip_prefix("!self-profile-output:") {
508-
profile = Some(serde_json::from_str(stripped).unwrap());
509-
continue;
510-
}
511500
if let Some(stripped) = line.strip_prefix("!self-profile-dir:") {
512-
dir = Some(PathBuf::from(stripped));
513-
continue;
514-
}
515-
if let Some(stripped) = line.strip_prefix("!self-profile-prefix:") {
516-
prefix = Some(String::from(stripped));
501+
self_profile_dir = Some(PathBuf::from(stripped));
517502
continue;
518503
}
519-
if let Some(stripped) = line.strip_prefix("!self-profile-file:") {
520-
file = Some(PathBuf::from(stripped));
504+
if let Some(stripped) = line.strip_prefix("!self-profile-crate:") {
505+
self_profile_crate = Some(String::from(stripped));
521506
continue;
522507
}
523508
if let Some(counter_file) = line.strip_prefix("!counters-file:") {
@@ -581,39 +566,13 @@ fn process_stat_output(
581566
);
582567
}
583568

584-
let files = if let (Some(prefix), Some(dir)) = (prefix, dir) {
585-
let mut string_index = PathBuf::new();
586-
let mut string_data = PathBuf::new();
587-
let mut events = PathBuf::new();
588-
for entry in fs::read_dir(&dir).unwrap() {
589-
let filename = entry.unwrap().file_name();
590-
let filename_str = filename.to_str().unwrap();
591-
let path = dir.join(filename_str);
592-
if filename_str.ends_with(".events") {
593-
assert!(filename_str.contains(&prefix), "{:?}", path);
594-
events = path;
595-
} else if filename_str.ends_with(".string_data") {
596-
assert!(filename_str.contains(&prefix), "{:?}", path);
597-
string_data = path;
598-
} else if filename_str.ends_with(".string_index") {
599-
assert!(filename_str.contains(&prefix), "{:?}", path);
600-
string_index = path;
601-
}
602-
}
603-
604-
Some(SelfProfileFiles::Seven {
605-
string_index,
606-
string_data,
607-
events,
608-
})
609-
} else {
610-
file.map(|file| SelfProfileFiles::Eight { file })
611-
};
612-
613569
if stats.is_empty() {
614570
return Err(DeserializeStatError::NoOutput(output));
615571
}
616-
572+
let (profile, files) = match (self_profile_dir, self_profile_crate) {
573+
(Some(dir), Some(krate)) => parse_self_profile(dir, krate)?,
574+
_ => (None, None),
575+
};
617576
Ok((stats, profile, files))
618577
}
619578

@@ -650,23 +609,44 @@ impl Stats {
650609

651610
#[derive(serde::Deserialize, Clone)]
652611
pub struct SelfProfile {
653-
pub query_data: Vec<QueryData>,
654612
pub artifact_sizes: Vec<ArtifactSize>,
655613
}
656614

657-
#[derive(serde::Deserialize, Clone)]
658-
pub struct ArtifactSize {
659-
pub label: QueryLabel,
660-
#[serde(rename = "value")]
661-
pub size: u64,
662-
}
663-
664-
#[derive(serde::Deserialize, Clone)]
665-
pub struct QueryData {
666-
pub label: QueryLabel,
667-
pub self_time: Duration,
668-
pub number_of_cache_hits: u32,
669-
pub invocation_count: u32,
670-
pub blocked_time: Duration,
671-
pub incremental_load_time: Duration,
615+
fn parse_self_profile(
616+
dir: PathBuf,
617+
crate_name: String,
618+
) -> std::io::Result<(Option<SelfProfile>, Option<SelfProfileFiles>)> {
619+
// First, find the `.mm_profdata` file with the self-profile data.
620+
let mut full_path = None;
621+
// We don't know the pid of rustc, and can't easily get it -- we only know the
622+
// `perf` pid. So just blindly look in the directory to hopefully find it.
623+
for entry in fs::read_dir(dir)? {
624+
let entry = entry?;
625+
if entry.file_name().to_str().map_or(false, |s| {
626+
s.starts_with(&crate_name) && s.ends_with("mm_profdata")
627+
}) {
628+
full_path = Some(entry.path());
629+
break;
630+
}
631+
}
632+
let (profile, files) = if let Some(profile_path) = full_path {
633+
// measureme 0.8+ uses a single file
634+
let data = fs::read(&profile_path)?;
635+
let results = analyzeme::ProfilingData::from_paged_buffer(data, None)
636+
.map_err(|error| {
637+
eprintln!("Cannot read self-profile data: {error:?}");
638+
std::io::Error::new(ErrorKind::InvalidData, error)
639+
})?
640+
.perform_analysis();
641+
let profile = SelfProfile {
642+
artifact_sizes: results.artifact_sizes,
643+
};
644+
let files = SelfProfileFiles::Eight { file: profile_path };
645+
(Some(profile), Some(files))
646+
} else {
647+
// The old "3 files format" is not supported by analyzeme anymore, so we don't handle it
648+
// here.
649+
(None, None)
650+
};
651+
Ok((profile, files))
672652
}

0 commit comments

Comments
 (0)