]> git.proxmox.com Git - rustc.git/blame - src/librustc/util/profiling.rs
New upstream version 1.40.0+dfsg1
[rustc.git] / src / librustc / util / profiling.rs
CommitLineData
48663c56 1use std::error::Error;
dc9dc135 2use std::fs;
48663c56 3use std::mem::{self, Discriminant};
dc9dc135 4use std::path::Path;
532ac7d7 5use std::process;
e74abb32 6use std::sync::Arc;
9fa01778 7use std::thread::ThreadId;
48663c56 8use std::u32;
b7449926 9
48663c56 10use crate::ty::query::QueryName;
532ac7d7 11
48663c56
XL
12use measureme::{StringId, TimestampKind};
13
14/// MmapSerializatioSink is faster on macOS and Linux
15/// but FileSerializationSink is faster on Windows
16#[cfg(not(windows))]
e74abb32 17type SerializationSink = measureme::MmapSerializationSink;
48663c56 18#[cfg(windows)]
e74abb32
XL
19type SerializationSink = measureme::FileSerializationSink;
20
21type Profiler = measureme::Profiler<SerializationSink>;
22
b7449926 23
9fa01778
XL
24#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)]
25pub enum ProfileCategory {
26 Parsing,
27 Expansion,
28 TypeChecking,
29 BorrowChecking,
30 Codegen,
31 Linking,
32 Other,
33}
b7449926 34
48663c56
XL
35bitflags! {
36 struct EventFilter: u32 {
37 const GENERIC_ACTIVITIES = 1 << 0;
38 const QUERY_PROVIDERS = 1 << 1;
39 const QUERY_CACHE_HITS = 1 << 2;
40 const QUERY_BLOCKED = 1 << 3;
41 const INCR_CACHE_LOADS = 1 << 4;
42
43 const DEFAULT = Self::GENERIC_ACTIVITIES.bits |
44 Self::QUERY_PROVIDERS.bits |
45 Self::QUERY_BLOCKED.bits |
46 Self::INCR_CACHE_LOADS.bits;
47
48 // empty() and none() aren't const-fns unfortunately
49 const NONE = 0;
50 const ALL = !Self::NONE.bits;
9fa01778
XL
51 }
52}
b7449926 53
48663c56
XL
54const EVENT_FILTERS_BY_NAME: &[(&str, EventFilter)] = &[
55 ("none", EventFilter::NONE),
56 ("all", EventFilter::ALL),
57 ("generic-activity", EventFilter::GENERIC_ACTIVITIES),
58 ("query-provider", EventFilter::QUERY_PROVIDERS),
59 ("query-cache-hit", EventFilter::QUERY_CACHE_HITS),
60 ("query-blocked" , EventFilter::QUERY_BLOCKED),
61 ("incr-cache-load", EventFilter::INCR_CACHE_LOADS),
62];
63
532ac7d7
XL
64fn thread_id_to_u64(tid: ThreadId) -> u64 {
65 unsafe { mem::transmute::<ThreadId, u64>(tid) }
9fa01778
XL
66}
67
e74abb32
XL
68
69/// A reference to the SelfProfiler. It can be cloned and sent across thread
70/// boundaries at will.
71#[derive(Clone)]
72pub struct SelfProfilerRef {
73 // This field is `None` if self-profiling is disabled for the current
74 // compilation session.
75 profiler: Option<Arc<SelfProfiler>>,
76
77 // We store the filter mask directly in the reference because that doesn't
78 // cost anything and allows for filtering with checking if the profiler is
79 // actually enabled.
80 event_filter_mask: EventFilter,
81}
82
83impl SelfProfilerRef {
84
85 pub fn new(profiler: Option<Arc<SelfProfiler>>) -> SelfProfilerRef {
86 // If there is no SelfProfiler then the filter mask is set to NONE,
87 // ensuring that nothing ever tries to actually access it.
88 let event_filter_mask = profiler
89 .as_ref()
90 .map(|p| p.event_filter_mask)
91 .unwrap_or(EventFilter::NONE);
92
93 SelfProfilerRef {
94 profiler,
95 event_filter_mask,
96 }
97 }
98
99 // This shim makes sure that calls only get executed if the filter mask
100 // lets them pass. It also contains some trickery to make sure that
101 // code is optimized for non-profiling compilation sessions, i.e. anything
102 // past the filter check is never inlined so it doesn't clutter the fast
103 // path.
104 #[inline(always)]
105 fn exec<F>(&self, event_filter: EventFilter, f: F) -> TimingGuard<'_>
106 where F: for<'a> FnOnce(&'a SelfProfiler) -> TimingGuard<'a>
107 {
108 #[inline(never)]
109 fn cold_call<F>(profiler_ref: &SelfProfilerRef, f: F) -> TimingGuard<'_>
110 where F: for<'a> FnOnce(&'a SelfProfiler) -> TimingGuard<'a>
111 {
112 let profiler = profiler_ref.profiler.as_ref().unwrap();
113 f(&**profiler)
114 }
115
116 if unlikely!(self.event_filter_mask.contains(event_filter)) {
117 cold_call(self, f)
118 } else {
119 TimingGuard::none()
120 }
121 }
122
123 /// Start profiling a generic activity. Profiling continues until the
124 /// TimingGuard returned from this call is dropped.
125 #[inline(always)]
126 pub fn generic_activity(&self, event_id: &str) -> TimingGuard<'_> {
127 self.exec(EventFilter::GENERIC_ACTIVITIES, |profiler| {
128 let event_id = profiler.profiler.alloc_string(event_id);
129 TimingGuard::start(
130 profiler,
131 profiler.generic_activity_event_kind,
132 event_id
133 )
134 })
135 }
136
137 /// Start profiling a query provider. Profiling continues until the
138 /// TimingGuard returned from this call is dropped.
139 #[inline(always)]
140 pub fn query_provider(&self, query_name: QueryName) -> TimingGuard<'_> {
141 self.exec(EventFilter::QUERY_PROVIDERS, |profiler| {
142 let event_id = SelfProfiler::get_query_name_string_id(query_name);
143 TimingGuard::start(profiler, profiler.query_event_kind, event_id)
144 })
145 }
146
147 /// Record a query in-memory cache hit.
148 #[inline(always)]
149 pub fn query_cache_hit(&self, query_name: QueryName) {
150 self.non_guard_query_event(
151 |profiler| profiler.query_cache_hit_event_kind,
152 query_name,
153 EventFilter::QUERY_CACHE_HITS,
154 TimestampKind::Instant,
155 );
156 }
157
158 /// Start profiling a query being blocked on a concurrent execution.
159 /// Profiling continues until the TimingGuard returned from this call is
160 /// dropped.
161 #[inline(always)]
162 pub fn query_blocked(&self, query_name: QueryName) -> TimingGuard<'_> {
163 self.exec(EventFilter::QUERY_BLOCKED, |profiler| {
164 let event_id = SelfProfiler::get_query_name_string_id(query_name);
165 TimingGuard::start(profiler, profiler.query_blocked_event_kind, event_id)
166 })
167 }
168
169 /// Start profiling how long it takes to load a query result from the
170 /// incremental compilation on-disk cache. Profiling continues until the
171 /// TimingGuard returned from this call is dropped.
172 #[inline(always)]
173 pub fn incr_cache_loading(&self, query_name: QueryName) -> TimingGuard<'_> {
174 self.exec(EventFilter::INCR_CACHE_LOADS, |profiler| {
175 let event_id = SelfProfiler::get_query_name_string_id(query_name);
176 TimingGuard::start(
177 profiler,
178 profiler.incremental_load_result_event_kind,
179 event_id
180 )
181 })
182 }
183
184 #[inline(always)]
185 fn non_guard_query_event(
186 &self,
187 event_kind: fn(&SelfProfiler) -> StringId,
188 query_name: QueryName,
189 event_filter: EventFilter,
190 timestamp_kind: TimestampKind
191 ) {
192 drop(self.exec(event_filter, |profiler| {
193 let event_id = SelfProfiler::get_query_name_string_id(query_name);
194 let thread_id = thread_id_to_u64(std::thread::current().id());
195
196 profiler.profiler.record_event(
197 event_kind(profiler),
198 event_id,
199 thread_id,
200 timestamp_kind,
201 );
202
203 TimingGuard::none()
204 }));
205 }
206}
207
532ac7d7 208pub struct SelfProfiler {
48663c56
XL
209 profiler: Profiler,
210 event_filter_mask: EventFilter,
211 query_event_kind: StringId,
212 generic_activity_event_kind: StringId,
213 incremental_load_result_event_kind: StringId,
214 query_blocked_event_kind: StringId,
215 query_cache_hit_event_kind: StringId,
b7449926
XL
216}
217
218impl SelfProfiler {
dc9dc135
XL
219 pub fn new(
220 output_directory: &Path,
221 crate_name: Option<&str>,
222 event_filters: &Option<Vec<String>>
223 ) -> Result<SelfProfiler, Box<dyn Error>> {
224 fs::create_dir_all(output_directory)?;
225
226 let crate_name = crate_name.unwrap_or("unknown-crate");
227 let filename = format!("{}-{}.rustc_profile", crate_name, process::id());
228 let path = output_directory.join(&filename);
229 let profiler = Profiler::new(&path)?;
48663c56
XL
230
231 let query_event_kind = profiler.alloc_string("Query");
232 let generic_activity_event_kind = profiler.alloc_string("GenericActivity");
233 let incremental_load_result_event_kind = profiler.alloc_string("IncrementalLoadResult");
234 let query_blocked_event_kind = profiler.alloc_string("QueryBlocked");
235 let query_cache_hit_event_kind = profiler.alloc_string("QueryCacheHit");
236
237 let mut event_filter_mask = EventFilter::empty();
238
239 if let Some(ref event_filters) = *event_filters {
240 let mut unknown_events = vec![];
241 for item in event_filters {
242 if let Some(&(_, mask)) = EVENT_FILTERS_BY_NAME.iter()
243 .find(|&(name, _)| name == item) {
244 event_filter_mask |= mask;
245 } else {
246 unknown_events.push(item.clone());
247 }
248 }
249
250 // Warn about any unknown event names
251 if unknown_events.len() > 0 {
252 unknown_events.sort();
253 unknown_events.dedup();
254
255 warn!("Unknown self-profiler events specified: {}. Available options are: {}.",
256 unknown_events.join(", "),
257 EVENT_FILTERS_BY_NAME.iter()
258 .map(|&(name, _)| name.to_string())
259 .collect::<Vec<_>>()
260 .join(", "));
261 }
262 } else {
263 event_filter_mask = EventFilter::DEFAULT;
264 }
265
266 Ok(SelfProfiler {
267 profiler,
268 event_filter_mask,
269 query_event_kind,
270 generic_activity_event_kind,
271 incremental_load_result_event_kind,
272 query_blocked_event_kind,
273 query_cache_hit_event_kind,
274 })
275 }
276
277 fn get_query_name_string_id(query_name: QueryName) -> StringId {
278 let discriminant = unsafe {
279 mem::transmute::<Discriminant<QueryName>, u64>(mem::discriminant(&query_name))
b7449926
XL
280 };
281
48663c56
XL
282 StringId::reserved(discriminant as u32)
283 }
284
285 pub fn register_query_name(&self, query_name: QueryName) {
286 let id = SelfProfiler::get_query_name_string_id(query_name);
287 self.profiler.alloc_string_with_reserved_id(id, query_name.as_str());
b7449926 288 }
e74abb32 289}
b7449926 290
e74abb32
XL
291#[must_use]
292pub struct TimingGuard<'a>(Option<measureme::TimingGuard<'a, SerializationSink>>);
9fa01778 293
e74abb32 294impl<'a> TimingGuard<'a> {
9fa01778 295 #[inline]
e74abb32
XL
296 pub fn start(
297 profiler: &'a SelfProfiler,
298 event_kind: StringId,
299 event_id: StringId,
300 ) -> TimingGuard<'a> {
48663c56 301 let thread_id = thread_id_to_u64(std::thread::current().id());
e74abb32
XL
302 let raw_profiler = &profiler.profiler;
303 let timing_guard = raw_profiler.start_recording_interval_event(event_kind,
304 event_id,
305 thread_id);
306 TimingGuard(Some(timing_guard))
9fa01778
XL
307 }
308
532ac7d7 309 #[inline]
e74abb32
XL
310 pub fn none() -> TimingGuard<'a> {
311 TimingGuard(None)
b7449926
XL
312 }
313}