1 use std::collections::HashMap;
3 use std::io::{BufWriter, Write};
6 use std::thread::ThreadId;
7 use std::time::Instant;
9 use crate::session::config::Options;
11 #[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)]
12 pub enum ProfileCategory {
22 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
23 pub enum ProfilerEvent {
24 QueryStart { query_name: &'static str, category: ProfileCategory, time: Instant },
25 QueryEnd { query_name: &'static str, category: ProfileCategory, time: Instant },
26 GenericActivityStart { category: ProfileCategory, time: Instant },
27 GenericActivityEnd { category: ProfileCategory, time: Instant },
28 IncrementalLoadResultStart { query_name: &'static str, time: Instant },
29 IncrementalLoadResultEnd { query_name: &'static str, time: Instant },
30 QueryCacheHit { query_name: &'static str, category: ProfileCategory, time: Instant },
31 QueryCount { query_name: &'static str, category: ProfileCategory, count: usize, time: Instant },
32 QueryBlockedStart { query_name: &'static str, category: ProfileCategory, time: Instant },
33 QueryBlockedEnd { query_name: &'static str, category: ProfileCategory, time: Instant },
37 fn timestamp(&self) -> Instant {
38 use self::ProfilerEvent::*;
41 QueryStart { time, .. } |
42 QueryEnd { time, .. } |
43 GenericActivityStart { time, .. } |
44 GenericActivityEnd { time, .. } |
45 QueryCacheHit { time, .. } |
46 QueryCount { time, .. } |
47 IncrementalLoadResultStart { time, .. } |
48 IncrementalLoadResultEnd { time, .. } |
49 QueryBlockedStart { time, .. } |
50 QueryBlockedEnd { time, .. } => *time
55 fn thread_id_to_u64(tid: ThreadId) -> u64 {
56 unsafe { mem::transmute::<ThreadId, u64>(tid) }
59 pub struct SelfProfiler {
60 events: HashMap<ThreadId, Vec<ProfilerEvent>>,
64 pub fn new() -> SelfProfiler {
65 let mut profiler = SelfProfiler {
66 events: HashMap::new(),
69 profiler.start_activity(ProfileCategory::Other);
75 pub fn start_activity(&mut self, category: ProfileCategory) {
76 self.record(ProfilerEvent::GenericActivityStart {
83 pub fn end_activity(&mut self, category: ProfileCategory) {
84 self.record(ProfilerEvent::GenericActivityEnd {
91 pub fn record_computed_queries(
93 query_name: &'static str,
94 category: ProfileCategory,
97 self.record(ProfilerEvent::QueryCount {
101 time: Instant::now(),
106 pub fn record_query_hit(&mut self, query_name: &'static str, category: ProfileCategory) {
107 self.record(ProfilerEvent::QueryCacheHit {
110 time: Instant::now(),
115 pub fn start_query(&mut self, query_name: &'static str, category: ProfileCategory) {
116 self.record(ProfilerEvent::QueryStart {
119 time: Instant::now(),
124 pub fn end_query(&mut self, query_name: &'static str, category: ProfileCategory) {
125 self.record(ProfilerEvent::QueryEnd {
128 time: Instant::now(),
133 pub fn incremental_load_result_start(&mut self, query_name: &'static str) {
134 self.record(ProfilerEvent::IncrementalLoadResultStart {
136 time: Instant::now(),
141 pub fn incremental_load_result_end(&mut self, query_name: &'static str) {
142 self.record(ProfilerEvent::IncrementalLoadResultEnd {
144 time: Instant::now(),
149 pub fn query_blocked_start(&mut self, query_name: &'static str, category: ProfileCategory) {
150 self.record(ProfilerEvent::QueryBlockedStart {
153 time: Instant::now(),
158 pub fn query_blocked_end(&mut self, query_name: &'static str, category: ProfileCategory) {
159 self.record(ProfilerEvent::QueryBlockedEnd {
162 time: Instant::now(),
167 fn record(&mut self, event: ProfilerEvent) {
168 let thread_id = std::thread::current().id();
169 let events = self.events.entry(thread_id).or_default();
174 pub fn dump_raw_events(&self, opts: &Options) {
175 use self::ProfilerEvent::*;
177 //find the earliest Instant to use as t=0
178 //when serializing the events, we'll calculate a Duration
179 //using (instant - min_instant)
183 .map(|(_, values)| values[0].timestamp())
187 let pid = process::id();
190 format!("{}.profile_events.json", opts.crate_name.clone().unwrap_or_default());
192 let mut file = BufWriter::new(fs::File::create(filename).unwrap());
194 let threads: Vec<_> =
198 .map(|tid| format!("{}", thread_id_to_u64(*tid)))
206 \"crate_name\": \"{}\",\
207 \"opt_level\": \"{:?}\",\
215 opts.crate_name.clone().unwrap_or_default(),
217 if opts.incremental.is_some() { "true" } else { "false" },
220 let mut is_first = true;
221 for (thread_id, events) in &self.events {
222 let thread_id = thread_id_to_u64(*thread_id);
224 for event in events {
228 writeln!(file, ",").unwrap();
231 let (secs, nanos) = {
232 let duration = event.timestamp() - min_instant;
233 (duration.as_secs(), duration.subsec_nanos())
237 QueryStart { query_name, category, time: _ } =>
241 \"query_name\": \"{}\",\
242 \"category\": \"{:?}\",\
256 QueryEnd { query_name, category, time: _ } =>
260 \"query_name\": \"{}\",\
261 \"category\": \"{:?}\",\
275 GenericActivityStart { category, time: _ } =>
278 \"GenericActivityStart\": {{\
279 \"category\": \"{:?}\",\
292 GenericActivityEnd { category, time: _ } =>
295 \"GenericActivityEnd\": {{\
296 \"category\": \"{:?}\",\
309 QueryCacheHit { query_name, category, time: _ } =>
312 \"QueryCacheHit\": {{\
313 \"query_name\": \"{}\",\
314 \"category\": \"{:?}\",\
328 QueryCount { query_name, category, count, time: _ } =>
332 \"query_name\": \"{}\",\
333 \"category\": \"{:?}\",\
349 IncrementalLoadResultStart { query_name, time: _ } =>
352 \"IncrementalLoadResultStart\": {{\
353 \"query_name\": \"{}\",\
366 IncrementalLoadResultEnd { query_name, time: _ } =>
369 \"IncrementalLoadResultEnd\": {{\
370 \"query_name\": \"{}\",\
383 QueryBlockedStart { query_name, category, time: _ } =>
386 \"QueryBlockedStart\": {{\
387 \"query_name\": \"{}\",\
388 \"category\": \"{:?}\",\
402 QueryBlockedEnd { query_name, category, time: _ } =>
405 \"QueryBlockedEnd\": {{\
406 \"query_name\": \"{}\",\
407 \"category\": \"{:?}\",\
425 write!(file, "] }}").unwrap();