Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(23)

Side by Side Diff: third_party/tcmalloc/chromium/src/pprof

Issue 9311003: Update the tcmalloc chromium branch to r144 (gperftools 2.0), and merge chromium-specific changes. (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: Rebasec Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 #! /usr/bin/env perl 1 #! /usr/bin/env perl
2 2
3 # Copyright (c) 1998-2007, Google Inc. 3 # Copyright (c) 1998-2007, Google Inc.
4 # All rights reserved. 4 # All rights reserved.
5 # 5 #
6 # Redistribution and use in source and binary forms, with or without 6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions are 7 # modification, are permitted provided that the following conditions are
8 # met: 8 # met:
9 # 9 #
10 # * Redistributions of source code must retain the above copyright 10 # * Redistributions of source code must retain the above copyright
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
65 # Generates disassembly listing of all routines with at least one 65 # Generates disassembly listing of all routines with at least one
66 # sample that match the --disasm=<regexp> pattern. The listing is 66 # sample that match the --disasm=<regexp> pattern. The listing is
67 # annotated with the flat and cumulative sample counts at each PC value. 67 # annotated with the flat and cumulative sample counts at each PC value.
68 # 68 #
69 # TODO: Use color to indicate files? 69 # TODO: Use color to indicate files?
70 70
71 use strict; 71 use strict;
72 use warnings; 72 use warnings;
73 use Getopt::Long; 73 use Getopt::Long;
74 74
75 my $PPROF_VERSION = "1.7"; 75 my $PPROF_VERSION = "2.0";
76 76
77 # These are the object tools we use which can come from a 77 # These are the object tools we use which can come from a
78 # user-specified location using --tools, from the PPROF_TOOLS 78 # user-specified location using --tools, from the PPROF_TOOLS
79 # environment variable, or from the environment. 79 # environment variable, or from the environment.
80 my %obj_tool_map = ( 80 my %obj_tool_map = (
81 "objdump" => "objdump", 81 "objdump" => "objdump",
82 "nm" => "nm", 82 "nm" => "nm",
83 "addr2line" => "addr2line", 83 "addr2line" => "addr2line",
84 "c++filt" => "c++filt", 84 "c++filt" => "c++filt",
85 ## ConfigureObjTools may add architecture-specific entries: 85 ## ConfigureObjTools may add architecture-specific entries:
86 #"nm_pdb" => "nm-pdb", # for reading windows (PDB-format) executables 86 #"nm_pdb" => "nm-pdb", # for reading windows (PDB-format) executables
87 #"addr2line_pdb" => "addr2line-pdb", # ditto 87 #"addr2line_pdb" => "addr2line-pdb", # ditto
88 #"otool" => "otool", # equivalent of objdump on OS X 88 #"otool" => "otool", # equivalent of objdump on OS X
89 ); 89 );
90 my $DOT = "dot"; # leave non-absolute, since it may be in /usr/local 90 # NOTE: these are lists, so you can put in commandline flags if you want.
91 my $GV = "gv"; 91 my @DOT = ("dot"); # leave non-absolute, since it may be in /usr/local
92 my $EVINCE = "evince"; # could also be xpdf or perhaps acroread 92 my @GV = ("gv");
93 my $KCACHEGRIND = "kcachegrind"; 93 my @EVINCE = ("evince"); # could also be xpdf or perhaps acroread
94 my $PS2PDF = "ps2pdf"; 94 my @KCACHEGRIND = ("kcachegrind");
95 my @PS2PDF = ("ps2pdf");
95 # These are used for dynamic profiles 96 # These are used for dynamic profiles
96 my $URL_FETCHER = "curl -s"; 97 my @URL_FETCHER = ("curl", "-s");
97 98
98 # These are the web pages that servers need to support for dynamic profiles 99 # These are the web pages that servers need to support for dynamic profiles
99 my $HEAP_PAGE = "/pprof/heap"; 100 my $HEAP_PAGE = "/pprof/heap";
100 my $PROFILE_PAGE = "/pprof/profile"; # must support cgi-param "?seconds=#" 101 my $PROFILE_PAGE = "/pprof/profile"; # must support cgi-param "?seconds=#"
101 my $PMUPROFILE_PAGE = "/pprof/pmuprofile(?:\\?.*)?"; # must support cgi-param 102 my $PMUPROFILE_PAGE = "/pprof/pmuprofile(?:\\?.*)?"; # must support cgi-param
102 # ?seconds=#&event=x&period=n 103 # ?seconds=#&event=x&period=n
103 my $GROWTH_PAGE = "/pprof/growth"; 104 my $GROWTH_PAGE = "/pprof/growth";
104 my $CONTENTION_PAGE = "/pprof/contention"; 105 my $CONTENTION_PAGE = "/pprof/contention";
105 my $WALL_PAGE = "/pprof/wall(?:\\?.*)?"; # accepts options like namefilter 106 my $WALL_PAGE = "/pprof/wall(?:\\?.*)?"; # accepts options like namefilter
106 my $FILTEREDPROFILE_PAGE = "/pprof/filteredprofile(?:\\?.*)?"; 107 my $FILTEREDPROFILE_PAGE = "/pprof/filteredprofile(?:\\?.*)?";
107 my $CENSUSPROFILE_PAGE = "/pprof/censusprofile"; # must support "?seconds=#" 108 my $CENSUSPROFILE_PAGE = "/pprof/censusprofile(?:\\?.*)?"; # must support cgi-pa ram
109 # "?seconds=#",
110 # "?tags_regexp=#" and
111 # "?type=#".
108 my $SYMBOL_PAGE = "/pprof/symbol"; # must support symbol lookup via POST 112 my $SYMBOL_PAGE = "/pprof/symbol"; # must support symbol lookup via POST
109 my $PROGRAM_NAME_PAGE = "/pprof/cmdline"; 113 my $PROGRAM_NAME_PAGE = "/pprof/cmdline";
110 114
111 # These are the web pages that can be named on the command line. 115 # These are the web pages that can be named on the command line.
112 # All the alternatives must begin with /. 116 # All the alternatives must begin with /.
113 my $PROFILES = "($HEAP_PAGE|$PROFILE_PAGE|$PMUPROFILE_PAGE|" . 117 my $PROFILES = "($HEAP_PAGE|$PROFILE_PAGE|$PMUPROFILE_PAGE|" .
114 "$GROWTH_PAGE|$CONTENTION_PAGE|$WALL_PAGE|" . 118 "$GROWTH_PAGE|$CONTENTION_PAGE|$WALL_PAGE|" .
115 "$FILTEREDPROFILE_PAGE|$CENSUSPROFILE_PAGE)"; 119 "$FILTEREDPROFILE_PAGE|$CENSUSPROFILE_PAGE)";
116 120
117 # default binary name 121 # default binary name
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
149 pprof [options] <profile> 153 pprof [options] <profile>
150 <profile> is a remote form. Symbols are obtained from host:port$SYMBOL_PAGE 154 <profile> is a remote form. Symbols are obtained from host:port$SYMBOL_PAGE
151 155
152 Each name can be: 156 Each name can be:
153 /path/to/profile - a path to a profile file 157 /path/to/profile - a path to a profile file
154 host:port[/<service>] - a location of a service to get profile from 158 host:port[/<service>] - a location of a service to get profile from
155 159
156 The /<service> can be $HEAP_PAGE, $PROFILE_PAGE, /pprof/pmuprofile, 160 The /<service> can be $HEAP_PAGE, $PROFILE_PAGE, /pprof/pmuprofile,
157 $GROWTH_PAGE, $CONTENTION_PAGE, /pprof/wall, 161 $GROWTH_PAGE, $CONTENTION_PAGE, /pprof/wall,
158 $CENSUSPROFILE_PAGE, or /pprof/filteredprofile. 162 $CENSUSPROFILE_PAGE, or /pprof/filteredprofile.
159 For instance: "pprof http://myserver.com:80$HEAP_PAGE". 163 For instance:
164 pprof http://myserver.com:80$HEAP_PAGE
160 If /<service> is omitted, the service defaults to $PROFILE_PAGE (cpu profilin g). 165 If /<service> is omitted, the service defaults to $PROFILE_PAGE (cpu profilin g).
161 pprof --symbols <program> 166 pprof --symbols <program>
162 Maps addresses to symbol names. In this mode, stdin should be a 167 Maps addresses to symbol names. In this mode, stdin should be a
163 list of library mappings, in the same format as is found in the heap- 168 list of library mappings, in the same format as is found in the heap-
164 and cpu-profile files (this loosely matches that of /proc/self/maps 169 and cpu-profile files (this loosely matches that of /proc/self/maps
165 on linux), followed by a list of hex addresses to map, one per line. 170 on linux), followed by a list of hex addresses to map, one per line.
166 171
167 For more help with querying remote servers, including how to add the 172 For more help with querying remote servers, including how to add the
168 necessary server-side support code, see this filename (or one like it): 173 necessary server-side support code, see this filename (or one like it):
169 174
170 /usr/doc/google-perftools-$PPROF_VERSION/pprof_remote_servers.html 175 /usr/doc/gperftools-$PPROF_VERSION/pprof_remote_servers.html
171 176
172 Options: 177 Options:
173 --cum Sort by cumulative data 178 --cum Sort by cumulative data
174 --base=<base> Subtract <base> from <profile> before display 179 --base=<base> Subtract <base> from <profile> before display
175 --interactive Run in interactive mode (interactive "help" gives help) [ default] 180 --interactive Run in interactive mode (interactive "help" gives help) [ default]
176 --seconds=<n> Length of time for dynamic profiles [default=30 secs] 181 --seconds=<n> Length of time for dynamic profiles [default=30 secs]
177 --add_lib=<file> Read additional symbols and line info from the given libr ary 182 --add_lib=<file> Read additional symbols and line info from the given libr ary
178 --lib_prefix=<dir> Comma separated list of library path prefixes 183 --lib_prefix=<dir> Comma separated list of library path prefixes
179 184
180 Reporting Granularity: 185 Reporting Granularity:
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
258 Outputs one line per procedure for localhost:1234 263 Outputs one line per procedure for localhost:1234
259 pprof --raw localhost:1234 > ./local.raw 264 pprof --raw localhost:1234 > ./local.raw
260 pprof --text ./local.raw 265 pprof --text ./local.raw
261 Fetches a remote profile for later analysis and then 266 Fetches a remote profile for later analysis and then
262 analyzes it in text mode. 267 analyzes it in text mode.
263 EOF 268 EOF
264 } 269 }
265 270
266 sub version_string { 271 sub version_string {
267 return <<EOF 272 return <<EOF
268 pprof (part of google-perftools $PPROF_VERSION) 273 pprof (part of gperftools $PPROF_VERSION)
269 274
270 Copyright 1998-2007 Google Inc. 275 Copyright 1998-2007 Google Inc.
271 276
272 This is BSD licensed software; see the source for copying conditions 277 This is BSD licensed software; see the source for copying conditions
273 and license information. 278 and license information.
274 There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A 279 There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
275 PARTICULAR PURPOSE. 280 PARTICULAR PURPOSE.
276 EOF 281 EOF
277 } 282 }
278 283
(...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after
490 RunUnitTests(); 495 RunUnitTests();
491 # Should not return 496 # Should not return
492 exit(1); 497 exit(1);
493 } 498 }
494 499
495 # Binary name and profile arguments list 500 # Binary name and profile arguments list
496 $main::prog = ""; 501 $main::prog = "";
497 @main::pfile_args = (); 502 @main::pfile_args = ();
498 503
499 # Remote profiling without a binary (using $SYMBOL_PAGE instead) 504 # Remote profiling without a binary (using $SYMBOL_PAGE instead)
500 if (IsProfileURL($ARGV[0])) { 505 if (@ARGV > 0) {
501 $main::use_symbol_page = 1; 506 if (IsProfileURL($ARGV[0])) {
502 } elsif (IsSymbolizedProfileFile($ARGV[0])) { 507 $main::use_symbol_page = 1;
503 $main::use_symbolized_profile = 1; 508 } elsif (IsSymbolizedProfileFile($ARGV[0])) {
504 $main::prog = $UNKNOWN_BINARY; # will be set later from the profile file 509 $main::use_symbolized_profile = 1;
510 $main::prog = $UNKNOWN_BINARY; # will be set later from the profile file
511 }
505 } 512 }
506 513
507 if ($main::use_symbol_page || $main::use_symbolized_profile) { 514 if ($main::use_symbol_page || $main::use_symbolized_profile) {
508 # We don't need a binary! 515 # We don't need a binary!
509 my %disabled = ('--lines' => $main::opt_lines, 516 my %disabled = ('--lines' => $main::opt_lines,
510 '--disasm' => $main::opt_disasm); 517 '--disasm' => $main::opt_disasm);
511 for my $option (keys %disabled) { 518 for my $option (keys %disabled) {
512 usage("$option cannot be used without a binary") if $disabled{$option}; 519 usage("$option cannot be used without a binary") if $disabled{$option};
513 } 520 }
514 # Set $main::prog later... 521 # Set $main::prog later...
(...skipping 23 matching lines...) Expand all
538 if ($main::use_symbol_page) { 545 if ($main::use_symbol_page) {
539 unless (IsProfileURL($main::pfile_args[0])) { 546 unless (IsProfileURL($main::pfile_args[0])) {
540 error("The first profile should be a remote form to use $SYMBOL_PAGE\n"); 547 error("The first profile should be a remote form to use $SYMBOL_PAGE\n");
541 } 548 }
542 CheckSymbolPage(); 549 CheckSymbolPage();
543 $main::prog = FetchProgramName(); 550 $main::prog = FetchProgramName();
544 } elsif (!$main::use_symbolized_profile) { # may not need objtools! 551 } elsif (!$main::use_symbolized_profile) { # may not need objtools!
545 ConfigureObjTools($main::prog) 552 ConfigureObjTools($main::prog)
546 } 553 }
547 554
548 # Break the opt_list_prefix into the prefix_list array 555 # Break the opt_lib_prefix into the prefix_list array
549 @prefix_list = split (',', $main::opt_lib_prefix); 556 @prefix_list = split (',', $main::opt_lib_prefix);
550 557
551 # Remove trailing / from the prefixes, in the list to prevent 558 # Remove trailing / from the prefixes, in the list to prevent
552 # searching things like /my/path//lib/mylib.so 559 # searching things like /my/path//lib/mylib.so
553 foreach (@prefix_list) { 560 foreach (@prefix_list) {
554 s|/+$||; 561 s|/+$||;
555 } 562 }
556 } 563 }
557 564
558 sub Main() { 565 sub Main() {
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
636 643
637 # Get derived profiles 644 # Get derived profiles
638 my $flat = FlatProfile($reduced); 645 my $flat = FlatProfile($reduced);
639 my $cumulative = CumulativeProfile($reduced); 646 my $cumulative = CumulativeProfile($reduced);
640 647
641 # Print 648 # Print
642 if (!$main::opt_interactive) { 649 if (!$main::opt_interactive) {
643 if ($main::opt_disasm) { 650 if ($main::opt_disasm) {
644 PrintDisassembly($libs, $flat, $cumulative, $main::opt_disasm); 651 PrintDisassembly($libs, $flat, $cumulative, $main::opt_disasm);
645 } elsif ($main::opt_list) { 652 } elsif ($main::opt_list) {
646 PrintListing($libs, $flat, $cumulative, $main::opt_list); 653 PrintListing($total, $libs, $flat, $cumulative, $main::opt_list, 0);
647 } elsif ($main::opt_text) { 654 } elsif ($main::opt_text) {
648 # Make sure the output is empty when have nothing to report 655 # Make sure the output is empty when have nothing to report
649 # (only matters when --heapcheck is given but we must be 656 # (only matters when --heapcheck is given but we must be
650 # compatible with old branches that did not pass --heapcheck always): 657 # compatible with old branches that did not pass --heapcheck always):
651 if ($total != 0) { 658 if ($total != 0) {
652 printf("Total: %s %s\n", Unparse($total), Units()); 659 printf("Total: %s %s\n", Unparse($total), Units());
653 } 660 }
654 PrintText($symbols, $flat, $cumulative, -1); 661 PrintText($symbols, $flat, $cumulative, -1);
655 } elsif ($main::opt_raw) { 662 } elsif ($main::opt_raw) {
656 PrintSymbolizedProfile($symbols, $profile, $main::prog); 663 PrintSymbolizedProfile($symbols, $profile, $main::prog);
657 } elsif ($main::opt_callgrind) { 664 } elsif ($main::opt_callgrind) {
658 PrintCallgrind($calls); 665 PrintCallgrind($calls);
659 } else { 666 } else {
660 if (PrintDot($main::prog, $symbols, $profile, $flat, $cumulative, $total)) { 667 if (PrintDot($main::prog, $symbols, $profile, $flat, $cumulative, $total)) {
661 if ($main::opt_gv) { 668 if ($main::opt_gv) {
662 RunGV(TempName($main::next_tmpfile, "ps"), ""); 669 RunGV(TempName($main::next_tmpfile, "ps"), "");
663 } elsif ($main::opt_evince) { 670 } elsif ($main::opt_evince) {
664 » RunEvince(TempName($main::next_tmpfile, "pdf"), ""); 671 RunEvince(TempName($main::next_tmpfile, "pdf"), "");
665 } elsif ($main::opt_web) { 672 } elsif ($main::opt_web) {
666 my $tmp = TempName($main::next_tmpfile, "svg"); 673 my $tmp = TempName($main::next_tmpfile, "svg");
667 RunWeb($tmp); 674 RunWeb($tmp);
668 # The command we run might hand the file name off 675 # The command we run might hand the file name off
669 # to an already running browser instance and then exit. 676 # to an already running browser instance and then exit.
670 # Normally, we'd remove $tmp on exit (right now), 677 # Normally, we'd remove $tmp on exit (right now),
671 # but fork a child to remove $tmp a little later, so that the 678 # but fork a child to remove $tmp a little later, so that the
672 # browser has time to load it first. 679 # browser has time to load it first.
673 delete $main::tempnames{$tmp}; 680 delete $main::tempnames{$tmp};
674 if (fork() == 0) { 681 if (fork() == 0) {
(...skipping 28 matching lines...) Expand all
703 if (-e '/lib/libtermcap.so.2') { 710 if (-e '/lib/libtermcap.so.2') {
704 return 0; # libtermcap exists, so readline should be okay 711 return 0; # libtermcap exists, so readline should be okay
705 } else { 712 } else {
706 return 1; 713 return 1;
707 } 714 }
708 } 715 }
709 716
710 sub RunGV { 717 sub RunGV {
711 my $fname = shift; 718 my $fname = shift;
712 my $bg = shift; # "" or " &" if we should run in background 719 my $bg = shift; # "" or " &" if we should run in background
713 if (!system("$GV --version >$dev_null 2>&1")) { 720 if (!system(ShellEscape(@GV, "--version") . " >$dev_null 2>&1")) {
714 # Options using double dash are supported by this gv version. 721 # Options using double dash are supported by this gv version.
715 # Also, turn on noantialias to better handle bug in gv for 722 # Also, turn on noantialias to better handle bug in gv for
716 # postscript files with large dimensions. 723 # postscript files with large dimensions.
717 # TODO: Maybe we should not pass the --noantialias flag 724 # TODO: Maybe we should not pass the --noantialias flag
718 # if the gv version is known to work properly without the flag. 725 # if the gv version is known to work properly without the flag.
719 system("$GV --scale=$main::opt_scale --noantialias " . $fname . $bg); 726 system(ShellEscape(@GV, "--scale=$main::opt_scale", "--noantialias", $fname)
727 . $bg);
720 } else { 728 } else {
721 # Old gv version - only supports options that use single dash. 729 # Old gv version - only supports options that use single dash.
722 print STDERR "$GV -scale $main::opt_scale\n"; 730 print STDERR ShellEscape(@GV, "-scale", $main::opt_scale) . "\n";
723 system("$GV -scale $main::opt_scale " . $fname . $bg); 731 system(ShellEscape(@GV, "-scale", "$main::opt_scale", $fname) . $bg);
724 } 732 }
725 } 733 }
726 734
727 sub RunEvince { 735 sub RunEvince {
728 my $fname = shift; 736 my $fname = shift;
729 my $bg = shift; # "" or " &" if we should run in background 737 my $bg = shift; # "" or " &" if we should run in background
730 system("$EVINCE " . $fname . $bg); 738 system(ShellEscape(@EVINCE, $fname) . $bg);
731 } 739 }
732 740
733 sub RunWeb { 741 sub RunWeb {
734 my $fname = shift; 742 my $fname = shift;
735 print STDERR "Loading web page file:///$fname\n"; 743 print STDERR "Loading web page file:///$fname\n";
736 744
737 if (`uname` =~ /Darwin/) { 745 if (`uname` =~ /Darwin/) {
738 # OS X: open will use standard preference for SVG files. 746 # OS X: open will use standard preference for SVG files.
739 system("/usr/bin/open", $fname); 747 system("/usr/bin/open", $fname);
740 return; 748 return;
(...skipping 13 matching lines...) Expand all
754 return; 762 return;
755 } 763 }
756 } 764 }
757 765
758 print STDERR "Could not load web browser.\n"; 766 print STDERR "Could not load web browser.\n";
759 } 767 }
760 768
761 sub RunKcachegrind { 769 sub RunKcachegrind {
762 my $fname = shift; 770 my $fname = shift;
763 my $bg = shift; # "" or " &" if we should run in background 771 my $bg = shift; # "" or " &" if we should run in background
764 print STDERR "Starting '$KCACHEGRIND " . $fname . $bg . "'\n"; 772 print STDERR "Starting '@KCACHEGRIND " . $fname . $bg . "'\n";
765 system("$KCACHEGRIND " . $fname . $bg); 773 system(ShellEscape(@KCACHEGRIND, $fname) . $bg);
766 } 774 }
767 775
768 776
769 ##### Interactive helper routines ##### 777 ##### Interactive helper routines #####
770 778
771 sub InteractiveMode { 779 sub InteractiveMode {
772 $| = 1; # Make output unbuffered for interactive mode 780 $| = 1; # Make output unbuffered for interactive mode
773 my ($orig_profile, $symbols, $libs, $total) = @_; 781 my ($orig_profile, $symbols, $libs, $total) = @_;
774 782
775 print STDERR "Welcome to pprof! For help, type 'help'.\n"; 783 print STDERR "Welcome to pprof! For help, type 'help'.\n";
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
832 840
833 if (m/^\s*(text|top)(\d*)\s*(.*)/) { 841 if (m/^\s*(text|top)(\d*)\s*(.*)/) {
834 $main::opt_text = 1; 842 $main::opt_text = 1;
835 843
836 my $line_limit = ($2 ne "") ? int($2) : 10; 844 my $line_limit = ($2 ne "") ? int($2) : 10;
837 845
838 my $routine; 846 my $routine;
839 my $ignore; 847 my $ignore;
840 ($routine, $ignore) = ParseInteractiveArgs($3); 848 ($routine, $ignore) = ParseInteractiveArgs($3);
841 849
842 my $profile = ProcessProfile($orig_profile, $symbols, "", $ignore); 850 my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
843 my $reduced = ReduceProfile($symbols, $profile); 851 my $reduced = ReduceProfile($symbols, $profile);
844 852
845 # Get derived profiles 853 # Get derived profiles
846 my $flat = FlatProfile($reduced); 854 my $flat = FlatProfile($reduced);
847 my $cumulative = CumulativeProfile($reduced); 855 my $cumulative = CumulativeProfile($reduced);
848 856
849 PrintText($symbols, $flat, $cumulative, $line_limit); 857 PrintText($symbols, $flat, $cumulative, $line_limit);
850 return 1; 858 return 1;
851 } 859 }
852 if (m/^\s*callgrind\s*([^ \n]*)/) { 860 if (m/^\s*callgrind\s*([^ \n]*)/) {
853 $main::opt_callgrind = 1; 861 $main::opt_callgrind = 1;
854 862
855 # Get derived profiles 863 # Get derived profiles
856 my $calls = ExtractCalls($symbols, $orig_profile); 864 my $calls = ExtractCalls($symbols, $orig_profile);
857 my $filename = $1; 865 my $filename = $1;
858 if ( $1 eq '' ) { 866 if ( $1 eq '' ) {
859 $filename = TempName($main::next_tmpfile, "callgrind"); 867 $filename = TempName($main::next_tmpfile, "callgrind");
860 } 868 }
861 PrintCallgrind($calls, $filename); 869 PrintCallgrind($calls, $filename);
862 if ( $1 eq '' ) { 870 if ( $1 eq '' ) {
863 RunKcachegrind($filename, " & "); 871 RunKcachegrind($filename, " & ");
864 $main::next_tmpfile++; 872 $main::next_tmpfile++;
865 } 873 }
866 874
867 return 1; 875 return 1;
868 } 876 }
869 if (m/^\s*list\s*(.+)/) { 877 if (m/^\s*(web)?list\s*(.+)/) {
878 my $html = (defined($1) && ($1 eq "web"));
870 $main::opt_list = 1; 879 $main::opt_list = 1;
871 880
872 my $routine; 881 my $routine;
873 my $ignore; 882 my $ignore;
874 ($routine, $ignore) = ParseInteractiveArgs($1); 883 ($routine, $ignore) = ParseInteractiveArgs($2);
875 884
876 my $profile = ProcessProfile($orig_profile, $symbols, "", $ignore); 885 my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
877 my $reduced = ReduceProfile($symbols, $profile); 886 my $reduced = ReduceProfile($symbols, $profile);
878 887
879 # Get derived profiles 888 # Get derived profiles
880 my $flat = FlatProfile($reduced); 889 my $flat = FlatProfile($reduced);
881 my $cumulative = CumulativeProfile($reduced); 890 my $cumulative = CumulativeProfile($reduced);
882 891
883 PrintListing($libs, $flat, $cumulative, $routine); 892 PrintListing($total, $libs, $flat, $cumulative, $routine, $html);
884 return 1; 893 return 1;
885 } 894 }
886 if (m/^\s*disasm\s*(.+)/) { 895 if (m/^\s*disasm\s*(.+)/) {
887 $main::opt_disasm = 1; 896 $main::opt_disasm = 1;
888 897
889 my $routine; 898 my $routine;
890 my $ignore; 899 my $ignore;
891 ($routine, $ignore) = ParseInteractiveArgs($1); 900 ($routine, $ignore) = ParseInteractiveArgs($1);
892 901
893 # Process current profile to account for various settings 902 # Process current profile to account for various settings
894 my $profile = ProcessProfile($orig_profile, $symbols, "", $ignore); 903 my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
895 my $reduced = ReduceProfile($symbols, $profile); 904 my $reduced = ReduceProfile($symbols, $profile);
896 905
897 # Get derived profiles 906 # Get derived profiles
898 my $flat = FlatProfile($reduced); 907 my $flat = FlatProfile($reduced);
899 my $cumulative = CumulativeProfile($reduced); 908 my $cumulative = CumulativeProfile($reduced);
900 909
901 PrintDisassembly($libs, $flat, $cumulative, $routine); 910 PrintDisassembly($libs, $flat, $cumulative, $routine);
902 return 1; 911 return 1;
903 } 912 }
904 if (m/^\s*(gv|web|evince)\s*(.*)/) { 913 if (m/^\s*(gv|web|evince)\s*(.*)/) {
905 $main::opt_gv = 0; 914 $main::opt_gv = 0;
906 $main::opt_evince = 0; 915 $main::opt_evince = 0;
907 $main::opt_web = 0; 916 $main::opt_web = 0;
908 if ($1 eq "gv") { 917 if ($1 eq "gv") {
909 $main::opt_gv = 1; 918 $main::opt_gv = 1;
910 } elsif ($1 eq "evince") { 919 } elsif ($1 eq "evince") {
911 $main::opt_evince = 1; 920 $main::opt_evince = 1;
912 } elsif ($1 eq "web") { 921 } elsif ($1 eq "web") {
913 $main::opt_web = 1; 922 $main::opt_web = 1;
914 } 923 }
915 924
916 my $focus; 925 my $focus;
917 my $ignore; 926 my $ignore;
918 ($focus, $ignore) = ParseInteractiveArgs($2); 927 ($focus, $ignore) = ParseInteractiveArgs($2);
919 928
920 # Process current profile to account for various settings 929 # Process current profile to account for various settings
921 my $profile = ProcessProfile($orig_profile, $symbols, $focus, $ignore); 930 my $profile = ProcessProfile($total, $orig_profile, $symbols,
931 $focus, $ignore);
922 my $reduced = ReduceProfile($symbols, $profile); 932 my $reduced = ReduceProfile($symbols, $profile);
923 933
924 # Get derived profiles 934 # Get derived profiles
925 my $flat = FlatProfile($reduced); 935 my $flat = FlatProfile($reduced);
926 my $cumulative = CumulativeProfile($reduced); 936 my $cumulative = CumulativeProfile($reduced);
927 937
928 if (PrintDot($main::prog, $symbols, $profile, $flat, $cumulative, $total)) { 938 if (PrintDot($main::prog, $symbols, $profile, $flat, $cumulative, $total)) {
929 if ($main::opt_gv) { 939 if ($main::opt_gv) {
930 RunGV(TempName($main::next_tmpfile, "ps"), " &"); 940 RunGV(TempName($main::next_tmpfile, "ps"), " &");
931 } elsif ($main::opt_evince) { 941 } elsif ($main::opt_evince) {
932 RunEvince(TempName($main::next_tmpfile, "pdf"), " &"); 942 RunEvince(TempName($main::next_tmpfile, "pdf"), " &");
933 } elsif ($main::opt_web) { 943 } elsif ($main::opt_web) {
934 RunWeb(TempName($main::next_tmpfile, "svg")); 944 RunWeb(TempName($main::next_tmpfile, "svg"));
935 } 945 }
936 $main::next_tmpfile++; 946 $main::next_tmpfile++;
937 } 947 }
938 return 1; 948 return 1;
939 } 949 }
940 if (m/^\s*$/) { 950 if (m/^\s*$/) {
941 return 1; 951 return 1;
942 } 952 }
943 print STDERR "Unknown command: try 'help'.\n"; 953 print STDERR "Unknown command: try 'help'.\n";
944 return 1; 954 return 1;
945 } 955 }
946 956
947 957
948 sub ProcessProfile { 958 sub ProcessProfile {
959 my $total_count = shift;
949 my $orig_profile = shift; 960 my $orig_profile = shift;
950 my $symbols = shift; 961 my $symbols = shift;
951 my $focus = shift; 962 my $focus = shift;
952 my $ignore = shift; 963 my $ignore = shift;
953 964
954 # Process current profile to account for various settings 965 # Process current profile to account for various settings
955 my $profile = $orig_profile; 966 my $profile = $orig_profile;
956 my $total_count = TotalProfile($profile);
957 printf("Total: %s %s\n", Unparse($total_count), Units()); 967 printf("Total: %s %s\n", Unparse($total_count), Units());
958 if ($focus ne '') { 968 if ($focus ne '') {
959 $profile = FocusProfile($symbols, $profile, $focus); 969 $profile = FocusProfile($symbols, $profile, $focus);
960 my $focus_count = TotalProfile($profile); 970 my $focus_count = TotalProfile($profile);
961 printf("After focusing on '%s': %s %s of %s (%0.1f%%)\n", 971 printf("After focusing on '%s': %s %s of %s (%0.1f%%)\n",
962 $focus, 972 $focus,
963 Unparse($focus_count), Units(), 973 Unparse($focus_count), Units(),
964 Unparse($total_count), ($focus_count*100.0) / $total_count); 974 Unparse($total_count), ($focus_count*100.0) / $total_count);
965 } 975 }
966 if ($ignore ne '') { 976 if ($ignore ne '') {
(...skipping 26 matching lines...) Expand all
993 web [focus] [-ignore1] [-ignore2] 1003 web [focus] [-ignore1] [-ignore2]
994 Like GV, but displays profile in your web browser instead of using 1004 Like GV, but displays profile in your web browser instead of using
995 Ghostview. Works best if your web browser is already running. 1005 Ghostview. Works best if your web browser is already running.
996 To change the browser that gets used: 1006 To change the browser that gets used:
997 On Linux, set the /etc/alternatives/gnome-www-browser symlink. 1007 On Linux, set the /etc/alternatives/gnome-www-browser symlink.
998 On OS X, change the Finder association for SVG files. 1008 On OS X, change the Finder association for SVG files.
999 1009
1000 list [routine_regexp] [-ignore1] [-ignore2] 1010 list [routine_regexp] [-ignore1] [-ignore2]
1001 Show source listing of routines whose names match "routine_regexp" 1011 Show source listing of routines whose names match "routine_regexp"
1002 1012
1013 weblist [routine_regexp] [-ignore1] [-ignore2]
1014 Displays a source listing of routines whose names match "routine_regexp"
1015 in a web browser. You can click on source lines to view the
1016 corresponding disassembly.
1017
1003 top [--cum] [-ignore1] [-ignore2] 1018 top [--cum] [-ignore1] [-ignore2]
1004 top20 [--cum] [-ignore1] [-ignore2] 1019 top20 [--cum] [-ignore1] [-ignore2]
1005 top37 [--cum] [-ignore1] [-ignore2] 1020 top37 [--cum] [-ignore1] [-ignore2]
1006 Show top lines ordered by flat profile count, or cumulative count 1021 Show top lines ordered by flat profile count, or cumulative count
1007 if --cum is specified. If a number is present after 'top', the 1022 if --cum is specified. If a number is present after 'top', the
1008 top K routines will be shown (defaults to showing the top 10) 1023 top K routines will be shown (defaults to showing the top 10)
1009 1024
1010 disasm [routine_regexp] [-ignore1] [-ignore2] 1025 disasm [routine_regexp] [-ignore1] [-ignore2]
1011 Show disassembly of routines whose names match "routine_regexp", 1026 Show disassembly of routines whose names match "routine_regexp",
1012 annotated with sample counts. 1027 annotated with sample counts.
1013 1028
1014 callgrind 1029 callgrind
1015 callgrind [filename] 1030 callgrind [filename]
1016 Generates callgrind file. If no filename is given, kcachegrind is called. 1031 Generates callgrind file. If no filename is given, kcachegrind is called.
1017 1032
1018 help - This listing 1033 help - This listing
1019 quit or ^D - End pprof 1034 quit or ^D - End pprof
1020 1035
1021 For commands that accept optional -ignore tags, samples where any routine in 1036 For commands that accept optional -ignore tags, samples where any routine in
1022 the stack trace matches the regular expression in any of the -ignore 1037 the stack trace matches the regular expression in any of the -ignore
1023 parameters will be ignored. 1038 parameters will be ignored.
1024 1039
1025 Further pprof details are available at this location (or one similar): 1040 Further pprof details are available at this location (or one similar):
1026 1041
1027 /usr/doc/google-perftools-$PPROF_VERSION/cpu_profiler.html 1042 /usr/doc/gperftools-$PPROF_VERSION/cpu_profiler.html
1028 /usr/doc/google-perftools-$PPROF_VERSION/heap_profiler.html 1043 /usr/doc/gperftools-$PPROF_VERSION/heap_profiler.html
1029 1044
1030 ENDOFHELP 1045 ENDOFHELP
1031 } 1046 }
1032 sub ParseInteractiveArgs { 1047 sub ParseInteractiveArgs {
1033 my $args = shift; 1048 my $args = shift;
1034 my $focus = ""; 1049 my $focus = "";
1035 my $ignore = ""; 1050 my $ignore = "";
1036 my @x = split(/ +/, $args); 1051 my @x = split(/ +/, $args);
1037 foreach $a (@x) { 1052 foreach $a (@x) {
1038 if ($a =~ m/^(--|-)lines$/) { 1053 if ($a =~ m/^(--|-)lines$/) {
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after
1168 if ($f != 0 || $c != 0) { 1183 if ($f != 0 || $c != 0) {
1169 printf("%8s %6s %6s %8s %6s %s\n", 1184 printf("%8s %6s %6s %8s %6s %s\n",
1170 Unparse($f), 1185 Unparse($f),
1171 Percent($f, $total), 1186 Percent($f, $total),
1172 Percent($running_sum, $total), 1187 Percent($running_sum, $total),
1173 Unparse($c), 1188 Unparse($c),
1174 Percent($c, $total), 1189 Percent($c, $total),
1175 $sym); 1190 $sym);
1176 } 1191 }
1177 $lines++; 1192 $lines++;
1178 last if ($line_limit >= 0 && $lines > $line_limit); 1193 last if ($line_limit >= 0 && $lines >= $line_limit);
1179 } 1194 }
1180 } 1195 }
1181 1196
1197 # Callgrind format has a compression for repeated function and file
1198 # names. You show the name the first time, and just use its number
1199 # subsequently. This can cut down the file to about a third or a
1200 # quarter of its uncompressed size. $key and $val are the key/value
1201 # pair that would normally be printed by callgrind; $map is a map from
1202 # value to number.
1203 sub CompressedCGName {
1204 my($key, $val, $map) = @_;
1205 my $idx = $map->{$val};
1206 # For very short keys, providing an index hurts rather than helps.
1207 if (length($val) <= 3) {
1208 return "$key=$val\n";
1209 } elsif (defined($idx)) {
1210 return "$key=($idx)\n";
1211 } else {
1212 # scalar(keys $map) gives the number of items in the map.
1213 $idx = scalar(keys(%{$map})) + 1;
1214 $map->{$val} = $idx;
1215 return "$key=($idx) $val\n";
1216 }
1217 }
1218
1182 # Print the call graph in a way that's suiteable for callgrind. 1219 # Print the call graph in a way that's suiteable for callgrind.
1183 sub PrintCallgrind { 1220 sub PrintCallgrind {
1184 my $calls = shift; 1221 my $calls = shift;
1185 my $filename; 1222 my $filename;
1223 my %filename_to_index_map;
1224 my %fnname_to_index_map;
1225
1186 if ($main::opt_interactive) { 1226 if ($main::opt_interactive) {
1187 $filename = shift; 1227 $filename = shift;
1188 print STDERR "Writing callgrind file to '$filename'.\n" 1228 print STDERR "Writing callgrind file to '$filename'.\n"
1189 } else { 1229 } else {
1190 $filename = "&STDOUT"; 1230 $filename = "&STDOUT";
1191 } 1231 }
1192 open(CG, ">".$filename ); 1232 open(CG, ">$filename");
1193 printf CG ("events: Hits\n\n"); 1233 printf CG ("events: Hits\n\n");
1194 foreach my $call ( map { $_->[0] } 1234 foreach my $call ( map { $_->[0] }
1195 sort { $a->[1] cmp $b ->[1] || 1235 sort { $a->[1] cmp $b ->[1] ||
1196 $a->[2] <=> $b->[2] } 1236 $a->[2] <=> $b->[2] }
1197 map { /([^:]+):(\d+):([^ ]+)( -> ([^:]+):(\d+):(.+))?/; 1237 map { /([^:]+):(\d+):([^ ]+)( -> ([^:]+):(\d+):(.+))?/;
1198 [$_, $1, $2] } 1238 [$_, $1, $2] }
1199 keys %$calls ) { 1239 keys %$calls ) {
1200 my $count = int($calls->{$call}); 1240 my $count = int($calls->{$call});
1201 $call =~ /([^:]+):(\d+):([^ ]+)( -> ([^:]+):(\d+):(.+))?/; 1241 $call =~ /([^:]+):(\d+):([^ ]+)( -> ([^:]+):(\d+):(.+))?/;
1202 my ( $caller_file, $caller_line, $caller_function, 1242 my ( $caller_file, $caller_line, $caller_function,
1203 $callee_file, $callee_line, $callee_function ) = 1243 $callee_file, $callee_line, $callee_function ) =
1204 ( $1, $2, $3, $5, $6, $7 ); 1244 ( $1, $2, $3, $5, $6, $7 );
1205 1245
1206 1246 # TODO(csilvers): for better compression, collect all the
1207 printf CG ("fl=$caller_file\nfn=$caller_function\n"); 1247 # caller/callee_files and functions first, before printing
1248 # anything, and only compress those referenced more than once.
1249 printf CG CompressedCGName("fl", $caller_file, \%filename_to_index_map);
1250 printf CG CompressedCGName("fn", $caller_function, \%fnname_to_index_map);
1208 if (defined $6) { 1251 if (defined $6) {
1209 printf CG ("cfl=$callee_file\n"); 1252 printf CG CompressedCGName("cfl", $callee_file, \%filename_to_index_map);
1210 printf CG ("cfn=$callee_function\n"); 1253 printf CG CompressedCGName("cfn", $callee_function, \%fnname_to_index_map) ;
1211 printf CG ("calls=$count $callee_line\n"); 1254 printf CG ("calls=$count $callee_line\n");
1212 } 1255 }
1213 printf CG ("$caller_line $count\n\n"); 1256 printf CG ("$caller_line $count\n\n");
1214 } 1257 }
1215 } 1258 }
1216 1259
1217 # Print disassembly for all all routines that match $main::opt_disasm 1260 # Print disassembly for all all routines that match $main::opt_disasm
1218 sub PrintDisassembly { 1261 sub PrintDisassembly {
1219 my $libs = shift; 1262 my $libs = shift;
1220 my $flat = shift; 1263 my $flat = shift;
(...skipping 28 matching lines...) Expand all
1249 # [start_address, filename, linenumber, instruction, limit_address] 1292 # [start_address, filename, linenumber, instruction, limit_address]
1250 # E.g., 1293 # E.g.,
1251 # ["0x806c43d", "/foo/bar.cc", 131, "ret", "0x806c440"] 1294 # ["0x806c43d", "/foo/bar.cc", 131, "ret", "0x806c440"]
1252 sub Disassemble { 1295 sub Disassemble {
1253 my $prog = shift; 1296 my $prog = shift;
1254 my $offset = shift; 1297 my $offset = shift;
1255 my $start_addr = shift; 1298 my $start_addr = shift;
1256 my $end_addr = shift; 1299 my $end_addr = shift;
1257 1300
1258 my $objdump = $obj_tool_map{"objdump"}; 1301 my $objdump = $obj_tool_map{"objdump"};
1259 my $cmd = sprintf("$objdump -C -d -l --no-show-raw-insn " . 1302 my $cmd = ShellEscape($objdump, "-C", "-d", "-l", "--no-show-raw-insn",
1260 "--start-address=0x$start_addr " . 1303 "--start-address=0x$start_addr",
1261 "--stop-address=0x$end_addr $prog"); 1304 "--stop-address=0x$end_addr", $prog);
1262 open(OBJDUMP, "$cmd |") || error("$objdump: $!\n"); 1305 open(OBJDUMP, "$cmd |") || error("$cmd: $!\n");
1263 my @result = (); 1306 my @result = ();
1264 my $filename = ""; 1307 my $filename = "";
1265 my $linenumber = -1; 1308 my $linenumber = -1;
1266 my $last = ["", "", "", ""]; 1309 my $last = ["", "", "", ""];
1267 while (<OBJDUMP>) { 1310 while (<OBJDUMP>) {
1268 s/\r//g; # turn windows-looking lines into unix-looking lines 1311 s/\r//g; # turn windows-looking lines into unix-looking lines
1269 chop; 1312 chop;
1270 if (m|\s*([^:\s]+):(\d+)\s*$|) { 1313 if (m|\s*([^:\s]+):(\d+)\s*$|) {
1271 # Location line of the form: 1314 # Location line of the form:
1272 # <filename>:<linenumber> 1315 # <filename>:<linenumber>
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
1315 print(($symbols->{$pc}->[0] || "??") . "\n"); 1358 print(($symbols->{$pc}->[0] || "??") . "\n");
1316 } 1359 }
1317 } 1360 }
1318 1361
1319 1362
1320 # For sorting functions by name 1363 # For sorting functions by name
1321 sub ByName { 1364 sub ByName {
1322 return ShortFunctionName($a) cmp ShortFunctionName($b); 1365 return ShortFunctionName($a) cmp ShortFunctionName($b);
1323 } 1366 }
1324 1367
1325 # Print source-listing for all all routines that match $main::opt_list 1368 # Print source-listing for all all routines that match $list_opts
1326 sub PrintListing { 1369 sub PrintListing {
1370 my $total = shift;
1327 my $libs = shift; 1371 my $libs = shift;
1328 my $flat = shift; 1372 my $flat = shift;
1329 my $cumulative = shift; 1373 my $cumulative = shift;
1330 my $list_opts = shift; 1374 my $list_opts = shift;
1375 my $html = shift;
1331 1376
1377 my $output = \*STDOUT;
1378 my $fname = "";
1379
1380 if ($html) {
1381 # Arrange to write the output to a temporary file
1382 $fname = TempName($main::next_tmpfile, "html");
1383 $main::next_tmpfile++;
1384 if (!open(TEMP, ">$fname")) {
1385 print STDERR "$fname: $!\n";
1386 return;
1387 }
1388 $output = \*TEMP;
1389 print $output HtmlListingHeader();
1390 printf $output ("<div class=\"legend\">%s<br>Total: %s %s</div>\n",
1391 $main::prog, Unparse($total), Units());
1392 }
1393
1394 my $listed = 0;
1332 foreach my $lib (@{$libs}) { 1395 foreach my $lib (@{$libs}) {
1333 my $symbol_table = GetProcedureBoundaries($lib->[0], $list_opts); 1396 my $symbol_table = GetProcedureBoundaries($lib->[0], $list_opts);
1334 my $offset = AddressSub($lib->[1], $lib->[3]); 1397 my $offset = AddressSub($lib->[1], $lib->[3]);
1335 foreach my $routine (sort ByName keys(%{$symbol_table})) { 1398 foreach my $routine (sort ByName keys(%{$symbol_table})) {
1336 # Print if there are any samples in this routine 1399 # Print if there are any samples in this routine
1337 my $start_addr = $symbol_table->{$routine}->[0]; 1400 my $start_addr = $symbol_table->{$routine}->[0];
1338 my $end_addr = $symbol_table->{$routine}->[1]; 1401 my $end_addr = $symbol_table->{$routine}->[1];
1339 my $length = hex(AddressSub($end_addr, $start_addr)); 1402 my $length = hex(AddressSub($end_addr, $start_addr));
1340 my $addr = AddressAdd($start_addr, $offset); 1403 my $addr = AddressAdd($start_addr, $offset);
1341 for (my $i = 0; $i < $length; $i++) { 1404 for (my $i = 0; $i < $length; $i++) {
1342 if (defined($cumulative->{$addr})) { 1405 if (defined($cumulative->{$addr})) {
1343 PrintSource($lib->[0], $offset, 1406 $listed += PrintSource(
1344 $routine, $flat, $cumulative, 1407 $lib->[0], $offset,
1345 $start_addr, $end_addr); 1408 $routine, $flat, $cumulative,
1409 $start_addr, $end_addr,
1410 $html,
1411 $output);
1346 last; 1412 last;
1347 } 1413 }
1348 $addr = AddressInc($addr); 1414 $addr = AddressInc($addr);
1349 } 1415 }
1350 } 1416 }
1351 } 1417 }
1418
1419 if ($html) {
1420 if ($listed > 0) {
1421 print $output HtmlListingFooter();
1422 close($output);
1423 RunWeb($fname);
1424 } else {
1425 close($output);
1426 unlink($fname);
1427 }
1428 }
1429 }
1430
1431 sub HtmlListingHeader {
1432 return <<'EOF';
1433 <DOCTYPE html>
1434 <html>
1435 <head>
1436 <title>Pprof listing</title>
1437 <style type="text/css">
1438 body {
1439 font-family: sans-serif;
1440 }
1441 h1 {
1442 font-size: 1.5em;
1443 margin-bottom: 4px;
1444 }
1445 .legend {
1446 font-size: 1.25em;
1447 }
1448 .line {
1449 color: #aaaaaa;
1450 }
1451 .nop {
1452 color: #aaaaaa;
1453 }
1454 .unimportant {
1455 color: #cccccc;
1456 }
1457 .disasmloc {
1458 color: #000000;
1459 }
1460 .deadsrc {
1461 cursor: pointer;
1462 }
1463 .deadsrc:hover {
1464 background-color: #eeeeee;
1465 }
1466 .livesrc {
1467 color: #0000ff;
1468 cursor: pointer;
1469 }
1470 .livesrc:hover {
1471 background-color: #eeeeee;
1472 }
1473 .asm {
1474 color: #008800;
1475 display: none;
1476 }
1477 </style>
1478 <script type="text/javascript">
1479 function pprof_toggle_asm(e) {
1480 var target;
1481 if (!e) e = window.event;
1482 if (e.target) target = e.target;
1483 else if (e.srcElement) target = e.srcElement;
1484
1485 if (target) {
1486 var asm = target.nextSibling;
1487 if (asm && asm.className == "asm") {
1488 asm.style.display = (asm.style.display == "block" ? "" : "block");
1489 e.preventDefault();
1490 return false;
1491 }
1492 }
1493 }
1494 </script>
1495 </head>
1496 <body>
1497 EOF
1498 }
1499
1500 sub HtmlListingFooter {
1501 return <<'EOF';
1502 </body>
1503 </html>
1504 EOF
1505 }
1506
1507 sub HtmlEscape {
1508 my $text = shift;
1509 $text =~ s/&/&amp;/g;
1510 $text =~ s/</&lt;/g;
1511 $text =~ s/>/&gt;/g;
1512 return $text;
1352 } 1513 }
1353 1514
1354 # Returns the indentation of the line, if it has any non-whitespace 1515 # Returns the indentation of the line, if it has any non-whitespace
1355 # characters. Otherwise, returns -1. 1516 # characters. Otherwise, returns -1.
1356 sub Indentation { 1517 sub Indentation {
1357 my $line = shift; 1518 my $line = shift;
1358 if (m/^(\s*)\S/) { 1519 if (m/^(\s*)\S/) {
1359 return length($1); 1520 return length($1);
1360 } else { 1521 } else {
1361 return -1; 1522 return -1;
1362 } 1523 }
1363 } 1524 }
1364 1525
1526 # If the symbol table contains inlining info, Disassemble() may tag an
1527 # instruction with a location inside an inlined function. But for
1528 # source listings, we prefer to use the location in the function we
1529 # are listing. So use MapToSymbols() to fetch full location
1530 # information for each instruction and then pick out the first
1531 # location from a location list (location list contains callers before
1532 # callees in case of inlining).
1533 #
1534 # After this routine has run, each entry in $instructions contains:
1535 # [0] start address
1536 # [1] filename for function we are listing
1537 # [2] line number for function we are listing
1538 # [3] disassembly
1539 # [4] limit address
1540 # [5] most specific filename (may be different from [1] due to inlining)
1541 # [6] most specific line number (may be different from [2] due to inlining)
1542 sub GetTopLevelLineNumbers {
1543 my ($lib, $offset, $instructions) = @_;
1544 my $pcs = [];
1545 for (my $i = 0; $i <= $#{$instructions}; $i++) {
1546 push(@{$pcs}, $instructions->[$i]->[0]);
1547 }
1548 my $symbols = {};
1549 MapToSymbols($lib, $offset, $pcs, $symbols);
1550 for (my $i = 0; $i <= $#{$instructions}; $i++) {
1551 my $e = $instructions->[$i];
1552 push(@{$e}, $e->[1]);
1553 push(@{$e}, $e->[2]);
1554 my $addr = $e->[0];
1555 my $sym = $symbols->{$addr};
1556 if (defined($sym)) {
1557 if ($#{$sym} >= 2 && $sym->[1] =~ m/^(.*):(\d+)$/) {
1558 $e->[1] = $1; # File name
1559 $e->[2] = $2; # Line number
1560 }
1561 }
1562 }
1563 }
1564
1365 # Print source-listing for one routine 1565 # Print source-listing for one routine
1366 sub PrintSource { 1566 sub PrintSource {
1367 my $prog = shift; 1567 my $prog = shift;
1368 my $offset = shift; 1568 my $offset = shift;
1369 my $routine = shift; 1569 my $routine = shift;
1370 my $flat = shift; 1570 my $flat = shift;
1371 my $cumulative = shift; 1571 my $cumulative = shift;
1372 my $start_addr = shift; 1572 my $start_addr = shift;
1373 my $end_addr = shift; 1573 my $end_addr = shift;
1574 my $html = shift;
1575 my $output = shift;
1374 1576
1375 # Disassemble all instructions (just to get line numbers) 1577 # Disassemble all instructions (just to get line numbers)
1376 my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr); 1578 my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr);
1579 GetTopLevelLineNumbers($prog, $offset, \@instructions);
1377 1580
1378 # Hack 1: assume that the first source file encountered in the 1581 # Hack 1: assume that the first source file encountered in the
1379 # disassembly contains the routine 1582 # disassembly contains the routine
1380 my $filename = undef; 1583 my $filename = undef;
1381 for (my $i = 0; $i <= $#instructions; $i++) { 1584 for (my $i = 0; $i <= $#instructions; $i++) {
1382 if ($instructions[$i]->[2] >= 0) { 1585 if ($instructions[$i]->[2] >= 0) {
1383 $filename = $instructions[$i]->[1]; 1586 $filename = $instructions[$i]->[1];
1384 last; 1587 last;
1385 } 1588 }
1386 } 1589 }
1387 if (!defined($filename)) { 1590 if (!defined($filename)) {
1388 print STDERR "no filename found in $routine\n"; 1591 print STDERR "no filename found in $routine\n";
1389 return; 1592 return 0;
1390 } 1593 }
1391 1594
1392 # Hack 2: assume that the largest line number from $filename is the 1595 # Hack 2: assume that the largest line number from $filename is the
1393 # end of the procedure. This is typically safe since if P1 contains 1596 # end of the procedure. This is typically safe since if P1 contains
1394 # an inlined call to P2, then P2 usually occurs earlier in the 1597 # an inlined call to P2, then P2 usually occurs earlier in the
1395 # source file. If this does not work, we might have to compute a 1598 # source file. If this does not work, we might have to compute a
1396 # density profile or just print all regions we find. 1599 # density profile or just print all regions we find.
1397 my $lastline = 0; 1600 my $lastline = 0;
1398 for (my $i = 0; $i <= $#instructions; $i++) { 1601 for (my $i = 0; $i <= $#instructions; $i++) {
1399 my $f = $instructions[$i]->[1]; 1602 my $f = $instructions[$i]->[1];
(...skipping 12 matching lines...) Expand all
1412 last; 1615 last;
1413 } 1616 }
1414 } 1617 }
1415 1618
1416 # Hack 4: Extend last line forward until its indentation is less than 1619 # Hack 4: Extend last line forward until its indentation is less than
1417 # the indentation we saw on $firstline 1620 # the indentation we saw on $firstline
1418 my $oldlastline = $lastline; 1621 my $oldlastline = $lastline;
1419 { 1622 {
1420 if (!open(FILE, "<$filename")) { 1623 if (!open(FILE, "<$filename")) {
1421 print STDERR "$filename: $!\n"; 1624 print STDERR "$filename: $!\n";
1422 return; 1625 return 0;
1423 } 1626 }
1424 my $l = 0; 1627 my $l = 0;
1425 my $first_indentation = -1; 1628 my $first_indentation = -1;
1426 while (<FILE>) { 1629 while (<FILE>) {
1427 s/\r//g; # turn windows-looking lines into unix-looking lines 1630 s/\r//g; # turn windows-looking lines into unix-looking lines
1428 $l++; 1631 $l++;
1429 my $indent = Indentation($_); 1632 my $indent = Indentation($_);
1430 if ($l >= $firstline) { 1633 if ($l >= $firstline) {
1431 if ($first_indentation < 0 && $indent >= 0) { 1634 if ($first_indentation < 0 && $indent >= 0) {
1432 $first_indentation = $indent; 1635 $first_indentation = $indent;
1433 last if ($first_indentation == 0); 1636 last if ($first_indentation == 0);
1434 } 1637 }
1435 } 1638 }
1436 if ($l >= $lastline && $indent >= 0) { 1639 if ($l >= $lastline && $indent >= 0) {
1437 if ($indent >= $first_indentation) { 1640 if ($indent >= $first_indentation) {
1438 $lastline = $l+1; 1641 $lastline = $l+1;
1439 } else { 1642 } else {
1440 last; 1643 last;
1441 } 1644 }
1442 } 1645 }
1443 } 1646 }
1444 close(FILE); 1647 close(FILE);
1445 } 1648 }
1446 1649
1447 # Assign all samples to the range $firstline,$lastline, 1650 # Assign all samples to the range $firstline,$lastline,
1448 # Hack 4: If an instruction does not occur in the range, its samples 1651 # Hack 4: If an instruction does not occur in the range, its samples
1449 # are moved to the next instruction that occurs in the range. 1652 # are moved to the next instruction that occurs in the range.
1450 my $samples1 = {}; 1653 my $samples1 = {}; # Map from line number to flat count
1451 my $samples2 = {}; 1654 my $samples2 = {}; # Map from line number to cumulative count
1452 my $running1 = 0; # Unassigned flat counts 1655 my $running1 = 0; # Unassigned flat counts
1453 my $running2 = 0; # Unassigned cumulative counts 1656 my $running2 = 0; # Unassigned cumulative counts
1454 my $total1 = 0; # Total flat counts 1657 my $total1 = 0; # Total flat counts
1455 my $total2 = 0; # Total cumulative counts 1658 my $total2 = 0; # Total cumulative counts
1659 my %disasm = (); # Map from line number to disassembly
1660 my $running_disasm = ""; # Unassigned disassembly
1661 my $skip_marker = "---\n";
1662 if ($html) {
1663 $skip_marker = "";
1664 for (my $l = $firstline; $l <= $lastline; $l++) {
1665 $disasm{$l} = "";
1666 }
1667 }
1668 my $last_dis_filename = '';
1669 my $last_dis_linenum = -1;
1670 my $last_touched_line = -1; # To detect gaps in disassembly for a line
1456 foreach my $e (@instructions) { 1671 foreach my $e (@instructions) {
1457 # Add up counts for all address that fall inside this instruction 1672 # Add up counts for all address that fall inside this instruction
1458 my $c1 = 0; 1673 my $c1 = 0;
1459 my $c2 = 0; 1674 my $c2 = 0;
1460 for (my $a = $e->[0]; $a lt $e->[4]; $a = AddressInc($a)) { 1675 for (my $a = $e->[0]; $a lt $e->[4]; $a = AddressInc($a)) {
1461 $c1 += GetEntry($flat, $a); 1676 $c1 += GetEntry($flat, $a);
1462 $c2 += GetEntry($cumulative, $a); 1677 $c2 += GetEntry($cumulative, $a);
1463 } 1678 }
1679
1680 if ($html) {
1681 my $dis = sprintf(" %6s %6s \t\t%8s: %s ",
1682 HtmlPrintNumber($c1),
1683 HtmlPrintNumber($c2),
1684 UnparseAddress($offset, $e->[0]),
1685 CleanDisassembly($e->[3]));
1686
1687 # Append the most specific source line associated with this instruction
1688 if (length($dis) < 80) { $dis .= (' ' x (80 - length($dis))) };
1689 $dis = HtmlEscape($dis);
1690 my $f = $e->[5];
1691 my $l = $e->[6];
1692 if ($f ne $last_dis_filename) {
1693 $dis .= sprintf("<span class=disasmloc>%s:%d</span>",
1694 HtmlEscape(CleanFileName($f)), $l);
1695 } elsif ($l ne $last_dis_linenum) {
1696 # De-emphasize the unchanged file name portion
1697 $dis .= sprintf("<span class=unimportant>%s</span>" .
1698 "<span class=disasmloc>:%d</span>",
1699 HtmlEscape(CleanFileName($f)), $l);
1700 } else {
1701 # De-emphasize the entire location
1702 $dis .= sprintf("<span class=unimportant>%s:%d</span>",
1703 HtmlEscape(CleanFileName($f)), $l);
1704 }
1705 $last_dis_filename = $f;
1706 $last_dis_linenum = $l;
1707 $running_disasm .= $dis;
1708 $running_disasm .= "\n";
1709 }
1710
1464 $running1 += $c1; 1711 $running1 += $c1;
1465 $running2 += $c2; 1712 $running2 += $c2;
1466 $total1 += $c1; 1713 $total1 += $c1;
1467 $total2 += $c2; 1714 $total2 += $c2;
1468 my $file = $e->[1]; 1715 my $file = $e->[1];
1469 my $line = $e->[2]; 1716 my $line = $e->[2];
1470 if (($file eq $filename) && 1717 if (($file eq $filename) &&
1471 ($line >= $firstline) && 1718 ($line >= $firstline) &&
1472 ($line <= $lastline)) { 1719 ($line <= $lastline)) {
1473 # Assign all accumulated samples to this line 1720 # Assign all accumulated samples to this line
1474 AddEntry($samples1, $line, $running1); 1721 AddEntry($samples1, $line, $running1);
1475 AddEntry($samples2, $line, $running2); 1722 AddEntry($samples2, $line, $running2);
1476 $running1 = 0; 1723 $running1 = 0;
1477 $running2 = 0; 1724 $running2 = 0;
1725 if ($html) {
1726 if ($line != $last_touched_line && $disasm{$line} ne '') {
1727 $disasm{$line} .= "\n";
1728 }
1729 $disasm{$line} .= $running_disasm;
1730 $running_disasm = '';
1731 $last_touched_line = $line;
1732 }
1478 } 1733 }
1479 } 1734 }
1480 1735
1481 # Assign any leftover samples to $lastline 1736 # Assign any leftover samples to $lastline
1482 AddEntry($samples1, $lastline, $running1); 1737 AddEntry($samples1, $lastline, $running1);
1483 AddEntry($samples2, $lastline, $running2); 1738 AddEntry($samples2, $lastline, $running2);
1739 if ($html) {
1740 if ($lastline != $last_touched_line && $disasm{$lastline} ne '') {
1741 $disasm{$lastline} .= "\n";
1742 }
1743 $disasm{$lastline} .= $running_disasm;
1744 }
1484 1745
1485 printf("ROUTINE ====================== %s in %s\n" . 1746 if ($html) {
1486 "%6s %6s Total %s (flat / cumulative)\n", 1747 printf $output (
1487 ShortFunctionName($routine), 1748 "<h1>%s</h1>%s\n<pre onClick=\"pprof_toggle_asm()\">\n" .
1488 $filename, 1749 "Total:%6s %6s (flat / cumulative %s)\n",
1489 Units(), 1750 HtmlEscape(ShortFunctionName($routine)),
1490 Unparse($total1), 1751 HtmlEscape(CleanFileName($filename)),
1491 Unparse($total2)); 1752 Unparse($total1),
1753 Unparse($total2),
1754 Units());
1755 } else {
1756 printf $output (
1757 "ROUTINE ====================== %s in %s\n" .
1758 "%6s %6s Total %s (flat / cumulative)\n",
1759 ShortFunctionName($routine),
1760 CleanFileName($filename),
1761 Unparse($total1),
1762 Unparse($total2),
1763 Units());
1764 }
1492 if (!open(FILE, "<$filename")) { 1765 if (!open(FILE, "<$filename")) {
1493 print STDERR "$filename: $!\n"; 1766 print STDERR "$filename: $!\n";
1494 return; 1767 return 0;
1495 } 1768 }
1496 my $l = 0; 1769 my $l = 0;
1497 while (<FILE>) { 1770 while (<FILE>) {
1498 s/\r//g; # turn windows-looking lines into unix-looking lines 1771 s/\r//g; # turn windows-looking lines into unix-looking lines
1499 $l++; 1772 $l++;
1500 if ($l >= $firstline - 5 && 1773 if ($l >= $firstline - 5 &&
1501 (($l <= $oldlastline + 5) || ($l <= $lastline))) { 1774 (($l <= $oldlastline + 5) || ($l <= $lastline))) {
1502 chop; 1775 chop;
1503 my $text = $_; 1776 my $text = $_;
1504 if ($l == $firstline) { printf("---\n"); } 1777 if ($l == $firstline) { print $output $skip_marker; }
1505 printf("%6s %6s %4d: %s\n", 1778 my $n1 = GetEntry($samples1, $l);
1506 UnparseAlt(GetEntry($samples1, $l)), 1779 my $n2 = GetEntry($samples2, $l);
1507 UnparseAlt(GetEntry($samples2, $l)), 1780 if ($html) {
1508 $l, 1781 # Emit a span that has one of the following classes:
1509 $text); 1782 # livesrc -- has samples
1510 if ($l == $lastline) { printf("---\n"); } 1783 # deadsrc -- has disassembly, but with no samples
1784 # nop -- has no matching disasembly
1785 # Also emit an optional span containing disassembly.
1786 my $dis = $disasm{$l};
1787 my $asm = "";
1788 if (defined($dis) && $dis ne '') {
1789 $asm = "<span class=\"asm\">" . $dis . "</span>";
1790 }
1791 my $source_class = (($n1 + $n2 > 0)
1792 ? "livesrc"
1793 : (($asm ne "") ? "deadsrc" : "nop"));
1794 printf $output (
1795 "<span class=\"line\">%5d</span> " .
1796 "<span class=\"%s\">%6s %6s %s</span>%s\n",
1797 $l, $source_class,
1798 HtmlPrintNumber($n1),
1799 HtmlPrintNumber($n2),
1800 HtmlEscape($text),
1801 $asm);
1802 } else {
1803 printf $output(
1804 "%6s %6s %4d: %s\n",
1805 UnparseAlt($n1),
1806 UnparseAlt($n2),
1807 $l,
1808 $text);
1809 }
1810 if ($l == $lastline) { print $output $skip_marker; }
1511 }; 1811 };
1512 } 1812 }
1513 close(FILE); 1813 close(FILE);
1814 if ($html) {
1815 print $output "</pre>\n";
1816 }
1817 return 1;
1514 } 1818 }
1515 1819
1516 # Return the source line for the specified file/linenumber. 1820 # Return the source line for the specified file/linenumber.
1517 # Returns undef if not found. 1821 # Returns undef if not found.
1518 sub SourceLine { 1822 sub SourceLine {
1519 my $file = shift; 1823 my $file = shift;
1520 my $line = shift; 1824 my $line = shift;
1521 1825
1522 # Look in cache 1826 # Look in cache
1523 if (!defined($main::source_cache{$file})) { 1827 if (!defined($main::source_cache{$file})) {
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after
1646 printf("%6s %6s %5d: %s", 1950 printf("%6s %6s %5d: %s",
1647 UnparseAlt($flat_sum{$l}), 1951 UnparseAlt($flat_sum{$l}),
1648 UnparseAlt($cum_sum{$l}), 1952 UnparseAlt($cum_sum{$l}),
1649 $l, 1953 $l,
1650 $line); 1954 $line);
1651 } 1955 }
1652 1956
1653 # Print disassembly 1957 # Print disassembly
1654 for (my $x = $first_inst; $x <= $last_inst; $x++) { 1958 for (my $x = $first_inst; $x <= $last_inst; $x++) {
1655 my $e = $instructions[$x]; 1959 my $e = $instructions[$x];
1656 my $address = $e->[0];
1657 $address = AddressSub($address, $offset); # Make relative to section
1658 $address =~ s/^0x//;
1659 $address =~ s/^0*//;
1660
1661 # Trim symbols
1662 my $d = $e->[3];
1663 while ($d =~ s/\([^()%]*\)(\s*const)?//g) { } # Argument types, not (%rax)
1664 while ($d =~ s/(\w+)<[^<>]*>/$1/g) { } # Remove template arguments
1665
1666 printf("%6s %6s %8s: %6s\n", 1960 printf("%6s %6s %8s: %6s\n",
1667 UnparseAlt($flat_count[$x]), 1961 UnparseAlt($flat_count[$x]),
1668 UnparseAlt($cum_count[$x]), 1962 UnparseAlt($cum_count[$x]),
1669 $address, 1963 UnparseAddress($offset, $e->[0]),
1670 $d); 1964 CleanDisassembly($e->[3]));
1671 } 1965 }
1672 } 1966 }
1673 } 1967 }
1674 1968
1675 # Print DOT graph 1969 # Print DOT graph
1676 sub PrintDot { 1970 sub PrintDot {
1677 my $prog = shift; 1971 my $prog = shift;
1678 my $symbols = shift; 1972 my $symbols = shift;
1679 my $raw = shift; 1973 my $raw = shift;
1680 my $flat = shift; 1974 my $flat = shift;
(...skipping 25 matching lines...) Expand all
1706 } 2000 }
1707 2001
1708 if ($nodelimit > 0 || $edgelimit > 0) { 2002 if ($nodelimit > 0 || $edgelimit > 0) {
1709 printf STDERR ("Dropping nodes with <= %s %s; edges with <= %s abs(%s)\n", 2003 printf STDERR ("Dropping nodes with <= %s %s; edges with <= %s abs(%s)\n",
1710 Unparse($nodelimit), Units(), 2004 Unparse($nodelimit), Units(),
1711 Unparse($edgelimit), Units()); 2005 Unparse($edgelimit), Units());
1712 } 2006 }
1713 2007
1714 # Open DOT output file 2008 # Open DOT output file
1715 my $output; 2009 my $output;
2010 my $escaped_dot = ShellEscape(@DOT);
2011 my $escaped_ps2pdf = ShellEscape(@PS2PDF);
1716 if ($main::opt_gv) { 2012 if ($main::opt_gv) {
1717 $output = "| $DOT -Tps2 >" . TempName($main::next_tmpfile, "ps"); 2013 my $escaped_outfile = ShellEscape(TempName($main::next_tmpfile, "ps"));
2014 $output = "| $escaped_dot -Tps2 >$escaped_outfile";
1718 } elsif ($main::opt_evince) { 2015 } elsif ($main::opt_evince) {
1719 $output = "| $DOT -Tps2 | $PS2PDF - " . TempName($main::next_tmpfile, "pdf") ; 2016 my $escaped_outfile = ShellEscape(TempName($main::next_tmpfile, "pdf"));
2017 $output = "| $escaped_dot -Tps2 | $escaped_ps2pdf - $escaped_outfile";
1720 } elsif ($main::opt_ps) { 2018 } elsif ($main::opt_ps) {
1721 $output = "| $DOT -Tps2"; 2019 $output = "| $escaped_dot -Tps2";
1722 } elsif ($main::opt_pdf) { 2020 } elsif ($main::opt_pdf) {
1723 $output = "| $DOT -Tps2 | $PS2PDF - -"; 2021 $output = "| $escaped_dot -Tps2 | $escaped_ps2pdf - -";
1724 } elsif ($main::opt_web || $main::opt_svg) { 2022 } elsif ($main::opt_web || $main::opt_svg) {
1725 # We need to post-process the SVG, so write to a temporary file always. 2023 # We need to post-process the SVG, so write to a temporary file always.
1726 $output = "| $DOT -Tsvg >" . TempName($main::next_tmpfile, "svg"); 2024 my $escaped_outfile = ShellEscape(TempName($main::next_tmpfile, "svg"));
2025 $output = "| $escaped_dot -Tsvg >$escaped_outfile";
1727 } elsif ($main::opt_gif) { 2026 } elsif ($main::opt_gif) {
1728 $output = "| $DOT -Tgif"; 2027 $output = "| $escaped_dot -Tgif";
1729 } else { 2028 } else {
1730 $output = ">&STDOUT"; 2029 $output = ">&STDOUT";
1731 } 2030 }
1732 open(DOT, $output) || error("$output: $!\n"); 2031 open(DOT, $output) || error("$output: $!\n");
1733 2032
1734 # Title 2033 # Title
1735 printf DOT ("digraph \"%s; %s %s\" {\n", 2034 printf DOT ("digraph \"%s; %s %s\" {\n",
1736 $prog, 2035 $prog,
1737 Unparse($overall_total), 2036 Unparse($overall_total),
1738 Units()); 2037 Units());
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
1799 Percent($f, $local_total), 2098 Percent($f, $local_total),
1800 $extra, 2099 $extra,
1801 $fs, 2100 $fs,
1802 $style, 2101 $style,
1803 ); 2102 );
1804 } 2103 }
1805 2104
1806 # Get edges and counts per edge 2105 # Get edges and counts per edge
1807 my %edge = (); 2106 my %edge = ();
1808 my $n; 2107 my $n;
2108 my $fullname_to_shortname_map = {};
2109 FillFullnameToShortnameMap($symbols, $fullname_to_shortname_map);
1809 foreach my $k (keys(%{$raw})) { 2110 foreach my $k (keys(%{$raw})) {
1810 # TODO: omit low %age edges 2111 # TODO: omit low %age edges
1811 $n = $raw->{$k}; 2112 $n = $raw->{$k};
1812 my @translated = TranslateStack($symbols, $k); 2113 my @translated = TranslateStack($symbols, $fullname_to_shortname_map, $k);
1813 for (my $i = 1; $i <= $#translated; $i++) { 2114 for (my $i = 1; $i <= $#translated; $i++) {
1814 my $src = $translated[$i]; 2115 my $src = $translated[$i];
1815 my $dst = $translated[$i-1]; 2116 my $dst = $translated[$i-1];
1816 #next if ($src eq $dst); # Avoid self-edges? 2117 #next if ($src eq $dst); # Avoid self-edges?
1817 if (exists($node{$src}) && exists($node{$dst})) { 2118 if (exists($node{$src}) && exists($node{$dst})) {
1818 my $edge_label = "$src\001$dst"; 2119 my $edge_label = "$src\001$dst";
1819 if (!exists($edge{$edge_label})) { 2120 if (!exists($edge{$edge_label})) {
1820 $edge{$edge_label} = 0; 2121 $edge{$edge_label} = 0;
1821 } 2122 }
1822 $edge{$edge_label} += $n; 2123 $edge{$edge_label} += $n;
(...skipping 363 matching lines...) Expand 10 before | Expand all | Expand 10 after
2186 if(state == 'pan' || state == 'move') { 2487 if(state == 'pan' || state == 'move') {
2187 // Quit pan mode 2488 // Quit pan mode
2188 state = ''; 2489 state = '';
2189 } 2490 }
2190 } 2491 }
2191 2492
2192 ]]></script> 2493 ]]></script>
2193 EOF 2494 EOF
2194 } 2495 }
2195 2496
2497 # Provides a map from fullname to shortname for cases where the
2498 # shortname is ambiguous. The symlist has both the fullname and
2499 # shortname for all symbols, which is usually fine, but sometimes --
2500 # such as overloaded functions -- two different fullnames can map to
2501 # the same shortname. In that case, we use the address of the
2502 # function to disambiguate the two. This function fills in a map that
2503 # maps fullnames to modified shortnames in such cases. If a fullname
2504 # is not present in the map, the 'normal' shortname provided by the
2505 # symlist is the appropriate one to use.
2506 sub FillFullnameToShortnameMap {
2507 my $symbols = shift;
2508 my $fullname_to_shortname_map = shift;
2509 my $shortnames_seen_once = {};
2510 my $shortnames_seen_more_than_once = {};
2511
2512 foreach my $symlist (values(%{$symbols})) {
2513 # TODO(csilvers): deal with inlined symbols too.
2514 my $shortname = $symlist->[0];
2515 my $fullname = $symlist->[2];
2516 if ($fullname !~ /<[0-9a-fA-F]+>$/) { # fullname doesn't end in an address
2517 next; # the only collisions we care about are when addresses differ
2518 }
2519 if (defined($shortnames_seen_once->{$shortname}) &&
2520 $shortnames_seen_once->{$shortname} ne $fullname) {
2521 $shortnames_seen_more_than_once->{$shortname} = 1;
2522 } else {
2523 $shortnames_seen_once->{$shortname} = $fullname;
2524 }
2525 }
2526
2527 foreach my $symlist (values(%{$symbols})) {
2528 my $shortname = $symlist->[0];
2529 my $fullname = $symlist->[2];
2530 # TODO(csilvers): take in a list of addresses we care about, and only
2531 # store in the map if $symlist->[1] is in that list. Saves space.
2532 next if defined($fullname_to_shortname_map->{$fullname});
2533 if (defined($shortnames_seen_more_than_once->{$shortname})) {
2534 if ($fullname =~ /<0*([^>]*)>$/) { # fullname has address at end of it
2535 $fullname_to_shortname_map->{$fullname} = "$shortname\@$1";
2536 }
2537 }
2538 }
2539 }
2540
2196 # Return a small number that identifies the argument. 2541 # Return a small number that identifies the argument.
2197 # Multiple calls with the same argument will return the same number. 2542 # Multiple calls with the same argument will return the same number.
2198 # Calls with different arguments will return different numbers. 2543 # Calls with different arguments will return different numbers.
2199 sub ShortIdFor { 2544 sub ShortIdFor {
2200 my $key = shift; 2545 my $key = shift;
2201 my $id = $main::uniqueid{$key}; 2546 my $id = $main::uniqueid{$key};
2202 if (!defined($id)) { 2547 if (!defined($id)) {
2203 $id = keys(%main::uniqueid) + 1; 2548 $id = keys(%main::uniqueid) + 1;
2204 $main::uniqueid{$key} = $id; 2549 $main::uniqueid{$key} = $id;
2205 } 2550 }
2206 return $id; 2551 return $id;
2207 } 2552 }
2208 2553
2209 # Translate a stack of addresses into a stack of symbols 2554 # Translate a stack of addresses into a stack of symbols
2210 sub TranslateStack { 2555 sub TranslateStack {
2211 my $symbols = shift; 2556 my $symbols = shift;
2557 my $fullname_to_shortname_map = shift;
2212 my $k = shift; 2558 my $k = shift;
2213 2559
2214 my @addrs = split(/\n/, $k); 2560 my @addrs = split(/\n/, $k);
2215 my @result = (); 2561 my @result = ();
2216 for (my $i = 0; $i <= $#addrs; $i++) { 2562 for (my $i = 0; $i <= $#addrs; $i++) {
2217 my $a = $addrs[$i]; 2563 my $a = $addrs[$i];
2218 2564
2219 # Skip large addresses since they sometimes show up as fake entries on RH9 2565 # Skip large addresses since they sometimes show up as fake entries on RH9
2220 if (length($a) > 8 && $a gt "7fffffffffffffff") { 2566 if (length($a) > 8 && $a gt "7fffffffffffffff") {
2221 next; 2567 next;
(...skipping 11 matching lines...) Expand all
2233 } 2579 }
2234 2580
2235 # We can have a sequence of symbols for a particular entry 2581 # We can have a sequence of symbols for a particular entry
2236 # (more than one symbol in the case of inlining). Callers 2582 # (more than one symbol in the case of inlining). Callers
2237 # come before callees in symlist, so walk backwards since 2583 # come before callees in symlist, so walk backwards since
2238 # the translated stack should contain callees before callers. 2584 # the translated stack should contain callees before callers.
2239 for (my $j = $#{$symlist}; $j >= 2; $j -= 3) { 2585 for (my $j = $#{$symlist}; $j >= 2; $j -= 3) {
2240 my $func = $symlist->[$j-2]; 2586 my $func = $symlist->[$j-2];
2241 my $fileline = $symlist->[$j-1]; 2587 my $fileline = $symlist->[$j-1];
2242 my $fullfunc = $symlist->[$j]; 2588 my $fullfunc = $symlist->[$j];
2589 if (defined($fullname_to_shortname_map->{$fullfunc})) {
2590 $func = $fullname_to_shortname_map->{$fullfunc};
2591 }
2243 if ($j > 2) { 2592 if ($j > 2) {
2244 $func = "$func (inline)"; 2593 $func = "$func (inline)";
2245 } 2594 }
2246 2595
2247 # Do not merge nodes corresponding to Callback::Run since that 2596 # Do not merge nodes corresponding to Callback::Run since that
2248 # causes confusing cycles in dot display. Instead, we synthesize 2597 # causes confusing cycles in dot display. Instead, we synthesize
2249 # a unique name for this frame per caller. 2598 # a unique name for this frame per caller.
2250 if ($func =~ m/Callback.*::Run$/) { 2599 if ($func =~ m/Callback.*::Run$/) {
2251 my $caller = ($i > 0) ? $addrs[$i-1] : 0; 2600 my $caller = ($i > 0) ? $addrs[$i-1] : 0;
2252 $func = "Run#" . ShortIdFor($caller); 2601 $func = "Run#" . ShortIdFor($caller);
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
2319 # Alternate pretty-printed form: 0 maps to "." 2668 # Alternate pretty-printed form: 0 maps to "."
2320 sub UnparseAlt { 2669 sub UnparseAlt {
2321 my $num = shift; 2670 my $num = shift;
2322 if ($num == 0) { 2671 if ($num == 0) {
2323 return "."; 2672 return ".";
2324 } else { 2673 } else {
2325 return Unparse($num); 2674 return Unparse($num);
2326 } 2675 }
2327 } 2676 }
2328 2677
2678 # Alternate pretty-printed form: 0 maps to ""
2679 sub HtmlPrintNumber {
2680 my $num = shift;
2681 if ($num == 0) {
2682 return "";
2683 } else {
2684 return Unparse($num);
2685 }
2686 }
2687
2329 # Return output units 2688 # Return output units
2330 sub Units { 2689 sub Units {
2331 if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') { 2690 if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {
2332 if ($main::opt_inuse_objects || $main::opt_alloc_objects) { 2691 if ($main::opt_inuse_objects || $main::opt_alloc_objects) {
2333 return "objects"; 2692 return "objects";
2334 } else { 2693 } else {
2335 if ($main::opt_show_bytes) { 2694 if ($main::opt_show_bytes) {
2336 return "B"; 2695 return "B";
2337 } else { 2696 } else {
2338 return "MB"; 2697 return "MB";
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after
2475 '::do_malloc_or_cpp_alloc', 2834 '::do_malloc_or_cpp_alloc',
2476 'DoSampledAllocation', 2835 'DoSampledAllocation',
2477 'simple_alloc::allocate', 2836 'simple_alloc::allocate',
2478 '__malloc_alloc_template::allocate', 2837 '__malloc_alloc_template::allocate',
2479 '__builtin_delete', 2838 '__builtin_delete',
2480 '__builtin_new', 2839 '__builtin_new',
2481 '__builtin_vec_delete', 2840 '__builtin_vec_delete',
2482 '__builtin_vec_new', 2841 '__builtin_vec_new',
2483 'operator new', 2842 'operator new',
2484 'operator new[]', 2843 'operator new[]',
2844 # The entry to our memory-allocation routines on OS X
2845 'malloc_zone_malloc',
2846 'malloc_zone_calloc',
2847 'malloc_zone_valloc',
2848 'malloc_zone_realloc',
2849 'malloc_zone_memalign',
2850 'malloc_zone_free',
2485 # These mark the beginning/end of our custom sections 2851 # These mark the beginning/end of our custom sections
2486 '__start_google_malloc', 2852 '__start_google_malloc',
2487 '__stop_google_malloc', 2853 '__stop_google_malloc',
2488 '__start_malloc_hook', 2854 '__start_malloc_hook',
2489 '__stop_malloc_hook') { 2855 '__stop_malloc_hook') {
2490 $skip{$name} = 1; 2856 $skip{$name} = 1;
2491 $skip{"_" . $name} = 1; # Mach (OS X) adds a _ prefix to everything 2857 $skip{"_" . $name} = 1; # Mach (OS X) adds a _ prefix to everything
2492 } 2858 }
2493 # TODO: Remove TCMalloc once everything has been 2859 # TODO: Remove TCMalloc once everything has been
2494 # moved into the tcmalloc:: namespace and we have flushed 2860 # moved into the tcmalloc:: namespace and we have flushed
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
2566 AddEntry($result, $reduced_path, $count); 2932 AddEntry($result, $reduced_path, $count);
2567 } 2933 }
2568 return $result; 2934 return $result;
2569 } 2935 }
2570 2936
2571 # Reduce profile to granularity given by user 2937 # Reduce profile to granularity given by user
2572 sub ReduceProfile { 2938 sub ReduceProfile {
2573 my $symbols = shift; 2939 my $symbols = shift;
2574 my $profile = shift; 2940 my $profile = shift;
2575 my $result = {}; 2941 my $result = {};
2942 my $fullname_to_shortname_map = {};
2943 FillFullnameToShortnameMap($symbols, $fullname_to_shortname_map);
2576 foreach my $k (keys(%{$profile})) { 2944 foreach my $k (keys(%{$profile})) {
2577 my $count = $profile->{$k}; 2945 my $count = $profile->{$k};
2578 my @translated = TranslateStack($symbols, $k); 2946 my @translated = TranslateStack($symbols, $fullname_to_shortname_map, $k);
2579 my @path = (); 2947 my @path = ();
2580 my %seen = (); 2948 my %seen = ();
2581 $seen{''} = 1; # So that empty keys are skipped 2949 $seen{''} = 1; # So that empty keys are skipped
2582 foreach my $e (@translated) { 2950 foreach my $e (@translated) {
2583 # To avoid double-counting due to recursion, skip a stack-trace 2951 # To avoid double-counting due to recursion, skip a stack-trace
2584 # entry if it has already been seen 2952 # entry if it has already been seen
2585 if (!$seen{$e}) { 2953 if (!$seen{$e}) {
2586 $seen{$e} = 1; 2954 $seen{$e} = 1;
2587 push(@path, $e); 2955 push(@path, $e);
2588 } 2956 }
(...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after
2775 $pcs->{$pc} = 1; 3143 $pcs->{$pc} = 1;
2776 push @k, $pc; 3144 push @k, $pc;
2777 } 3145 }
2778 AddEntry($profile, (join "\n", @k), $count); 3146 AddEntry($profile, (join "\n", @k), $count);
2779 } 3147 }
2780 3148
2781 ##### Code to profile a server dynamically ##### 3149 ##### Code to profile a server dynamically #####
2782 3150
2783 sub CheckSymbolPage { 3151 sub CheckSymbolPage {
2784 my $url = SymbolPageURL(); 3152 my $url = SymbolPageURL();
2785 open(SYMBOL, "$URL_FETCHER '$url' |"); 3153 my $command = ShellEscape(@URL_FETCHER, $url);
3154 open(SYMBOL, "$command |") or error($command);
2786 my $line = <SYMBOL>; 3155 my $line = <SYMBOL>;
2787 $line =~ s/\r//g; # turn windows-looking lines into unix-looking lines 3156 $line =~ s/\r//g; # turn windows-looking lines into unix-looking lines
2788 close(SYMBOL); 3157 close(SYMBOL);
2789 unless (defined($line)) { 3158 unless (defined($line)) {
2790 error("$url doesn't exist\n"); 3159 error("$url doesn't exist\n");
2791 } 3160 }
2792 3161
2793 if ($line =~ /^num_symbols:\s+(\d+)$/) { 3162 if ($line =~ /^num_symbols:\s+(\d+)$/) {
2794 if ($1 == 0) { 3163 if ($1 == 0) {
2795 error("Stripped binary. No symbols available.\n"); 3164 error("Stripped binary. No symbols available.\n");
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
2832 3201
2833 # We fetch symbols from the first profile argument. 3202 # We fetch symbols from the first profile argument.
2834 sub SymbolPageURL { 3203 sub SymbolPageURL {
2835 my ($host, $baseURL, $path) = ParseProfileURL($main::pfile_args[0]); 3204 my ($host, $baseURL, $path) = ParseProfileURL($main::pfile_args[0]);
2836 return "$baseURL$SYMBOL_PAGE"; 3205 return "$baseURL$SYMBOL_PAGE";
2837 } 3206 }
2838 3207
2839 sub FetchProgramName() { 3208 sub FetchProgramName() {
2840 my ($host, $baseURL, $path) = ParseProfileURL($main::pfile_args[0]); 3209 my ($host, $baseURL, $path) = ParseProfileURL($main::pfile_args[0]);
2841 my $url = "$baseURL$PROGRAM_NAME_PAGE"; 3210 my $url = "$baseURL$PROGRAM_NAME_PAGE";
2842 my $command_line = "$URL_FETCHER '$url'"; 3211 my $command_line = ShellEscape(@URL_FETCHER, $url);
2843 open(CMDLINE, "$command_line |") or error($command_line); 3212 open(CMDLINE, "$command_line |") or error($command_line);
2844 my $cmdline = <CMDLINE>; 3213 my $cmdline = <CMDLINE>;
2845 $cmdline =~ s/\r//g; # turn windows-looking lines into unix-looking lines 3214 $cmdline =~ s/\r//g; # turn windows-looking lines into unix-looking lines
2846 close(CMDLINE); 3215 close(CMDLINE);
2847 error("Failed to get program name from $url\n") unless defined($cmdline); 3216 error("Failed to get program name from $url\n") unless defined($cmdline);
2848 $cmdline =~ s/\x00.+//; # Remove argv[1] and latters. 3217 $cmdline =~ s/\x00.+//; # Remove argv[1] and latters.
2849 $cmdline =~ s!\n!!g; # Remove LFs. 3218 $cmdline =~ s!\n!!g; # Remove LFs.
2850 return $cmdline; 3219 return $cmdline;
2851 } 3220 }
2852 3221
2853 # Gee, curl's -L (--location) option isn't reliable at least 3222 # Gee, curl's -L (--location) option isn't reliable at least
2854 # with its 7.12.3 version. Curl will forget to post data if 3223 # with its 7.12.3 version. Curl will forget to post data if
2855 # there is a redirection. This function is a workaround for 3224 # there is a redirection. This function is a workaround for
2856 # curl. Redirection happens on borg hosts. 3225 # curl. Redirection happens on borg hosts.
2857 sub ResolveRedirectionForCurl { 3226 sub ResolveRedirectionForCurl {
2858 my $url = shift; 3227 my $url = shift;
2859 my $command_line = "$URL_FETCHER --head '$url'"; 3228 my $command_line = ShellEscape(@URL_FETCHER, "--head", $url);
2860 open(CMDLINE, "$command_line |") or error($command_line); 3229 open(CMDLINE, "$command_line |") or error($command_line);
2861 while (<CMDLINE>) { 3230 while (<CMDLINE>) {
2862 s/\r//g; # turn windows-looking lines into unix-looking lines 3231 s/\r//g; # turn windows-looking lines into unix-looking lines
2863 if (/^Location: (.*)/) { 3232 if (/^Location: (.*)/) {
2864 $url = $1; 3233 $url = $1;
2865 } 3234 }
2866 } 3235 }
2867 close(CMDLINE); 3236 close(CMDLINE);
2868 return $url; 3237 return $url;
2869 } 3238 }
2870 3239
2871 # Add a timeout flat to URL_FETCHER 3240 # Add a timeout flat to URL_FETCHER. Returns a new list.
2872 sub AddFetchTimeout { 3241 sub AddFetchTimeout {
2873 my $fetcher = shift;
2874 my $timeout = shift; 3242 my $timeout = shift;
3243 my @fetcher = shift;
2875 if (defined($timeout)) { 3244 if (defined($timeout)) {
2876 if ($fetcher =~ m/\bcurl -s/) { 3245 if (join(" ", @fetcher) =~ m/\bcurl -s/) {
2877 $fetcher .= sprintf(" --max-time %d", $timeout); 3246 push(@fetcher, "--max-time", sprintf("%d", $timeout));
2878 } elsif ($fetcher =~ m/\brpcget\b/) { 3247 } elsif (join(" ", @fetcher) =~ m/\brpcget\b/) {
2879 $fetcher .= sprintf(" --deadline=%d", $timeout); 3248 push(@fetcher, sprintf("--deadline=%d", $timeout));
2880 } 3249 }
2881 } 3250 }
2882 return $fetcher; 3251 return @fetcher;
2883 } 3252 }
2884 3253
2885 # Reads a symbol map from the file handle name given as $1, returning 3254 # Reads a symbol map from the file handle name given as $1, returning
2886 # the resulting symbol map. Also processes variables relating to symbols. 3255 # the resulting symbol map. Also processes variables relating to symbols.
2887 # Currently, the only variable processed is 'binary=<value>' which updates 3256 # Currently, the only variable processed is 'binary=<value>' which updates
2888 # $main::prog to have the correct program name. 3257 # $main::prog to have the correct program name.
2889 sub ReadSymbols { 3258 sub ReadSymbols {
2890 my $in = shift; 3259 my $in = shift;
2891 my $map = {}; 3260 my $map = {};
2892 while (<$in>) { 3261 while (<$in>) {
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
2932 if (!defined($symbol_map)) { 3301 if (!defined($symbol_map)) {
2933 my $post_data = join("+", sort((map {"0x" . "$_"} @pcs))); 3302 my $post_data = join("+", sort((map {"0x" . "$_"} @pcs)));
2934 3303
2935 open(POSTFILE, ">$main::tmpfile_sym"); 3304 open(POSTFILE, ">$main::tmpfile_sym");
2936 print POSTFILE $post_data; 3305 print POSTFILE $post_data;
2937 close(POSTFILE); 3306 close(POSTFILE);
2938 3307
2939 my $url = SymbolPageURL(); 3308 my $url = SymbolPageURL();
2940 3309
2941 my $command_line; 3310 my $command_line;
2942 if ($URL_FETCHER =~ m/\bcurl -s/) { 3311 if (join(" ", @URL_FETCHER) =~ m/\bcurl -s/) {
2943 $url = ResolveRedirectionForCurl($url); 3312 $url = ResolveRedirectionForCurl($url);
2944 $command_line = "$URL_FETCHER -d '\@$main::tmpfile_sym' '$url'"; 3313 $command_line = ShellEscape(@URL_FETCHER, "-d", "\@$main::tmpfile_sym",
3314 $url);
2945 } else { 3315 } else {
2946 $command_line = "$URL_FETCHER --post '$url' < '$main::tmpfile_sym'"; 3316 $command_line = (ShellEscape(@URL_FETCHER, "--post", $url)
3317 . " < " . ShellEscape($main::tmpfile_sym));
2947 } 3318 }
2948 # We use c++filt in case $SYMBOL_PAGE gives us mangled symbols. 3319 # We use c++filt in case $SYMBOL_PAGE gives us mangled symbols.
2949 my $cppfilt = $obj_tool_map{"c++filt"}; 3320 my $escaped_cppfilt = ShellEscape($obj_tool_map{"c++filt"});
2950 open(SYMBOL, "$command_line | $cppfilt |") or error($command_line); 3321 open(SYMBOL, "$command_line | $escaped_cppfilt |") or error($command_line);
2951 $symbol_map = ReadSymbols(*SYMBOL{IO}); 3322 $symbol_map = ReadSymbols(*SYMBOL{IO});
2952 close(SYMBOL); 3323 close(SYMBOL);
2953 } 3324 }
2954 3325
2955 my $symbols = {}; 3326 my $symbols = {};
2956 foreach my $pc (@pcs) { 3327 foreach my $pc (@pcs) {
2957 my $fullname; 3328 my $fullname;
2958 # For 64 bits binaries, symbols are extracted with 8 leading zeroes. 3329 # For 64 bits binaries, symbols are extracted with 8 leading zeroes.
2959 # Then /symbol reads the long symbols in as uint64, and outputs 3330 # Then /symbol reads the long symbols in as uint64, and outputs
2960 # the result with a "0x%08llx" format which get rid of the zeroes. 3331 # the result with a "0x%08llx" format which get rid of the zeroes.
2961 # By removing all the leading zeroes in both $pc and the symbols from 3332 # By removing all the leading zeroes in both $pc and the symbols from
2962 # /symbol, the symbols match and are retrievable from the map. 3333 # /symbol, the symbols match and are retrievable from the map.
2963 my $shortpc = $pc; 3334 my $shortpc = $pc;
2964 $shortpc =~ s/^0*//; 3335 $shortpc =~ s/^0*//;
2965 # Each line may have a list of names, which includes the function 3336 # Each line may have a list of names, which includes the function
2966 # and also other functions it has inlined. They are separated 3337 # and also other functions it has inlined. They are separated (in
2967 # (in PrintSymbolizedFile), by --, which is illegal in function names. 3338 # PrintSymbolizedProfile), by --, which is illegal in function names.
2968 my $fullnames; 3339 my $fullnames;
2969 if (defined($symbol_map->{$shortpc})) { 3340 if (defined($symbol_map->{$shortpc})) {
2970 $fullnames = $symbol_map->{$shortpc}; 3341 $fullnames = $symbol_map->{$shortpc};
2971 } else { 3342 } else {
2972 $fullnames = "0x" . $pc; # Just use addresses 3343 $fullnames = "0x" . $pc; # Just use addresses
2973 } 3344 }
2974 my $sym = []; 3345 my $sym = [];
2975 $symbols->{$pc} = $sym; 3346 $symbols->{$pc} = $sym;
2976 foreach my $fullname (split("--", $fullnames)) { 3347 foreach my $fullname (split("--", $fullnames)) {
2977 my $name = ShortFunctionName($fullname); 3348 my $name = ShortFunctionName($fullname);
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
3035 mkdir($profile_dir) 3406 mkdir($profile_dir)
3036 || die("Unable to create profile directory $profile_dir: $!\n"); 3407 || die("Unable to create profile directory $profile_dir: $!\n");
3037 } 3408 }
3038 my $tmp_profile = "$profile_dir/.tmp.$profile_file"; 3409 my $tmp_profile = "$profile_dir/.tmp.$profile_file";
3039 my $real_profile = "$profile_dir/$profile_file"; 3410 my $real_profile = "$profile_dir/$profile_file";
3040 3411
3041 if ($fetch_name_only > 0) { 3412 if ($fetch_name_only > 0) {
3042 return $real_profile; 3413 return $real_profile;
3043 } 3414 }
3044 3415
3045 my $fetcher = AddFetchTimeout($URL_FETCHER, $fetch_timeout); 3416 my @fetcher = AddFetchTimeout($fetch_timeout, @URL_FETCHER);
3046 my $cmd = "$fetcher '$url' > '$tmp_profile'"; 3417 my $cmd = ShellEscape(@fetcher, $url) . " > " . ShellEscape($tmp_profile);
3047 if ($path =~ m/$PROFILE_PAGE|$PMUPROFILE_PAGE|$CENSUSPROFILE_PAGE/){ 3418 if ($path =~ m/$PROFILE_PAGE|$PMUPROFILE_PAGE|$CENSUSPROFILE_PAGE/){
3048 print STDERR "Gathering CPU profile from $url for $main::opt_seconds secon ds to\n ${real_profile}\n"; 3419 print STDERR "Gathering CPU profile from $url for $main::opt_seconds secon ds to\n ${real_profile}\n";
3049 if ($encourage_patience) { 3420 if ($encourage_patience) {
3050 print STDERR "Be patient...\n"; 3421 print STDERR "Be patient...\n";
3051 } 3422 }
3052 } else { 3423 } else {
3053 print STDERR "Fetching $path profile from $url to\n ${real_profile}\n"; 3424 print STDERR "Fetching $path profile from $url to\n ${real_profile}\n";
3054 } 3425 }
3055 3426
3056 (system($cmd) == 0) || error("Failed to get profile: $cmd: $!\n"); 3427 (system($cmd) == 0) || error("Failed to get profile: $cmd: $!\n");
3057 (system("mv $tmp_profile $real_profile") == 0) || error("Unable to rename pr ofile\n"); 3428 (system("mv", $tmp_profile, $real_profile) == 0) || error("Unable to rename profile\n");
3058 print STDERR "Wrote profile to $real_profile\n"; 3429 print STDERR "Wrote profile to $real_profile\n";
3059 $main::collected_profile = $real_profile; 3430 $main::collected_profile = $real_profile;
3060 return $main::collected_profile; 3431 return $main::collected_profile;
3061 } 3432 }
3062 } 3433 }
3063 3434
3064 # Collect profiles in parallel 3435 # Collect profiles in parallel
3065 sub FetchDynamicProfiles { 3436 sub FetchDynamicProfiles {
3066 my $items = scalar(@main::pfile_args); 3437 my $items = scalar(@main::pfile_args);
3067 my $levels = log($items) / log(2); 3438 my $levels = log($items) / log(2);
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after
3161 @$slots = unpack($self->{unpack_code} . "*", $str); 3532 @$slots = unpack($self->{unpack_code} . "*", $str);
3162 } else { 3533 } else {
3163 # If we're a 64-bit profile, check if we're a 64-bit-capable 3534 # If we're a 64-bit profile, check if we're a 64-bit-capable
3164 # perl. Otherwise, each slot will be represented as a float 3535 # perl. Otherwise, each slot will be represented as a float
3165 # instead of an int64, losing precision and making all the 3536 # instead of an int64, losing precision and making all the
3166 # 64-bit addresses wrong. We won't complain yet, but will 3537 # 64-bit addresses wrong. We won't complain yet, but will
3167 # later if we ever see a value that doesn't fit in 32 bits. 3538 # later if we ever see a value that doesn't fit in 32 bits.
3168 my $has_q = 0; 3539 my $has_q = 0;
3169 eval { $has_q = pack("Q", "1") ? 1 : 1; }; 3540 eval { $has_q = pack("Q", "1") ? 1 : 1; };
3170 if (!$has_q) { 3541 if (!$has_q) {
3171 » $self->{perl_is_64bit} = 0; 3542 $self->{perl_is_64bit} = 0;
3172 } 3543 }
3173 read($self->{file}, $str, 8); 3544 read($self->{file}, $str, 8);
3174 if (substr($str, 4, 4) eq chr(0)x4) { 3545 if (substr($str, 4, 4) eq chr(0)x4) {
3175 # We'd love to use 'Q', but it's a) not universal, b) not endian-proof. 3546 # We'd love to use 'Q', but it's a) not universal, b) not endian-proof.
3176 $self->{unpack_code} = 'V'; # Little-endian. 3547 $self->{unpack_code} = 'V'; # Little-endian.
3177 } elsif (substr($str, 0, 4) eq chr(0)x4) { 3548 } elsif (substr($str, 0, 4) eq chr(0)x4) {
3178 $self->{unpack_code} = 'N'; # Big-endian 3549 $self->{unpack_code} = 'N'; # Big-endian
3179 } else { 3550 } else {
3180 ::error("$fname: header size >= 2**32\n"); 3551 ::error("$fname: header size >= 2**32\n");
3181 } 3552 }
(...skipping 15 matching lines...) Expand all
3197 # This is the easy case: unpack provides 32-bit unpacking primitives. 3568 # This is the easy case: unpack provides 32-bit unpacking primitives.
3198 @$slots = unpack($self->{unpack_code} . "*", $str); 3569 @$slots = unpack($self->{unpack_code} . "*", $str);
3199 } else { 3570 } else {
3200 # We need to unpack 32 bits at a time and combine. 3571 # We need to unpack 32 bits at a time and combine.
3201 my @b32_values = unpack($self->{unpack_code} . "*", $str); 3572 my @b32_values = unpack($self->{unpack_code} . "*", $str);
3202 my @b64_values = (); 3573 my @b64_values = ();
3203 for (my $i = 0; $i < $#b32_values; $i += 2) { 3574 for (my $i = 0; $i < $#b32_values; $i += 2) {
3204 # TODO(csilvers): if this is a 32-bit perl, the math below 3575 # TODO(csilvers): if this is a 32-bit perl, the math below
3205 # could end up in a too-large int, which perl will promote 3576 # could end up in a too-large int, which perl will promote
3206 # to a double, losing necessary precision. Deal with that. 3577 # to a double, losing necessary precision. Deal with that.
3207 » # Right now, we just die. 3578 # Right now, we just die.
3208 » my ($lo, $hi) = ($b32_values[$i], $b32_values[$i+1]); 3579 my ($lo, $hi) = ($b32_values[$i], $b32_values[$i+1]);
3209 if ($self->{unpack_code} eq 'N') { # big-endian 3580 if ($self->{unpack_code} eq 'N') { # big-endian
3210 » ($lo, $hi) = ($hi, $lo); 3581 ($lo, $hi) = ($hi, $lo);
3211 » } 3582 }
3212 » my $value = $lo + $hi * (2**32); 3583 my $value = $lo + $hi * (2**32);
3213 » if (!$self->{perl_is_64bit} && # check value is exactly represented 3584 if (!$self->{perl_is_64bit} && # check value is exactly represented
3214 » (($value % (2**32)) != $lo || int($value / (2**32)) != $hi)) { 3585 (($value % (2**32)) != $lo || int($value / (2**32)) != $hi)) {
3215 » ::error("Need a 64-bit perl to process this 64-bit profile.\n"); 3586 ::error("Need a 64-bit perl to process this 64-bit profile.\n");
3216 » } 3587 }
3217 » push(@b64_values, $value); 3588 push(@b64_values, $value);
3218 } 3589 }
3219 @$slots = @b64_values; 3590 @$slots = @b64_values;
3220 } 3591 }
3221 } 3592 }
3222 3593
3223 # Access the i-th long in the file (logically), or -1 at EOF. 3594 # Access the i-th long in the file (logically), or -1 at EOF.
3224 sub get { 3595 sub get {
3225 my ($self, $idx) = @_; 3596 my ($self, $idx) = @_;
3226 my $slots = $self->{slots}; 3597 my $slots = $self->{slots};
3227 while ($#$slots >= 0) { 3598 while ($#$slots >= 0) {
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
3335 if (!defined($header)) { # means "at EOF" 3706 if (!defined($header)) { # means "at EOF"
3336 error("Profile is empty.\n"); 3707 error("Profile is empty.\n");
3337 } 3708 }
3338 3709
3339 my $symbols; 3710 my $symbols;
3340 if ($header =~ m/^--- *$symbol_marker/o) { 3711 if ($header =~ m/^--- *$symbol_marker/o) {
3341 # Verify that the user asked for a symbolized profile 3712 # Verify that the user asked for a symbolized profile
3342 if (!$main::use_symbolized_profile) { 3713 if (!$main::use_symbolized_profile) {
3343 # we have both a binary and symbolized profiles, abort 3714 # we have both a binary and symbolized profiles, abort
3344 error("FATAL ERROR: Symbolized profile\n $fname\ncannot be used with " . 3715 error("FATAL ERROR: Symbolized profile\n $fname\ncannot be used with " .
3345 » "a binary arg. Try again without passing\n $prog\n"); 3716 "a binary arg. Try again without passing\n $prog\n");
3346 } 3717 }
3347 # Read the symbol section of the symbolized profile file. 3718 # Read the symbol section of the symbolized profile file.
3348 $symbols = ReadSymbols(*PROFILE{IO}); 3719 $symbols = ReadSymbols(*PROFILE{IO});
3349 # Read the next line to get the header for the remaining profile. 3720 # Read the next line to get the header for the remaining profile.
3350 $header = ReadProfileHeader(*PROFILE) || ""; 3721 $header = ReadProfileHeader(*PROFILE) || "";
3351 } 3722 }
3352 3723
3353 $main::profile_type = ''; 3724 $main::profile_type = '';
3354 if ($header =~ m/^heap profile:.*$growth_marker/o) { 3725 if ($header =~ m/^heap profile:.*$growth_marker/o) {
3355 $main::profile_type = 'growth'; 3726 $main::profile_type = 'growth';
(...skipping 280 matching lines...) Expand 10 before | Expand all | Expand 10 after
3636 if (m/^\s*(\d+):\s+(\d+)\s+\[\s*(\d+):\s+(\d+)\]\s+@\s+(.*)$/) { 4007 if (m/^\s*(\d+):\s+(\d+)\s+\[\s*(\d+):\s+(\d+)\]\s+@\s+(.*)$/) {
3637 my $stack = $5; 4008 my $stack = $5;
3638 my ($n1, $s1, $n2, $s2) = ($1, $2, $3, $4); 4009 my ($n1, $s1, $n2, $s2) = ($1, $2, $3, $4);
3639 4010
3640 if ($sample_adjustment) { 4011 if ($sample_adjustment) {
3641 if ($sampling_algorithm == 2) { 4012 if ($sampling_algorithm == 2) {
3642 # Remote-heap version 2 4013 # Remote-heap version 2
3643 # The sampling frequency is the rate of a Poisson process. 4014 # The sampling frequency is the rate of a Poisson process.
3644 # This means that the probability of sampling an allocation of 4015 # This means that the probability of sampling an allocation of
3645 # size X with sampling rate Y is 1 - exp(-X/Y) 4016 # size X with sampling rate Y is 1 - exp(-X/Y)
3646 » if ($n1 != 0) { 4017 if ($n1 != 0) {
3647 » my $ratio = (($s1*1.0)/$n1)/($sample_adjustment); 4018 my $ratio = (($s1*1.0)/$n1)/($sample_adjustment);
3648 » my $scale_factor = 1/(1 - exp(-$ratio)); 4019 my $scale_factor = 1/(1 - exp(-$ratio));
3649 » $n1 *= $scale_factor; 4020 $n1 *= $scale_factor;
3650 » $s1 *= $scale_factor; 4021 $s1 *= $scale_factor;
3651 » } 4022 }
3652 » if ($n2 != 0) { 4023 if ($n2 != 0) {
3653 » my $ratio = (($s2*1.0)/$n2)/($sample_adjustment); 4024 my $ratio = (($s2*1.0)/$n2)/($sample_adjustment);
3654 » my $scale_factor = 1/(1 - exp(-$ratio)); 4025 my $scale_factor = 1/(1 - exp(-$ratio));
3655 » $n2 *= $scale_factor; 4026 $n2 *= $scale_factor;
3656 » $s2 *= $scale_factor; 4027 $s2 *= $scale_factor;
3657 » } 4028 }
3658 } else { 4029 } else {
3659 # Remote-heap version 1 4030 # Remote-heap version 1
3660 my $ratio; 4031 my $ratio;
3661 $ratio = (($s1*1.0)/$n1)/($sample_adjustment); 4032 $ratio = (($s1*1.0)/$n1)/($sample_adjustment);
3662 if ($ratio < 1) { 4033 if ($ratio < 1) {
3663 $n1 /= $ratio; 4034 $n1 /= $ratio;
3664 $s1 /= $ratio; 4035 $s1 /= $ratio;
3665 } 4036 }
3666 $ratio = (($s2*1.0)/$n2)/($sample_adjustment); 4037 $ratio = (($s2*1.0)/$n2)/($sample_adjustment);
3667 if ($ratio < 1) { 4038 if ($ratio < 1) {
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
3771 4142
3772 my $r = {}; 4143 my $r = {};
3773 $r->{version} = 0; 4144 $r->{version} = 0;
3774 $r->{period} = $sampling_period; 4145 $r->{period} = $sampling_period;
3775 $r->{profile} = $profile; 4146 $r->{profile} = $profile;
3776 $r->{libs} = ParseLibraries($prog, $map, $pcs); 4147 $r->{libs} = ParseLibraries($prog, $map, $pcs);
3777 $r->{pcs} = $pcs; 4148 $r->{pcs} = $pcs;
3778 return $r; 4149 return $r;
3779 } 4150 }
3780 4151
3781 # Given a hex value in the form "0x1abcd" return "0001abcd" or 4152 # Given a hex value in the form "0x1abcd" or "1abcd", return either
3782 # "000000000001abcd", depending on the current address length. 4153 # "0001abcd" or "000000000001abcd", depending on the current (global)
3783 # There's probably a more idiomatic (or faster) way to do this... 4154 # address length.
3784 sub HexExtend { 4155 sub HexExtend {
3785 my $addr = shift; 4156 my $addr = shift;
3786 4157
3787 $addr =~ s/^0x//; 4158 $addr =~ s/^(0x)?0*//;
3788 4159 my $zeros_needed = $address_length - length($addr);
3789 if (length $addr > $address_length) { 4160 if ($zeros_needed < 0) {
3790 printf STDERR "Warning: address $addr is longer than address length $addres s_length\n"; 4161 printf STDERR "Warning: address $addr is longer than address length $address _length\n";
4162 return $addr;
3791 } 4163 }
3792 4164 return ("0" x $zeros_needed) . $addr;
3793 return substr("000000000000000".$addr, -$address_length);
3794 } 4165 }
3795 4166
3796 ##### Symbol extraction ##### 4167 ##### Symbol extraction #####
3797 4168
3798 # Aggressively search the lib_prefix values for the given library 4169 # Aggressively search the lib_prefix values for the given library
3799 # If all else fails, just return the name of the library unmodified. 4170 # If all else fails, just return the name of the library unmodified.
3800 # If the lib_prefix is "/my/path,/other/path" and $file is "/lib/dir/mylib.so" 4171 # If the lib_prefix is "/my/path,/other/path" and $file is "/lib/dir/mylib.so"
3801 # it will search the following locations in this order, until it finds a file: 4172 # it will search the following locations in this order, until it finds a file:
3802 # /my/path/lib/dir/mylib.so 4173 # /my/path/lib/dir/mylib.so
3803 # /other/path/lib/dir/mylib.so 4174 # /other/path/lib/dir/mylib.so
(...skipping 30 matching lines...) Expand all
3834 4205
3835 # Parse text section header of a library using objdump 4206 # Parse text section header of a library using objdump
3836 sub ParseTextSectionHeaderFromObjdump { 4207 sub ParseTextSectionHeaderFromObjdump {
3837 my $lib = shift; 4208 my $lib = shift;
3838 4209
3839 my $size = undef; 4210 my $size = undef;
3840 my $vma; 4211 my $vma;
3841 my $file_offset; 4212 my $file_offset;
3842 # Get objdump output from the library file to figure out how to 4213 # Get objdump output from the library file to figure out how to
3843 # map between mapped addresses and addresses in the library. 4214 # map between mapped addresses and addresses in the library.
3844 my $objdump = $obj_tool_map{"objdump"}; 4215 my $cmd = ShellEscape($obj_tool_map{"objdump"}, "-h", $lib);
3845 open(OBJDUMP, "$objdump -h $lib |") 4216 open(OBJDUMP, "$cmd |") || error("$cmd: $!\n");
3846 || error("$objdump $lib: $!\n");
3847 while (<OBJDUMP>) { 4217 while (<OBJDUMP>) {
3848 s/\r//g; # turn windows-looking lines into unix-looking lines 4218 s/\r//g; # turn windows-looking lines into unix-looking lines
3849 # Idx Name Size VMA LMA File off Algn 4219 # Idx Name Size VMA LMA File off Algn
3850 # 10 .text 00104b2c 420156f0 420156f0 000156f0 2**4 4220 # 10 .text 00104b2c 420156f0 420156f0 000156f0 2**4
3851 # For 64-bit objects, VMA and LMA will be 16 hex digits, size and file 4221 # For 64-bit objects, VMA and LMA will be 16 hex digits, size and file
3852 # offset may still be 8. But AddressSub below will still handle that. 4222 # offset may still be 8. But AddressSub below will still handle that.
3853 my @x = split; 4223 my @x = split;
3854 if (($#x >= 6) && ($x[1] eq '.text')) { 4224 if (($#x >= 6) && ($x[1] eq '.text')) {
3855 $size = $x[2]; 4225 $size = $x[2];
3856 $vma = $x[3]; 4226 $vma = $x[3];
(...skipping 17 matching lines...) Expand all
3874 4244
3875 # Parse text section header of a library using otool (on OS X) 4245 # Parse text section header of a library using otool (on OS X)
3876 sub ParseTextSectionHeaderFromOtool { 4246 sub ParseTextSectionHeaderFromOtool {
3877 my $lib = shift; 4247 my $lib = shift;
3878 4248
3879 my $size = undef; 4249 my $size = undef;
3880 my $vma = undef; 4250 my $vma = undef;
3881 my $file_offset = undef; 4251 my $file_offset = undef;
3882 # Get otool output from the library file to figure out how to 4252 # Get otool output from the library file to figure out how to
3883 # map between mapped addresses and addresses in the library. 4253 # map between mapped addresses and addresses in the library.
3884 my $otool = $obj_tool_map{"otool"}; 4254 my $command = ShellEscape($obj_tool_map{"otool"}, "-l", $lib);
3885 open(OTOOL, "$otool -l $lib |") 4255 open(OTOOL, "$command |") || error("$command: $!\n");
3886 || error("$otool $lib: $!\n");
3887 my $cmd = ""; 4256 my $cmd = "";
3888 my $sectname = ""; 4257 my $sectname = "";
3889 my $segname = ""; 4258 my $segname = "";
3890 foreach my $line (<OTOOL>) { 4259 foreach my $line (<OTOOL>) {
3891 $line =~ s/\r//g; # turn windows-looking lines into unix-looking lines 4260 $line =~ s/\r//g; # turn windows-looking lines into unix-looking lines
3892 # Load command <#> 4261 # Load command <#>
3893 # cmd LC_SEGMENT 4262 # cmd LC_SEGMENT
3894 # [...] 4263 # [...]
3895 # Section 4264 # Section
3896 # sectname __text 4265 # sectname __text
(...skipping 321 matching lines...) Expand 10 before | Expand all | Expand 10 after
4218 my $libname = $lib->[0]; 4587 my $libname = $lib->[0];
4219 my $start = $lib->[1]; 4588 my $start = $lib->[1];
4220 my $finish = $lib->[2]; 4589 my $finish = $lib->[2];
4221 my $offset = $lib->[3]; 4590 my $offset = $lib->[3];
4222 4591
4223 # Get list of pcs that belong in this library. 4592 # Get list of pcs that belong in this library.
4224 my $contained = []; 4593 my $contained = [];
4225 my ($start_pc_index, $finish_pc_index); 4594 my ($start_pc_index, $finish_pc_index);
4226 # Find smallest finish_pc_index such that $finish < $pc[$finish_pc_index]. 4595 # Find smallest finish_pc_index such that $finish < $pc[$finish_pc_index].
4227 for ($finish_pc_index = $#pcs + 1; $finish_pc_index > 0; 4596 for ($finish_pc_index = $#pcs + 1; $finish_pc_index > 0;
4228 » $finish_pc_index--) { 4597 $finish_pc_index--) {
4229 last if $pcs[$finish_pc_index - 1] le $finish; 4598 last if $pcs[$finish_pc_index - 1] le $finish;
4230 } 4599 }
4231 # Find smallest start_pc_index such that $start <= $pc[$start_pc_index]. 4600 # Find smallest start_pc_index such that $start <= $pc[$start_pc_index].
4232 for ($start_pc_index = $finish_pc_index; $start_pc_index > 0; 4601 for ($start_pc_index = $finish_pc_index; $start_pc_index > 0;
4233 » $start_pc_index--) { 4602 $start_pc_index--) {
4234 last if $pcs[$start_pc_index - 1] lt $start; 4603 last if $pcs[$start_pc_index - 1] lt $start;
4235 } 4604 }
4236 # This keeps PC values higher than $pc[$finish_pc_index] in @pcs, 4605 # This keeps PC values higher than $pc[$finish_pc_index] in @pcs,
4237 # in case there are overlaps in libraries and the main binary. 4606 # in case there are overlaps in libraries and the main binary.
4238 @{$contained} = splice(@pcs, $start_pc_index, 4607 @{$contained} = splice(@pcs, $start_pc_index,
4239 » » » $finish_pc_index - $start_pc_index); 4608 $finish_pc_index - $start_pc_index);
4240 # Map to symbols 4609 # Map to symbols
4241 MapToSymbols($libname, AddressSub($start, $offset), $contained, $symbols); 4610 MapToSymbols($libname, AddressSub($start, $offset), $contained, $symbols);
4242 } 4611 }
4243 4612
4244 return $symbols; 4613 return $symbols;
4245 } 4614 }
4246 4615
4247 # Map list of PC values to symbols for a given image 4616 # Map list of PC values to symbols for a given image
4248 sub MapToSymbols { 4617 sub MapToSymbols {
4249 my $image = shift; 4618 my $image = shift;
4250 my $offset = shift; 4619 my $offset = shift;
4251 my $pclist = shift; 4620 my $pclist = shift;
4252 my $symbols = shift; 4621 my $symbols = shift;
4253 4622
4254 my $debug = 0; 4623 my $debug = 0;
4255 4624
4256 # Ignore empty binaries 4625 # Ignore empty binaries
4257 if ($#{$pclist} < 0) { return; } 4626 if ($#{$pclist} < 0) { return; }
4258 4627
4259 # Figure out the addr2line command to use 4628 # Figure out the addr2line command to use
4260 my $addr2line = $obj_tool_map{"addr2line"}; 4629 my $addr2line = $obj_tool_map{"addr2line"};
4261 my $cmd = "$addr2line -f -C -e $image"; 4630 my $cmd = ShellEscape($addr2line, "-f", "-C", "-e", $image);
4262 if (exists $obj_tool_map{"addr2line_pdb"}) { 4631 if (exists $obj_tool_map{"addr2line_pdb"}) {
4263 $addr2line = $obj_tool_map{"addr2line_pdb"}; 4632 $addr2line = $obj_tool_map{"addr2line_pdb"};
4264 $cmd = "$addr2line --demangle -f -C -e $image"; 4633 $cmd = ShellEscape($addr2line, "--demangle", "-f", "-C", "-e", $image);
4265 } 4634 }
4266 4635
4267 # If "addr2line" isn't installed on the system at all, just use 4636 # If "addr2line" isn't installed on the system at all, just use
4268 # nm to get what info we can (function names, but not line numbers). 4637 # nm to get what info we can (function names, but not line numbers).
4269 if (system("$addr2line --help >$dev_null 2>&1") != 0) { 4638 if (system(ShellEscape($addr2line, "--help") . " >$dev_null 2>&1") != 0) {
4270 MapSymbolsWithNM($image, $offset, $pclist, $symbols); 4639 MapSymbolsWithNM($image, $offset, $pclist, $symbols);
4271 return; 4640 return;
4272 } 4641 }
4273 4642
4274 # "addr2line -i" can produce a variable number of lines per input 4643 # "addr2line -i" can produce a variable number of lines per input
4275 # address, with no separator that allows us to tell when data for 4644 # address, with no separator that allows us to tell when data for
4276 # the next address starts. So we find the address for a special 4645 # the next address starts. So we find the address for a special
4277 # symbol (_fini) and interleave this address between all real 4646 # symbol (_fini) and interleave this address between all real
4278 # addresses passed to addr2line. The name of this special symbol 4647 # addresses passed to addr2line. The name of this special symbol
4279 # can then be used as a separator. 4648 # can then be used as a separator.
4280 $sep_address = undef; # May be filled in by MapSymbolsWithNM() 4649 $sep_address = undef; # May be filled in by MapSymbolsWithNM()
4281 my $nm_symbols = {}; 4650 my $nm_symbols = {};
4282 MapSymbolsWithNM($image, $offset, $pclist, $nm_symbols); 4651 MapSymbolsWithNM($image, $offset, $pclist, $nm_symbols);
4283 # TODO(csilvers): only add '-i' if addr2line supports it.
4284 if (defined($sep_address)) { 4652 if (defined($sep_address)) {
4285 # Only add " -i" to addr2line if the binary supports it. 4653 # Only add " -i" to addr2line if the binary supports it.
4286 # addr2line --help returns 0, but not if it sees an unknown flag first. 4654 # addr2line --help returns 0, but not if it sees an unknown flag first.
4287 if (system("$cmd -i --help >$dev_null 2>&1") == 0) { 4655 if (system("$cmd -i --help >$dev_null 2>&1") == 0) {
4288 $cmd .= " -i"; 4656 $cmd .= " -i";
4289 } else { 4657 } else {
4290 $sep_address = undef; # no need for sep_address if we don't support -i 4658 $sep_address = undef; # no need for sep_address if we don't support -i
4291 } 4659 }
4292 } 4660 }
4293 4661
4294 # Make file with all PC values with intervening 'sep_address' so 4662 # Make file with all PC values with intervening 'sep_address' so
4295 # that we can reliably detect the end of inlined function list 4663 # that we can reliably detect the end of inlined function list
4296 open(ADDRESSES, ">$main::tmpfile_sym") || error("$main::tmpfile_sym: $!\n"); 4664 open(ADDRESSES, ">$main::tmpfile_sym") || error("$main::tmpfile_sym: $!\n");
4297 if ($debug) { print("---- $image ---\n"); } 4665 if ($debug) { print("---- $image ---\n"); }
4298 for (my $i = 0; $i <= $#{$pclist}; $i++) { 4666 for (my $i = 0; $i <= $#{$pclist}; $i++) {
4299 # addr2line always reads hex addresses, and does not need '0x' prefix. 4667 # addr2line always reads hex addresses, and does not need '0x' prefix.
4300 if ($debug) { printf STDERR ("%s\n", $pclist->[$i]); } 4668 if ($debug) { printf STDERR ("%s\n", $pclist->[$i]); }
4301 printf ADDRESSES ("%s\n", AddressSub($pclist->[$i], $offset)); 4669 printf ADDRESSES ("%s\n", AddressSub($pclist->[$i], $offset));
4302 if (defined($sep_address)) { 4670 if (defined($sep_address)) {
4303 printf ADDRESSES ("%s\n", $sep_address); 4671 printf ADDRESSES ("%s\n", $sep_address);
4304 } 4672 }
4305 } 4673 }
4306 close(ADDRESSES); 4674 close(ADDRESSES);
4307 if ($debug) { 4675 if ($debug) {
4308 print("----\n"); 4676 print("----\n");
4309 system("cat $main::tmpfile_sym"); 4677 system("cat", $main::tmpfile_sym);
4310 print("----\n"); 4678 print("----\n");
4311 system("$cmd <$main::tmpfile_sym"); 4679 system("$cmd < " . ShellEscape($main::tmpfile_sym));
4312 print("----\n"); 4680 print("----\n");
4313 } 4681 }
4314 4682
4315 open(SYMBOLS, "$cmd <$main::tmpfile_sym |") || error("$cmd: $!\n"); 4683 open(SYMBOLS, "$cmd <" . ShellEscape($main::tmpfile_sym) . " |")
4684 || error("$cmd: $!\n");
4316 my $count = 0; # Index in pclist 4685 my $count = 0; # Index in pclist
4317 while (<SYMBOLS>) { 4686 while (<SYMBOLS>) {
4318 # Read fullfunction and filelineinfo from next pair of lines 4687 # Read fullfunction and filelineinfo from next pair of lines
4319 s/\r?\n$//g; 4688 s/\r?\n$//g;
4320 my $fullfunction = $_; 4689 my $fullfunction = $_;
4321 $_ = <SYMBOLS>; 4690 $_ = <SYMBOLS>;
4322 s/\r?\n$//g; 4691 s/\r?\n$//g;
4323 my $filelinenum = $_; 4692 my $filelinenum = $_;
4324 4693
4325 if (defined($sep_address) && $fullfunction eq $sep_symbol) { 4694 if (defined($sep_address) && $fullfunction eq $sep_symbol) {
4326 # Terminating marker for data for this address 4695 # Terminating marker for data for this address
4327 $count++; 4696 $count++;
4328 next; 4697 next;
4329 } 4698 }
4330 4699
4331 $filelinenum =~ s|\\|/|g; # turn windows-style paths into unix-style paths 4700 $filelinenum =~ s|\\|/|g; # turn windows-style paths into unix-style paths
4332 4701
4333 my $pcstr = $pclist->[$count]; 4702 my $pcstr = $pclist->[$count];
4334 my $function = ShortFunctionName($fullfunction); 4703 my $function = ShortFunctionName($fullfunction);
4335 if ($fullfunction eq '??') { 4704 my $nms = $nm_symbols->{$pcstr};
4336 # See if nm found a symbol 4705 if (defined($nms)) {
4337 my $nms = $nm_symbols->{$pcstr}; 4706 if ($fullfunction eq '??') {
4338 if (defined($nms)) { 4707 # nm found a symbol for us.
4339 $function = $nms->[0]; 4708 $function = $nms->[0];
4340 $fullfunction = $nms->[2]; 4709 $fullfunction = $nms->[2];
4710 } else {
4711 # MapSymbolsWithNM tags each routine with its starting address,
4712 # useful in case the image has multiple occurrences of this
4713 # routine. (It uses a syntax that resembles template paramters,
4714 # that are automatically stripped out by ShortFunctionName().)
4715 # addr2line does not provide the same information. So we check
4716 # if nm disambiguated our symbol, and if so take the annotated
4717 # (nm) version of the routine-name. TODO(csilvers): this won't
4718 # catch overloaded, inlined symbols, which nm doesn't see.
4719 # Better would be to do a check similar to nm's, in this fn.
4720 if ($nms->[2] =~ m/^\Q$function\E/) { # sanity check it's the right fn
4721 $function = $nms->[0];
4722 $fullfunction = $nms->[2];
4723 }
4341 } 4724 }
4342 } 4725 }
4343 4726
4344 # Prepend to accumulated symbols for pcstr 4727 # Prepend to accumulated symbols for pcstr
4345 # (so that caller comes before callee) 4728 # (so that caller comes before callee)
4346 my $sym = $symbols->{$pcstr}; 4729 my $sym = $symbols->{$pcstr};
4347 if (!defined($sym)) { 4730 if (!defined($sym)) {
4348 $sym = []; 4731 $sym = [];
4349 $symbols->{$pcstr} = $sym; 4732 $symbols->{$pcstr} = $sym;
4350 } 4733 }
4351 unshift(@{$sym}, $function, $filelinenum, $fullfunction); 4734 unshift(@{$sym}, $function, $filelinenum, $fullfunction);
4352 if ($debug) { printf STDERR ("%s => [%s]\n", $pcstr, join(" ", @{$sym})); } 4735 if ($debug) { printf STDERR ("%s => [%s]\n", $pcstr, join(" ", @{$sym})); }
4353 if (!defined($sep_address)) { 4736 if (!defined($sep_address)) {
4354 # Inlining is off, se this entry ends immediately 4737 # Inlining is off, so this entry ends immediately
4355 $count++; 4738 $count++;
4356 } 4739 }
4357 } 4740 }
4358 close(SYMBOLS); 4741 close(SYMBOLS);
4359 } 4742 }
4360 4743
4361 # Use nm to map the list of referenced PCs to symbols. Return true iff we 4744 # Use nm to map the list of referenced PCs to symbols. Return true iff we
4362 # are able to read procedure information via nm. 4745 # are able to read procedure information via nm.
4363 sub MapSymbolsWithNM { 4746 sub MapSymbolsWithNM {
4364 my $image = shift; 4747 my $image = shift;
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
4407 } 4790 }
4408 4791
4409 sub ShortFunctionName { 4792 sub ShortFunctionName {
4410 my $function = shift; 4793 my $function = shift;
4411 while ($function =~ s/\([^()]*\)(\s*const)?//g) { } # Argument types 4794 while ($function =~ s/\([^()]*\)(\s*const)?//g) { } # Argument types
4412 while ($function =~ s/<[^<>]*>//g) { } # Remove template arguments 4795 while ($function =~ s/<[^<>]*>//g) { } # Remove template arguments
4413 $function =~ s/^.*\s+(\w+::)/$1/; # Remove leading type 4796 $function =~ s/^.*\s+(\w+::)/$1/; # Remove leading type
4414 return $function; 4797 return $function;
4415 } 4798 }
4416 4799
4800 # Trim overly long symbols found in disassembler output
4801 sub CleanDisassembly {
4802 my $d = shift;
4803 while ($d =~ s/\([^()%]*\)(\s*const)?//g) { } # Argument types, not (%rax)
4804 while ($d =~ s/(\w+)<[^<>]*>/$1/g) { } # Remove template arguments
4805 return $d;
4806 }
4807
4808 # Clean file name for display
4809 sub CleanFileName {
4810 my ($f) = @_;
4811 $f =~ s|^/proc/self/cwd/||;
4812 $f =~ s|^\./||;
4813 return $f;
4814 }
4815
4816 # Make address relative to section and clean up for display
4817 sub UnparseAddress {
4818 my ($offset, $address) = @_;
4819 $address = AddressSub($address, $offset);
4820 $address =~ s/^0x//;
4821 $address =~ s/^0*//;
4822 return $address;
4823 }
4824
4417 ##### Miscellaneous ##### 4825 ##### Miscellaneous #####
4418 4826
4419 # Find the right versions of the above object tools to use. The 4827 # Find the right versions of the above object tools to use. The
4420 # argument is the program file being analyzed, and should be an ELF 4828 # argument is the program file being analyzed, and should be an ELF
4421 # 32-bit or ELF 64-bit executable file. The location of the tools 4829 # 32-bit or ELF 64-bit executable file. The location of the tools
4422 # is determined by considering the following options in this order: 4830 # is determined by considering the following options in this order:
4423 # 1) --tools option, if set 4831 # 1) --tools option, if set
4424 # 2) PPROF_TOOLS environment variable, if set 4832 # 2) PPROF_TOOLS environment variable, if set
4425 # 3) the environment 4833 # 3) the environment
4426 sub ConfigureObjTools { 4834 sub ConfigureObjTools {
4427 my $prog_file = shift; 4835 my $prog_file = shift;
4428 4836
4429 # Check for the existence of $prog_file because /usr/bin/file does not 4837 # Check for the existence of $prog_file because /usr/bin/file does not
4430 # predictably return error status in prod. 4838 # predictably return error status in prod.
4431 (-e $prog_file) || error("$prog_file does not exist.\n"); 4839 (-e $prog_file) || error("$prog_file does not exist.\n");
4432 4840
4433 my $file_type = undef; 4841 my $file_type = undef;
4434 if (-e "/usr/bin/file") { 4842 if (-e "/usr/bin/file") {
4435 # Follow symlinks (at least for systems where "file" supports that). 4843 # Follow symlinks (at least for systems where "file" supports that).
4436 $file_type = `/usr/bin/file -L $prog_file 2>$dev_null || /usr/bin/file $prog _file`; 4844 my $escaped_prog_file = ShellEscape($prog_file);
4845 $file_type = `/usr/bin/file -L $escaped_prog_file 2>$dev_null ||
4846 /usr/bin/file $escaped_prog_file`;
4437 } elsif ($^O == "MSWin32") { 4847 } elsif ($^O == "MSWin32") {
4438 $file_type = "MS Windows"; 4848 $file_type = "MS Windows";
4439 } else { 4849 } else {
4440 print STDERR "WARNING: Can't determine the file type of $prog_file"; 4850 print STDERR "WARNING: Can't determine the file type of $prog_file";
4441 } 4851 }
4442 4852
4443 if ($file_type =~ /64-bit/) { 4853 if ($file_type =~ /64-bit/) {
4444 # Change $address_length to 16 if the program file is ELF 64-bit. 4854 # Change $address_length to 16 if the program file is ELF 64-bit.
4445 # We can't detect this from many (most?) heap or lock contention 4855 # We can't detect this from many (most?) heap or lock contention
4446 # profiles, since the actual addresses referenced are generally in low 4856 # profiles, since the actual addresses referenced are generally in low
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
4508 if (-x "$dirname$tool") { 4918 if (-x "$dirname$tool") {
4509 $path = "$dirname$tool"; 4919 $path = "$dirname$tool";
4510 } else { 4920 } else {
4511 $path = $tool; 4921 $path = $tool;
4512 } 4922 }
4513 } 4923 }
4514 if ($main::opt_debug) { print STDERR "Using '$path' for '$tool'.\n"; } 4924 if ($main::opt_debug) { print STDERR "Using '$path' for '$tool'.\n"; }
4515 return $path; 4925 return $path;
4516 } 4926 }
4517 4927
4928 sub ShellEscape {
4929 my @escaped_words = ();
4930 foreach my $word (@_) {
4931 my $escaped_word = $word;
4932 if ($word =~ m![^a-zA-Z0-9/.,_=-]!) { # check for anything not in whitelist
4933 $escaped_word =~ s/'/'\\''/;
4934 $escaped_word = "'$escaped_word'";
4935 }
4936 push(@escaped_words, $escaped_word);
4937 }
4938 return join(" ", @escaped_words);
4939 }
4940
4518 sub cleanup { 4941 sub cleanup {
4519 unlink($main::tmpfile_sym); 4942 unlink($main::tmpfile_sym);
4520 unlink(keys %main::tempnames); 4943 unlink(keys %main::tempnames);
4521 4944
4522 # We leave any collected profiles in $HOME/pprof in case the user wants 4945 # We leave any collected profiles in $HOME/pprof in case the user wants
4523 # to look at them later. We print a message informing them of this. 4946 # to look at them later. We print a message informing them of this.
4524 if ((scalar(@main::profile_files) > 0) && 4947 if ((scalar(@main::profile_files) > 0) &&
4525 defined($main::collected_profile)) { 4948 defined($main::collected_profile)) {
4526 if (scalar(@main::profile_files) == 1) { 4949 if (scalar(@main::profile_files) == 1) {
4527 print STDERR "Dynamically gathered profile is in $main::collected_profile\ n"; 4950 print STDERR "Dynamically gathered profile is in $main::collected_profile\ n";
(...skipping 17 matching lines...) Expand all
4545 print STDERR $msg; 4968 print STDERR $msg;
4546 cleanup(); 4969 cleanup();
4547 exit(1); 4970 exit(1);
4548 } 4971 }
4549 4972
4550 4973
4551 # Run $nm_command and get all the resulting procedure boundaries whose 4974 # Run $nm_command and get all the resulting procedure boundaries whose
4552 # names match "$regexp" and returns them in a hashtable mapping from 4975 # names match "$regexp" and returns them in a hashtable mapping from
4553 # procedure name to a two-element vector of [start address, end address] 4976 # procedure name to a two-element vector of [start address, end address]
4554 sub GetProcedureBoundariesViaNm { 4977 sub GetProcedureBoundariesViaNm {
4555 my $nm_command = shift; 4978 my $escaped_nm_command = shift; # shell-escaped
4556 my $regexp = shift; 4979 my $regexp = shift;
4557 4980
4558 my $symbol_table = {}; 4981 my $symbol_table = {};
4559 open(NM, "$nm_command |") || error("$nm_command: $!\n"); 4982 open(NM, "$escaped_nm_command |") || error("$escaped_nm_command: $!\n");
4560 my $last_start = "0"; 4983 my $last_start = "0";
4561 my $routine = ""; 4984 my $routine = "";
4562 while (<NM>) { 4985 while (<NM>) {
4563 s/\r//g; # turn windows-looking lines into unix-looking lines 4986 s/\r//g; # turn windows-looking lines into unix-looking lines
4564 if (m/^\s*([0-9a-f]+) (.) (..*)/) { 4987 if (m/^\s*([0-9a-f]+) (.) (..*)/) {
4565 my $start_val = $1; 4988 my $start_val = $1;
4566 my $type = $2; 4989 my $type = $2;
4567 my $this_routine = $3; 4990 my $this_routine = $3;
4568 4991
4569 # It's possible for two symbols to share the same address, if 4992 # It's possible for two symbols to share the same address, if
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
4627 } 5050 }
4628 5051
4629 # Gets the procedure boundaries for all routines in "$image" whose names 5052 # Gets the procedure boundaries for all routines in "$image" whose names
4630 # match "$regexp" and returns them in a hashtable mapping from procedure 5053 # match "$regexp" and returns them in a hashtable mapping from procedure
4631 # name to a two-element vector of [start address, end address]. 5054 # name to a two-element vector of [start address, end address].
4632 # Will return an empty map if nm is not installed or not working properly. 5055 # Will return an empty map if nm is not installed or not working properly.
4633 sub GetProcedureBoundaries { 5056 sub GetProcedureBoundaries {
4634 my $image = shift; 5057 my $image = shift;
4635 my $regexp = shift; 5058 my $regexp = shift;
4636 5059
5060 # If $image doesn't start with /, then put ./ in front of it. This works
5061 # around an obnoxious bug in our probing of nm -f behavior.
5062 # "nm -f $image" is supposed to fail on GNU nm, but if:
5063 #
5064 # a. $image starts with [BbSsPp] (for example, bin/foo/bar), AND
5065 # b. you have a.out in your current directory (a not uncommon occurence)
5066 #
5067 # then "nm -f $image" succeeds because -f only looks at the first letter of
5068 # the argument, which looks valid because it's [BbSsPp], and then since
5069 # there's no image provided, it looks for a.out and finds it.
5070 #
5071 # This regex makes sure that $image starts with . or /, forcing the -f
5072 # parsing to fail since . and / are not valid formats.
5073 $image =~ s#^[^/]#./$&#;
5074
4637 # For libc libraries, the copy in /usr/lib/debug contains debugging symbols 5075 # For libc libraries, the copy in /usr/lib/debug contains debugging symbols
4638 my $debugging = DebuggingLibrary($image); 5076 my $debugging = DebuggingLibrary($image);
4639 if ($debugging) { 5077 if ($debugging) {
4640 $image = $debugging; 5078 $image = $debugging;
4641 } 5079 }
4642 5080
4643 my $nm = $obj_tool_map{"nm"}; 5081 my $nm = $obj_tool_map{"nm"};
4644 my $cppfilt = $obj_tool_map{"c++filt"}; 5082 my $cppfilt = $obj_tool_map{"c++filt"};
4645 5083
4646 # nm can fail for two reasons: 1) $image isn't a debug library; 2) nm 5084 # nm can fail for two reasons: 1) $image isn't a debug library; 2) nm
4647 # binary doesn't support --demangle. In addition, for OS X we need 5085 # binary doesn't support --demangle. In addition, for OS X we need
4648 # to use the -f flag to get 'flat' nm output (otherwise we don't sort 5086 # to use the -f flag to get 'flat' nm output (otherwise we don't sort
4649 # properly and get incorrect results). Unfortunately, GNU nm uses -f 5087 # properly and get incorrect results). Unfortunately, GNU nm uses -f
4650 # in an incompatible way. So first we test whether our nm supports 5088 # in an incompatible way. So first we test whether our nm supports
4651 # --demangle and -f. 5089 # --demangle and -f.
4652 my $demangle_flag = ""; 5090 my $demangle_flag = "";
4653 my $cppfilt_flag = ""; 5091 my $cppfilt_flag = "";
4654 if (system("$nm --demangle $image >$dev_null 2>&1") == 0) { 5092 my $to_devnull = ">$dev_null 2>&1";
5093 if (system(ShellEscape($nm, "--demangle", "image") . $to_devnull) == 0) {
4655 # In this mode, we do "nm --demangle <foo>" 5094 # In this mode, we do "nm --demangle <foo>"
4656 $demangle_flag = "--demangle"; 5095 $demangle_flag = "--demangle";
4657 $cppfilt_flag = ""; 5096 $cppfilt_flag = "";
4658 } elsif (system("$cppfilt $image >$dev_null 2>&1") == 0) { 5097 } elsif (system(ShellEscape($cppfilt, $image) . $to_devnull) == 0) {
4659 # In this mode, we do "nm <foo> | c++filt" 5098 # In this mode, we do "nm <foo> | c++filt"
4660 $cppfilt_flag = " | $cppfilt"; 5099 $cppfilt_flag = " | " . ShellEscape($cppfilt);
4661 }; 5100 };
4662 my $flatten_flag = ""; 5101 my $flatten_flag = "";
4663 if (system("$nm -f $image >$dev_null 2>&1") == 0) { 5102 if (system(ShellEscape($nm, "-f", $image) . $to_devnull) == 0) {
4664 $flatten_flag = "-f"; 5103 $flatten_flag = "-f";
4665 } 5104 }
4666 5105
4667 # Finally, in the case $imagie isn't a debug library, we try again with 5106 # Finally, in the case $imagie isn't a debug library, we try again with
4668 # -D to at least get *exported* symbols. If we can't use --demangle, 5107 # -D to at least get *exported* symbols. If we can't use --demangle,
4669 # we use c++filt instead, if it exists on this system. 5108 # we use c++filt instead, if it exists on this system.
4670 my @nm_commands = ("$nm -n $flatten_flag $demangle_flag" . 5109 my @nm_commands = (ShellEscape($nm, "-n", $flatten_flag, $demangle_flag,
4671 " $image 2>$dev_null $cppfilt_flag", 5110 $image) . " 2>$dev_null $cppfilt_flag",
4672 "$nm -D -n $flatten_flag $demangle_flag" . 5111 ShellEscape($nm, "-D", "-n", $flatten_flag, $demangle_flag,
4673 " $image 2>$dev_null $cppfilt_flag", 5112 $image) . " 2>$dev_null $cppfilt_flag",
4674 # 6nm is for Go binaries 5113 # 6nm is for Go binaries
4675 » » "6nm $image 2>$dev_null | sort", 5114 ShellEscape("6nm", "$image") . " 2>$dev_null | sort",
4676 ); 5115 );
4677 5116
4678 # If the executable is an MS Windows PDB-format executable, we'll 5117 # If the executable is an MS Windows PDB-format executable, we'll
4679 # have set up obj_tool_map("nm_pdb"). In this case, we actually 5118 # have set up obj_tool_map("nm_pdb"). In this case, we actually
4680 # want to use both unix nm and windows-specific nm_pdb, since 5119 # want to use both unix nm and windows-specific nm_pdb, since
4681 # PDB-format executables can apparently include dwarf .o files. 5120 # PDB-format executables can apparently include dwarf .o files.
4682 if (exists $obj_tool_map{"nm_pdb"}) { 5121 if (exists $obj_tool_map{"nm_pdb"}) {
4683 my $nm_pdb = $obj_tool_map{"nm_pdb"}; 5122 push(@nm_commands,
4684 push(@nm_commands, "$nm_pdb --demangle $image 2>$dev_null"); 5123 ShellEscape($obj_tool_map{"nm_pdb"}, "--demangle", $image)
5124 . " 2>$dev_null");
4685 } 5125 }
4686 5126
4687 foreach my $nm_command (@nm_commands) { 5127 foreach my $nm_command (@nm_commands) {
4688 my $symbol_table = GetProcedureBoundariesViaNm($nm_command, $regexp); 5128 my $symbol_table = GetProcedureBoundariesViaNm($nm_command, $regexp);
4689 return $symbol_table if (%{$symbol_table}); 5129 return $symbol_table if (%{$symbol_table});
4690 } 5130 }
4691 my $symbol_table = {}; 5131 my $symbol_table = {};
4692 return $symbol_table; 5132 return $symbol_table;
4693 } 5133 }
4694 5134
(...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after
4899 $error_count += AddressAddUnitTest($unit_test_data_8, $unit_test_data_16); 5339 $error_count += AddressAddUnitTest($unit_test_data_8, $unit_test_data_16);
4900 $error_count += AddressSubUnitTest($unit_test_data_8, $unit_test_data_16); 5340 $error_count += AddressSubUnitTest($unit_test_data_8, $unit_test_data_16);
4901 $error_count += AddressIncUnitTest($unit_test_data_8, $unit_test_data_16); 5341 $error_count += AddressIncUnitTest($unit_test_data_8, $unit_test_data_16);
4902 if ($error_count > 0) { 5342 if ($error_count > 0) {
4903 print STDERR $error_count, " errors: FAILED\n"; 5343 print STDERR $error_count, " errors: FAILED\n";
4904 } else { 5344 } else {
4905 print STDERR "PASS\n"; 5345 print STDERR "PASS\n";
4906 } 5346 }
4907 exit ($error_count); 5347 exit ($error_count);
4908 } 5348 }
OLDNEW
« no previous file with comments | « third_party/tcmalloc/chromium/src/page_heap_allocator.h ('k') | third_party/tcmalloc/chromium/src/profile-handler.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698