]> git.lizzy.rs Git - rust.git/blob - src/librustc_codegen_llvm/time_graph.rs
Auto merge of #50603 - eddyb:issue-49955, r=nikomatsakis
[rust.git] / src / librustc_codegen_llvm / time_graph.rs
1 // Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use std::collections::HashMap;
12 use std::fs::File;
13 use std::io::prelude::*;
14 use std::marker::PhantomData;
15 use std::mem;
16 use std::sync::{Arc, Mutex};
17 use std::time::Instant;
18
19 const OUTPUT_WIDTH_IN_PX: u64 = 1000;
20 const TIME_LINE_HEIGHT_IN_PX: u64 = 20;
21 const TIME_LINE_HEIGHT_STRIDE_IN_PX: usize = 30;
22
23 #[derive(Clone)]
24 struct Timing {
25     start: Instant,
26     end: Instant,
27     work_package_kind: WorkPackageKind,
28     name: String,
29     events: Vec<(String, Instant)>,
30 }
31
32 #[derive(Clone, Copy, Hash, Eq, PartialEq, Debug)]
33 pub struct TimelineId(pub usize);
34
35 #[derive(Clone)]
36 struct PerThread {
37     timings: Vec<Timing>,
38     open_work_package: Option<(Instant, WorkPackageKind, String)>,
39 }
40
41 #[derive(Clone)]
42 pub struct TimeGraph {
43     data: Arc<Mutex<HashMap<TimelineId, PerThread>>>,
44 }
45
46 #[derive(Clone, Copy)]
47 pub struct WorkPackageKind(pub &'static [&'static str]);
48
49 pub struct Timeline {
50     token: Option<RaiiToken>,
51 }
52
53 struct RaiiToken {
54     graph: TimeGraph,
55     timeline: TimelineId,
56     events: Vec<(String, Instant)>,
57     // The token must not be Send:
58     _marker: PhantomData<*const ()>
59 }
60
61
62 impl Drop for RaiiToken {
63     fn drop(&mut self) {
64         self.graph.end(self.timeline, mem::replace(&mut self.events, Vec::new()));
65     }
66 }
67
68 impl TimeGraph {
69     pub fn new() -> TimeGraph {
70         TimeGraph {
71             data: Arc::new(Mutex::new(HashMap::new()))
72         }
73     }
74
75     pub fn start(&self,
76                  timeline: TimelineId,
77                  work_package_kind: WorkPackageKind,
78                  name: &str) -> Timeline {
79         {
80             let mut table = self.data.lock().unwrap();
81
82             let data = table.entry(timeline).or_insert(PerThread {
83                 timings: Vec::new(),
84                 open_work_package: None,
85             });
86
87             assert!(data.open_work_package.is_none());
88             data.open_work_package = Some((Instant::now(), work_package_kind, name.to_string()));
89         }
90
91         Timeline {
92             token: Some(RaiiToken {
93                 graph: self.clone(),
94                 timeline,
95                 events: Vec::new(),
96                 _marker: PhantomData,
97             }),
98         }
99     }
100
101     fn end(&self, timeline: TimelineId, events: Vec<(String, Instant)>) {
102         let end = Instant::now();
103
104         let mut table = self.data.lock().unwrap();
105         let data = table.get_mut(&timeline).unwrap();
106
107         if let Some((start, work_package_kind, name)) = data.open_work_package.take() {
108             data.timings.push(Timing {
109                 start,
110                 end,
111                 work_package_kind,
112                 name,
113                 events,
114             });
115         } else {
116             bug!("end timing without start?")
117         }
118     }
119
120     pub fn dump(&self, output_filename: &str) {
121         let table = self.data.lock().unwrap();
122
123         for data in table.values() {
124             assert!(data.open_work_package.is_none());
125         }
126
127         let mut threads: Vec<PerThread> =
128             table.values().map(|data| data.clone()).collect();
129
130         threads.sort_by_key(|timeline| timeline.timings[0].start);
131
132         let earliest_instant = threads[0].timings[0].start;
133         let latest_instant = threads.iter()
134                                        .map(|timeline| timeline.timings
135                                                                .last()
136                                                                .unwrap()
137                                                                .end)
138                                        .max()
139                                        .unwrap();
140         let max_distance = distance(earliest_instant, latest_instant);
141
142         let mut file = File::create(format!("{}.html", output_filename)).unwrap();
143
144         writeln!(file, "
145             <html>
146             <head>
147                 <style>
148                     #threads a {{
149                         position: absolute;
150                         overflow: hidden;
151                     }}
152                     #threads {{
153                         height: {total_height}px;
154                         width: {width}px;
155                     }}
156
157                     .timeline {{
158                         display: none;
159                         width: {width}px;
160                         position: relative;
161                     }}
162
163                     .timeline:target {{
164                         display: block;
165                     }}
166
167                     .event {{
168                         position: absolute;
169                     }}
170                 </style>
171             </head>
172             <body>
173                 <div id='threads'>
174         ",
175             total_height = threads.len() * TIME_LINE_HEIGHT_STRIDE_IN_PX,
176             width = OUTPUT_WIDTH_IN_PX,
177         ).unwrap();
178
179         let mut color = 0;
180         for (line_index, thread) in threads.iter().enumerate() {
181             let line_top = line_index * TIME_LINE_HEIGHT_STRIDE_IN_PX;
182
183             for span in &thread.timings {
184                 let start = distance(earliest_instant, span.start);
185                 let end = distance(earliest_instant, span.end);
186
187                 let start = normalize(start, max_distance, OUTPUT_WIDTH_IN_PX);
188                 let end = normalize(end, max_distance, OUTPUT_WIDTH_IN_PX);
189
190                 let colors = span.work_package_kind.0;
191
192                 writeln!(file, "<a href='#timing{}'
193                                    style='top:{}px; \
194                                           left:{}px; \
195                                           width:{}px; \
196                                           height:{}px; \
197                                           background:{};'>{}</a>",
198                     color,
199                     line_top,
200                     start,
201                     end - start,
202                     TIME_LINE_HEIGHT_IN_PX,
203                     colors[color % colors.len()],
204                     span.name,
205                     ).unwrap();
206
207                 color += 1;
208             }
209         }
210
211         writeln!(file, "
212             </div>
213         ").unwrap();
214
215         let mut idx = 0;
216         for thread in threads.iter() {
217             for timing in &thread.timings {
218                 let colors = timing.work_package_kind.0;
219                 let height = TIME_LINE_HEIGHT_STRIDE_IN_PX * timing.events.len();
220                 writeln!(file, "<div class='timeline'
221                                      id='timing{}'
222                                      style='background:{};height:{}px;'>",
223                          idx,
224                          colors[idx % colors.len()],
225                          height).unwrap();
226                 idx += 1;
227                 let max = distance(timing.start, timing.end);
228                 for (i, &(ref event, time)) in timing.events.iter().enumerate() {
229                     let i = i as u64;
230                     let time = distance(timing.start, time);
231                     let at = normalize(time, max, OUTPUT_WIDTH_IN_PX);
232                     writeln!(file, "<span class='event'
233                                           style='left:{}px;\
234                                                  top:{}px;'>{}</span>",
235                              at,
236                              TIME_LINE_HEIGHT_IN_PX * i,
237                              event).unwrap();
238                 }
239                 writeln!(file, "</div>").unwrap();
240             }
241         }
242
243         writeln!(file, "
244             </body>
245             </html>
246         ").unwrap();
247     }
248 }
249
250 impl Timeline {
251     pub fn noop() -> Timeline {
252         Timeline { token: None }
253     }
254
255     /// Record an event which happened at this moment on this timeline.
256     ///
257     /// Events are displayed in the eventual HTML output where you can click on
258     /// a particular timeline and it'll expand to all of the events that
259     /// happened on that timeline. This can then be used to drill into a
260     /// particular timeline and see what events are happening and taking the
261     /// most time.
262     pub fn record(&mut self, name: &str) {
263         if let Some(ref mut token) = self.token {
264             token.events.push((name.to_string(), Instant::now()));
265         }
266     }
267 }
268
269 fn distance(zero: Instant, x: Instant) -> u64 {
270
271     let duration = x.duration_since(zero);
272     (duration.as_secs() * 1_000_000_000 + duration.subsec_nanos() as u64) // / div
273 }
274
275 fn normalize(distance: u64, max: u64, max_pixels: u64) -> u64 {
276     (max_pixels * distance) / max
277 }
278