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