]> git.lizzy.rs Git - rust.git/blob - src/librustc_driver/profile/trace.rs
Auto merge of #53815 - F001:if-let-guard, r=petrochenkov
[rust.git] / src / librustc_driver / profile / trace.rs
1 // Copyright 2012-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 super::*;
12 use syntax_pos::SpanData;
13 use rustc_data_structures::fx::FxHashMap;
14 use rustc::util::common::QueryMsg;
15 use std::fs::File;
16 use std::time::{Duration, Instant};
17 use rustc::dep_graph::{DepNode};
18
19 #[derive(Debug, Clone, Eq, PartialEq)]
20 pub struct Query {
21     pub span: SpanData,
22     pub msg: QueryMsg,
23 }
24 pub enum Effect {
25     QueryBegin(Query, CacheCase),
26     TimeBegin(String),
27     TaskBegin(DepNode),
28 }
29 pub enum CacheCase {
30     Hit, Miss
31 }
32 /// Recursive trace structure
33 pub struct Rec {
34     pub effect: Effect,
35     pub start: Instant,
36     pub dur_self: Duration,
37     pub dur_total: Duration,
38     pub extent: Box<Vec<Rec>>,
39 }
40 pub struct QueryMetric {
41     pub count: usize,
42     pub dur_self: Duration,
43     pub dur_total: Duration,
44 }
45
46 pub fn cons_of_query_msg(q: &trace::Query) -> String {
47     let s = format!("{:?}", q.msg);
48     let cons: Vec<&str> = s.split(|d| d == '(' || d == '{').collect();
49     assert!(cons.len() > 0 && cons[0] != "");
50     cons[0].to_string()
51 }
52
53 pub fn cons_of_key(k: &DepNode) -> String {
54     let s = format!("{:?}", k);
55     let cons: Vec<&str> = s.split(|d| d == '(' || d == '{').collect();
56     assert!(cons.len() > 0 && cons[0] != "");
57     cons[0].to_string()
58 }
59
60 // First return value is text; second return value is a CSS class
61 pub fn html_of_effect(eff: &Effect) -> (String, String) {
62     match *eff {
63         Effect::TimeBegin(ref msg) => {
64             (msg.clone(),
65              "time-begin".to_string())
66         },
67         Effect::TaskBegin(ref key) => {
68             let cons = cons_of_key(key);
69             (cons.clone(), format!("{} task-begin", cons))
70         },
71         Effect::QueryBegin(ref qmsg, ref cc) => {
72             let cons = cons_of_query_msg(qmsg);
73             (cons.clone(),
74              format!("{} {}",
75                      cons,
76                      match *cc {
77                          CacheCase::Hit => "hit",
78                          CacheCase::Miss => "miss",
79                      }))
80         }
81     }
82 }
83
84 // First return value is text; second return value is a CSS class
85 fn html_of_duration(_start: &Instant, dur: &Duration) -> (String, String) {
86     use rustc::util::common::duration_to_secs_str;
87     (duration_to_secs_str(dur.clone()),
88      String::new()
89     )
90 }
91
92 fn html_of_fraction(frac: f64) -> (String, String) {
93     let css = {
94         if       frac > 0.50  { "frac-50".to_string() }
95         else if  frac > 0.40  { "frac-40".to_string() }
96         else if  frac > 0.30  { "frac-30".to_string() }
97         else if  frac > 0.20  { "frac-20".to_string() }
98         else if  frac > 0.10  { "frac-10".to_string() }
99         else if  frac > 0.05  { "frac-05".to_string() }
100         else if  frac > 0.02  { "frac-02".to_string() }
101         else if  frac > 0.01  { "frac-01".to_string() }
102         else if  frac > 0.001 { "frac-001".to_string() }
103         else                  { "frac-0".to_string() }
104     };
105     let percent = frac * 100.0;
106     if percent > 0.1 { (format!("{:.1}%", percent), css) }
107     else { ("< 0.1%".to_string(), css) }
108 }
109
110 fn total_duration(traces: &[Rec]) -> Duration {
111     let mut sum : Duration = Duration::new(0,0);
112     for t in traces.iter() {
113         sum += t.dur_total;
114     }
115     return sum
116 }
117
118 fn duration_div(nom: Duration, den: Duration) -> f64 {
119     fn to_nanos(d: Duration) -> u64 {
120         d.as_secs() * 1_000_000_000 + d.subsec_nanos() as u64
121     }
122
123     to_nanos(nom) as f64 / to_nanos(den) as f64
124 }
125
126 fn write_traces_rec(file: &mut File, traces: &[Rec], total: Duration, depth: usize) {
127     for t in traces {
128         let (eff_text, eff_css_classes) = html_of_effect(&t.effect);
129         let (dur_text, dur_css_classes) = html_of_duration(&t.start, &t.dur_total);
130         let fraction = duration_div(t.dur_total, total);
131         let percent = fraction * 100.0;
132         let (frc_text, frc_css_classes) = html_of_fraction(fraction);
133         write!(file, "<div class=\"trace depth-{} extent-{}{} {} {} {}\">\n",
134                depth,
135                t.extent.len(),
136                /* Heuristic for 'important' CSS class: */
137                if t.extent.len() > 5 || percent >= 1.0 {
138                    " important" }
139                else { "" },
140                eff_css_classes,
141                dur_css_classes,
142                frc_css_classes,
143         ).unwrap();
144         write!(file, "<div class=\"eff\">{}</div>\n", eff_text).unwrap();
145         write!(file, "<div class=\"dur\">{}</div>\n", dur_text).unwrap();
146         write!(file, "<div class=\"frc\">{}</div>\n", frc_text).unwrap();
147         write_traces_rec(file, &t.extent, total, depth + 1);
148         write!(file, "</div>\n").unwrap();
149     }
150 }
151
152 fn compute_counts_rec(counts: &mut FxHashMap<String,QueryMetric>, traces: &[Rec]) {
153     for t in traces.iter() {
154         match t.effect {
155             Effect::TimeBegin(ref msg) => {
156                 let qm = match counts.get(msg) {
157                     Some(_qm) => { panic!("TimeBegin with non-unique, repeat message") }
158                     None => QueryMetric{
159                         count: 1,
160                         dur_self: t.dur_self,
161                         dur_total: t.dur_total,
162                     }};
163                 counts.insert(msg.clone(), qm);
164             },
165             Effect::TaskBegin(ref key) => {
166                 let cons = cons_of_key(key);
167                 let qm = match counts.get(&cons) {
168                     Some(qm) =>
169                         QueryMetric{
170                             count: qm.count + 1,
171                             dur_self: qm.dur_self + t.dur_self,
172                             dur_total: qm.dur_total + t.dur_total,
173                         },
174                     None => QueryMetric{
175                         count: 1,
176                         dur_self: t.dur_self,
177                         dur_total: t.dur_total,
178                     }};
179                 counts.insert(cons, qm);
180             },
181             Effect::QueryBegin(ref qmsg, ref _cc) => {
182                 let qcons = cons_of_query_msg(qmsg);
183                 let qm = match counts.get(&qcons) {
184                     Some(qm) =>
185                         QueryMetric{
186                             count: qm.count + 1,
187                             dur_total: qm.dur_total + t.dur_total,
188                             dur_self: qm.dur_self + t.dur_self
189                         },
190                     None => QueryMetric{
191                         count: 1,
192                         dur_total: t.dur_total,
193                         dur_self: t.dur_self,
194                     }
195                 };
196                 counts.insert(qcons, qm);
197             }
198         }
199         compute_counts_rec(counts, &t.extent)
200     }
201 }
202
203 pub fn write_counts(count_file: &mut File, counts: &mut FxHashMap<String,QueryMetric>) {
204     use rustc::util::common::duration_to_secs_str;
205     use std::cmp::Reverse;
206
207     let mut data = counts.iter().map(|(ref cons, ref qm)|
208         (cons.clone(), qm.count.clone(), qm.dur_total.clone(), qm.dur_self.clone())
209     ).collect::<Vec<_>>();
210     data.sort_by_key(|k| Reverse(k.3));
211     for (cons, count, dur_total, dur_self) in data {
212         write!(count_file, "{}, {}, {}, {}\n",
213                cons, count,
214                duration_to_secs_str(dur_total),
215                duration_to_secs_str(dur_self)
216         ).unwrap();
217     }
218 }
219
220 pub fn write_traces(html_file: &mut File, counts_file: &mut File, traces: &[Rec]) {
221     let capacity = traces.iter().fold(0, |acc, t| acc + 1 + t.extent.len());
222     let mut counts = FxHashMap::with_capacity_and_hasher(capacity, Default::default());
223     compute_counts_rec(&mut counts, traces);
224     write_counts(counts_file, &mut counts);
225
226     let total : Duration = total_duration(traces);
227     write_traces_rec(html_file, traces, total, 0)
228 }
229
230 pub fn write_style(html_file: &mut File) {
231     write!(html_file,"{}", "
232 body {
233     font-family: sans-serif;
234     background: black;
235 }
236 .trace {
237     color: black;
238     display: inline-block;
239     border-style: solid;
240     border-color: red;
241     border-width: 1px;
242     border-radius: 5px;
243     padding: 0px;
244     margin: 1px;
245     font-size: 0px;
246 }
247 .task-begin {
248     border-width: 1px;
249     color: white;
250     border-color: #ff8;
251     font-size: 0px;
252 }
253 .miss {
254     border-color: red;
255     border-width: 1px;
256 }
257 .extent-0 {
258     padding: 2px;
259 }
260 .time-begin {
261     border-width: 4px;
262     font-size: 12px;
263     color: white;
264     border-color: #afa;
265 }
266 .important {
267     border-width: 3px;
268     font-size: 12px;
269     color: white;
270     border-color: #f77;
271 }
272 .hit {
273     padding: 0px;
274     border-color: blue;
275     border-width: 3px;
276 }
277 .eff {
278   color: #fff;
279   display: inline-block;
280 }
281 .frc {
282   color: #7f7;
283   display: inline-block;
284 }
285 .dur {
286   display: none
287 }
288 .frac-50 {
289   padding: 10px;
290   border-width: 10px;
291   font-size: 32px;
292 }
293 .frac-40 {
294   padding: 8px;
295   border-width: 8px;
296   font-size: 24px;
297 }
298 .frac-30 {
299   padding: 6px;
300   border-width: 6px;
301   font-size: 18px;
302 }
303 .frac-20 {
304   padding: 4px;
305   border-width: 6px;
306   font-size: 16px;
307 }
308 .frac-10 {
309   padding: 2px;
310   border-width: 6px;
311   font-size: 14px;
312 }
313 ").unwrap();
314 }