blob: d334a9476ce249d0d2f271fc5706b68ce0bb688c [file] [log] [blame]
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use rustc::session::Session;
use rustc::util::common::{ProfQDumpParams, ProfileQueriesMsg, profq_msg, profq_set_chan};
use std::sync::mpsc::{Receiver};
use std::io::{Write};
use rustc::dep_graph::{DepNode};
use std::time::{Duration, Instant};
pub mod trace;
/// begin a profile thread, if not already running
pub fn begin(sess: &Session) {
use std::thread;
use std::sync::mpsc::{channel};
let (tx, rx) = channel();
if profq_set_chan(sess, tx) {
thread::spawn(move || profile_queries_thread(rx));
}
}
/// dump files with profiling information to the given base path, and
/// wait for this dump to complete.
///
/// wraps the RPC (send/recv channel logic) of requesting a dump.
pub fn dump(sess: &Session, path: String) {
use std::sync::mpsc::{channel};
let (tx, rx) = channel();
let params = ProfQDumpParams {
path,
ack: tx,
// FIXME: Add another compiler flag to toggle whether this log
// is written; false for now
dump_profq_msg_log: true,
};
profq_msg(sess, ProfileQueriesMsg::Dump(params));
let _ = rx.recv().unwrap();
}
// State for parsing recursive trace structure in separate thread, via messages
#[derive(Clone, Eq, PartialEq)]
enum ParseState {
// No (local) parse state; may be parsing a tree, focused on a
// sub-tree that could be anything.
Clear,
// Have Query information from the last message
HaveQuery(trace::Query, Instant),
// Have "time-begin" information from the last message (doit flag, and message)
HaveTimeBegin(String, Instant),
// Have "task-begin" information from the last message
HaveTaskBegin(DepNode, Instant),
}
struct StackFrame {
pub parse_st: ParseState,
pub traces: Vec<trace::Rec>,
}
fn total_duration(traces: &[trace::Rec]) -> Duration {
let mut sum : Duration = Duration::new(0, 0);
for t in traces.iter() { sum += t.dur_total; }
return sum
}
// profiling thread; retains state (in local variables) and dump traces, upon request.
fn profile_queries_thread(r: Receiver<ProfileQueriesMsg>) {
use self::trace::*;
use std::fs::File;
use std::time::{Instant};
let mut profq_msgs: Vec<ProfileQueriesMsg> = vec![];
let mut frame: StackFrame = StackFrame { parse_st: ParseState::Clear, traces: vec![] };
let mut stack: Vec<StackFrame> = vec![];
loop {
let msg = r.recv();
if let Err(_recv_err) = msg {
// FIXME: Perhaps do something smarter than simply quitting?
break
};
let msg = msg.unwrap();
debug!("profile_queries_thread: {:?}", msg);
// Meta-level versus _actual_ queries messages
match msg {
ProfileQueriesMsg::Halt => return,
ProfileQueriesMsg::Dump(params) => {
assert!(stack.is_empty());
assert!(frame.parse_st == ParseState::Clear);
{
// write log of all messages
if params.dump_profq_msg_log {
let mut log_file =
File::create(format!("{}.log.txt", params.path)).unwrap();
for m in profq_msgs.iter() {
writeln!(&mut log_file, "{:?}", m).unwrap()
};
}
// write HTML file, and counts file
let html_path = format!("{}.html", params.path);
let mut html_file = File::create(&html_path).unwrap();
let counts_path = format!("{}.counts.txt", params.path);
let mut counts_file = File::create(&counts_path).unwrap();
writeln!(html_file,
"<html>\n<head>\n<link rel=\"stylesheet\" type=\"text/css\" href=\"{}\">",
"profile_queries.css").unwrap();
writeln!(html_file, "<style>").unwrap();
trace::write_style(&mut html_file);
writeln!(html_file, "</style>\n</head>\n<body>").unwrap();
trace::write_traces(&mut html_file, &mut counts_file, &frame.traces);
writeln!(html_file, "</body>\n</html>").unwrap();
let ack_path = format!("{}.ack", params.path);
let ack_file = File::create(&ack_path).unwrap();
drop(ack_file);
// Tell main thread that we are done, e.g., so it can exit
params.ack.send(()).unwrap();
}
continue
}
// Actual query message:
msg => {
// Record msg in our log
profq_msgs.push(msg.clone());
// Respond to the message, knowing that we've already handled Halt and Dump, above.
match (frame.parse_st.clone(), msg) {
(_,ProfileQueriesMsg::Halt) => unreachable!(),
(_,ProfileQueriesMsg::Dump(_)) => unreachable!(),
// Parse State: Clear
(ParseState::Clear,
ProfileQueriesMsg::QueryBegin(span, querymsg)) => {
let start = Instant::now();
frame.parse_st = ParseState::HaveQuery
(Query { span, msg: querymsg }, start)
},
(ParseState::Clear,
ProfileQueriesMsg::CacheHit) => {
panic!("parse error: unexpected CacheHit; expected QueryBegin")
},
(ParseState::Clear,
ProfileQueriesMsg::ProviderBegin) => {
panic!("parse error: expected QueryBegin before beginning a provider")
},
(ParseState::Clear,
ProfileQueriesMsg::ProviderEnd) => {
let provider_extent = frame.traces;
match stack.pop() {
None =>
panic!("parse error: expected a stack frame; found an empty stack"),
Some(old_frame) => {
match old_frame.parse_st {
ParseState::HaveQuery(q, start) => {
let duration = start.elapsed();
frame = StackFrame{
parse_st:ParseState::Clear,
traces:old_frame.traces
};
let dur_extent = total_duration(&provider_extent);
let trace = Rec {
effect: Effect::QueryBegin(q, CacheCase::Miss),
extent: Box::new(provider_extent),
start: start,
dur_self: duration - dur_extent,
dur_total: duration,
};
frame.traces.push( trace );
},
_ => panic!("internal parse error: malformed parse stack")
}
}
}
},
(ParseState::Clear,
ProfileQueriesMsg::TimeBegin(msg)) => {
let start = Instant::now();
frame.parse_st = ParseState::HaveTimeBegin(msg, start);
stack.push(frame);
frame = StackFrame{parse_st:ParseState::Clear, traces:vec![]};
},
(_, ProfileQueriesMsg::TimeBegin(_)) =>
panic!("parse error; did not expect time begin here"),
(ParseState::Clear,
ProfileQueriesMsg::TimeEnd) => {
let provider_extent = frame.traces;
match stack.pop() {
None =>
panic!("parse error: expected a stack frame; found an empty stack"),
Some(old_frame) => {
match old_frame.parse_st {
ParseState::HaveTimeBegin(msg, start) => {
let duration = start.elapsed();
frame = StackFrame{
parse_st:ParseState::Clear,
traces:old_frame.traces
};
let dur_extent = total_duration(&provider_extent);
let trace = Rec {
effect: Effect::TimeBegin(msg),
extent: Box::new(provider_extent),
start: start,
dur_total: duration,
dur_self: duration - dur_extent,
};
frame.traces.push( trace );
},
_ => panic!("internal parse error: malformed parse stack")
}
}
}
},
(_, ProfileQueriesMsg::TimeEnd) => { panic!("parse error") }
(ParseState::Clear,
ProfileQueriesMsg::TaskBegin(key)) => {
let start = Instant::now();
frame.parse_st = ParseState::HaveTaskBegin(key, start);
stack.push(frame);
frame = StackFrame{parse_st:ParseState::Clear, traces:vec![]};
},
(_, ProfileQueriesMsg::TaskBegin(_)) =>
panic!("parse error; did not expect time begin here"),
(ParseState::Clear,
ProfileQueriesMsg::TaskEnd) => {
let provider_extent = frame.traces;
match stack.pop() {
None =>
panic!("parse error: expected a stack frame; found an empty stack"),
Some(old_frame) => {
match old_frame.parse_st {
ParseState::HaveTaskBegin(key, start) => {
let duration = start.elapsed();
frame = StackFrame{
parse_st:ParseState::Clear,
traces:old_frame.traces
};
let dur_extent = total_duration(&provider_extent);
let trace = Rec {
effect: Effect::TaskBegin(key),
extent: Box::new(provider_extent),
start: start,
dur_total: duration,
dur_self: duration - dur_extent,
};
frame.traces.push( trace );
},
_ => panic!("internal parse error: malformed parse stack")
}
}
}
},
(_, ProfileQueriesMsg::TaskEnd) => { panic!("parse error") }
// Parse State: HaveQuery
(ParseState::HaveQuery(q,start),
ProfileQueriesMsg::CacheHit) => {
let duration = start.elapsed();
let trace : Rec = Rec{
effect: Effect::QueryBegin(q, CacheCase::Hit),
extent: Box::new(vec![]),
start: start,
dur_self: duration,
dur_total: duration,
};
frame.traces.push( trace );
frame.parse_st = ParseState::Clear;
},
(ParseState::HaveQuery(_,_),
ProfileQueriesMsg::ProviderBegin) => {
stack.push(frame);
frame = StackFrame{parse_st:ParseState::Clear, traces:vec![]};
},
// Parse errors:
(ParseState::HaveQuery(q,_),
ProfileQueriesMsg::ProviderEnd) => {
panic!("parse error: unexpected ProviderEnd; \
expected something else to follow BeginQuery for {:?}", q)
},
(ParseState::HaveQuery(q1,_),
ProfileQueriesMsg::QueryBegin(span2,querymsg2)) => {
panic!("parse error: unexpected QueryBegin; \
earlier query is unfinished: {:?} and now {:?}",
q1, Query{span:span2, msg:querymsg2})
},
(ParseState::HaveTimeBegin(_, _), _) => {
unreachable!()
},
(ParseState::HaveTaskBegin(_, _), _) => {
unreachable!()
},
}
}
}
}
}