OLD | NEW |
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 Loading... |
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 = "1.8.2"; |
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: |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
149 pprof [options] <profile> | 149 pprof [options] <profile> |
150 <profile> is a remote form. Symbols are obtained from host:port$SYMBOL_PAGE | 150 <profile> is a remote form. Symbols are obtained from host:port$SYMBOL_PAGE |
151 | 151 |
152 Each name can be: | 152 Each name can be: |
153 /path/to/profile - a path to a profile file | 153 /path/to/profile - a path to a profile file |
154 host:port[/<service>] - a location of a service to get profile from | 154 host:port[/<service>] - a location of a service to get profile from |
155 | 155 |
156 The /<service> can be $HEAP_PAGE, $PROFILE_PAGE, /pprof/pmuprofile, | 156 The /<service> can be $HEAP_PAGE, $PROFILE_PAGE, /pprof/pmuprofile, |
157 $GROWTH_PAGE, $CONTENTION_PAGE, /pprof/wall, | 157 $GROWTH_PAGE, $CONTENTION_PAGE, /pprof/wall, |
158 $CENSUSPROFILE_PAGE, or /pprof/filteredprofile. | 158 $CENSUSPROFILE_PAGE, or /pprof/filteredprofile. |
159 For instance: "pprof http://myserver.com:80$HEAP_PAGE". | 159 For instance: |
| 160 pprof http://myserver.com:80$HEAP_PAGE |
160 If /<service> is omitted, the service defaults to $PROFILE_PAGE (cpu profilin
g). | 161 If /<service> is omitted, the service defaults to $PROFILE_PAGE (cpu profilin
g). |
161 pprof --symbols <program> | 162 pprof --symbols <program> |
162 Maps addresses to symbol names. In this mode, stdin should be a | 163 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- | 164 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 | 165 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. | 166 on linux), followed by a list of hex addresses to map, one per line. |
166 | 167 |
167 For more help with querying remote servers, including how to add the | 168 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): | 169 necessary server-side support code, see this filename (or one like it): |
169 | 170 |
(...skipping 368 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
538 if ($main::use_symbol_page) { | 539 if ($main::use_symbol_page) { |
539 unless (IsProfileURL($main::pfile_args[0])) { | 540 unless (IsProfileURL($main::pfile_args[0])) { |
540 error("The first profile should be a remote form to use $SYMBOL_PAGE\n"); | 541 error("The first profile should be a remote form to use $SYMBOL_PAGE\n"); |
541 } | 542 } |
542 CheckSymbolPage(); | 543 CheckSymbolPage(); |
543 $main::prog = FetchProgramName(); | 544 $main::prog = FetchProgramName(); |
544 } elsif (!$main::use_symbolized_profile) { # may not need objtools! | 545 } elsif (!$main::use_symbolized_profile) { # may not need objtools! |
545 ConfigureObjTools($main::prog) | 546 ConfigureObjTools($main::prog) |
546 } | 547 } |
547 | 548 |
548 # Break the opt_list_prefix into the prefix_list array | 549 # Break the opt_lib_prefix into the prefix_list array |
549 @prefix_list = split (',', $main::opt_lib_prefix); | 550 @prefix_list = split (',', $main::opt_lib_prefix); |
550 | 551 |
551 # Remove trailing / from the prefixes, in the list to prevent | 552 # Remove trailing / from the prefixes, in the list to prevent |
552 # searching things like /my/path//lib/mylib.so | 553 # searching things like /my/path//lib/mylib.so |
553 foreach (@prefix_list) { | 554 foreach (@prefix_list) { |
554 s|/+$||; | 555 s|/+$||; |
555 } | 556 } |
556 } | 557 } |
557 | 558 |
558 sub Main() { | 559 sub Main() { |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
636 | 637 |
637 # Get derived profiles | 638 # Get derived profiles |
638 my $flat = FlatProfile($reduced); | 639 my $flat = FlatProfile($reduced); |
639 my $cumulative = CumulativeProfile($reduced); | 640 my $cumulative = CumulativeProfile($reduced); |
640 | 641 |
641 # Print | 642 # Print |
642 if (!$main::opt_interactive) { | 643 if (!$main::opt_interactive) { |
643 if ($main::opt_disasm) { | 644 if ($main::opt_disasm) { |
644 PrintDisassembly($libs, $flat, $cumulative, $main::opt_disasm); | 645 PrintDisassembly($libs, $flat, $cumulative, $main::opt_disasm); |
645 } elsif ($main::opt_list) { | 646 } elsif ($main::opt_list) { |
646 PrintListing($libs, $flat, $cumulative, $main::opt_list); | 647 PrintListing($total, $libs, $flat, $cumulative, $main::opt_list, 0); |
647 } elsif ($main::opt_text) { | 648 } elsif ($main::opt_text) { |
648 # Make sure the output is empty when have nothing to report | 649 # Make sure the output is empty when have nothing to report |
649 # (only matters when --heapcheck is given but we must be | 650 # (only matters when --heapcheck is given but we must be |
650 # compatible with old branches that did not pass --heapcheck always): | 651 # compatible with old branches that did not pass --heapcheck always): |
651 if ($total != 0) { | 652 if ($total != 0) { |
652 printf("Total: %s %s\n", Unparse($total), Units()); | 653 printf("Total: %s %s\n", Unparse($total), Units()); |
653 } | 654 } |
654 PrintText($symbols, $flat, $cumulative, -1); | 655 PrintText($symbols, $flat, $cumulative, -1); |
655 } elsif ($main::opt_raw) { | 656 } elsif ($main::opt_raw) { |
656 PrintSymbolizedProfile($symbols, $profile, $main::prog); | 657 PrintSymbolizedProfile($symbols, $profile, $main::prog); |
(...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
832 | 833 |
833 if (m/^\s*(text|top)(\d*)\s*(.*)/) { | 834 if (m/^\s*(text|top)(\d*)\s*(.*)/) { |
834 $main::opt_text = 1; | 835 $main::opt_text = 1; |
835 | 836 |
836 my $line_limit = ($2 ne "") ? int($2) : 10; | 837 my $line_limit = ($2 ne "") ? int($2) : 10; |
837 | 838 |
838 my $routine; | 839 my $routine; |
839 my $ignore; | 840 my $ignore; |
840 ($routine, $ignore) = ParseInteractiveArgs($3); | 841 ($routine, $ignore) = ParseInteractiveArgs($3); |
841 | 842 |
842 my $profile = ProcessProfile($orig_profile, $symbols, "", $ignore); | 843 my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore); |
843 my $reduced = ReduceProfile($symbols, $profile); | 844 my $reduced = ReduceProfile($symbols, $profile); |
844 | 845 |
845 # Get derived profiles | 846 # Get derived profiles |
846 my $flat = FlatProfile($reduced); | 847 my $flat = FlatProfile($reduced); |
847 my $cumulative = CumulativeProfile($reduced); | 848 my $cumulative = CumulativeProfile($reduced); |
848 | 849 |
849 PrintText($symbols, $flat, $cumulative, $line_limit); | 850 PrintText($symbols, $flat, $cumulative, $line_limit); |
850 return 1; | 851 return 1; |
851 } | 852 } |
852 if (m/^\s*callgrind\s*([^ \n]*)/) { | 853 if (m/^\s*callgrind\s*([^ \n]*)/) { |
853 $main::opt_callgrind = 1; | 854 $main::opt_callgrind = 1; |
854 | 855 |
855 # Get derived profiles | 856 # Get derived profiles |
856 my $calls = ExtractCalls($symbols, $orig_profile); | 857 my $calls = ExtractCalls($symbols, $orig_profile); |
857 my $filename = $1; | 858 my $filename = $1; |
858 if ( $1 eq '' ) { | 859 if ( $1 eq '' ) { |
859 $filename = TempName($main::next_tmpfile, "callgrind"); | 860 $filename = TempName($main::next_tmpfile, "callgrind"); |
860 } | 861 } |
861 PrintCallgrind($calls, $filename); | 862 PrintCallgrind($calls, $filename); |
862 if ( $1 eq '' ) { | 863 if ( $1 eq '' ) { |
863 RunKcachegrind($filename, " & "); | 864 RunKcachegrind($filename, " & "); |
864 $main::next_tmpfile++; | 865 $main::next_tmpfile++; |
865 } | 866 } |
866 | 867 |
867 return 1; | 868 return 1; |
868 } | 869 } |
869 if (m/^\s*list\s*(.+)/) { | 870 if (m/^\s*(web)?list\s*(.+)/) { |
| 871 my $html = (defined($1) && ($1 eq "web")); |
870 $main::opt_list = 1; | 872 $main::opt_list = 1; |
871 | 873 |
872 my $routine; | 874 my $routine; |
873 my $ignore; | 875 my $ignore; |
874 ($routine, $ignore) = ParseInteractiveArgs($1); | 876 ($routine, $ignore) = ParseInteractiveArgs($2); |
875 | 877 |
876 my $profile = ProcessProfile($orig_profile, $symbols, "", $ignore); | 878 my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore); |
877 my $reduced = ReduceProfile($symbols, $profile); | 879 my $reduced = ReduceProfile($symbols, $profile); |
878 | 880 |
879 # Get derived profiles | 881 # Get derived profiles |
880 my $flat = FlatProfile($reduced); | 882 my $flat = FlatProfile($reduced); |
881 my $cumulative = CumulativeProfile($reduced); | 883 my $cumulative = CumulativeProfile($reduced); |
882 | 884 |
883 PrintListing($libs, $flat, $cumulative, $routine); | 885 PrintListing($total, $libs, $flat, $cumulative, $routine, $html); |
884 return 1; | 886 return 1; |
885 } | 887 } |
886 if (m/^\s*disasm\s*(.+)/) { | 888 if (m/^\s*disasm\s*(.+)/) { |
887 $main::opt_disasm = 1; | 889 $main::opt_disasm = 1; |
888 | 890 |
889 my $routine; | 891 my $routine; |
890 my $ignore; | 892 my $ignore; |
891 ($routine, $ignore) = ParseInteractiveArgs($1); | 893 ($routine, $ignore) = ParseInteractiveArgs($1); |
892 | 894 |
893 # Process current profile to account for various settings | 895 # Process current profile to account for various settings |
894 my $profile = ProcessProfile($orig_profile, $symbols, "", $ignore); | 896 my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore); |
895 my $reduced = ReduceProfile($symbols, $profile); | 897 my $reduced = ReduceProfile($symbols, $profile); |
896 | 898 |
897 # Get derived profiles | 899 # Get derived profiles |
898 my $flat = FlatProfile($reduced); | 900 my $flat = FlatProfile($reduced); |
899 my $cumulative = CumulativeProfile($reduced); | 901 my $cumulative = CumulativeProfile($reduced); |
900 | 902 |
901 PrintDisassembly($libs, $flat, $cumulative, $routine); | 903 PrintDisassembly($libs, $flat, $cumulative, $routine); |
902 return 1; | 904 return 1; |
903 } | 905 } |
904 if (m/^\s*(gv|web|evince)\s*(.*)/) { | 906 if (m/^\s*(gv|web|evince)\s*(.*)/) { |
905 $main::opt_gv = 0; | 907 $main::opt_gv = 0; |
906 $main::opt_evince = 0; | 908 $main::opt_evince = 0; |
907 $main::opt_web = 0; | 909 $main::opt_web = 0; |
908 if ($1 eq "gv") { | 910 if ($1 eq "gv") { |
909 $main::opt_gv = 1; | 911 $main::opt_gv = 1; |
910 } elsif ($1 eq "evince") { | 912 } elsif ($1 eq "evince") { |
911 $main::opt_evince = 1; | 913 $main::opt_evince = 1; |
912 } elsif ($1 eq "web") { | 914 } elsif ($1 eq "web") { |
913 $main::opt_web = 1; | 915 $main::opt_web = 1; |
914 } | 916 } |
915 | 917 |
916 my $focus; | 918 my $focus; |
917 my $ignore; | 919 my $ignore; |
918 ($focus, $ignore) = ParseInteractiveArgs($2); | 920 ($focus, $ignore) = ParseInteractiveArgs($2); |
919 | 921 |
920 # Process current profile to account for various settings | 922 # Process current profile to account for various settings |
921 my $profile = ProcessProfile($orig_profile, $symbols, $focus, $ignore); | 923 my $profile = ProcessProfile($total, $orig_profile, $symbols, |
| 924 $focus, $ignore); |
922 my $reduced = ReduceProfile($symbols, $profile); | 925 my $reduced = ReduceProfile($symbols, $profile); |
923 | 926 |
924 # Get derived profiles | 927 # Get derived profiles |
925 my $flat = FlatProfile($reduced); | 928 my $flat = FlatProfile($reduced); |
926 my $cumulative = CumulativeProfile($reduced); | 929 my $cumulative = CumulativeProfile($reduced); |
927 | 930 |
928 if (PrintDot($main::prog, $symbols, $profile, $flat, $cumulative, $total)) { | 931 if (PrintDot($main::prog, $symbols, $profile, $flat, $cumulative, $total)) { |
929 if ($main::opt_gv) { | 932 if ($main::opt_gv) { |
930 RunGV(TempName($main::next_tmpfile, "ps"), " &"); | 933 RunGV(TempName($main::next_tmpfile, "ps"), " &"); |
931 } elsif ($main::opt_evince) { | 934 } elsif ($main::opt_evince) { |
932 RunEvince(TempName($main::next_tmpfile, "pdf"), " &"); | 935 RunEvince(TempName($main::next_tmpfile, "pdf"), " &"); |
933 } elsif ($main::opt_web) { | 936 } elsif ($main::opt_web) { |
934 RunWeb(TempName($main::next_tmpfile, "svg")); | 937 RunWeb(TempName($main::next_tmpfile, "svg")); |
935 } | 938 } |
936 $main::next_tmpfile++; | 939 $main::next_tmpfile++; |
937 } | 940 } |
938 return 1; | 941 return 1; |
939 } | 942 } |
940 if (m/^\s*$/) { | 943 if (m/^\s*$/) { |
941 return 1; | 944 return 1; |
942 } | 945 } |
943 print STDERR "Unknown command: try 'help'.\n"; | 946 print STDERR "Unknown command: try 'help'.\n"; |
944 return 1; | 947 return 1; |
945 } | 948 } |
946 | 949 |
947 | 950 |
948 sub ProcessProfile { | 951 sub ProcessProfile { |
| 952 my $total_count = shift; |
949 my $orig_profile = shift; | 953 my $orig_profile = shift; |
950 my $symbols = shift; | 954 my $symbols = shift; |
951 my $focus = shift; | 955 my $focus = shift; |
952 my $ignore = shift; | 956 my $ignore = shift; |
953 | 957 |
954 # Process current profile to account for various settings | 958 # Process current profile to account for various settings |
955 my $profile = $orig_profile; | 959 my $profile = $orig_profile; |
956 my $total_count = TotalProfile($profile); | |
957 printf("Total: %s %s\n", Unparse($total_count), Units()); | 960 printf("Total: %s %s\n", Unparse($total_count), Units()); |
958 if ($focus ne '') { | 961 if ($focus ne '') { |
959 $profile = FocusProfile($symbols, $profile, $focus); | 962 $profile = FocusProfile($symbols, $profile, $focus); |
960 my $focus_count = TotalProfile($profile); | 963 my $focus_count = TotalProfile($profile); |
961 printf("After focusing on '%s': %s %s of %s (%0.1f%%)\n", | 964 printf("After focusing on '%s': %s %s of %s (%0.1f%%)\n", |
962 $focus, | 965 $focus, |
963 Unparse($focus_count), Units(), | 966 Unparse($focus_count), Units(), |
964 Unparse($total_count), ($focus_count*100.0) / $total_count); | 967 Unparse($total_count), ($focus_count*100.0) / $total_count); |
965 } | 968 } |
966 if ($ignore ne '') { | 969 if ($ignore ne '') { |
(...skipping 26 matching lines...) Expand all Loading... |
993 web [focus] [-ignore1] [-ignore2] | 996 web [focus] [-ignore1] [-ignore2] |
994 Like GV, but displays profile in your web browser instead of using | 997 Like GV, but displays profile in your web browser instead of using |
995 Ghostview. Works best if your web browser is already running. | 998 Ghostview. Works best if your web browser is already running. |
996 To change the browser that gets used: | 999 To change the browser that gets used: |
997 On Linux, set the /etc/alternatives/gnome-www-browser symlink. | 1000 On Linux, set the /etc/alternatives/gnome-www-browser symlink. |
998 On OS X, change the Finder association for SVG files. | 1001 On OS X, change the Finder association for SVG files. |
999 | 1002 |
1000 list [routine_regexp] [-ignore1] [-ignore2] | 1003 list [routine_regexp] [-ignore1] [-ignore2] |
1001 Show source listing of routines whose names match "routine_regexp" | 1004 Show source listing of routines whose names match "routine_regexp" |
1002 | 1005 |
| 1006 weblist [routine_regexp] [-ignore1] [-ignore2] |
| 1007 Displays a source listing of routines whose names match "routine_regexp" |
| 1008 in a web browser. You can click on source lines to view the |
| 1009 corresponding disassembly. |
| 1010 |
1003 top [--cum] [-ignore1] [-ignore2] | 1011 top [--cum] [-ignore1] [-ignore2] |
1004 top20 [--cum] [-ignore1] [-ignore2] | 1012 top20 [--cum] [-ignore1] [-ignore2] |
1005 top37 [--cum] [-ignore1] [-ignore2] | 1013 top37 [--cum] [-ignore1] [-ignore2] |
1006 Show top lines ordered by flat profile count, or cumulative count | 1014 Show top lines ordered by flat profile count, or cumulative count |
1007 if --cum is specified. If a number is present after 'top', the | 1015 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) | 1016 top K routines will be shown (defaults to showing the top 10) |
1009 | 1017 |
1010 disasm [routine_regexp] [-ignore1] [-ignore2] | 1018 disasm [routine_regexp] [-ignore1] [-ignore2] |
1011 Show disassembly of routines whose names match "routine_regexp", | 1019 Show disassembly of routines whose names match "routine_regexp", |
1012 annotated with sample counts. | 1020 annotated with sample counts. |
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1168 if ($f != 0 || $c != 0) { | 1176 if ($f != 0 || $c != 0) { |
1169 printf("%8s %6s %6s %8s %6s %s\n", | 1177 printf("%8s %6s %6s %8s %6s %s\n", |
1170 Unparse($f), | 1178 Unparse($f), |
1171 Percent($f, $total), | 1179 Percent($f, $total), |
1172 Percent($running_sum, $total), | 1180 Percent($running_sum, $total), |
1173 Unparse($c), | 1181 Unparse($c), |
1174 Percent($c, $total), | 1182 Percent($c, $total), |
1175 $sym); | 1183 $sym); |
1176 } | 1184 } |
1177 $lines++; | 1185 $lines++; |
1178 last if ($line_limit >= 0 && $lines > $line_limit); | 1186 last if ($line_limit >= 0 && $lines >= $line_limit); |
1179 } | 1187 } |
1180 } | 1188 } |
1181 | 1189 |
1182 # Print the call graph in a way that's suiteable for callgrind. | 1190 # Print the call graph in a way that's suiteable for callgrind. |
1183 sub PrintCallgrind { | 1191 sub PrintCallgrind { |
1184 my $calls = shift; | 1192 my $calls = shift; |
1185 my $filename; | 1193 my $filename; |
1186 if ($main::opt_interactive) { | 1194 if ($main::opt_interactive) { |
1187 $filename = shift; | 1195 $filename = shift; |
1188 print STDERR "Writing callgrind file to '$filename'.\n" | 1196 print STDERR "Writing callgrind file to '$filename'.\n" |
(...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1315 print(($symbols->{$pc}->[0] || "??") . "\n"); | 1323 print(($symbols->{$pc}->[0] || "??") . "\n"); |
1316 } | 1324 } |
1317 } | 1325 } |
1318 | 1326 |
1319 | 1327 |
1320 # For sorting functions by name | 1328 # For sorting functions by name |
1321 sub ByName { | 1329 sub ByName { |
1322 return ShortFunctionName($a) cmp ShortFunctionName($b); | 1330 return ShortFunctionName($a) cmp ShortFunctionName($b); |
1323 } | 1331 } |
1324 | 1332 |
1325 # Print source-listing for all all routines that match $main::opt_list | 1333 # Print source-listing for all all routines that match $list_opts |
1326 sub PrintListing { | 1334 sub PrintListing { |
| 1335 my $total = shift; |
1327 my $libs = shift; | 1336 my $libs = shift; |
1328 my $flat = shift; | 1337 my $flat = shift; |
1329 my $cumulative = shift; | 1338 my $cumulative = shift; |
1330 my $list_opts = shift; | 1339 my $list_opts = shift; |
| 1340 my $html = shift; |
1331 | 1341 |
| 1342 my $output = \*STDOUT; |
| 1343 my $fname = ""; |
| 1344 |
| 1345 if ($html) { |
| 1346 # Arrange to write the output to a temporary file |
| 1347 $fname = TempName($main::next_tmpfile, "html"); |
| 1348 $main::next_tmpfile++; |
| 1349 if (!open(TEMP, ">$fname")) { |
| 1350 print STDERR "$fname: $!\n"; |
| 1351 return; |
| 1352 } |
| 1353 $output = \*TEMP; |
| 1354 print $output HtmlListingHeader(); |
| 1355 printf $output ("<div class=\"legend\">%s<br>Total: %s %s</div>\n", |
| 1356 $main::prog, Unparse($total), Units()); |
| 1357 } |
| 1358 |
| 1359 my $listed = 0; |
1332 foreach my $lib (@{$libs}) { | 1360 foreach my $lib (@{$libs}) { |
1333 my $symbol_table = GetProcedureBoundaries($lib->[0], $list_opts); | 1361 my $symbol_table = GetProcedureBoundaries($lib->[0], $list_opts); |
1334 my $offset = AddressSub($lib->[1], $lib->[3]); | 1362 my $offset = AddressSub($lib->[1], $lib->[3]); |
1335 foreach my $routine (sort ByName keys(%{$symbol_table})) { | 1363 foreach my $routine (sort ByName keys(%{$symbol_table})) { |
1336 # Print if there are any samples in this routine | 1364 # Print if there are any samples in this routine |
1337 my $start_addr = $symbol_table->{$routine}->[0]; | 1365 my $start_addr = $symbol_table->{$routine}->[0]; |
1338 my $end_addr = $symbol_table->{$routine}->[1]; | 1366 my $end_addr = $symbol_table->{$routine}->[1]; |
1339 my $length = hex(AddressSub($end_addr, $start_addr)); | 1367 my $length = hex(AddressSub($end_addr, $start_addr)); |
1340 my $addr = AddressAdd($start_addr, $offset); | 1368 my $addr = AddressAdd($start_addr, $offset); |
1341 for (my $i = 0; $i < $length; $i++) { | 1369 for (my $i = 0; $i < $length; $i++) { |
1342 if (defined($cumulative->{$addr})) { | 1370 if (defined($cumulative->{$addr})) { |
1343 PrintSource($lib->[0], $offset, | 1371 $listed += PrintSource( |
1344 $routine, $flat, $cumulative, | 1372 $lib->[0], $offset, |
1345 $start_addr, $end_addr); | 1373 $routine, $flat, $cumulative, |
| 1374 $start_addr, $end_addr, |
| 1375 $html, |
| 1376 $output); |
1346 last; | 1377 last; |
1347 } | 1378 } |
1348 $addr = AddressInc($addr); | 1379 $addr = AddressInc($addr); |
1349 } | 1380 } |
1350 } | 1381 } |
1351 } | 1382 } |
| 1383 |
| 1384 if ($html) { |
| 1385 if ($listed > 0) { |
| 1386 print $output HtmlListingFooter(); |
| 1387 close($output); |
| 1388 RunWeb($fname); |
| 1389 } else { |
| 1390 close($output); |
| 1391 unlink($fname); |
| 1392 } |
| 1393 } |
| 1394 } |
| 1395 |
| 1396 sub HtmlListingHeader { |
| 1397 return <<'EOF'; |
| 1398 <DOCTYPE html> |
| 1399 <html> |
| 1400 <head> |
| 1401 <title>Pprof listing</title> |
| 1402 <style type="text/css"> |
| 1403 body { |
| 1404 font-family: sans-serif; |
| 1405 } |
| 1406 h1 { |
| 1407 font-size: 1.5em; |
| 1408 margin-bottom: 4px; |
| 1409 } |
| 1410 .legend { |
| 1411 font-size: 1.25em; |
| 1412 } |
| 1413 .line { |
| 1414 color: #aaaaaa; |
| 1415 } |
| 1416 .nop { |
| 1417 color: #aaaaaa; |
| 1418 } |
| 1419 .unimportant { |
| 1420 color: #cccccc; |
| 1421 } |
| 1422 .disasmloc { |
| 1423 color: #000000; |
| 1424 } |
| 1425 .deadsrc { |
| 1426 cursor: pointer; |
| 1427 } |
| 1428 .deadsrc:hover { |
| 1429 background-color: #eeeeee; |
| 1430 } |
| 1431 .livesrc { |
| 1432 color: #0000ff; |
| 1433 cursor: pointer; |
| 1434 } |
| 1435 .livesrc:hover { |
| 1436 background-color: #eeeeee; |
| 1437 } |
| 1438 .asm { |
| 1439 color: #008800; |
| 1440 display: none; |
| 1441 } |
| 1442 </style> |
| 1443 <script type="text/javascript"> |
| 1444 function pprof_toggle_asm(e) { |
| 1445 var target; |
| 1446 if (!e) e = window.event; |
| 1447 if (e.target) target = e.target; |
| 1448 else if (e.srcElement) target = e.srcElement; |
| 1449 |
| 1450 if (target) { |
| 1451 var asm = target.nextSibling; |
| 1452 if (asm && asm.className == "asm") { |
| 1453 asm.style.display = (asm.style.display == "block" ? "" : "block"); |
| 1454 e.preventDefault(); |
| 1455 return false; |
| 1456 } |
| 1457 } |
| 1458 } |
| 1459 </script> |
| 1460 </head> |
| 1461 <body> |
| 1462 EOF |
| 1463 } |
| 1464 |
| 1465 sub HtmlListingFooter { |
| 1466 return <<'EOF'; |
| 1467 </body> |
| 1468 </html> |
| 1469 EOF |
| 1470 } |
| 1471 |
| 1472 sub HtmlEscape { |
| 1473 my $text = shift; |
| 1474 $text =~ s/&/&/g; |
| 1475 $text =~ s/</</g; |
| 1476 $text =~ s/>/>/g; |
| 1477 return $text; |
1352 } | 1478 } |
1353 | 1479 |
1354 # Returns the indentation of the line, if it has any non-whitespace | 1480 # Returns the indentation of the line, if it has any non-whitespace |
1355 # characters. Otherwise, returns -1. | 1481 # characters. Otherwise, returns -1. |
1356 sub Indentation { | 1482 sub Indentation { |
1357 my $line = shift; | 1483 my $line = shift; |
1358 if (m/^(\s*)\S/) { | 1484 if (m/^(\s*)\S/) { |
1359 return length($1); | 1485 return length($1); |
1360 } else { | 1486 } else { |
1361 return -1; | 1487 return -1; |
1362 } | 1488 } |
1363 } | 1489 } |
1364 | 1490 |
| 1491 # If the symbol table contains inlining info, Disassemble() may tag an |
| 1492 # instruction with a location inside an inlined function. But for |
| 1493 # source listings, we prefer to use the location in the function we |
| 1494 # are listing. So use MapToSymbols() to fetch full location |
| 1495 # information for each instruction and then pick out the first |
| 1496 # location from a location list (location list contains callers before |
| 1497 # callees in case of inlining). |
| 1498 # |
| 1499 # After this routine has run, each entry in $instructions contains: |
| 1500 # [0] start address |
| 1501 # [1] filename for function we are listing |
| 1502 # [2] line number for function we are listing |
| 1503 # [3] disassembly |
| 1504 # [4] limit address |
| 1505 # [5] most specific filename (may be different from [1] due to inlining) |
| 1506 # [6] most specific line number (may be different from [2] due to inlining) |
| 1507 sub GetTopLevelLineNumbers { |
| 1508 my ($lib, $offset, $instructions) = @_; |
| 1509 my $pcs = []; |
| 1510 for (my $i = 0; $i <= $#{$instructions}; $i++) { |
| 1511 push(@{$pcs}, $instructions->[$i]->[0]); |
| 1512 } |
| 1513 my $symbols = {}; |
| 1514 MapToSymbols($lib, $offset, $pcs, $symbols); |
| 1515 for (my $i = 0; $i <= $#{$instructions}; $i++) { |
| 1516 my $e = $instructions->[$i]; |
| 1517 push(@{$e}, $e->[1]); |
| 1518 push(@{$e}, $e->[2]); |
| 1519 my $addr = $e->[0]; |
| 1520 my $sym = $symbols->{$addr}; |
| 1521 if (defined($sym)) { |
| 1522 if ($#{$sym} >= 2 && $sym->[1] =~ m/^(.*):(\d+)$/) { |
| 1523 $e->[1] = $1; # File name |
| 1524 $e->[2] = $2; # Line number |
| 1525 } |
| 1526 } |
| 1527 } |
| 1528 } |
| 1529 |
1365 # Print source-listing for one routine | 1530 # Print source-listing for one routine |
1366 sub PrintSource { | 1531 sub PrintSource { |
1367 my $prog = shift; | 1532 my $prog = shift; |
1368 my $offset = shift; | 1533 my $offset = shift; |
1369 my $routine = shift; | 1534 my $routine = shift; |
1370 my $flat = shift; | 1535 my $flat = shift; |
1371 my $cumulative = shift; | 1536 my $cumulative = shift; |
1372 my $start_addr = shift; | 1537 my $start_addr = shift; |
1373 my $end_addr = shift; | 1538 my $end_addr = shift; |
| 1539 my $html = shift; |
| 1540 my $output = shift; |
1374 | 1541 |
1375 # Disassemble all instructions (just to get line numbers) | 1542 # Disassemble all instructions (just to get line numbers) |
1376 my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr); | 1543 my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr); |
| 1544 GetTopLevelLineNumbers($prog, $offset, \@instructions); |
1377 | 1545 |
1378 # Hack 1: assume that the first source file encountered in the | 1546 # Hack 1: assume that the first source file encountered in the |
1379 # disassembly contains the routine | 1547 # disassembly contains the routine |
1380 my $filename = undef; | 1548 my $filename = undef; |
1381 for (my $i = 0; $i <= $#instructions; $i++) { | 1549 for (my $i = 0; $i <= $#instructions; $i++) { |
1382 if ($instructions[$i]->[2] >= 0) { | 1550 if ($instructions[$i]->[2] >= 0) { |
1383 $filename = $instructions[$i]->[1]; | 1551 $filename = $instructions[$i]->[1]; |
1384 last; | 1552 last; |
1385 } | 1553 } |
1386 } | 1554 } |
1387 if (!defined($filename)) { | 1555 if (!defined($filename)) { |
1388 print STDERR "no filename found in $routine\n"; | 1556 print STDERR "no filename found in $routine\n"; |
1389 return; | 1557 return 0; |
1390 } | 1558 } |
1391 | 1559 |
1392 # Hack 2: assume that the largest line number from $filename is the | 1560 # 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 | 1561 # 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 | 1562 # 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 | 1563 # source file. If this does not work, we might have to compute a |
1396 # density profile or just print all regions we find. | 1564 # density profile or just print all regions we find. |
1397 my $lastline = 0; | 1565 my $lastline = 0; |
1398 for (my $i = 0; $i <= $#instructions; $i++) { | 1566 for (my $i = 0; $i <= $#instructions; $i++) { |
1399 my $f = $instructions[$i]->[1]; | 1567 my $f = $instructions[$i]->[1]; |
(...skipping 12 matching lines...) Expand all Loading... |
1412 last; | 1580 last; |
1413 } | 1581 } |
1414 } | 1582 } |
1415 | 1583 |
1416 # Hack 4: Extend last line forward until its indentation is less than | 1584 # Hack 4: Extend last line forward until its indentation is less than |
1417 # the indentation we saw on $firstline | 1585 # the indentation we saw on $firstline |
1418 my $oldlastline = $lastline; | 1586 my $oldlastline = $lastline; |
1419 { | 1587 { |
1420 if (!open(FILE, "<$filename")) { | 1588 if (!open(FILE, "<$filename")) { |
1421 print STDERR "$filename: $!\n"; | 1589 print STDERR "$filename: $!\n"; |
1422 return; | 1590 return 0; |
1423 } | 1591 } |
1424 my $l = 0; | 1592 my $l = 0; |
1425 my $first_indentation = -1; | 1593 my $first_indentation = -1; |
1426 while (<FILE>) { | 1594 while (<FILE>) { |
1427 s/\r//g; # turn windows-looking lines into unix-looking lines | 1595 s/\r//g; # turn windows-looking lines into unix-looking lines |
1428 $l++; | 1596 $l++; |
1429 my $indent = Indentation($_); | 1597 my $indent = Indentation($_); |
1430 if ($l >= $firstline) { | 1598 if ($l >= $firstline) { |
1431 if ($first_indentation < 0 && $indent >= 0) { | 1599 if ($first_indentation < 0 && $indent >= 0) { |
1432 $first_indentation = $indent; | 1600 $first_indentation = $indent; |
1433 last if ($first_indentation == 0); | 1601 last if ($first_indentation == 0); |
1434 } | 1602 } |
1435 } | 1603 } |
1436 if ($l >= $lastline && $indent >= 0) { | 1604 if ($l >= $lastline && $indent >= 0) { |
1437 if ($indent >= $first_indentation) { | 1605 if ($indent >= $first_indentation) { |
1438 $lastline = $l+1; | 1606 $lastline = $l+1; |
1439 } else { | 1607 } else { |
1440 last; | 1608 last; |
1441 } | 1609 } |
1442 } | 1610 } |
1443 } | 1611 } |
1444 close(FILE); | 1612 close(FILE); |
1445 } | 1613 } |
1446 | 1614 |
1447 # Assign all samples to the range $firstline,$lastline, | 1615 # Assign all samples to the range $firstline,$lastline, |
1448 # Hack 4: If an instruction does not occur in the range, its samples | 1616 # 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. | 1617 # are moved to the next instruction that occurs in the range. |
1450 my $samples1 = {}; | 1618 my $samples1 = {}; # Map from line number to flat count |
1451 my $samples2 = {}; | 1619 my $samples2 = {}; # Map from line number to cumulative count |
1452 my $running1 = 0; # Unassigned flat counts | 1620 my $running1 = 0; # Unassigned flat counts |
1453 my $running2 = 0; # Unassigned cumulative counts | 1621 my $running2 = 0; # Unassigned cumulative counts |
1454 my $total1 = 0; # Total flat counts | 1622 my $total1 = 0; # Total flat counts |
1455 my $total2 = 0; # Total cumulative counts | 1623 my $total2 = 0; # Total cumulative counts |
| 1624 my %disasm = (); # Map from line number to disassembly |
| 1625 my $running_disasm = ""; # Unassigned disassembly |
| 1626 my $skip_marker = "---\n"; |
| 1627 if ($html) { |
| 1628 $skip_marker = ""; |
| 1629 for (my $l = $firstline; $l <= $lastline; $l++) { |
| 1630 $disasm{$l} = ""; |
| 1631 } |
| 1632 } |
| 1633 my $last_dis_filename = ''; |
| 1634 my $last_dis_linenum = -1; |
| 1635 my $last_touched_line = -1; # To detect gaps in disassembly for a line |
1456 foreach my $e (@instructions) { | 1636 foreach my $e (@instructions) { |
1457 # Add up counts for all address that fall inside this instruction | 1637 # Add up counts for all address that fall inside this instruction |
1458 my $c1 = 0; | 1638 my $c1 = 0; |
1459 my $c2 = 0; | 1639 my $c2 = 0; |
1460 for (my $a = $e->[0]; $a lt $e->[4]; $a = AddressInc($a)) { | 1640 for (my $a = $e->[0]; $a lt $e->[4]; $a = AddressInc($a)) { |
1461 $c1 += GetEntry($flat, $a); | 1641 $c1 += GetEntry($flat, $a); |
1462 $c2 += GetEntry($cumulative, $a); | 1642 $c2 += GetEntry($cumulative, $a); |
1463 } | 1643 } |
| 1644 |
| 1645 if ($html) { |
| 1646 my $dis = sprintf(" %6s %6s \t\t%8s: %s ", |
| 1647 HtmlPrintNumber($c1), |
| 1648 HtmlPrintNumber($c2), |
| 1649 UnparseAddress($offset, $e->[0]), |
| 1650 CleanDisassembly($e->[3])); |
| 1651 |
| 1652 # Append the most specific source line associated with this instruction |
| 1653 if (length($dis) < 80) { $dis .= (' ' x (80 - length($dis))) }; |
| 1654 $dis = HtmlEscape($dis); |
| 1655 my $f = $e->[5]; |
| 1656 my $l = $e->[6]; |
| 1657 if ($f ne $last_dis_filename) { |
| 1658 $dis .= sprintf("<span class=disasmloc>%s:%d</span>", |
| 1659 HtmlEscape(CleanFileName($f)), $l); |
| 1660 } elsif ($l ne $last_dis_linenum) { |
| 1661 # De-emphasize the unchanged file name portion |
| 1662 $dis .= sprintf("<span class=unimportant>%s</span>" . |
| 1663 "<span class=disasmloc>:%d</span>", |
| 1664 HtmlEscape(CleanFileName($f)), $l); |
| 1665 } else { |
| 1666 # De-emphasize the entire location |
| 1667 $dis .= sprintf("<span class=unimportant>%s:%d</span>", |
| 1668 HtmlEscape(CleanFileName($f)), $l); |
| 1669 } |
| 1670 $last_dis_filename = $f; |
| 1671 $last_dis_linenum = $l; |
| 1672 $running_disasm .= $dis; |
| 1673 $running_disasm .= "\n"; |
| 1674 } |
| 1675 |
1464 $running1 += $c1; | 1676 $running1 += $c1; |
1465 $running2 += $c2; | 1677 $running2 += $c2; |
1466 $total1 += $c1; | 1678 $total1 += $c1; |
1467 $total2 += $c2; | 1679 $total2 += $c2; |
1468 my $file = $e->[1]; | 1680 my $file = $e->[1]; |
1469 my $line = $e->[2]; | 1681 my $line = $e->[2]; |
1470 if (($file eq $filename) && | 1682 if (($file eq $filename) && |
1471 ($line >= $firstline) && | 1683 ($line >= $firstline) && |
1472 ($line <= $lastline)) { | 1684 ($line <= $lastline)) { |
1473 # Assign all accumulated samples to this line | 1685 # Assign all accumulated samples to this line |
1474 AddEntry($samples1, $line, $running1); | 1686 AddEntry($samples1, $line, $running1); |
1475 AddEntry($samples2, $line, $running2); | 1687 AddEntry($samples2, $line, $running2); |
1476 $running1 = 0; | 1688 $running1 = 0; |
1477 $running2 = 0; | 1689 $running2 = 0; |
| 1690 if ($html) { |
| 1691 if ($line != $last_touched_line && $disasm{$line} ne '') { |
| 1692 $disasm{$line} .= "\n"; |
| 1693 } |
| 1694 $disasm{$line} .= $running_disasm; |
| 1695 $running_disasm = ''; |
| 1696 $last_touched_line = $line; |
| 1697 } |
1478 } | 1698 } |
1479 } | 1699 } |
1480 | 1700 |
1481 # Assign any leftover samples to $lastline | 1701 # Assign any leftover samples to $lastline |
1482 AddEntry($samples1, $lastline, $running1); | 1702 AddEntry($samples1, $lastline, $running1); |
1483 AddEntry($samples2, $lastline, $running2); | 1703 AddEntry($samples2, $lastline, $running2); |
| 1704 if ($html) { |
| 1705 if ($lastline != $last_touched_line && $disasm{$lastline} ne '') { |
| 1706 $disasm{$lastline} .= "\n"; |
| 1707 } |
| 1708 $disasm{$lastline} .= $running_disasm; |
| 1709 } |
1484 | 1710 |
1485 printf("ROUTINE ====================== %s in %s\n" . | 1711 if ($html) { |
1486 "%6s %6s Total %s (flat / cumulative)\n", | 1712 printf $output ( |
1487 ShortFunctionName($routine), | 1713 "<h1>%s</h1>%s\n<pre onClick=\"pprof_toggle_asm()\">\n" . |
1488 $filename, | 1714 "Total:%6s %6s (flat / cumulative %s)\n", |
1489 Units(), | 1715 HtmlEscape(ShortFunctionName($routine)), |
1490 Unparse($total1), | 1716 HtmlEscape(CleanFileName($filename)), |
1491 Unparse($total2)); | 1717 Unparse($total1), |
| 1718 Unparse($total2), |
| 1719 Units()); |
| 1720 } else { |
| 1721 printf $output ( |
| 1722 "ROUTINE ====================== %s in %s\n" . |
| 1723 "%6s %6s Total %s (flat / cumulative)\n", |
| 1724 ShortFunctionName($routine), |
| 1725 CleanFileName($filename), |
| 1726 Unparse($total1), |
| 1727 Unparse($total2), |
| 1728 Units()); |
| 1729 } |
1492 if (!open(FILE, "<$filename")) { | 1730 if (!open(FILE, "<$filename")) { |
1493 print STDERR "$filename: $!\n"; | 1731 print STDERR "$filename: $!\n"; |
1494 return; | 1732 return 0; |
1495 } | 1733 } |
1496 my $l = 0; | 1734 my $l = 0; |
1497 while (<FILE>) { | 1735 while (<FILE>) { |
1498 s/\r//g; # turn windows-looking lines into unix-looking lines | 1736 s/\r//g; # turn windows-looking lines into unix-looking lines |
1499 $l++; | 1737 $l++; |
1500 if ($l >= $firstline - 5 && | 1738 if ($l >= $firstline - 5 && |
1501 (($l <= $oldlastline + 5) || ($l <= $lastline))) { | 1739 (($l <= $oldlastline + 5) || ($l <= $lastline))) { |
1502 chop; | 1740 chop; |
1503 my $text = $_; | 1741 my $text = $_; |
1504 if ($l == $firstline) { printf("---\n"); } | 1742 if ($l == $firstline) { print $output $skip_marker; } |
1505 printf("%6s %6s %4d: %s\n", | 1743 my $n1 = GetEntry($samples1, $l); |
1506 UnparseAlt(GetEntry($samples1, $l)), | 1744 my $n2 = GetEntry($samples2, $l); |
1507 UnparseAlt(GetEntry($samples2, $l)), | 1745 if ($html) { |
1508 $l, | 1746 # Emit a span that has one of the following classes: |
1509 $text); | 1747 # livesrc -- has samples |
1510 if ($l == $lastline) { printf("---\n"); } | 1748 # deadsrc -- has disassembly, but with no samples |
| 1749 # nop -- has no matching disasembly |
| 1750 # Also emit an optional span containing disassembly. |
| 1751 my $dis = $disasm{$l}; |
| 1752 my $asm = ""; |
| 1753 if (defined($dis) && $dis ne '') { |
| 1754 $asm = "<span class=\"asm\">" . $dis . "</span>"; |
| 1755 } |
| 1756 my $source_class = (($n1 + $n2 > 0) |
| 1757 ? "livesrc" |
| 1758 : (($asm ne "") ? "deadsrc" : "nop")); |
| 1759 printf $output ( |
| 1760 "<span class=\"line\">%5d</span> " . |
| 1761 "<span class=\"%s\">%6s %6s %s</span>%s\n", |
| 1762 $l, $source_class, |
| 1763 HtmlPrintNumber($n1), |
| 1764 HtmlPrintNumber($n2), |
| 1765 HtmlEscape($text), |
| 1766 $asm); |
| 1767 } else { |
| 1768 printf $output( |
| 1769 "%6s %6s %4d: %s\n", |
| 1770 UnparseAlt($n1), |
| 1771 UnparseAlt($n2), |
| 1772 $l, |
| 1773 $text); |
| 1774 } |
| 1775 if ($l == $lastline) { print $output $skip_marker; } |
1511 }; | 1776 }; |
1512 } | 1777 } |
1513 close(FILE); | 1778 close(FILE); |
| 1779 if ($html) { |
| 1780 print $output "</pre>\n"; |
| 1781 } |
| 1782 return 1; |
1514 } | 1783 } |
1515 | 1784 |
1516 # Return the source line for the specified file/linenumber. | 1785 # Return the source line for the specified file/linenumber. |
1517 # Returns undef if not found. | 1786 # Returns undef if not found. |
1518 sub SourceLine { | 1787 sub SourceLine { |
1519 my $file = shift; | 1788 my $file = shift; |
1520 my $line = shift; | 1789 my $line = shift; |
1521 | 1790 |
1522 # Look in cache | 1791 # Look in cache |
1523 if (!defined($main::source_cache{$file})) { | 1792 if (!defined($main::source_cache{$file})) { |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1646 printf("%6s %6s %5d: %s", | 1915 printf("%6s %6s %5d: %s", |
1647 UnparseAlt($flat_sum{$l}), | 1916 UnparseAlt($flat_sum{$l}), |
1648 UnparseAlt($cum_sum{$l}), | 1917 UnparseAlt($cum_sum{$l}), |
1649 $l, | 1918 $l, |
1650 $line); | 1919 $line); |
1651 } | 1920 } |
1652 | 1921 |
1653 # Print disassembly | 1922 # Print disassembly |
1654 for (my $x = $first_inst; $x <= $last_inst; $x++) { | 1923 for (my $x = $first_inst; $x <= $last_inst; $x++) { |
1655 my $e = $instructions[$x]; | 1924 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", | 1925 printf("%6s %6s %8s: %6s\n", |
1667 UnparseAlt($flat_count[$x]), | 1926 UnparseAlt($flat_count[$x]), |
1668 UnparseAlt($cum_count[$x]), | 1927 UnparseAlt($cum_count[$x]), |
1669 $address, | 1928 UnparseAddress($offset, $e->[0]), |
1670 $d); | 1929 CleanDisassembly($e->[3])); |
1671 } | 1930 } |
1672 } | 1931 } |
1673 } | 1932 } |
1674 | 1933 |
1675 # Print DOT graph | 1934 # Print DOT graph |
1676 sub PrintDot { | 1935 sub PrintDot { |
1677 my $prog = shift; | 1936 my $prog = shift; |
1678 my $symbols = shift; | 1937 my $symbols = shift; |
1679 my $raw = shift; | 1938 my $raw = shift; |
1680 my $flat = shift; | 1939 my $flat = shift; |
(...skipping 638 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2319 # Alternate pretty-printed form: 0 maps to "." | 2578 # Alternate pretty-printed form: 0 maps to "." |
2320 sub UnparseAlt { | 2579 sub UnparseAlt { |
2321 my $num = shift; | 2580 my $num = shift; |
2322 if ($num == 0) { | 2581 if ($num == 0) { |
2323 return "."; | 2582 return "."; |
2324 } else { | 2583 } else { |
2325 return Unparse($num); | 2584 return Unparse($num); |
2326 } | 2585 } |
2327 } | 2586 } |
2328 | 2587 |
| 2588 # Alternate pretty-printed form: 0 maps to "" |
| 2589 sub HtmlPrintNumber { |
| 2590 my $num = shift; |
| 2591 if ($num == 0) { |
| 2592 return ""; |
| 2593 } else { |
| 2594 return Unparse($num); |
| 2595 } |
| 2596 } |
| 2597 |
2329 # Return output units | 2598 # Return output units |
2330 sub Units { | 2599 sub Units { |
2331 if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') { | 2600 if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') { |
2332 if ($main::opt_inuse_objects || $main::opt_alloc_objects) { | 2601 if ($main::opt_inuse_objects || $main::opt_alloc_objects) { |
2333 return "objects"; | 2602 return "objects"; |
2334 } else { | 2603 } else { |
2335 if ($main::opt_show_bytes) { | 2604 if ($main::opt_show_bytes) { |
2336 return "B"; | 2605 return "B"; |
2337 } else { | 2606 } else { |
2338 return "MB"; | 2607 return "MB"; |
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2475 '::do_malloc_or_cpp_alloc', | 2744 '::do_malloc_or_cpp_alloc', |
2476 'DoSampledAllocation', | 2745 'DoSampledAllocation', |
2477 'simple_alloc::allocate', | 2746 'simple_alloc::allocate', |
2478 '__malloc_alloc_template::allocate', | 2747 '__malloc_alloc_template::allocate', |
2479 '__builtin_delete', | 2748 '__builtin_delete', |
2480 '__builtin_new', | 2749 '__builtin_new', |
2481 '__builtin_vec_delete', | 2750 '__builtin_vec_delete', |
2482 '__builtin_vec_new', | 2751 '__builtin_vec_new', |
2483 'operator new', | 2752 'operator new', |
2484 'operator new[]', | 2753 'operator new[]', |
| 2754 # The entry to our memory-allocation routines on OS X |
| 2755 'malloc_zone_malloc', |
| 2756 'malloc_zone_calloc', |
| 2757 'malloc_zone_valloc', |
| 2758 'malloc_zone_realloc', |
| 2759 'malloc_zone_memalign', |
| 2760 'malloc_zone_free', |
2485 # These mark the beginning/end of our custom sections | 2761 # These mark the beginning/end of our custom sections |
2486 '__start_google_malloc', | 2762 '__start_google_malloc', |
2487 '__stop_google_malloc', | 2763 '__stop_google_malloc', |
2488 '__start_malloc_hook', | 2764 '__start_malloc_hook', |
2489 '__stop_malloc_hook') { | 2765 '__stop_malloc_hook') { |
2490 $skip{$name} = 1; | 2766 $skip{$name} = 1; |
2491 $skip{"_" . $name} = 1; # Mach (OS X) adds a _ prefix to everything | 2767 $skip{"_" . $name} = 1; # Mach (OS X) adds a _ prefix to everything |
2492 } | 2768 } |
2493 # TODO: Remove TCMalloc once everything has been | 2769 # TODO: Remove TCMalloc once everything has been |
2494 # moved into the tcmalloc:: namespace and we have flushed | 2770 # moved into the tcmalloc:: namespace and we have flushed |
(...skipping 1474 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3969 my $buildvar = ""; | 4245 my $buildvar = ""; |
3970 foreach my $l (split("\n", $map)) { | 4246 foreach my $l (split("\n", $map)) { |
3971 if ($l =~ m/^\s*build=(.*)$/) { | 4247 if ($l =~ m/^\s*build=(.*)$/) { |
3972 $buildvar = $1; | 4248 $buildvar = $1; |
3973 } | 4249 } |
3974 | 4250 |
3975 my $start; | 4251 my $start; |
3976 my $finish; | 4252 my $finish; |
3977 my $offset; | 4253 my $offset; |
3978 my $lib; | 4254 my $lib; |
3979 if ($l =~ /^($h)-($h)\s+..x.\s+($h)\s+\S+:\S+\s+\d+\s+(\S+\.(so|dll|dylib|bu
ndle)((\.\d+)+\w*(\.\d+){0,3})?)$/i) { | 4255 if ($l =~ /^($h)-($h)\s+..x.\s+($h)\s+\S+:\S+\s+\d+\s+(\S+(\.(so|dll|dylib|b
undle)|chrome)((\.\d+)+\w*(\.\d+){0,3})?)$/i) { |
3980 # Full line from /proc/self/maps. Example: | 4256 # Full line from /proc/self/maps. Example: |
3981 # 40000000-40015000 r-xp 00000000 03:01 12845071 /lib/ld-2.3.2.so | 4257 # 40000000-40015000 r-xp 00000000 03:01 12845071 /lib/ld-2.3.2.so |
3982 $start = HexExtend($1); | 4258 $start = HexExtend($1); |
3983 $finish = HexExtend($2); | 4259 $finish = HexExtend($2); |
3984 $offset = HexExtend($3); | 4260 $offset = HexExtend($3); |
3985 $lib = $4; | 4261 $lib = $4; |
3986 $lib =~ s|\\|/|g; # turn windows-style paths into unix-style paths | 4262 $lib =~ s|\\|/|g; # turn windows-style paths into unix-style paths |
3987 } elsif ($l =~ /^\s*($h)-($h):\s*(\S+\.so(\.\d+)*)/) { | 4263 } elsif ($l =~ /^\s*($h)-($h):\s*(\S+\.so(\.\d+)*)/) { |
3988 # Cooked line from DumpAddressMap. Example: | 4264 # Cooked line from DumpAddressMap. Example: |
3989 # 40000000-40015000: /lib/ld-2.3.2.so | 4265 # 40000000-40015000: /lib/ld-2.3.2.so |
3990 $start = HexExtend($1); | 4266 $start = HexExtend($1); |
3991 $finish = HexExtend($2); | 4267 $finish = HexExtend($2); |
3992 $offset = $zero_offset; | 4268 $offset = $zero_offset; |
3993 $lib = $3; | 4269 $lib = $3; |
3994 } else { | 4270 } else { |
3995 next; | 4271 next; |
3996 } | 4272 } |
3997 | 4273 |
3998 # Expand "$build" variable if available | 4274 # Expand "$build" variable if available |
3999 $lib =~ s/\$build\b/$buildvar/g; | 4275 $lib =~ s/\$build\b/$buildvar/g; |
4000 | |
4001 $lib = FindLibrary($lib); | 4276 $lib = FindLibrary($lib); |
4002 | 4277 |
4003 # Check for pre-relocated libraries, which use pre-relocated symbol tables | 4278 # Check for pre-relocated libraries, which use pre-relocated symbol tables |
4004 # and thus require adjusting the offset that we'll use to translate | 4279 # and thus require adjusting the offset that we'll use to translate |
4005 # VM addresses into symbol table addresses. | 4280 # VM addresses into symbol table addresses. |
4006 # Only do this if we're not going to fetch the symbol table from a | 4281 # Only do this if we're not going to fetch the symbol table from a |
4007 # debugging copy of the library. | 4282 # debugging copy of the library. |
4008 if (!DebuggingLibrary($lib)) { | 4283 if (!DebuggingLibrary($lib)) { |
4009 my $text = ParseTextSectionHeader($lib); | 4284 my $text = ParseTextSectionHeader($lib); |
4010 if (defined($text)) { | 4285 if (defined($text)) { |
(...skipping 396 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4407 } | 4682 } |
4408 | 4683 |
4409 sub ShortFunctionName { | 4684 sub ShortFunctionName { |
4410 my $function = shift; | 4685 my $function = shift; |
4411 while ($function =~ s/\([^()]*\)(\s*const)?//g) { } # Argument types | 4686 while ($function =~ s/\([^()]*\)(\s*const)?//g) { } # Argument types |
4412 while ($function =~ s/<[^<>]*>//g) { } # Remove template arguments | 4687 while ($function =~ s/<[^<>]*>//g) { } # Remove template arguments |
4413 $function =~ s/^.*\s+(\w+::)/$1/; # Remove leading type | 4688 $function =~ s/^.*\s+(\w+::)/$1/; # Remove leading type |
4414 return $function; | 4689 return $function; |
4415 } | 4690 } |
4416 | 4691 |
| 4692 # Trim overly long symbols found in disassembler output |
| 4693 sub CleanDisassembly { |
| 4694 my $d = shift; |
| 4695 while ($d =~ s/\([^()%]*\)(\s*const)?//g) { } # Argument types, not (%rax) |
| 4696 while ($d =~ s/(\w+)<[^<>]*>/$1/g) { } # Remove template arguments |
| 4697 return $d; |
| 4698 } |
| 4699 |
| 4700 # Clean file name for display |
| 4701 sub CleanFileName { |
| 4702 my ($f) = @_; |
| 4703 $f =~ s|^/proc/self/cwd/||; |
| 4704 $f =~ s|^\./||; |
| 4705 return $f; |
| 4706 } |
| 4707 |
| 4708 # Make address relative to section and clean up for display |
| 4709 sub UnparseAddress { |
| 4710 my ($offset, $address) = @_; |
| 4711 $address = AddressSub($address, $offset); |
| 4712 $address =~ s/^0x//; |
| 4713 $address =~ s/^0*//; |
| 4714 return $address; |
| 4715 } |
| 4716 |
4417 ##### Miscellaneous ##### | 4717 ##### Miscellaneous ##### |
4418 | 4718 |
4419 # Find the right versions of the above object tools to use. The | 4719 # 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 | 4720 # 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 | 4721 # 32-bit or ELF 64-bit executable file. The location of the tools |
4422 # is determined by considering the following options in this order: | 4722 # is determined by considering the following options in this order: |
4423 # 1) --tools option, if set | 4723 # 1) --tools option, if set |
4424 # 2) PPROF_TOOLS environment variable, if set | 4724 # 2) PPROF_TOOLS environment variable, if set |
4425 # 3) the environment | 4725 # 3) the environment |
4426 sub ConfigureObjTools { | 4726 sub ConfigureObjTools { |
(...skipping 472 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4899 $error_count += AddressAddUnitTest($unit_test_data_8, $unit_test_data_16); | 5199 $error_count += AddressAddUnitTest($unit_test_data_8, $unit_test_data_16); |
4900 $error_count += AddressSubUnitTest($unit_test_data_8, $unit_test_data_16); | 5200 $error_count += AddressSubUnitTest($unit_test_data_8, $unit_test_data_16); |
4901 $error_count += AddressIncUnitTest($unit_test_data_8, $unit_test_data_16); | 5201 $error_count += AddressIncUnitTest($unit_test_data_8, $unit_test_data_16); |
4902 if ($error_count > 0) { | 5202 if ($error_count > 0) { |
4903 print STDERR $error_count, " errors: FAILED\n"; | 5203 print STDERR $error_count, " errors: FAILED\n"; |
4904 } else { | 5204 } else { |
4905 print STDERR "PASS\n"; | 5205 print STDERR "PASS\n"; |
4906 } | 5206 } |
4907 exit ($error_count); | 5207 exit ($error_count); |
4908 } | 5208 } |
OLD | NEW |