11use crate :: prelude:: * ;
22use crate :: run:: runner:: helpers:: run_command_with_log_pipe:: run_command_with_log_pipe_and_callback;
33use crate :: run:: runner:: helpers:: setup:: run_with_sudo;
4+ use crate :: run:: runner:: valgrind:: helpers:: ignored_objects_path:: get_objects_path_to_ignore;
5+ use crate :: run:: runner:: valgrind:: helpers:: perf_maps:: harvest_perf_maps_for_pids;
46use anyhow:: Context ;
57use fifo:: { PerfFifo , RunnerFifo } ;
68use futures:: stream:: FuturesUnordered ;
9+ use metadata:: PerfMetadata ;
710use perf_map:: ProcessSymbols ;
811use procfs:: process:: MMPermissions ;
912use shared:: Command as FifoCommand ;
13+ use std:: collections:: HashSet ;
1014use std:: path:: PathBuf ;
1115use std:: process:: Command ;
1216use std:: time:: Duration ;
1317use std:: { cell:: OnceCell , collections:: HashMap , process:: ExitStatus } ;
1418use tempfile:: TempDir ;
1519use unwind_data:: UnwindData ;
1620
21+ mod metadata;
1722mod shared;
1823pub use shared:: * ;
1924
@@ -74,10 +79,21 @@ impl PerfRunner {
7479 . prefix ( PERF_DATA_PREFIX )
7580 . tempfile_in ( & self . perf_dir ) ?;
7681
82+ // Detect the mode based on the command to be executed
83+ let cg_mode = if bench_cmd. contains ( "cargo" ) {
84+ "dwarf"
85+ } else if bench_cmd. contains ( "pytest" ) {
86+ "fp"
87+ } else {
88+ warn ! ( "Couldn't detect call graph mode for command: {}" , bench_cmd) ;
89+ "dwarf"
90+ } ;
91+ debug ! ( "Using call graph mode: {}" , cg_mode) ;
92+
7793 cmd. args ( [
7894 "-c" ,
7995 & format ! (
80- "perf record --quiet --user-callchains --freq=999 --switch-output --control=fifo:{},{} --delay=-1 -g --call-graph=dwarf --output={} -- {bench_cmd}" ,
96+ "perf record --quiet --user-callchains --freq=999 --switch-output --control=fifo:{},{} --delay=-1 -g --call-graph={cg_mode} --output={} -- {bench_cmd}" ,
8197 perf_fifo. ctl_fifo_path. to_string_lossy( ) ,
8298 perf_fifo. ack_fifo_path. to_string_lossy( ) ,
8399 perf_file. path( ) . to_string_lossy( )
@@ -109,12 +125,6 @@ impl PerfRunner {
109125 pub async fn save_files_to ( & self , profile_folder : & PathBuf ) -> anyhow:: Result < ( ) > {
110126 let start = std:: time:: Instant :: now ( ) ;
111127
112- let bench_data = self
113- . benchmark_data
114- . get ( )
115- . expect ( "Benchmark order is not available" ) ;
116- bench_data. save_to ( profile_folder) . unwrap ( ) ;
117-
118128 // Copy the perf data files to the profile folder
119129 let copy_tasks = std:: fs:: read_dir ( & self . perf_dir ) ?
120130 . filter_map ( |entry| entry. ok ( ) )
@@ -141,16 +151,33 @@ impl PerfRunner {
141151 let dst_path = profile_folder. join ( dst_file_name) ;
142152 tokio:: fs:: copy ( src_path, dst_path) . await ?;
143153
144- Ok :: < _ , anyhow:: Error > ( ( ) )
154+ Ok :: < _ , anyhow:: Error > ( pid )
145155 } )
146156 } )
147157 . collect :: < FuturesUnordered < _ > > ( ) ;
158+
159+ let bench_data = self
160+ . benchmark_data
161+ . get ( )
162+ . expect ( "Benchmark order is not available" ) ;
148163 assert_eq ! (
149164 copy_tasks. len( ) ,
150165 bench_data. bench_count( ) ,
151166 "Benchmark count mismatch"
152167 ) ;
153- futures:: future:: try_join_all ( copy_tasks) . await ?;
168+
169+ // Harvest the perf maps generated by python. This will copy the perf
170+ // maps from /tmp to the profile folder. We have to write our own perf
171+ // maps to these files AFTERWARDS, otherwise it'll be overwritten!
172+ let perf_map_pids = futures:: future:: try_join_all ( copy_tasks)
173+ . await ?
174+ . into_iter ( )
175+ . filter_map ( Result :: ok)
176+ . collect :: < HashSet < _ > > ( ) ;
177+ harvest_perf_maps_for_pids ( profile_folder, & perf_map_pids) . await ?;
178+
179+ // Append perf maps, unwind info and other metadata
180+ bench_data. save_to ( profile_folder) . unwrap ( ) ;
154181
155182 let elapsed = start. elapsed ( ) ;
156183 debug ! ( "Perf teardown took: {:?}" , elapsed) ;
@@ -260,11 +287,31 @@ impl BenchmarkData {
260287 }
261288 }
262289
263- for ( pid, orders) in & self . bench_order_by_pid {
264- let dst_file_name = format ! ( "{}_.bench_order" , pid) ;
265- let dst_path = path. as_ref ( ) . join ( dst_file_name) ;
266- std:: fs:: write ( dst_path, orders. join ( "\n " ) ) ?;
267- }
290+ let metadata = PerfMetadata {
291+ bench_order_by_pid : self . bench_order_by_pid . clone ( ) ,
292+ ignored_modules : {
293+ let mut to_ignore = vec ! [ ] ;
294+
295+ // Check if any of the ignored modules has been loaded in the process
296+ for ignore_path in get_objects_path_to_ignore ( ) {
297+ for proc in self . symbols_by_pid . values ( ) {
298+ if let Some ( mapping) = proc. module_mapping ( & ignore_path) {
299+ let ( Some ( ( base_addr, _) ) , Some ( ( _, end_addr) ) ) = (
300+ mapping. iter ( ) . min_by_key ( |( base_addr, _) | base_addr) ,
301+ mapping. iter ( ) . max_by_key ( |( _, end_addr) | end_addr) ,
302+ ) else {
303+ continue ;
304+ } ;
305+
306+ to_ignore. push ( ( ignore_path. clone ( ) , * base_addr, * end_addr) ) ;
307+ }
308+ }
309+ }
310+
311+ to_ignore
312+ } ,
313+ } ;
314+ metadata. save_to ( & path) . unwrap ( ) ;
268315
269316 Ok ( ( ) )
270317 }
0 commit comments