]> git.lizzy.rs Git - rust.git/blob - src/librustc/util/profiling.rs
Rollup merge of #61273 - RalfJung:maybe-uninit, r=Centril
[rust.git] / src / librustc / util / profiling.rs
1 use std::borrow::Cow;
2 use std::error::Error;
3 use std::fs;
4 use std::mem::{self, Discriminant};
5 use std::path::Path;
6 use std::process;
7 use std::thread::ThreadId;
8 use std::u32;
9
10 use crate::ty::query::QueryName;
11
12 use measureme::{StringId, TimestampKind};
13
14 /// MmapSerializatioSink is faster on macOS and Linux
15 /// but FileSerializationSink is faster on Windows
16 #[cfg(not(windows))]
17 type Profiler = measureme::Profiler<measureme::MmapSerializationSink>;
18 #[cfg(windows)]
19 type Profiler = measureme::Profiler<measureme::FileSerializationSink>;
20
21 #[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)]
22 pub enum ProfileCategory {
23     Parsing,
24     Expansion,
25     TypeChecking,
26     BorrowChecking,
27     Codegen,
28     Linking,
29     Other,
30 }
31
32 bitflags! {
33     struct EventFilter: u32 {
34         const GENERIC_ACTIVITIES = 1 << 0;
35         const QUERY_PROVIDERS    = 1 << 1;
36         const QUERY_CACHE_HITS   = 1 << 2;
37         const QUERY_BLOCKED      = 1 << 3;
38         const INCR_CACHE_LOADS   = 1 << 4;
39
40         const DEFAULT = Self::GENERIC_ACTIVITIES.bits |
41                         Self::QUERY_PROVIDERS.bits |
42                         Self::QUERY_BLOCKED.bits |
43                         Self::INCR_CACHE_LOADS.bits;
44
45         // empty() and none() aren't const-fns unfortunately
46         const NONE = 0;
47         const ALL  = !Self::NONE.bits;
48     }
49 }
50
51 const EVENT_FILTERS_BY_NAME: &[(&str, EventFilter)] = &[
52     ("none", EventFilter::NONE),
53     ("all", EventFilter::ALL),
54     ("generic-activity", EventFilter::GENERIC_ACTIVITIES),
55     ("query-provider", EventFilter::QUERY_PROVIDERS),
56     ("query-cache-hit", EventFilter::QUERY_CACHE_HITS),
57     ("query-blocked" , EventFilter::QUERY_BLOCKED),
58     ("incr-cache-load", EventFilter::INCR_CACHE_LOADS),
59 ];
60
61 fn thread_id_to_u64(tid: ThreadId) -> u64 {
62     unsafe { mem::transmute::<ThreadId, u64>(tid) }
63 }
64
65 pub struct SelfProfiler {
66     profiler: Profiler,
67     event_filter_mask: EventFilter,
68     query_event_kind: StringId,
69     generic_activity_event_kind: StringId,
70     incremental_load_result_event_kind: StringId,
71     query_blocked_event_kind: StringId,
72     query_cache_hit_event_kind: StringId,
73 }
74
75 impl SelfProfiler {
76     pub fn new(
77         output_directory: &Path,
78         crate_name: Option<&str>,
79         event_filters: &Option<Vec<String>>
80     ) -> Result<SelfProfiler, Box<dyn Error>> {
81         fs::create_dir_all(output_directory)?;
82
83         let crate_name = crate_name.unwrap_or("unknown-crate");
84         let filename = format!("{}-{}.rustc_profile", crate_name, process::id());
85         let path = output_directory.join(&filename);
86         let profiler = Profiler::new(&path)?;
87
88         let query_event_kind = profiler.alloc_string("Query");
89         let generic_activity_event_kind = profiler.alloc_string("GenericActivity");
90         let incremental_load_result_event_kind = profiler.alloc_string("IncrementalLoadResult");
91         let query_blocked_event_kind = profiler.alloc_string("QueryBlocked");
92         let query_cache_hit_event_kind = profiler.alloc_string("QueryCacheHit");
93
94         let mut event_filter_mask = EventFilter::empty();
95
96         if let Some(ref event_filters) = *event_filters {
97             let mut unknown_events = vec![];
98             for item in event_filters {
99                 if let Some(&(_, mask)) = EVENT_FILTERS_BY_NAME.iter()
100                                                                .find(|&(name, _)| name == item) {
101                     event_filter_mask |= mask;
102                 } else {
103                     unknown_events.push(item.clone());
104                 }
105             }
106
107             // Warn about any unknown event names
108             if unknown_events.len() > 0 {
109                 unknown_events.sort();
110                 unknown_events.dedup();
111
112                 warn!("Unknown self-profiler events specified: {}. Available options are: {}.",
113                     unknown_events.join(", "),
114                     EVENT_FILTERS_BY_NAME.iter()
115                                          .map(|&(name, _)| name.to_string())
116                                          .collect::<Vec<_>>()
117                                          .join(", "));
118             }
119         } else {
120             event_filter_mask = EventFilter::DEFAULT;
121         }
122
123         Ok(SelfProfiler {
124             profiler,
125             event_filter_mask,
126             query_event_kind,
127             generic_activity_event_kind,
128             incremental_load_result_event_kind,
129             query_blocked_event_kind,
130             query_cache_hit_event_kind,
131         })
132     }
133
134     fn get_query_name_string_id(query_name: QueryName) -> StringId {
135         let discriminant = unsafe {
136             mem::transmute::<Discriminant<QueryName>, u64>(mem::discriminant(&query_name))
137         };
138
139         StringId::reserved(discriminant as u32)
140     }
141
142     pub fn register_query_name(&self, query_name: QueryName) {
143         let id = SelfProfiler::get_query_name_string_id(query_name);
144         self.profiler.alloc_string_with_reserved_id(id, query_name.as_str());
145     }
146
147     #[inline]
148     pub fn start_activity(
149         &self,
150         label: impl Into<Cow<'static, str>>,
151     ) {
152         if self.event_filter_mask.contains(EventFilter::GENERIC_ACTIVITIES) {
153             self.record(&label.into(), self.generic_activity_event_kind, TimestampKind::Start);
154         }
155     }
156
157     #[inline]
158     pub fn end_activity(
159         &self,
160         label: impl Into<Cow<'static, str>>,
161     ) {
162         if self.event_filter_mask.contains(EventFilter::GENERIC_ACTIVITIES) {
163             self.record(&label.into(), self.generic_activity_event_kind, TimestampKind::End);
164         }
165     }
166
167     #[inline]
168     pub fn record_query_hit(&self, query_name: QueryName) {
169         if self.event_filter_mask.contains(EventFilter::QUERY_CACHE_HITS) {
170             self.record_query(query_name, self.query_cache_hit_event_kind, TimestampKind::Instant);
171         }
172     }
173
174     #[inline]
175     pub fn start_query(&self, query_name: QueryName) {
176         if self.event_filter_mask.contains(EventFilter::QUERY_PROVIDERS) {
177             self.record_query(query_name, self.query_event_kind, TimestampKind::Start);
178         }
179     }
180
181     #[inline]
182     pub fn end_query(&self, query_name: QueryName) {
183         if self.event_filter_mask.contains(EventFilter::QUERY_PROVIDERS) {
184             self.record_query(query_name, self.query_event_kind, TimestampKind::End);
185         }
186     }
187
188     #[inline]
189     pub fn incremental_load_result_start(&self, query_name: QueryName) {
190         if self.event_filter_mask.contains(EventFilter::INCR_CACHE_LOADS) {
191             self.record_query(
192                 query_name,
193                 self.incremental_load_result_event_kind,
194                 TimestampKind::Start
195             );
196         }
197     }
198
199     #[inline]
200     pub fn incremental_load_result_end(&self, query_name: QueryName) {
201         if self.event_filter_mask.contains(EventFilter::INCR_CACHE_LOADS) {
202             self.record_query(
203                 query_name,
204                 self.incremental_load_result_event_kind,
205                 TimestampKind::End
206             );
207         }
208     }
209
210     #[inline]
211     pub fn query_blocked_start(&self, query_name: QueryName) {
212         if self.event_filter_mask.contains(EventFilter::QUERY_BLOCKED) {
213             self.record_query(query_name, self.query_blocked_event_kind, TimestampKind::Start);
214         }
215     }
216
217     #[inline]
218     pub fn query_blocked_end(&self, query_name: QueryName) {
219         if self.event_filter_mask.contains(EventFilter::QUERY_BLOCKED) {
220             self.record_query(query_name, self.query_blocked_event_kind, TimestampKind::End);
221         }
222     }
223
224     #[inline]
225     fn record(&self, event_id: &str, event_kind: StringId, timestamp_kind: TimestampKind) {
226         let thread_id = thread_id_to_u64(std::thread::current().id());
227
228         let event_id = self.profiler.alloc_string(event_id);
229         self.profiler.record_event(event_kind, event_id, thread_id, timestamp_kind);
230     }
231
232     #[inline]
233     fn record_query(
234         &self,
235         query_name: QueryName,
236         event_kind: StringId,
237         timestamp_kind: TimestampKind,
238     ) {
239         let dep_node_name = SelfProfiler::get_query_name_string_id(query_name);
240
241         let thread_id = thread_id_to_u64(std::thread::current().id());
242
243         self.profiler.record_event(event_kind, dep_node_name, thread_id, timestamp_kind);
244     }
245 }