]> git.lizzy.rs Git - rust.git/blob - src/librustc_data_structures/profiling.rs
Auto merge of #65345 - davidtwco:issue-64130-async-send-sync-error-improvements,...
[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
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 }