]> git.lizzy.rs Git - rust.git/blob - src/librustc_data_structures/profiling.rs
Rollup merge of #67430 - tspiteri:minus-inf, r=Dylan-DPC
[rust.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 /// A reference to the SelfProfiler. It can be cloned and sent across thread
71 /// boundaries at will.
72 #[derive(Clone)]
73 pub struct SelfProfilerRef {
74     // This field is `None` if self-profiling is disabled for the current
75     // compilation session.
76     profiler: Option<Arc<SelfProfiler>>,
77
78     // We store the filter mask directly in the reference because that doesn't
79     // cost anything and allows for filtering with checking if the profiler is
80     // actually enabled.
81     event_filter_mask: EventFilter,
82 }
83
84 impl SelfProfilerRef {
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 =
89             profiler.as_ref().map(|p| p.event_filter_mask).unwrap_or(EventFilter::NONE);
90
91         SelfProfilerRef { profiler, event_filter_mask }
92     }
93
94     // This shim makes sure that calls only get executed if the filter mask
95     // lets them pass. It also contains some trickery to make sure that
96     // code is optimized for non-profiling compilation sessions, i.e. anything
97     // past the filter check is never inlined so it doesn't clutter the fast
98     // path.
99     #[inline(always)]
100     fn exec<F>(&self, event_filter: EventFilter, f: F) -> TimingGuard<'_>
101     where
102         F: for<'a> FnOnce(&'a SelfProfiler) -> TimingGuard<'a>,
103     {
104         #[inline(never)]
105         fn cold_call<F>(profiler_ref: &SelfProfilerRef, f: F) -> TimingGuard<'_>
106         where
107             F: for<'a> FnOnce(&'a SelfProfiler) -> TimingGuard<'a>,
108         {
109             let profiler = profiler_ref.profiler.as_ref().unwrap();
110             f(&**profiler)
111         }
112
113         if unlikely!(self.event_filter_mask.contains(event_filter)) {
114             cold_call(self, f)
115         } else {
116             TimingGuard::none()
117         }
118     }
119
120     /// Start profiling a generic activity. Profiling continues until the
121     /// TimingGuard returned from this call is dropped.
122     #[inline(always)]
123     pub fn generic_activity(&self, event_id: &str) -> TimingGuard<'_> {
124         self.exec(EventFilter::GENERIC_ACTIVITIES, |profiler| {
125             let event_id = profiler.profiler.alloc_string(event_id);
126             TimingGuard::start(profiler, profiler.generic_activity_event_kind, event_id)
127         })
128     }
129
130     /// Start profiling a query provider. Profiling continues until the
131     /// TimingGuard returned from this call is dropped.
132     #[inline(always)]
133     pub fn query_provider(&self, query_name: impl QueryName) -> TimingGuard<'_> {
134         self.exec(EventFilter::QUERY_PROVIDERS, |profiler| {
135             let event_id = SelfProfiler::get_query_name_string_id(query_name);
136             TimingGuard::start(profiler, profiler.query_event_kind, event_id)
137         })
138     }
139
140     /// Record a query in-memory cache hit.
141     #[inline(always)]
142     pub fn query_cache_hit(&self, query_name: impl QueryName) {
143         self.instant_query_event(
144             |profiler| profiler.query_cache_hit_event_kind,
145             query_name,
146             EventFilter::QUERY_CACHE_HITS,
147         );
148     }
149
150     /// Start profiling a query being blocked on a concurrent execution.
151     /// Profiling continues until the TimingGuard returned from this call is
152     /// dropped.
153     #[inline(always)]
154     pub fn query_blocked(&self, query_name: impl QueryName) -> TimingGuard<'_> {
155         self.exec(EventFilter::QUERY_BLOCKED, |profiler| {
156             let event_id = SelfProfiler::get_query_name_string_id(query_name);
157             TimingGuard::start(profiler, profiler.query_blocked_event_kind, event_id)
158         })
159     }
160
161     /// Start profiling how long it takes to load a query result from the
162     /// incremental compilation on-disk cache. Profiling continues until the
163     /// TimingGuard returned from this call is dropped.
164     #[inline(always)]
165     pub fn incr_cache_loading(&self, query_name: impl QueryName) -> TimingGuard<'_> {
166         self.exec(EventFilter::INCR_CACHE_LOADS, |profiler| {
167             let event_id = SelfProfiler::get_query_name_string_id(query_name);
168             TimingGuard::start(profiler, profiler.incremental_load_result_event_kind, event_id)
169         })
170     }
171
172     #[inline(always)]
173     fn instant_query_event(
174         &self,
175         event_kind: fn(&SelfProfiler) -> StringId,
176         query_name: impl QueryName,
177         event_filter: EventFilter,
178     ) {
179         drop(self.exec(event_filter, |profiler| {
180             let event_id = SelfProfiler::get_query_name_string_id(query_name);
181             let thread_id = thread_id_to_u32(std::thread::current().id());
182
183             profiler.profiler.record_instant_event(event_kind(profiler), event_id, thread_id);
184
185             TimingGuard::none()
186         }));
187     }
188
189     pub fn register_queries(&self, f: impl FnOnce(&SelfProfiler)) {
190         if let Some(profiler) = &self.profiler {
191             f(&profiler)
192         }
193     }
194 }
195
196 pub struct SelfProfiler {
197     profiler: Profiler,
198     event_filter_mask: EventFilter,
199     query_event_kind: StringId,
200     generic_activity_event_kind: StringId,
201     incremental_load_result_event_kind: StringId,
202     query_blocked_event_kind: StringId,
203     query_cache_hit_event_kind: StringId,
204 }
205
206 impl SelfProfiler {
207     pub fn new(
208         output_directory: &Path,
209         crate_name: Option<&str>,
210         event_filters: &Option<Vec<String>>,
211     ) -> Result<SelfProfiler, Box<dyn Error>> {
212         fs::create_dir_all(output_directory)?;
213
214         let crate_name = crate_name.unwrap_or("unknown-crate");
215         let filename = format!("{}-{}.rustc_profile", crate_name, process::id());
216         let path = output_directory.join(&filename);
217         let profiler = Profiler::new(&path)?;
218
219         let query_event_kind = profiler.alloc_string("Query");
220         let generic_activity_event_kind = profiler.alloc_string("GenericActivity");
221         let incremental_load_result_event_kind = profiler.alloc_string("IncrementalLoadResult");
222         let query_blocked_event_kind = profiler.alloc_string("QueryBlocked");
223         let query_cache_hit_event_kind = profiler.alloc_string("QueryCacheHit");
224
225         let mut event_filter_mask = EventFilter::empty();
226
227         if let Some(ref event_filters) = *event_filters {
228             let mut unknown_events = vec![];
229             for item in event_filters {
230                 if let Some(&(_, mask)) =
231                     EVENT_FILTERS_BY_NAME.iter().find(|&(name, _)| name == item)
232                 {
233                     event_filter_mask |= mask;
234                 } else {
235                     unknown_events.push(item.clone());
236                 }
237             }
238
239             // Warn about any unknown event names
240             if unknown_events.len() > 0 {
241                 unknown_events.sort();
242                 unknown_events.dedup();
243
244                 warn!(
245                     "Unknown self-profiler events specified: {}. Available options are: {}.",
246                     unknown_events.join(", "),
247                     EVENT_FILTERS_BY_NAME
248                         .iter()
249                         .map(|&(name, _)| name.to_string())
250                         .collect::<Vec<_>>()
251                         .join(", ")
252                 );
253             }
254         } else {
255             event_filter_mask = EventFilter::DEFAULT;
256         }
257
258         Ok(SelfProfiler {
259             profiler,
260             event_filter_mask,
261             query_event_kind,
262             generic_activity_event_kind,
263             incremental_load_result_event_kind,
264             query_blocked_event_kind,
265             query_cache_hit_event_kind,
266         })
267     }
268
269     fn get_query_name_string_id(query_name: impl QueryName) -> StringId {
270         let discriminant =
271             unsafe { mem::transmute::<Discriminant<_>, u64>(query_name.discriminant()) };
272
273         StringId::reserved(discriminant as u32)
274     }
275
276     pub fn register_query_name(&self, query_name: impl QueryName) {
277         let id = SelfProfiler::get_query_name_string_id(query_name);
278         self.profiler.alloc_string_with_reserved_id(id, query_name.as_str());
279     }
280 }
281
282 #[must_use]
283 pub struct TimingGuard<'a>(Option<measureme::TimingGuard<'a, SerializationSink>>);
284
285 impl<'a> TimingGuard<'a> {
286     #[inline]
287     pub fn start(
288         profiler: &'a SelfProfiler,
289         event_kind: StringId,
290         event_id: StringId,
291     ) -> TimingGuard<'a> {
292         let thread_id = thread_id_to_u32(std::thread::current().id());
293         let raw_profiler = &profiler.profiler;
294         let timing_guard =
295             raw_profiler.start_recording_interval_event(event_kind, event_id, thread_id);
296         TimingGuard(Some(timing_guard))
297     }
298
299     #[inline]
300     pub fn none() -> TimingGuard<'a> {
301         TimingGuard(None)
302     }
303 }