@@ -72,6 +72,65 @@ pub struct CommandCacheKey {
72
72
cwd : Option < PathBuf > ,
73
73
}
74
74
75
+ #[ derive( Default , Clone ) ]
76
+ pub struct CommandProfile {
77
+ pub count : usize ,
78
+ pub total_duration : Duration ,
79
+ }
80
+
81
+ #[ derive( Default ) ]
82
+ pub struct CommandProfiler {
83
+ stats : Mutex < HashMap < CommandCacheKey , CommandProfile > > ,
84
+ }
85
+
86
+
87
+ impl CommandProfiler {
88
+ pub fn record ( & self , key : CommandCacheKey , duration : Duration ) {
89
+ let mut stats = self . stats . lock ( ) . unwrap ( ) ;
90
+ let entry = stats. entry ( key) . or_default ( ) ;
91
+ entry. count += 1 ;
92
+ entry. total_duration += duration;
93
+ }
94
+
95
+ pub fn report_slowest ( & self , top_n : usize ) {
96
+ let stats = self . stats . lock ( ) . unwrap ( ) ;
97
+ let mut entries: Vec < _ > = stats. iter ( ) . collect ( ) ;
98
+ entries. sort_by_key ( |( _, stat) | std:: cmp:: Reverse ( stat. total_duration ) ) ;
99
+
100
+ println ! ( "\n Top {top_n} slowest commands:" ) ;
101
+ for ( key, profile) in entries. into_iter ( ) . take ( top_n) {
102
+ println ! (
103
+ "- {:?} (count: {}, total: {:.2?}, avg: {:.2?})" ,
104
+ key. program,
105
+ profile. count,
106
+ profile. total_duration,
107
+ profile. total_duration / profile. count as u32
108
+ ) ;
109
+ }
110
+ }
111
+
112
+ pub fn to_json ( & self ) -> serde_json:: Value {
113
+ use serde_json:: json;
114
+
115
+ let stats = self . stats . lock ( ) . unwrap ( ) ;
116
+ let entries: Vec < _ > = stats
117
+ . iter ( )
118
+ . map ( |( key, profile) | {
119
+ json ! ( {
120
+ "program" : key. program,
121
+ "args" : key. args,
122
+ "envs" : key. envs,
123
+ "cwd" : key. cwd,
124
+ "count" : profile. count,
125
+ "total_duration_ms" : profile. total_duration. as_millis( ) ,
126
+ } )
127
+ } )
128
+ . collect ( ) ;
129
+
130
+ json ! ( { "commands" : entries } )
131
+ }
132
+ }
133
+
75
134
/// Wrapper around `std::process::Command`.
76
135
///
77
136
/// By default, the command will exit bootstrap if it fails.
0 commit comments