Auto merge of #58425 - wesleywiser:more_profiler_changes, r=michaelwoerister

[self-profiler] Make the profiler faster/more efficient

Related to #58372

r? @michaelwoerister
This commit is contained in:
bors 2019-03-03 19:48:12 +00:00
commit 87a436377a
9 changed files with 341 additions and 370 deletions

View file

@ -2625,6 +2625,7 @@ dependencies = [
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc 0.0.0", "rustc 0.0.0",
"rustc-demangle 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-demangle 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_allocator 0.0.0", "rustc_allocator 0.0.0",

View file

@ -1405,9 +1405,7 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
crate_attr: Vec<String> = (Vec::new(), parse_string_push, [TRACKED], crate_attr: Vec<String> = (Vec::new(), parse_string_push, [TRACKED],
"inject the given attribute in the crate"), "inject the given attribute in the crate"),
self_profile: bool = (false, parse_bool, [UNTRACKED], self_profile: bool = (false, parse_bool, [UNTRACKED],
"run the self profiler"), "run the self profiler and output the raw event data"),
profile_json: bool = (false, parse_bool, [UNTRACKED],
"output a json file with profiler results"),
emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED], emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED],
"emits a section containing stack size metadata"), "emits a section containing stack size metadata"),
plt: Option<bool> = (None, parse_opt_bool, [TRACKED], plt: Option<bool> = (None, parse_opt_bool, [TRACKED],

View file

@ -44,7 +44,9 @@ use std::fmt;
use std::io::Write; use std::io::Write;
use std::path::PathBuf; use std::path::PathBuf;
use std::time::Duration; use std::time::Duration;
use std::sync::mpsc; use std::sync::{Arc, mpsc};
use parking_lot::Mutex as PlMutex;
mod code_stats; mod code_stats;
pub mod config; pub mod config;
@ -127,11 +129,8 @@ pub struct Session {
/// Used by `-Z profile-queries` in `util::common`. /// Used by `-Z profile-queries` in `util::common`.
pub profile_channel: Lock<Option<mpsc::Sender<ProfileQueriesMsg>>>, pub profile_channel: Lock<Option<mpsc::Sender<ProfileQueriesMsg>>>,
/// Used by `-Z self-profile`. /// Used by -Z self-profile
pub self_profiling_active: bool, pub self_profiling: Option<Arc<PlMutex<SelfProfiler>>>,
/// Used by `-Z self-profile`.
pub self_profiling: Lock<SelfProfiler>,
/// Some measurements that are being gathered during compilation. /// Some measurements that are being gathered during compilation.
pub perf_stats: PerfStats, pub perf_stats: PerfStats,
@ -834,27 +833,23 @@ impl Session {
#[inline(never)] #[inline(never)]
#[cold] #[cold]
fn profiler_active<F: FnOnce(&mut SelfProfiler) -> ()>(&self, f: F) { fn profiler_active<F: FnOnce(&mut SelfProfiler) -> ()>(&self, f: F) {
let mut profiler = self.self_profiling.borrow_mut(); match &self.self_profiling {
f(&mut profiler); None => bug!("profiler_active() called but there was no profiler active"),
Some(profiler) => {
let mut p = profiler.lock();
f(&mut p);
}
}
} }
#[inline(always)] #[inline(always)]
pub fn profiler<F: FnOnce(&mut SelfProfiler) -> ()>(&self, f: F) { pub fn profiler<F: FnOnce(&mut SelfProfiler) -> ()>(&self, f: F) {
if unlikely!(self.self_profiling_active) { if unlikely!(self.self_profiling.is_some()) {
self.profiler_active(f) self.profiler_active(f)
} }
} }
pub fn print_profiler_results(&self) {
let mut profiler = self.self_profiling.borrow_mut();
profiler.print_results(&self.opts);
}
pub fn save_json_results(&self) {
let profiler = self.self_profiling.borrow();
profiler.save_results(&self.opts);
}
pub fn print_perf_stats(&self) { pub fn print_perf_stats(&self) {
println!( println!(
"Total time spent computing symbol hashes: {}", "Total time spent computing symbol hashes: {}",
@ -1136,6 +1131,10 @@ pub fn build_session_(
source_map: Lrc<source_map::SourceMap>, source_map: Lrc<source_map::SourceMap>,
driver_lint_caps: FxHashMap<lint::LintId, lint::Level>, driver_lint_caps: FxHashMap<lint::LintId, lint::Level>,
) -> Session { ) -> Session {
let self_profiler =
if sopts.debugging_opts.self_profile { Some(Arc::new(PlMutex::new(SelfProfiler::new()))) }
else { None };
let host_triple = TargetTriple::from_triple(config::host_triple()); let host_triple = TargetTriple::from_triple(config::host_triple());
let host = Target::search(&host_triple).unwrap_or_else(|e| let host = Target::search(&host_triple).unwrap_or_else(|e|
span_diagnostic span_diagnostic
@ -1185,9 +1184,6 @@ pub fn build_session_(
CguReuseTracker::new_disabled() CguReuseTracker::new_disabled()
}; };
let self_profiling_active = sopts.debugging_opts.self_profile ||
sopts.debugging_opts.profile_json;
let sess = Session { let sess = Session {
target: target_cfg, target: target_cfg,
host, host,
@ -1216,8 +1212,7 @@ pub fn build_session_(
imported_macro_spans: OneThread::new(RefCell::new(FxHashMap::default())), imported_macro_spans: OneThread::new(RefCell::new(FxHashMap::default())),
incr_comp_session: OneThread::new(RefCell::new(IncrCompSession::NotInitialized)), incr_comp_session: OneThread::new(RefCell::new(IncrCompSession::NotInitialized)),
cgu_reuse_tracker, cgu_reuse_tracker,
self_profiling_active, self_profiling: self_profiler,
self_profiling: Lock::new(SelfProfiler::new()),
profile_channel: Lock::new(None), profile_channel: Lock::new(None),
perf_stats: PerfStats { perf_stats: PerfStats {
symbol_hash_time: Lock::new(Duration::from_secs(0)), symbol_hash_time: Lock::new(Duration::from_secs(0)),

View file

@ -1,10 +1,13 @@
use std::collections::{BTreeMap, HashMap};
use std::fs; use std::fs;
use std::io::{self, Write}; use std::io::{BufWriter, Write};
use std::mem;
use std::process;
use std::thread::ThreadId; use std::thread::ThreadId;
use std::time::Instant; use std::time::{Duration, Instant, SystemTime};
use crate::session::config::{Options, OptLevel}; use crate::session::config::Options;
use rustc_data_structures::fx::FxHashMap;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)]
pub enum ProfileCategory { pub enum ProfileCategory {
@ -19,154 +22,55 @@ pub enum ProfileCategory {
#[derive(Clone, Copy, Debug, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ProfilerEvent { pub enum ProfilerEvent {
QueryStart { query_name: &'static str, category: ProfileCategory, time: Instant }, QueryStart { query_name: &'static str, category: ProfileCategory, time: u64 },
QueryEnd { query_name: &'static str, category: ProfileCategory, time: Instant }, QueryEnd { query_name: &'static str, category: ProfileCategory, time: u64 },
GenericActivityStart { category: ProfileCategory, time: Instant }, GenericActivityStart { category: ProfileCategory, time: u64 },
GenericActivityEnd { category: ProfileCategory, time: Instant }, GenericActivityEnd { category: ProfileCategory, time: u64 },
QueryCacheHit { query_name: &'static str, category: ProfileCategory }, IncrementalLoadResultStart { query_name: &'static str, time: u64 },
QueryCount { query_name: &'static str, category: ProfileCategory, count: usize }, IncrementalLoadResultEnd { query_name: &'static str, time: u64 },
IncrementalLoadResultStart { query_name: &'static str, time: Instant }, QueryCacheHit { query_name: &'static str, category: ProfileCategory, time: u64 },
IncrementalLoadResultEnd { query_name: &'static str, time: Instant }, QueryCount { query_name: &'static str, category: ProfileCategory, count: usize, time: u64 },
QueryBlockedStart { query_name: &'static str, category: ProfileCategory, time: Instant }, QueryBlockedStart { query_name: &'static str, category: ProfileCategory, time: u64 },
QueryBlockedEnd { query_name: &'static str, category: ProfileCategory, time: Instant }, QueryBlockedEnd { query_name: &'static str, category: ProfileCategory, time: u64 },
} }
impl ProfilerEvent { impl ProfilerEvent {
fn is_start_event(&self) -> bool { fn timestamp(&self) -> u64 {
use self::ProfilerEvent::*; use self::ProfilerEvent::*;
match self { match self {
QueryStart { .. } | QueryStart { time, .. } |
GenericActivityStart { .. } | QueryEnd { time, .. } |
IncrementalLoadResultStart { .. } | GenericActivityStart { time, .. } |
QueryBlockedStart { .. } => true, GenericActivityEnd { time, .. } |
QueryCacheHit { time, .. } |
QueryEnd { .. } | QueryCount { time, .. } |
GenericActivityEnd { .. } | IncrementalLoadResultStart { time, .. } |
QueryCacheHit { .. } | IncrementalLoadResultEnd { time, .. } |
QueryCount { .. } | QueryBlockedStart { time, .. } |
IncrementalLoadResultEnd { .. } | QueryBlockedEnd { time, .. } => *time
QueryBlockedEnd { .. } => false,
} }
} }
} }
fn thread_id_to_u64(tid: ThreadId) -> u64 {
unsafe { mem::transmute::<ThreadId, u64>(tid) }
}
pub struct SelfProfiler { pub struct SelfProfiler {
events: HashMap<ThreadId, Vec<ProfilerEvent>>, events: FxHashMap<ThreadId, Vec<ProfilerEvent>>,
} start_time: SystemTime,
start_instant: Instant,
struct CategoryResultData {
query_times: BTreeMap<&'static str, u64>,
query_cache_stats: BTreeMap<&'static str, (u64, u64)>, //(hits, total)
}
impl CategoryResultData {
fn new() -> CategoryResultData {
CategoryResultData {
query_times: BTreeMap::new(),
query_cache_stats: BTreeMap::new(),
}
}
fn total_time(&self) -> u64 {
self.query_times.iter().map(|(_, time)| time).sum()
}
fn total_cache_data(&self) -> (u64, u64) {
let (mut hits, mut total) = (0, 0);
for (_, (h, t)) in &self.query_cache_stats {
hits += h;
total += t;
}
(hits, total)
}
}
impl Default for CategoryResultData {
fn default() -> CategoryResultData {
CategoryResultData::new()
}
}
struct CalculatedResults {
categories: BTreeMap<ProfileCategory, CategoryResultData>,
crate_name: Option<String>,
optimization_level: OptLevel,
incremental: bool,
verbose: bool,
}
impl CalculatedResults {
fn new() -> CalculatedResults {
CalculatedResults {
categories: BTreeMap::new(),
crate_name: None,
optimization_level: OptLevel::No,
incremental: false,
verbose: false,
}
}
fn consolidate(mut cr1: CalculatedResults, cr2: CalculatedResults) -> CalculatedResults {
for (category, data) in cr2.categories {
let cr1_data = cr1.categories.entry(category).or_default();
for (query, time) in data.query_times {
*cr1_data.query_times.entry(query).or_default() += time;
}
for (query, (hits, total)) in data.query_cache_stats {
let (h, t) = cr1_data.query_cache_stats.entry(query).or_insert((0, 0));
*h += hits;
*t += total;
}
}
cr1
}
fn total_time(&self) -> u64 {
self.categories.iter().map(|(_, data)| data.total_time()).sum()
}
fn with_options(mut self, opts: &Options) -> CalculatedResults {
self.crate_name = opts.crate_name.clone();
self.optimization_level = opts.optimize;
self.incremental = opts.incremental.is_some();
self.verbose = opts.debugging_opts.verbose;
self
}
}
fn time_between_ns(start: Instant, end: Instant) -> u64 {
if start < end {
let time = end - start;
(time.as_secs() * 1_000_000_000) + (time.subsec_nanos() as u64)
} else {
debug!("time_between_ns: ignorning instance of end < start");
0
}
}
fn calculate_percent(numerator: u64, denominator: u64) -> f32 {
if denominator > 0 {
((numerator as f32) / (denominator as f32)) * 100.0
} else {
0.0
}
} }
impl SelfProfiler { impl SelfProfiler {
pub fn new() -> SelfProfiler { pub fn new() -> SelfProfiler {
let mut profiler = SelfProfiler { let profiler = SelfProfiler {
events: HashMap::new(), events: Default::default(),
start_time: SystemTime::now(),
start_instant: Instant::now(),
}; };
profiler.start_activity(ProfileCategory::Other);
profiler profiler
} }
@ -174,7 +78,7 @@ impl SelfProfiler {
pub fn start_activity(&mut self, category: ProfileCategory) { pub fn start_activity(&mut self, category: ProfileCategory) {
self.record(ProfilerEvent::GenericActivityStart { self.record(ProfilerEvent::GenericActivityStart {
category, category,
time: Instant::now(), time: self.get_time_from_start(),
}) })
} }
@ -182,7 +86,7 @@ impl SelfProfiler {
pub fn end_activity(&mut self, category: ProfileCategory) { pub fn end_activity(&mut self, category: ProfileCategory) {
self.record(ProfilerEvent::GenericActivityEnd { self.record(ProfilerEvent::GenericActivityEnd {
category, category,
time: Instant::now(), time: self.get_time_from_start(),
}) })
} }
@ -197,6 +101,7 @@ impl SelfProfiler {
query_name, query_name,
category, category,
count, count,
time: self.get_time_from_start(),
}) })
} }
@ -205,6 +110,7 @@ impl SelfProfiler {
self.record(ProfilerEvent::QueryCacheHit { self.record(ProfilerEvent::QueryCacheHit {
query_name, query_name,
category, category,
time: self.get_time_from_start(),
}) })
} }
@ -213,7 +119,7 @@ impl SelfProfiler {
self.record(ProfilerEvent::QueryStart { self.record(ProfilerEvent::QueryStart {
query_name, query_name,
category, category,
time: Instant::now(), time: self.get_time_from_start(),
}); });
} }
@ -222,7 +128,7 @@ impl SelfProfiler {
self.record(ProfilerEvent::QueryEnd { self.record(ProfilerEvent::QueryEnd {
query_name, query_name,
category, category,
time: Instant::now(), time: self.get_time_from_start(),
}) })
} }
@ -230,7 +136,7 @@ impl SelfProfiler {
pub fn incremental_load_result_start(&mut self, query_name: &'static str) { pub fn incremental_load_result_start(&mut self, query_name: &'static str) {
self.record(ProfilerEvent::IncrementalLoadResultStart { self.record(ProfilerEvent::IncrementalLoadResultStart {
query_name, query_name,
time: Instant::now(), time: self.get_time_from_start(),
}) })
} }
@ -238,7 +144,7 @@ impl SelfProfiler {
pub fn incremental_load_result_end(&mut self, query_name: &'static str) { pub fn incremental_load_result_end(&mut self, query_name: &'static str) {
self.record(ProfilerEvent::IncrementalLoadResultEnd { self.record(ProfilerEvent::IncrementalLoadResultEnd {
query_name, query_name,
time: Instant::now(), time: self.get_time_from_start(),
}) })
} }
@ -247,7 +153,7 @@ impl SelfProfiler {
self.record(ProfilerEvent::QueryBlockedStart { self.record(ProfilerEvent::QueryBlockedStart {
query_name, query_name,
category, category,
time: Instant::now(), time: self.get_time_from_start(),
}) })
} }
@ -256,7 +162,7 @@ impl SelfProfiler {
self.record(ProfilerEvent::QueryBlockedEnd { self.record(ProfilerEvent::QueryBlockedEnd {
query_name, query_name,
category, category,
time: Instant::now(), time: self.get_time_from_start(),
}) })
} }
@ -268,208 +174,255 @@ impl SelfProfiler {
events.push(event); events.push(event);
} }
fn calculate_thread_results(events: &Vec<ProfilerEvent>) -> CalculatedResults { #[inline]
fn get_time_from_start(&self) -> u64 {
let duration = Instant::now() - self.start_instant;
duration.as_nanos() as u64
}
pub fn dump_raw_events(&self, opts: &Options) {
use self::ProfilerEvent::*; use self::ProfilerEvent::*;
assert!( let pid = process::id();
events.last().map(|e| !e.is_start_event()).unwrap_or(true),
"there was an event running when calculate_reslts() was called"
);
let mut results = CalculatedResults::new(); let filename =
format!("{}.profile_events.json", opts.crate_name.clone().unwrap_or_default());
//(event, child time to subtract) let mut file = BufWriter::new(fs::File::create(filename).unwrap());
let mut query_stack = Vec::new();
for event in events { let threads: Vec<_> =
match event { self.events
QueryStart { .. } | GenericActivityStart { .. } => { .keys()
query_stack.push((event, 0)); .into_iter()
}, .map(|tid| format!("{}", thread_id_to_u64(*tid)))
QueryEnd { query_name, category, time: end_time } => { .collect();
let previous_query = query_stack.pop();
if let Some((QueryStart { write!(file,
query_name: p_query_name, "{{\
time: start_time, \"processes\": {{\
category: _ }, child_time_to_subtract)) = previous_query { \"{}\": {{\
assert_eq!( \"threads\": [{}],\
p_query_name, \"crate_name\": \"{}\",\
\"opt_level\": \"{:?}\",\
\"incremental\": {}\
}}\
}},\
\"events\": [\
",
pid,
threads.join(","),
opts.crate_name.clone().unwrap_or_default(),
opts.optimize,
if opts.incremental.is_some() { "true" } else { "false" },
).unwrap();
let mut is_first = true;
for (thread_id, events) in &self.events {
let thread_id = thread_id_to_u64(*thread_id);
for event in events {
if is_first {
is_first = false;
} else {
writeln!(file, ",").unwrap();
}
let (secs, nanos) = {
let time = self.start_time + Duration::from_nanos(event.timestamp());
let time_since_unix =
time.duration_since(SystemTime::UNIX_EPOCH).unwrap_or_default();
(time_since_unix.as_secs(), time_since_unix.subsec_nanos())
};
match event {
QueryStart { query_name, category, time: _ } =>
write!(file,
"{{ \
\"QueryStart\": {{ \
\"query_name\": \"{}\",\
\"category\": \"{:?}\",\
\"time\": {{\
\"secs\": {},\
\"nanos\": {}\
}},\
\"thread_id\": {}\
}}\
}}",
query_name, query_name,
"Saw a query end but the previous query wasn't the corresponding start"
);
let time_ns = time_between_ns(*start_time, *end_time);
let self_time_ns = time_ns - child_time_to_subtract;
let result_data = results.categories.entry(*category).or_default();
*result_data.query_times.entry(query_name).or_default() += self_time_ns;
if let Some((_, child_time_to_subtract)) = query_stack.last_mut() {
*child_time_to_subtract += time_ns;
}
} else {
bug!("Saw a query end but the previous event wasn't a query start");
}
}
GenericActivityEnd { category, time: end_time } => {
let previous_event = query_stack.pop();
if let Some((GenericActivityStart {
category: previous_category,
time: start_time }, child_time_to_subtract)) = previous_event {
assert_eq!(
previous_category,
category, category,
"Saw an end but the previous event wasn't the corresponding start" secs,
); nanos,
thread_id,
let time_ns = time_between_ns(*start_time, *end_time); ).unwrap(),
let self_time_ns = time_ns - child_time_to_subtract; QueryEnd { query_name, category, time: _ } =>
let result_data = results.categories.entry(*category).or_default(); write!(file,
"{{\
*result_data.query_times \"QueryEnd\": {{\
.entry("{time spent not running queries}") \"query_name\": \"{}\",\
.or_default() += self_time_ns; \"category\": \"{:?}\",\
\"time\": {{\
if let Some((_, child_time_to_subtract)) = query_stack.last_mut() { \"secs\": {},\
*child_time_to_subtract += time_ns; \"nanos\": {}\
} }},\
} else { \"thread_id\": {}\
bug!("Saw an activity end but the previous event wasn't an activity start"); }}\
} }}",
}, query_name,
QueryCacheHit { category, query_name } => { category,
let result_data = results.categories.entry(*category).or_default(); secs,
nanos,
let (hits, total) = thread_id,
result_data.query_cache_stats.entry(query_name).or_insert((0, 0)); ).unwrap(),
*hits += 1; GenericActivityStart { category, time: _ } =>
*total += 1; write!(file,
}, "{{
QueryCount { category, query_name, count } => { \"GenericActivityStart\": {{\
let result_data = results.categories.entry(*category).or_default(); \"category\": \"{:?}\",\
\"time\": {{\
let (_, totals) = \"secs\": {},\
result_data.query_cache_stats.entry(query_name).or_insert((0, 0)); \"nanos\": {}\
*totals += *count as u64; }},\
}, \"thread_id\": {}\
//we don't summarize incremental load result events in the simple output mode }}\
IncrementalLoadResultStart { .. } | IncrementalLoadResultEnd { .. } => { }, }}",
//we don't summarize parallel query blocking in the simple output mode category,
QueryBlockedStart { .. } | QueryBlockedEnd { .. } => { }, secs,
} nanos,
} thread_id,
).unwrap(),
//normalize the times to ms GenericActivityEnd { category, time: _ } =>
for (_, data) in &mut results.categories { write!(file,
for (_, time) in &mut data.query_times { "{{\
*time = *time / 1_000_000; \"GenericActivityEnd\": {{\
} \"category\": \"{:?}\",\
} \"time\": {{\
\"secs\": {},\
results \"nanos\": {}\
} }},\
\"thread_id\": {}\
fn get_results(&self, opts: &Options) -> CalculatedResults { }}\
self.events }}",
.iter() category,
.map(|(_, r)| SelfProfiler::calculate_thread_results(r)) secs,
.fold(CalculatedResults::new(), CalculatedResults::consolidate) nanos,
.with_options(opts) thread_id,
} ).unwrap(),
QueryCacheHit { query_name, category, time: _ } =>
pub fn print_results(&mut self, opts: &Options) { write!(file,
self.end_activity(ProfileCategory::Other); "{{\
\"QueryCacheHit\": {{\
let results = self.get_results(opts); \"query_name\": \"{}\",\
\"category\": \"{:?}\",\
let total_time = results.total_time() as f32; \"time\": {{\
\"secs\": {},\
let out = io::stderr(); \"nanos\": {}\
let mut lock = out.lock(); }},\
\"thread_id\": {}\
let crate_name = results.crate_name.map(|n| format!(" for {}", n)).unwrap_or_default(); }}\
}}",
writeln!(lock, "Self profiling results{}:", crate_name).unwrap(); query_name,
writeln!(lock).unwrap(); category,
secs,
writeln!(lock, "| Phase | Time (ms) \ nanos,
| Time (%) | Queries | Hits (%)") thread_id,
.unwrap(); ).unwrap(),
writeln!(lock, "| ----------------------------------------- | -------------- \ QueryCount { query_name, category, count, time: _ } =>
| -------- | -------------- | --------") write!(file,
.unwrap(); "{{\
\"QueryCount\": {{\
let mut categories: Vec<_> = results.categories.iter().collect(); \"query_name\": \"{}\",\
categories.sort_by_cached_key(|(_, d)| d.total_time()); \"category\": \"{:?}\",\
\"count\": {},\
for (category, data) in categories.iter().rev() { \"time\": {{\
let (category_hits, category_total) = data.total_cache_data(); \"secs\": {},\
let category_hit_percent = calculate_percent(category_hits, category_total); \"nanos\": {}\
}},\
writeln!( \"thread_id\": {}\
lock, }}\
"| {0: <41} | {1: >14} | {2: >8.2} | {3: >14} | {4: >8}", }}",
format!("{:?}", category), query_name,
data.total_time(), category,
((data.total_time() as f32) / total_time) * 100.0, count,
category_total, secs,
format!("{:.2}", category_hit_percent), nanos,
).unwrap(); thread_id,
).unwrap(),
//in verbose mode, show individual query data IncrementalLoadResultStart { query_name, time: _ } =>
if results.verbose { write!(file,
//don't show queries that took less than 1ms "{{\
let mut times: Vec<_> = data.query_times.iter().filter(|(_, t)| **t > 0).collect(); \"IncrementalLoadResultStart\": {{\
times.sort_by(|(_, time1), (_, time2)| time2.cmp(time1)); \"query_name\": \"{}\",\
\"time\": {{\
for (query, time) in times { \"secs\": {},\
let (hits, total) = data.query_cache_stats.get(query).unwrap_or(&(0, 0)); \"nanos\": {}\
let hit_percent = calculate_percent(*hits, *total); }},\
\"thread_id\": {}\
writeln!( }}\
lock, }}",
"| - {0: <39} | {1: >14} | {2: >8.2} | {3: >14} | {4: >8}", query_name,
query, secs,
time, nanos,
((*time as f32) / total_time) * 100.0, thread_id,
total, ).unwrap(),
format!("{:.2}", hit_percent), IncrementalLoadResultEnd { query_name, time: _ } =>
).unwrap(); write!(file,
"{{\
\"IncrementalLoadResultEnd\": {{\
\"query_name\": \"{}\",\
\"time\": {{\
\"secs\": {},\
\"nanos\": {}\
}},\
\"thread_id\": {}\
}}\
}}",
query_name,
secs,
nanos,
thread_id,
).unwrap(),
QueryBlockedStart { query_name, category, time: _ } =>
write!(file,
"{{\
\"QueryBlockedStart\": {{\
\"query_name\": \"{}\",\
\"category\": \"{:?}\",\
\"time\": {{\
\"secs\": {},\
\"nanos\": {}\
}},\
\"thread_id\": {}\
}}\
}}",
query_name,
category,
secs,
nanos,
thread_id,
).unwrap(),
QueryBlockedEnd { query_name, category, time: _ } =>
write!(file,
"{{\
\"QueryBlockedEnd\": {{\
\"query_name\": \"{}\",\
\"category\": \"{:?}\",\
\"time\": {{\
\"secs\": {},\
\"nanos\": {}\
}},\
\"thread_id\": {}\
}}\
}}",
query_name,
category,
secs,
nanos,
thread_id,
).unwrap()
} }
} }
} }
writeln!(lock).unwrap(); write!(file, "] }}").unwrap();
writeln!(lock, "Optimization level: {:?}", opts.optimize).unwrap();
writeln!(lock, "Incremental: {}", if results.incremental { "on" } else { "off" }).unwrap();
}
pub fn save_results(&self, opts: &Options) {
let results = self.get_results(opts);
let compilation_options =
format!("{{ \"optimization_level\": \"{:?}\", \"incremental\": {} }}",
results.optimization_level,
if results.incremental { "true" } else { "false" });
let mut category_data = String::new();
for (category, data) in &results.categories {
let (hits, total) = data.total_cache_data();
let hit_percent = calculate_percent(hits, total);
category_data.push_str(&format!("{{ \"category\": \"{:?}\", \"time_ms\": {}, \
\"query_count\": {}, \"query_hits\": {} }}",
category,
data.total_time(),
total,
format!("{:.2}", hit_percent)));
}
let json = format!("{{ \"category_data\": {}, \"compilation_options\": {} }}",
category_data,
compilation_options);
fs::write("self_profiler_results.json", json).unwrap();
} }
} }

View file

@ -19,6 +19,7 @@ memmap = "0.6"
log = "0.4.5" log = "0.4.5"
libc = "0.2.44" libc = "0.2.44"
jobserver = "0.1.11" jobserver = "0.1.11"
parking_lot = "0.7"
serialize = { path = "../libserialize" } serialize = { path = "../libserialize" }
syntax = { path = "../libsyntax" } syntax = { path = "../libsyntax" }

View file

@ -19,6 +19,7 @@ use rustc::util::time_graph::{self, TimeGraph, Timeline};
use rustc::hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc::hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc::ty::TyCtxt; use rustc::ty::TyCtxt;
use rustc::util::common::{time_depth, set_time_depth, print_time_passes_entry}; use rustc::util::common::{time_depth, set_time_depth, print_time_passes_entry};
use rustc::util::profiling::SelfProfiler;
use rustc_fs_util::link_or_copy; use rustc_fs_util::link_or_copy;
use rustc_data_structures::svh::Svh; use rustc_data_structures::svh::Svh;
use rustc_errors::{Handler, Level, DiagnosticBuilder, FatalError, DiagnosticId}; use rustc_errors::{Handler, Level, DiagnosticBuilder, FatalError, DiagnosticId};
@ -29,6 +30,7 @@ use syntax::ext::hygiene::Mark;
use syntax_pos::MultiSpan; use syntax_pos::MultiSpan;
use syntax_pos::symbol::Symbol; use syntax_pos::symbol::Symbol;
use jobserver::{Client, Acquired}; use jobserver::{Client, Acquired};
use parking_lot::Mutex as PlMutex;
use std::any::Any; use std::any::Any;
use std::fs; use std::fs;
@ -201,6 +203,7 @@ pub struct CodegenContext<B: WriteBackendMethods> {
// Resources needed when running LTO // Resources needed when running LTO
pub backend: B, pub backend: B,
pub time_passes: bool, pub time_passes: bool,
pub profiler: Option<Arc<PlMutex<SelfProfiler>>>,
pub lto: Lto, pub lto: Lto,
pub no_landing_pads: bool, pub no_landing_pads: bool,
pub save_temps: bool, pub save_temps: bool,
@ -254,6 +257,26 @@ impl<B: WriteBackendMethods> CodegenContext<B> {
ModuleKind::Allocator => &self.allocator_module_config, ModuleKind::Allocator => &self.allocator_module_config,
} }
} }
#[inline(never)]
#[cold]
fn profiler_active<F: FnOnce(&mut SelfProfiler) -> ()>(&self, f: F) {
match &self.profiler {
None => bug!("profiler_active() called but there was no profiler active"),
Some(profiler) => {
let mut p = profiler.lock();
f(&mut p);
}
}
}
#[inline(always)]
pub fn profile<F: FnOnce(&mut SelfProfiler) -> ()>(&self, f: F) {
if unlikely!(self.profiler.is_some()) {
self.profiler_active(f)
}
}
} }
fn generate_lto_work<B: ExtraBackendMethods>( fn generate_lto_work<B: ExtraBackendMethods>(
@ -1033,6 +1056,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
save_temps: sess.opts.cg.save_temps, save_temps: sess.opts.cg.save_temps,
opts: Arc::new(sess.opts.clone()), opts: Arc::new(sess.opts.clone()),
time_passes: sess.time_passes(), time_passes: sess.time_passes(),
profiler: sess.self_profiling.clone(),
exported_symbols, exported_symbols,
plugin_passes: sess.plugin_llvm_passes.borrow().clone(), plugin_passes: sess.plugin_llvm_passes.borrow().clone(),
remark: sess.opts.cg.remark.clone(), remark: sess.opts.cg.remark.clone(),

View file

@ -2,9 +2,11 @@
#![feature(box_patterns)] #![feature(box_patterns)]
#![feature(box_syntax)] #![feature(box_syntax)]
#![feature(core_intrinsics)]
#![feature(custom_attribute)] #![feature(custom_attribute)]
#![feature(libc)] #![feature(libc)]
#![feature(rustc_diagnostic_macros)] #![feature(rustc_diagnostic_macros)]
#![feature(stmt_expr_attributes)]
#![feature(in_band_lifetimes)] #![feature(in_band_lifetimes)]
#![feature(nll)] #![feature(nll)]
#![allow(unused_attributes)] #![allow(unused_attributes)]
@ -20,6 +22,7 @@
#[macro_use] extern crate log; #[macro_use] extern crate log;
#[macro_use] extern crate rustc; #[macro_use] extern crate rustc;
#[macro_use] extern crate rustc_data_structures;
#[macro_use] extern crate syntax; #[macro_use] extern crate syntax;
use std::path::PathBuf; use std::path::PathBuf;

View file

@ -349,14 +349,6 @@ pub fn compile_input(
sess.print_perf_stats(); sess.print_perf_stats();
} }
if sess.opts.debugging_opts.self_profile {
sess.print_profiler_results();
}
if sess.opts.debugging_opts.profile_json {
sess.save_json_results();
}
controller_entry_point!( controller_entry_point!(
compilation_done, compilation_done,
sess, sess,

View file

@ -276,6 +276,10 @@ fn run_compiler_with_pool<'a>(
&control) &control)
}; };
if sess.opts.debugging_opts.self_profile {
sess.profiler(|p| p.dump_raw_events(&sess.opts));
}
(result, Some(sess)) (result, Some(sess))
} }