Skip to content

Commit 45482c6

Browse files
committed
Basic profiling
1 parent 40cb447 commit 45482c6

File tree

11 files changed

+259
-1
lines changed

11 files changed

+259
-1
lines changed

src/librustc/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ pub mod util {
165165
pub mod nodemap;
166166
pub mod fs;
167167
pub mod time_graph;
168+
pub mod profiling;
168169
}
169170

170171
// A private module so that macro-expanded idents like

src/librustc/session/config.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ pub enum Sanitizer {
6565
Thread,
6666
}
6767

68-
#[derive(Clone, Copy, PartialEq, Hash)]
68+
#[derive(Clone, Copy, Debug, PartialEq, Hash)]
6969
pub enum OptLevel {
7070
No, // -O0
7171
Less, // -O1
@@ -1367,6 +1367,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
13671367
"disables the 'leak check' for subtyping; unsound, but useful for tests"),
13681368
crate_attr: Vec<String> = (Vec::new(), parse_string_push, [TRACKED],
13691369
"inject the given attribute in the crate"),
1370+
self_profile: bool = (false, parse_bool, [UNTRACKED],
1371+
"run the self profiler"),
13701372
}
13711373

13721374
pub fn default_lib_output() -> CrateType {

src/librustc/session/mod.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ use syntax::parse::ParseSess;
4040
use syntax::{ast, codemap};
4141
use syntax::feature_gate::AttributeType;
4242
use syntax_pos::{MultiSpan, Span};
43+
use util::profiling::SelfProfiler;
4344

4445
use rustc_target::spec::{LinkerFlavor, PanicStrategy};
4546
use rustc_target::spec::{Target, TargetTriple};
@@ -133,6 +134,9 @@ pub struct Session {
133134
/// Used by -Z profile-queries in util::common
134135
pub profile_channel: Lock<Option<mpsc::Sender<ProfileQueriesMsg>>>,
135136

137+
/// Used by -Z self-profile
138+
pub self_profiling: Lock<SelfProfiler>,
139+
136140
/// Some measurements that are being gathered during compilation.
137141
pub perf_stats: PerfStats,
138142

@@ -825,6 +829,16 @@ impl Session {
825829
}
826830
}
827831

832+
pub fn profiler<F: FnOnce(&mut SelfProfiler) -> ()>(&self, f: F) {
833+
let mut profiler = self.self_profiling.borrow_mut();
834+
f(&mut profiler);
835+
}
836+
837+
pub fn print_profiler_results(&self) {
838+
let mut profiler = self.self_profiling.borrow_mut();
839+
profiler.print_results(&self.opts);
840+
}
841+
828842
pub fn print_perf_stats(&self) {
829843
println!(
830844
"Total time spent computing symbol hashes: {}",
@@ -1125,6 +1139,7 @@ pub fn build_session_(
11251139
imported_macro_spans: OneThread::new(RefCell::new(HashMap::new())),
11261140
incr_comp_session: OneThread::new(RefCell::new(IncrCompSession::NotInitialized)),
11271141
ignored_attr_names: ich::compute_ignored_attr_names(),
1142+
self_profiling: Lock::new(SelfProfiler::new()),
11281143
profile_channel: Lock::new(None),
11291144
perf_stats: PerfStats {
11301145
symbol_hash_time: Lock::new(Duration::from_secs(0)),

src/librustc/ty/query/config.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use ty::subst::Substs;
2121
use ty::query::queries;
2222
use ty::query::Query;
2323
use ty::query::QueryCache;
24+
use util::profiling::ProfileCategory;
2425

2526
use std::hash::Hash;
2627
use std::fmt::Debug;
@@ -33,6 +34,7 @@ use ich::StableHashingContext;
3334

3435
pub trait QueryConfig<'tcx> {
3536
const NAME: &'static str;
37+
const CATEGORY: ProfileCategory;
3638

3739
type Key: Eq + Hash + Clone + Debug;
3840
type Value: Clone + for<'a> HashStable<StableHashingContext<'a>>;

src/librustc/ty/query/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ use ty::steal::Steal;
4646
use ty::subst::Substs;
4747
use util::nodemap::{DefIdSet, DefIdMap, ItemLocalSet};
4848
use util::common::{ErrorReported};
49+
use util::profiling::ProfileCategory::*;
4950

5051
use rustc_data_structures::indexed_set::IdxSetBuf;
5152
use rustc_target::spec::PanicStrategy;

src/librustc/ty/query/plumbing.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,13 +379,15 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
379379

380380
if dep_node.kind.is_anon() {
381381
profq_msg!(self, ProfileQueriesMsg::ProviderBegin);
382+
self.sess.profiler(|p| p.start_activity(Q::CATEGORY));
382383

383384
let res = job.start(self, |tcx| {
384385
tcx.dep_graph.with_anon_task(dep_node.kind, || {
385386
Q::compute(tcx.global_tcx(), key)
386387
})
387388
});
388389

390+
self.sess.profiler(|p| p.end_activity(Q::CATEGORY));
389391
profq_msg!(self, ProfileQueriesMsg::ProviderEnd);
390392
let ((result, dep_node_index), diagnostics) = res;
391393

@@ -523,6 +525,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
523525
key, dep_node);
524526

525527
profq_msg!(self, ProfileQueriesMsg::ProviderBegin);
528+
self.sess.profiler(|p| p.start_activity(Q::CATEGORY));
529+
526530
let res = job.start(self, |tcx| {
527531
if dep_node.kind.is_eval_always() {
528532
tcx.dep_graph.with_eval_always_task(dep_node,
@@ -536,6 +540,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
536540
Q::compute)
537541
}
538542
});
543+
544+
self.sess.profiler(|p| p.end_activity(Q::CATEGORY));
539545
profq_msg!(self, ProfileQueriesMsg::ProviderEnd);
540546

541547
let ((result, dep_node_index), diagnostics) = res;
@@ -655,6 +661,7 @@ macro_rules! define_queries_inner {
655661
rustc_data_structures::stable_hasher::StableHasher,
656662
ich::StableHashingContext
657663
};
664+
use util::profiling::ProfileCategory;
658665

659666
define_queries_struct! {
660667
tcx: $tcx,
@@ -768,6 +775,7 @@ macro_rules! define_queries_inner {
768775
type Value = $V;
769776

770777
const NAME: &'static str = stringify!($name);
778+
const CATEGORY: ProfileCategory = $category;
771779
}
772780

773781
impl<$tcx> QueryAccessors<$tcx> for queries::$name<$tcx> {

src/librustc/util/profiling.rs

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use session::config::Options;
12+
13+
use std::io::{self, StdoutLock, Write};
14+
use std::time::Instant;
15+
16+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
17+
pub enum ProfileCategory {
18+
Parsing,
19+
Expansion,
20+
TypeChecking,
21+
BorrowChecking,
22+
Codegen,
23+
Linking,
24+
Other,
25+
}
26+
27+
struct Categories<T> {
28+
parsing: T,
29+
expansion: T,
30+
type_checking: T,
31+
borrow_checking: T,
32+
codegen: T,
33+
linking: T,
34+
other: T,
35+
}
36+
37+
impl<T: Default> Categories<T> {
38+
fn new() -> Categories<T> {
39+
Categories {
40+
parsing: T::default(),
41+
expansion: T::default(),
42+
type_checking: T::default(),
43+
borrow_checking: T::default(),
44+
codegen: T::default(),
45+
linking: T::default(),
46+
other: T::default(),
47+
}
48+
}
49+
}
50+
51+
impl<T> Categories<T> {
52+
fn get(&self, category: ProfileCategory) -> &T {
53+
match category {
54+
ProfileCategory::Parsing => &self.parsing,
55+
ProfileCategory::Expansion => &self.expansion,
56+
ProfileCategory::TypeChecking => &self.type_checking,
57+
ProfileCategory::BorrowChecking => &self.borrow_checking,
58+
ProfileCategory::Codegen => &self.codegen,
59+
ProfileCategory::Linking => &self.linking,
60+
ProfileCategory::Other => &self.other,
61+
}
62+
}
63+
64+
fn set(&mut self, category: ProfileCategory, value: T) {
65+
match category {
66+
ProfileCategory::Parsing => self.parsing = value,
67+
ProfileCategory::Expansion => self.expansion = value,
68+
ProfileCategory::TypeChecking => self.type_checking = value,
69+
ProfileCategory::BorrowChecking => self.borrow_checking = value,
70+
ProfileCategory::Codegen => self.codegen = value,
71+
ProfileCategory::Linking => self.linking = value,
72+
ProfileCategory::Other => self.other = value,
73+
}
74+
}
75+
}
76+
77+
struct CategoryData {
78+
times: Categories<u64>,
79+
}
80+
81+
impl CategoryData {
82+
fn new() -> CategoryData {
83+
CategoryData {
84+
times: Categories::new(),
85+
}
86+
}
87+
88+
fn print(&self, lock: &mut StdoutLock) {
89+
writeln!(lock, "{0: <15} \t\t {1: <15}", "Parsing", self.times.parsing / 1_000_000).unwrap();
90+
writeln!(lock, "{0: <15} \t\t {1: <15}", "Expansion", self.times.expansion / 1_000_000).unwrap();
91+
writeln!(lock, "{0: <15} \t\t {1: <15}", "TypeChecking", self.times.type_checking / 1_000_000).unwrap();
92+
writeln!(lock, "{0: <15} \t\t {1: <15}", "BorrowChecking", self.times.borrow_checking / 1_000_000).unwrap();
93+
writeln!(lock, "{0: <15} \t\t {1: <15}", "Codegen", self.times.codegen / 1_000_000).unwrap();
94+
writeln!(lock, "{0: <15} \t\t {1: <15}", "Linking", self.times.linking / 1_000_000).unwrap();
95+
writeln!(lock, "{0: <15} \t\t {1: <15}", "Other", self.times.other / 1_000_000).unwrap();
96+
}
97+
}
98+
99+
pub struct SelfProfiler {
100+
timer_stack: Vec<ProfileCategory>,
101+
data: CategoryData,
102+
current_timer: Instant,
103+
}
104+
105+
pub struct ProfilerActivity<'a>(ProfileCategory, &'a mut SelfProfiler);
106+
107+
impl<'a> Drop for ProfilerActivity<'a> {
108+
fn drop(&mut self) {
109+
let ProfilerActivity (category, profiler) = self;
110+
111+
profiler.end_activity(*category);
112+
}
113+
}
114+
115+
impl SelfProfiler {
116+
pub fn new() -> SelfProfiler {
117+
let mut profiler = SelfProfiler {
118+
timer_stack: Vec::new(),
119+
data: CategoryData::new(),
120+
current_timer: Instant::now(),
121+
};
122+
123+
profiler.start_activity(ProfileCategory::Other);
124+
125+
profiler
126+
}
127+
128+
pub fn start_activity(&mut self, category: ProfileCategory) {
129+
match self.timer_stack.last().cloned() {
130+
None => {
131+
self.current_timer = Instant::now();
132+
},
133+
Some(current_category) if current_category == category => {
134+
//since the current category is the same as the new activity's category,
135+
//we don't need to do anything with the timer, we just need to push it on the stack
136+
}
137+
Some(current_category) => {
138+
let elapsed = self.stop_timer();
139+
140+
//record the current category's time
141+
let new_time = self.data.times.get(current_category) + elapsed;
142+
self.data.times.set(current_category, new_time);
143+
}
144+
}
145+
146+
//push the new category
147+
self.timer_stack.push(category);
148+
}
149+
150+
pub fn end_activity(&mut self, category: ProfileCategory) {
151+
match self.timer_stack.pop() {
152+
None => bug!("end_activity() was called but there was no running activity"),
153+
Some(c) =>
154+
assert!(
155+
c == category,
156+
"end_activity() was called but a different activity was running"),
157+
}
158+
159+
//check if the new running timer is in the same category as this one
160+
//if it is, we don't need to do anything
161+
if let Some(c) = self.timer_stack.last() {
162+
if *c == category {
163+
return;
164+
}
165+
}
166+
167+
//the new timer is different than the previous, so record the elapsed time and start a new timer
168+
let elapsed = self.stop_timer();
169+
let new_time = self.data.times.get(category) + elapsed;
170+
self.data.times.set(category, new_time);
171+
}
172+
173+
fn stop_timer(&mut self) -> u64 {
174+
let elapsed = self.current_timer.elapsed();
175+
176+
self.current_timer = Instant::now();
177+
178+
(elapsed.as_secs() * 1_000_000_000) + (elapsed.subsec_nanos() as u64)
179+
}
180+
181+
pub fn print_results(&mut self, opts: &Options) {
182+
self.end_activity(ProfileCategory::Other);
183+
184+
assert!(self.timer_stack.is_empty(), "there were timers running when print_results() was called");
185+
186+
let out = io::stdout();
187+
let mut lock = out.lock();
188+
189+
let crate_name = opts.crate_name.as_ref().map(|n| format!(" for {}", n)).unwrap_or_default();
190+
191+
writeln!(lock, "Self profiling results{}:", crate_name).unwrap();
192+
193+
self.data.print(&mut lock);
194+
195+
writeln!(lock).unwrap();
196+
writeln!(lock, "Optimization level: {:?}", opts.optimize).unwrap();
197+
198+
let incremental = if opts.incremental.is_some() { "on" } else { "off" };
199+
writeln!(lock, "Incremental: {}", incremental).unwrap();
200+
}
201+
202+
pub fn record_activity<'a>(&'a mut self, category: ProfileCategory) -> ProfilerActivity<'a> {
203+
self.start_activity(category);
204+
205+
ProfilerActivity(category, self)
206+
}
207+
}

src/librustc_codegen_llvm/base.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ use rustc::dep_graph::{DepNode, DepConstructor};
4545
use rustc::middle::cstore::{self, LinkMeta, LinkagePreference};
4646
use rustc::middle::exported_symbols;
4747
use rustc::util::common::{time, print_time_passes_entry};
48+
use rustc::util::profiling::ProfileCategory;
4849
use rustc::session::config::{self, NoDebugInfo};
4950
use rustc::session::Session;
5051
use rustc_incremental;
@@ -741,11 +742,13 @@ pub fn codegen_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
741742
let link_meta = link::build_link_meta(crate_hash);
742743

743744
// Codegen the metadata.
745+
tcx.sess.profiler(|p| p.start_activity(ProfileCategory::Codegen));
744746
let llmod_id = "metadata";
745747
let metadata_llvm_module = ModuleLlvm::new(tcx.sess, llmod_id);
746748
let metadata = time(tcx.sess, "write metadata", || {
747749
write_metadata(tcx, &metadata_llvm_module, &link_meta)
748750
});
751+
tcx.sess.profiler(|p| p.end_activity(ProfileCategory::Codegen));
749752

750753
let metadata_module = ModuleCodegen {
751754
name: link::METADATA_MODULE_NAME.to_string(),

src/librustc_codegen_llvm/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ use rustc::session::config::{OutputFilenames, OutputType, PrintRequest};
8484
use rustc::ty::{self, TyCtxt};
8585
use rustc::util::time_graph;
8686
use rustc::util::nodemap::{FxHashSet, FxHashMap};
87+
use rustc::util::profiling::ProfileCategory;
8788
use rustc_mir::monomorphize;
8889
use rustc_codegen_utils::codegen_backend::CodegenBackend;
8990

@@ -240,10 +241,12 @@ impl CodegenBackend for LlvmCodegenBackend {
240241

241242
// Run the linker on any artifacts that resulted from the LLVM run.
242243
// This should produce either a finished executable or library.
244+
sess.profiler(|p| p.start_activity(ProfileCategory::Linking));
243245
time(sess, "linking", || {
244246
back::link::link_binary(sess, &ongoing_codegen,
245247
outputs, &ongoing_codegen.crate_name.as_str());
246248
});
249+
sess.profiler(|p| p.end_activity(ProfileCategory::Linking));
247250

248251
// Now that we won't touch anything in the incremental compilation directory
249252
// any more, we can finalize it (which involves renaming it)

0 commit comments

Comments
 (0)