@@ -11,7 +11,7 @@ use std::fs;
1111use std:: io:: { stderr, Write } ;
1212use std:: path:: { Path , PathBuf } ;
1313use std:: process;
14- use std:: process:: Command ;
14+ use std:: process:: { Command , Stdio } ;
1515use std:: { str, time:: Instant } ;
1616use tokio:: runtime:: Runtime ;
1717
@@ -486,6 +486,117 @@ fn get_local_toolchain(
486486 Ok ( ( rustc, rustdoc, cargo) )
487487}
488488
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+
489600fn main ( ) {
490601 match main_result ( ) {
491602 Ok ( code) => process:: exit ( code) ,
@@ -585,6 +696,35 @@ fn main_result() -> anyhow::Result<i32> {
585696 ( @arg RUSTDOC : --rustdoc +takes_value "The path to the local rustdoc to benchmark" )
586697 )
587698
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+
588728 ( @subcommand install_next =>
589729 ( about: "Installs the next commit for perf.rust-lang.org" )
590730
@@ -796,38 +936,87 @@ fn main_result() -> anyhow::Result<i32> {
796936 let rustdoc = sub_m. value_of ( "RUSTDOC" ) ;
797937
798938 let ( rustc, rustdoc, cargo) = get_local_toolchain ( & build_kinds, rustc, rustdoc, cargo) ?;
799-
800939 let compiler = Compiler {
801940 rustc : & rustc,
802941 rustdoc : rustdoc. as_deref ( ) ,
803942 cargo : & cargo,
804943 triple : & target_triple,
805944 is_nightly : true ,
806945 } ;
807-
808946 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" ) ;
809976
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+ }
811985
986+ let benchmarks = get_benchmarks ( & benchmark_dir, include, exclude) ?;
812987 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,
8181002 & build_kinds,
8191003 & scenario_kinds,
820- compiler,
821- Some ( 1 ) ,
1004+ & mut errors,
8221005 ) ;
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- }
8301006 }
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+
8311020 errors. fail_if_nonzero ( ) ?;
8321021 Ok ( 0 )
8331022 }
0 commit comments