@@ -11,7 +11,7 @@ use std::fs;
11
11
use std:: io:: { stderr, Write } ;
12
12
use std:: path:: { Path , PathBuf } ;
13
13
use std:: process;
14
- use std:: process:: Command ;
14
+ use std:: process:: { Command , Stdio } ;
15
15
use std:: { str, time:: Instant } ;
16
16
use tokio:: runtime:: Runtime ;
17
17
@@ -486,6 +486,117 @@ fn get_local_toolchain(
486
486
Ok ( ( rustc, rustdoc, cargo) )
487
487
}
488
488
489
+ fn generate_cachegrind_diffs (
490
+ id1 : & str ,
491
+ id2 : & str ,
492
+ out_dir : & Path ,
493
+ benchmarks : & [ Benchmark ] ,
494
+ build_kinds : & [ BuildKind ] ,
495
+ scenario_kinds : & [ ScenarioKind ] ,
496
+ errors : & mut BenchmarkErrors ,
497
+ ) {
498
+ for benchmark in benchmarks {
499
+ for & build_kind in build_kinds {
500
+ for & scenario_kind in scenario_kinds {
501
+ if let ScenarioKind :: IncrPatched = scenario_kind {
502
+ continue ;
503
+ }
504
+ let filename = |prefix, id| {
505
+ format ! (
506
+ "{}-{}-{}-{:?}-{:?}" ,
507
+ prefix, id, benchmark. name, build_kind, scenario_kind
508
+ )
509
+ } ;
510
+ let id_diff = format ! ( "{}-{}" , id1, id2) ;
511
+ let cgout1 = out_dir. join ( filename ( "cgout" , id1) ) ;
512
+ let cgout2 = out_dir. join ( filename ( "cgout" , id2) ) ;
513
+ let cgdiff = out_dir. join ( filename ( "cgdiff" , & id_diff) ) ;
514
+ let cgann = out_dir. join ( filename ( "cgann" , & id_diff) ) ;
515
+
516
+ if let Err ( e) = cg_diff ( & cgout1, & cgout2, & cgdiff) {
517
+ errors. incr ( ) ;
518
+ eprintln ! ( "collector error: {:?}" , e) ;
519
+ continue ;
520
+ }
521
+ if let Err ( e) = cg_annotate ( & cgdiff, & cgann) {
522
+ errors. incr ( ) ;
523
+ eprintln ! ( "collector error: {:?}" , e) ;
524
+ continue ;
525
+ }
526
+ }
527
+ }
528
+ }
529
+ }
530
+
531
+ /// Compares two Cachegrind output files using cg_diff and writes result to path.
532
+ fn cg_diff ( cgout1 : & Path , cgout2 : & Path , path : & Path ) -> anyhow:: Result < ( ) > {
533
+ let output = Command :: new ( "cg_diff" )
534
+ . arg ( "--mod-filename=s#/rustc/[^/]*/##" )
535
+ . arg ( "--mod-funcname=s/[.]llvm[.].*//" )
536
+ . arg ( cgout1)
537
+ . arg ( cgout2)
538
+ . stderr ( Stdio :: inherit ( ) )
539
+ . output ( )
540
+ . context ( "failed to run `cg_diff`" ) ?;
541
+
542
+ if !output. status . success ( ) {
543
+ anyhow:: bail!( "failed to generate cachegrind diff" ) ;
544
+ }
545
+
546
+ fs:: write ( path, output. stdout ) . context ( "failed to write `cg_diff` output" ) ?;
547
+
548
+ Ok ( ( ) )
549
+ }
550
+
551
+ /// Post process Cachegrind output file and writes resutl to path.
552
+ fn cg_annotate ( cgout : & Path , path : & Path ) -> anyhow:: Result < ( ) > {
553
+ let output = Command :: new ( "cg_annotate" )
554
+ . arg ( "--show-percs=no" )
555
+ . arg ( cgout)
556
+ . stderr ( Stdio :: inherit ( ) )
557
+ . output ( )
558
+ . context ( "failed to run `cg_annotate`" ) ?;
559
+
560
+ if !output. status . success ( ) {
561
+ anyhow:: bail!( "failed to annotate cachegrind output" ) ;
562
+ }
563
+
564
+ fs:: write ( path, output. stdout ) . context ( "failed to write `cg_annotate` output" ) ?;
565
+
566
+ Ok ( ( ) )
567
+ }
568
+
569
+ fn profile (
570
+ compiler : Compiler ,
571
+ id : & str ,
572
+ profiler : Profiler ,
573
+ out_dir : & Path ,
574
+ benchmarks : & [ Benchmark ] ,
575
+ build_kinds : & [ BuildKind ] ,
576
+ scenario_kinds : & [ ScenarioKind ] ,
577
+ errors : & mut BenchmarkErrors ,
578
+ ) {
579
+ eprintln ! ( "Profiling {} with {:?}" , id, profiler) ;
580
+ for ( i, benchmark) in benchmarks. iter ( ) . enumerate ( ) {
581
+ eprintln ! ( "{}" , n_benchmarks_remaining( benchmarks. len( ) - i) ) ;
582
+ let mut processor = execute:: ProfileProcessor :: new ( profiler, out_dir, id) ;
583
+ let result = benchmark. measure (
584
+ & mut processor,
585
+ & build_kinds,
586
+ & scenario_kinds,
587
+ compiler,
588
+ Some ( 1 ) ,
589
+ ) ;
590
+ if let Err ( ref s) = result {
591
+ errors. incr ( ) ;
592
+ eprintln ! (
593
+ "collector error: Failed to profile '{}' with {:?}, recorded: {:?}" ,
594
+ benchmark. name, profiler, s
595
+ ) ;
596
+ }
597
+ }
598
+ }
599
+
489
600
fn main ( ) {
490
601
match main_result ( ) {
491
602
Ok ( code) => process:: exit ( code) ,
@@ -585,6 +696,35 @@ fn main_result() -> anyhow::Result<i32> {
585
696
( @arg RUSTDOC : --rustdoc +takes_value "The path to the local rustdoc to benchmark" )
586
697
)
587
698
699
+ ( @subcommand diff_local =>
700
+ ( about: "Profiles and compares two toolchains with one of several profilers" )
701
+
702
+ // Mandatory arguments
703
+ ( @arg PROFILER : +required +takes_value
704
+ "One of: 'self-profile', 'time-passes', 'perf-record',\n \
705
+ 'oprofile', 'cachegrind', 'callgrind', 'dhat', 'massif',\n \
706
+ 'eprintln', 'llvm-lines'")
707
+ ( @arg RUSTC_BEFORE : +required +takes_value "The path to the local rustc to benchmark" )
708
+ ( @arg RUSTC_AFTER : +required +takes_value "The path to the local rustc to benchmark" )
709
+
710
+ // Options
711
+ ( @arg BUILDS : --builds +takes_value
712
+ "One or more (comma-separated) of: 'Check', \n \
713
+ 'Debug', 'Doc', 'Opt', 'All'")
714
+ ( @arg CARGO : --cargo +takes_value "The path to the local Cargo to use" )
715
+ ( @arg EXCLUDE : --exclude +takes_value
716
+ "Exclude all benchmarks matching anything in\n \
717
+ this comma-separated list of patterns")
718
+ ( @arg INCLUDE : --include +takes_value
719
+ "Include only benchmarks matching something in\n \
720
+ this comma-separated list of patterns")
721
+ ( @arg OUT_DIR : --( "out-dir" ) +takes_value "Output directory" )
722
+ ( @arg RUNS : --runs +takes_value
723
+ "One or more (comma-separated) of: 'Full',\n \
724
+ 'IncrFull', 'IncrUnchanged', 'IncrPatched', 'All'")
725
+ ( @arg RUSTDOC : --rustdoc +takes_value "The path to the local rustdoc to benchmark" )
726
+ )
727
+
588
728
( @subcommand install_next =>
589
729
( about: "Installs the next commit for perf.rust-lang.org" )
590
730
@@ -796,38 +936,87 @@ fn main_result() -> anyhow::Result<i32> {
796
936
let rustdoc = sub_m. value_of ( "RUSTDOC" ) ;
797
937
798
938
let ( rustc, rustdoc, cargo) = get_local_toolchain ( & build_kinds, rustc, rustdoc, cargo) ?;
799
-
800
939
let compiler = Compiler {
801
940
rustc : & rustc,
802
941
rustdoc : rustdoc. as_deref ( ) ,
803
942
cargo : & cargo,
804
943
triple : & target_triple,
805
944
is_nightly : true ,
806
945
} ;
807
-
808
946
let benchmarks = get_benchmarks ( & benchmark_dir, include, exclude) ?;
947
+ let mut errors = BenchmarkErrors :: new ( ) ;
948
+ profile (
949
+ compiler,
950
+ id,
951
+ profiler,
952
+ & out_dir,
953
+ & benchmarks,
954
+ & build_kinds,
955
+ & scenario_kinds,
956
+ & mut errors,
957
+ ) ;
958
+ errors. fail_if_nonzero ( ) ?;
959
+ Ok ( 0 )
960
+ }
961
+
962
+ ( "diff_local" , Some ( sub_m) ) => {
963
+ // Mandatory arguments
964
+ let profiler = Profiler :: from_name ( sub_m. value_of ( "PROFILER" ) . unwrap ( ) ) ?;
965
+ let rustc1 = sub_m. value_of ( "RUSTC_BEFORE" ) . unwrap ( ) ;
966
+ let rustc2 = sub_m. value_of ( "RUSTC_AFTER" ) . unwrap ( ) ;
967
+
968
+ // Options
969
+ let build_kinds = build_kinds_from_arg ( & sub_m. value_of ( "BUILDS" ) ) ?;
970
+ let cargo = sub_m. value_of ( "CARGO" ) ;
971
+ let exclude = sub_m. value_of ( "EXCLUDE" ) ;
972
+ let include = sub_m. value_of ( "INCLUDE" ) ;
973
+ let out_dir = PathBuf :: from ( sub_m. value_of_os ( "OUT_DIR" ) . unwrap_or ( default_out_dir) ) ;
974
+ let scenario_kinds = scenario_kinds_from_arg ( sub_m. value_of ( "RUNS" ) ) ?;
975
+ let rustdoc = sub_m. value_of ( "RUSTDOC" ) ;
809
976
810
- eprintln ! ( "Profiling with {:?}" , profiler) ;
977
+ let id1 = rustc1. strip_prefix ( '+' ) . unwrap_or ( "before" ) ;
978
+ let id2 = rustc2. strip_prefix ( '+' ) . unwrap_or ( "after" ) ;
979
+ let mut toolchains = Vec :: new ( ) ;
980
+ for ( id, rustc) in [ ( id1, rustc1) , ( id2, rustc2) ] {
981
+ let ( rustc, rustdoc, cargo) =
982
+ get_local_toolchain ( & build_kinds, rustc, rustdoc, cargo) ?;
983
+ toolchains. push ( ( id. to_owned ( ) , rustc, rustdoc, cargo) ) ;
984
+ }
811
985
986
+ let benchmarks = get_benchmarks ( & benchmark_dir, include, exclude) ?;
812
987
let mut errors = BenchmarkErrors :: new ( ) ;
813
- for ( i, benchmark) in benchmarks. iter ( ) . enumerate ( ) {
814
- eprintln ! ( "{}" , n_benchmarks_remaining( benchmarks. len( ) - i) ) ;
815
- let mut processor = execute:: ProfileProcessor :: new ( profiler, & out_dir, & id) ;
816
- let result = benchmark. measure (
817
- & mut processor,
988
+ for ( id, rustc, rustdoc, cargo) in & toolchains {
989
+ let compiler = Compiler {
990
+ rustc : & rustc,
991
+ rustdoc : rustdoc. as_deref ( ) ,
992
+ cargo : & cargo,
993
+ triple : & target_triple,
994
+ is_nightly : true ,
995
+ } ;
996
+ profile (
997
+ compiler,
998
+ id,
999
+ profiler,
1000
+ & out_dir,
1001
+ & benchmarks,
818
1002
& build_kinds,
819
1003
& scenario_kinds,
820
- compiler,
821
- Some ( 1 ) ,
1004
+ & mut errors,
822
1005
) ;
823
- if let Err ( ref s) = result {
824
- errors. incr ( ) ;
825
- eprintln ! (
826
- "collector error: Failed to profile '{}' with {:?}, recorded: {:?}" ,
827
- benchmark. name, profiler, s
828
- ) ;
829
- }
830
1006
}
1007
+
1008
+ if let Profiler :: Cachegrind = profiler {
1009
+ generate_cachegrind_diffs (
1010
+ id1,
1011
+ id2,
1012
+ & out_dir,
1013
+ & benchmarks,
1014
+ & build_kinds,
1015
+ & scenario_kinds,
1016
+ & mut errors,
1017
+ ) ;
1018
+ }
1019
+
831
1020
errors. fail_if_nonzero ( ) ?;
832
1021
Ok ( 0 )
833
1022
}
0 commit comments