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.
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.
12 use syntax_pos::SpanData;
13 use rustc_data_structures::fx::FxHashMap;
14 use rustc::util::common::QueryMsg;
16 use std::time::{Duration, Instant};
17 use rustc::dep_graph::{DepNode};
19 #[derive(Debug, Clone, Eq, PartialEq)]
25 QueryBegin(Query, CacheCase),
32 /// Recursive trace structure
36 pub dur_self: Duration,
37 pub dur_total: Duration,
38 pub extent: Box<Vec<Rec>>,
40 pub struct QueryMetric {
42 pub dur_self: Duration,
43 pub dur_total: Duration,
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] != "");
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] != "");
60 // First return value is text; second return value is a CSS class
61 pub fn html_of_effect(eff: &Effect) -> (String, String) {
63 Effect::TimeBegin(ref msg) => {
65 "time-begin".to_string())
67 Effect::TaskBegin(ref key) => {
68 let cons = cons_of_key(key);
69 (cons.clone(), format!("{} task-begin", cons))
71 Effect::QueryBegin(ref qmsg, ref cc) => {
72 let cons = cons_of_query_msg(qmsg);
77 CacheCase::Hit => "hit",
78 CacheCase::Miss => "miss",
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()),
92 fn html_of_fraction(frac: f64) -> (String, String) {
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() }
105 let percent = frac * 100.0;
106 if percent > 0.1 { (format!("{:.1}%", percent), css) }
107 else { ("< 0.1%".to_string(), css) }
110 fn total_duration(traces: &[Rec]) -> Duration {
111 let mut sum : Duration = Duration::new(0,0);
112 for t in traces.iter() {
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
123 to_nanos(nom) as f64 / to_nanos(den) as f64
126 fn write_traces_rec(file: &mut File, traces: &[Rec], total: Duration, depth: usize) {
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",
136 /* Heuristic for 'important' CSS class: */
137 if t.extent.len() > 5 || percent >= 1.0 {
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();
152 fn compute_counts_rec(counts: &mut FxHashMap<String,QueryMetric>, traces: &[Rec]) {
153 for t in traces.iter() {
155 Effect::TimeBegin(ref msg) => {
156 let qm = match counts.get(msg) {
157 Some(_qm) => { panic!("TimeBegin with non-unique, repeat message") }
160 dur_self: t.dur_self,
161 dur_total: t.dur_total,
163 counts.insert(msg.clone(), qm);
165 Effect::TaskBegin(ref key) => {
166 let cons = cons_of_key(key);
167 let qm = match counts.get(&cons) {
171 dur_self: qm.dur_self + t.dur_self,
172 dur_total: qm.dur_total + t.dur_total,
176 dur_self: t.dur_self,
177 dur_total: t.dur_total,
179 counts.insert(cons, qm);
181 Effect::QueryBegin(ref qmsg, ref _cc) => {
182 let qcons = cons_of_query_msg(qmsg);
183 let qm = match counts.get(&qcons) {
187 dur_total: qm.dur_total + t.dur_total,
188 dur_self: qm.dur_self + t.dur_self
192 dur_total: t.dur_total,
193 dur_self: t.dur_self,
196 counts.insert(qcons, qm);
199 compute_counts_rec(counts, &t.extent)
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;
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",
214 duration_to_secs_str(dur_total),
215 duration_to_secs_str(dur_self)
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);
226 let total : Duration = total_duration(traces);
227 write_traces_rec(html_file, traces, total, 0)
230 pub fn write_style(html_file: &mut File) {
231 write!(html_file,"{}", "
233 font-family: sans-serif;
238 display: inline-block;
279 display: inline-block;
283 display: inline-block;