@@ -3,6 +3,7 @@ use std::fs;
3
3
use std:: io:: BufWriter ;
4
4
use std:: path:: PathBuf ;
5
5
use std:: time:: Duration ;
6
+ use std:: time:: SystemTime ;
6
7
7
8
use measureme:: { ProfilingData , TimestampKind } ;
8
9
@@ -42,6 +43,66 @@ struct Event {
42
43
#[ derive( StructOpt , Debug ) ]
43
44
struct Opt {
44
45
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
45
106
}
46
107
47
108
fn main ( ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
@@ -56,7 +117,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
56
117
let first_event_timestamp = data. iter ( ) . next ( ) . unwrap ( ) . timestamp - Duration :: from_micros ( 1 ) ;
57
118
58
119
let mut serializer = serde_json:: Serializer :: new ( chrome_file) ;
59
-
120
+ let thread_to_collapsed_thread = generate_thread_to_collapsed_thread_mapping ( & opt , & data ) ;
60
121
let mut event_iterator = data. iter ( ) ;
61
122
62
123
//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>> {
78
139
. duration_since ( first_event_timestamp)
79
140
. unwrap ( ) ,
80
141
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 ) ,
82
145
args : None ,
83
146
} ) ;
84
147
}
0 commit comments