]>
Commit | Line | Data |
---|---|---|
48663c56 | 1 | use std::error::Error; |
dc9dc135 | 2 | use std::fs; |
48663c56 | 3 | use std::mem::{self, Discriminant}; |
dc9dc135 | 4 | use std::path::Path; |
532ac7d7 | 5 | use std::process; |
e74abb32 | 6 | use std::sync::Arc; |
9fa01778 | 7 | use std::thread::ThreadId; |
48663c56 | 8 | use std::u32; |
b7449926 | 9 | |
48663c56 | 10 | use crate::ty::query::QueryName; |
532ac7d7 | 11 | |
48663c56 XL |
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))] | |
e74abb32 | 17 | type SerializationSink = measureme::MmapSerializationSink; |
48663c56 | 18 | #[cfg(windows)] |
e74abb32 XL |
19 | type SerializationSink = measureme::FileSerializationSink; |
20 | ||
21 | type Profiler = measureme::Profiler<SerializationSink>; | |
22 | ||
b7449926 | 23 | |
9fa01778 XL |
24 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)] |
25 | pub enum ProfileCategory { | |
26 | Parsing, | |
27 | Expansion, | |
28 | TypeChecking, | |
29 | BorrowChecking, | |
30 | Codegen, | |
31 | Linking, | |
32 | Other, | |
33 | } | |
b7449926 | 34 | |
48663c56 XL |
35 | bitflags! { |
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 |
54 | const 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 |
64 | fn 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)] | |
72 | pub 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 | ||
83 | impl 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 | 208 | pub 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 | ||
218 | impl 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] |
292 | pub struct TimingGuard<'a>(Option<measureme::TimingGuard<'a, SerializationSink>>); | |
9fa01778 | 293 | |
e74abb32 | 294 | impl<'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 | } |