Skip to content

Commit fe14560

Browse files
committed
add command to collaps threads without overlapping events
1 parent 1a593d0 commit fe14560

File tree

1 file changed

+65
-2
lines changed

1 file changed

+65
-2
lines changed

crox/src/main.rs

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::fs;
33
use std::io::BufWriter;
44
use std::path::PathBuf;
55
use std::time::Duration;
6+
use std::time::SystemTime;
67

78
use measureme::{ProfilingData, TimestampKind};
89

@@ -42,6 +43,66 @@ struct Event {
4243
#[derive(StructOpt, Debug)]
4344
struct Opt {
4445
file_prefix: PathBuf,
46+
/// collapse threads without overlapping events
47+
#[structopt(long = "collapse-threads")]
48+
collapse_threads: bool,
49+
}
50+
51+
// generate mapping from thread_id to collapsed thread_id or an empty map
52+
fn generate_thread_to_collapsed_thread_mapping(
53+
opt: &Opt,
54+
data: &ProfilingData,
55+
) -> BTreeMap<u64, u64> {
56+
let mut thread_to_collapsed_thread: BTreeMap<u64, u64> = BTreeMap::new();
57+
58+
if opt.collapse_threads {
59+
// collect start and end times for all threads
60+
let mut thread_start_and_end: BTreeMap<u64, (SystemTime, SystemTime)> = BTreeMap::new();
61+
for event in data.iter() {
62+
thread_start_and_end
63+
.entry(event.thread_id)
64+
.and_modify(|(start, end)| {
65+
if *start > event.timestamp {
66+
*start = event.timestamp;
67+
} else if *end < event.timestamp {
68+
*end = event.timestamp;
69+
}
70+
})
71+
.or_insert_with(|| (event.timestamp, event.timestamp));
72+
}
73+
// collect the the threads in order of the end time
74+
let mut end_to_thread = thread_start_and_end
75+
.iter()
76+
.map(|(&thread_id, &(_start, end))| (end, thread_id))
77+
.collect::<Vec<_>>();
78+
79+
end_to_thread.sort_unstable_by_key(|&(end, _thread_id)| end);
80+
let mut next_end_iter = end_to_thread.iter();
81+
82+
// used to get the thread that was first to end
83+
let &(temp_next_end, temp_next_thread_id) = next_end_iter.next().unwrap();
84+
let mut next_end = temp_next_end;
85+
let mut next_thread_id = temp_next_thread_id;
86+
87+
let mut current_thread_id = 0; // use new thread_ids to avoid strange gaps in the numbers
88+
for (&thread_id, &(start, _end)) in thread_start_and_end.iter() {
89+
if start > next_end {
90+
// need to lookup the thread_id due to new and collapsed threads
91+
let mapped_thread_id = *thread_to_collapsed_thread
92+
.get(&next_thread_id)
93+
.unwrap_or(&next_thread_id);
94+
95+
thread_to_collapsed_thread.insert(thread_id, mapped_thread_id);
96+
let &(temp_next_end, temp_next_thread_id) = next_end_iter.next().unwrap();
97+
next_end = temp_next_end;
98+
next_thread_id = temp_next_thread_id;
99+
} else {
100+
thread_to_collapsed_thread.insert(thread_id, current_thread_id);
101+
current_thread_id += 1;
102+
}
103+
}
104+
}
105+
thread_to_collapsed_thread
45106
}
46107

47108
fn main() -> Result<(), Box<dyn std::error::Error>> {
@@ -56,7 +117,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
56117
let first_event_timestamp = data.iter().next().unwrap().timestamp - Duration::from_micros(1);
57118

58119
let mut serializer = serde_json::Serializer::new(chrome_file);
59-
120+
let thread_to_collapsed_thread = generate_thread_to_collapsed_thread_mapping(&opt, &data);
60121
let mut event_iterator = data.iter();
61122

62123
//create an iterator so we can avoid allocating a Vec with every Event for serialization
@@ -78,7 +139,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
78139
.duration_since(first_event_timestamp)
79140
.unwrap(),
80141
process_id: 0,
81-
thread_id: event.thread_id,
142+
thread_id: *thread_to_collapsed_thread
143+
.get(&event.thread_id)
144+
.unwrap_or(&event.thread_id),
82145
args: None,
83146
});
84147
}

0 commit comments

Comments
 (0)