Skip to content

Commit 357b9be

Browse files
committed
Add support for capturing self-profile data during xperf
Also fake the cpu-clock counter because it's necessary to get the self-profiling data to display in the site.
1 parent 0fdf9bf commit 357b9be

File tree

3 files changed

+90
-60
lines changed

3 files changed

+90
-60
lines changed

collector/src/etw_parser.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -287,10 +287,11 @@ fn parse_events(r: &mut dyn BufRead, headers: Vec<EventHeader>) -> anyhow::Resul
287287
Ok(events)
288288
}
289289

290-
#[derive(Debug, Eq, PartialEq)]
290+
#[derive(Debug, PartialEq)]
291291
pub struct Counters {
292292
pub instructions_retired: u64,
293293
pub total_cycles: u64,
294+
pub cpu_clock: f64,
294295
}
295296

296297
impl std::ops::Add for Counters {
@@ -300,6 +301,7 @@ impl std::ops::Add for Counters {
300301
Self {
301302
instructions_retired: self.instructions_retired + rhs.instructions_retired,
302303
total_cycles: self.total_cycles + rhs.total_cycles,
304+
cpu_clock: self.cpu_clock + rhs.cpu_clock,
303305
}
304306
}
305307
}
@@ -314,6 +316,7 @@ impl std::ops::Sub for Counters {
314316
Self {
315317
instructions_retired: self.instructions_retired - rhs.instructions_retired,
316318
total_cycles: self.total_cycles - rhs.total_cycles,
319+
cpu_clock: self.cpu_clock - rhs.cpu_clock,
317320
}
318321
}
319322
}
@@ -323,6 +326,11 @@ impl Default for Counters {
323326
Self {
324327
instructions_retired: 0,
325328
total_cycles: 0,
329+
// FIXME(wesleywiser): We should be properly calculating this value by taking the total time
330+
// each rustc thread runs per core and adding them togther. This placeholder value is here
331+
// so that we can still render the detailed query statistics page (although the "Time (%)"
332+
// column will show the wrong value).
333+
cpu_clock: 1.0,
326334
}
327335
}
328336
}
@@ -332,6 +340,8 @@ impl From<&Pmc> for Counters {
332340
Self {
333341
instructions_retired: pmc.instructions_retired,
334342
total_cycles: pmc.total_cycles,
343+
// FIXME(wesleywiser): see comment in `<Counters as Default>::default()`.
344+
cpu_clock: 0.0,
335345
}
336346
}
337347
}
@@ -593,9 +603,10 @@ FirstReliableCSwitchEventTimeStamp, 6016
593603
})
594604
];
595605

596-
let expected= Counters {
606+
let expected = Counters {
597607
instructions_retired: 1561453,
598608
total_cycles: 366237,
609+
cpu_clock: 1.0,
599610
};
600611

601612
assert_eq!(expected, super::process_events(&events)?);

collector/src/execute.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1418,6 +1418,7 @@ fn process_stat_output(
14181418

14191419
stats.insert("cycles".into(), counters.total_cycles as f64);
14201420
stats.insert("instructions:u".into(), counters.instructions_retired as f64);
1421+
stats.insert("cpu-clock".into(), counters.cpu_clock);
14211422
continue;
14221423
}
14231424

collector/src/rustc-fake.rs

Lines changed: 76 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::env;
33
use std::ffi::OsString;
44
use std::fs;
55
use std::path::Path;
6+
use std::path::PathBuf;
67
use std::process::Command;
78
use std::time::{Duration, Instant};
89

@@ -77,67 +78,11 @@ fn main() {
7778
print_memory();
7879
print_time(dur);
7980
if wrapper == "perf-stat-self-profile" {
80-
let crate_name = args
81-
.windows(2)
82-
.find(|args| args[0] == "--crate-name")
83-
.and_then(|args| args[1].to_str())
84-
.expect("rustc to be invoked with crate name");
85-
let mut prefix = None;
86-
let mut full_path = None;
87-
// We don't know the pid of rustc, and can't easily get it -- we only know the
88-
// `perf` pid. So just blindly look in the directory to hopefully find it.
89-
for entry in fs::read_dir(&prof_out_dir).unwrap() {
90-
let entry = entry.unwrap();
91-
if entry
92-
.file_name()
93-
.to_str()
94-
.map_or(false, |s| s.starts_with(crate_name))
95-
{
96-
if entry.file_name().to_str().unwrap().ends_with("mm_profdata") {
97-
full_path = Some(entry.path());
98-
break;
99-
}
100-
let file = entry.file_name().to_str().unwrap().to_owned();
101-
let new_prefix = Some(file[..file.find('.').unwrap()].to_owned());
102-
assert!(
103-
prefix.is_none() || prefix == new_prefix,
104-
"prefix={:?}, new_prefix={:?}",
105-
prefix,
106-
new_prefix
107-
);
108-
prefix = new_prefix;
109-
}
110-
}
111-
if let Some(profile_data) = full_path {
112-
// measureme 0.8 has a single file
113-
println!("!self-profile-file:{}", profile_data.to_str().unwrap());
114-
let filename = profile_data.file_name().unwrap().to_str().unwrap();
115-
let json = match run_summarize("summarize", &prof_out_dir, filename) {
116-
Ok(s) => s,
117-
Err(e1) => {
118-
match run_summarize("summarize-9.0", &prof_out_dir, filename) {
119-
Ok(s) => s,
120-
Err(e2) => {
121-
panic!("failed to run summarize and summarize-9.0. Errors:\nsummarize: {:?}\nsummarize-9.0: {:?}", e1, e2);
122-
}
123-
}
124-
}
125-
};
126-
println!("!self-profile-output:{}", json);
127-
} else {
128-
let prefix = prefix.expect(&format!("found prefix {:?}", prof_out_dir));
129-
let json = run_summarize("summarize", &prof_out_dir, &prefix)
130-
.or_else(|_| run_summarize("summarize-0.7", &prof_out_dir, &prefix))
131-
.expect("able to run summarize or summarize-0.7");
132-
println!("!self-profile-dir:{}", prof_out_dir.to_str().unwrap());
133-
println!("!self-profile-prefix:{}", prefix);
134-
println!("!self-profile-output:{}", json);
135-
}
81+
process_self_profile_output(prof_out_dir, &args[..]);
13682
}
13783
}
13884

13985
"xperf-stat" | "xperf-stat-self-profile" => {
140-
// TODO handle self-profiling
14186
// Read the path to tracelog.exe from either an environment variable, falling back to assuming it's on the PATH.
14287
let tracelog = std::env::var("TRACELOG").unwrap_or("tracelog.exe".to_string());
14388
let mut cmd = Command::new(tracelog);
@@ -148,7 +93,17 @@ fn main() {
14893
assert!(status.success(), "tracelog did not complete successfully");
14994

15095
let mut tool = Command::new(tool);
151-
tool.args(args);
96+
tool.args(&args);
97+
98+
let prof_out_dir = std::env::current_dir().unwrap().join("self-profile-output");
99+
if wrapper == "xperf-stat-self-profile" {
100+
tool.arg(&format!(
101+
"-Zself-profile={}",
102+
prof_out_dir.to_str().unwrap()
103+
));
104+
let _ = fs::remove_dir_all(&prof_out_dir);
105+
let _ = fs::create_dir_all(&prof_out_dir);
106+
}
152107

153108
let start = Instant::now();
154109
let status = tool.status().expect("tool failed to start");
@@ -172,6 +127,10 @@ fn main() {
172127

173128
let counters_file = std::env::current_dir().unwrap().join("pmc_counters.txt");
174129
println!("!counters-file:{}", counters_file.to_str().unwrap());
130+
131+
if wrapper == "xperf-stat-self-profile" {
132+
process_self_profile_output(prof_out_dir, &args[..]);
133+
}
175134
}
176135

177136
"self-profile" => {
@@ -325,6 +284,65 @@ fn main() {
325284
}
326285
}
327286

287+
fn process_self_profile_output(prof_out_dir: PathBuf, args: &[OsString]) {
288+
let crate_name = args
289+
.windows(2)
290+
.find(|args| args[0] == "--crate-name")
291+
.and_then(|args| args[1].to_str())
292+
.expect("rustc to be invoked with crate name");
293+
let mut prefix = None;
294+
let mut full_path = None;
295+
// We don't know the pid of rustc, and can't easily get it -- we only know the
296+
// `perf` pid. So just blindly look in the directory to hopefully find it.
297+
for entry in fs::read_dir(&prof_out_dir).unwrap() {
298+
let entry = entry.unwrap();
299+
if entry
300+
.file_name()
301+
.to_str()
302+
.map_or(false, |s| s.starts_with(crate_name))
303+
{
304+
if entry.file_name().to_str().unwrap().ends_with("mm_profdata") {
305+
full_path = Some(entry.path());
306+
break;
307+
}
308+
let file = entry.file_name().to_str().unwrap().to_owned();
309+
let new_prefix = Some(file[..file.find('.').unwrap()].to_owned());
310+
assert!(
311+
prefix.is_none() || prefix == new_prefix,
312+
"prefix={:?}, new_prefix={:?}",
313+
prefix,
314+
new_prefix
315+
);
316+
prefix = new_prefix;
317+
}
318+
}
319+
if let Some(profile_data) = full_path {
320+
// measureme 0.8 has a single file
321+
println!("!self-profile-file:{}", profile_data.to_str().unwrap());
322+
let filename = profile_data.file_name().unwrap().to_str().unwrap();
323+
let json = match run_summarize("summarize", &prof_out_dir, filename) {
324+
Ok(s) => s,
325+
Err(e1) => {
326+
match run_summarize("summarize-9.0", &prof_out_dir, filename) {
327+
Ok(s) => s,
328+
Err(e2) => {
329+
panic!("failed to run summarize and summarize-9.0. Errors:\nsummarize: {:?}\nsummarize-9.0: {:?}", e1, e2);
330+
}
331+
}
332+
}
333+
};
334+
println!("!self-profile-output:{}", json);
335+
} else {
336+
let prefix = prefix.expect(&format!("found prefix {:?}", prof_out_dir));
337+
let json = run_summarize("summarize", &prof_out_dir, &prefix)
338+
.or_else(|_| run_summarize("summarize-0.7", &prof_out_dir, &prefix))
339+
.expect("able to run summarize or summarize-0.7");
340+
println!("!self-profile-dir:{}", prof_out_dir.to_str().unwrap());
341+
println!("!self-profile-prefix:{}", prefix);
342+
println!("!self-profile-output:{}", json);
343+
}
344+
}
345+
328346
/// Run a command via bash, in order to redirect its output to a file.
329347
/// `redirect` should be something like "> out" or "2> out".
330348
fn bash_command(tool: OsString, args: Vec<OsString>, redirect: &str) -> Command {

0 commit comments

Comments
 (0)