| 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"; |
| 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 1912 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 4407 } | 4683 } |
| 4408 | 4684 |
| 4409 sub ShortFunctionName { | 4685 sub ShortFunctionName { |
| 4410 my $function = shift; | 4686 my $function = shift; |
| 4411 while ($function =~ s/\([^()]*\)(\s*const)?//g) { } # Argument types | 4687 while ($function =~ s/\([^()]*\)(\s*const)?//g) { } # Argument types |
| 4412 while ($function =~ s/<[^<>]*>//g) { } # Remove template arguments | 4688 while ($function =~ s/<[^<>]*>//g) { } # Remove template arguments |
| 4413 $function =~ s/^.*\s+(\w+::)/$1/; # Remove leading type | 4689 $function =~ s/^.*\s+(\w+::)/$1/; # Remove leading type |
| 4414 return $function; | 4690 return $function; |
| 4415 } | 4691 } |
| 4416 | 4692 |
| 4693 # Trim overly long symbols found in disassembler output |
| 4694 sub CleanDisassembly { |
| 4695 my $d = shift; |
| 4696 while ($d =~ s/\([^()%]*\)(\s*const)?//g) { } # Argument types, not (%rax) |
| 4697 while ($d =~ s/(\w+)<[^<>]*>/$1/g) { } # Remove template arguments |
| 4698 return $d; |
| 4699 } |
| 4700 |
| 4701 # Clean file name for display |
| 4702 sub CleanFileName { |
| 4703 my ($f) = @_; |
| 4704 $f =~ s|^/proc/self/cwd/||; |
| 4705 $f =~ s|^\./||; |
| 4706 return $f; |
| 4707 } |
| 4708 |
| 4709 # Make address relative to section and clean up for display |
| 4710 sub UnparseAddress { |
| 4711 my ($offset, $address) = @_; |
| 4712 $address = AddressSub($address, $offset); |
| 4713 $address =~ s/^0x//; |
| 4714 $address =~ s/^0*//; |
| 4715 return $address; |
| 4716 } |
| 4717 |
| 4417 ##### Miscellaneous ##### | 4718 ##### Miscellaneous ##### |
| 4418 | 4719 |
| 4419 # Find the right versions of the above object tools to use. The | 4720 # 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 | 4721 # 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 | 4722 # 32-bit or ELF 64-bit executable file. The location of the tools |
| 4422 # is determined by considering the following options in this order: | 4723 # is determined by considering the following options in this order: |
| 4423 # 1) --tools option, if set | 4724 # 1) --tools option, if set |
| 4424 # 2) PPROF_TOOLS environment variable, if set | 4725 # 2) PPROF_TOOLS environment variable, if set |
| 4425 # 3) the environment | 4726 # 3) the environment |
| 4426 sub ConfigureObjTools { | 4727 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); | 5200 $error_count += AddressAddUnitTest($unit_test_data_8, $unit_test_data_16); |
| 4900 $error_count += AddressSubUnitTest($unit_test_data_8, $unit_test_data_16); | 5201 $error_count += AddressSubUnitTest($unit_test_data_8, $unit_test_data_16); |
| 4901 $error_count += AddressIncUnitTest($unit_test_data_8, $unit_test_data_16); | 5202 $error_count += AddressIncUnitTest($unit_test_data_8, $unit_test_data_16); |
| 4902 if ($error_count > 0) { | 5203 if ($error_count > 0) { |
| 4903 print STDERR $error_count, " errors: FAILED\n"; | 5204 print STDERR $error_count, " errors: FAILED\n"; |
| 4904 } else { | 5205 } else { |
| 4905 print STDERR "PASS\n"; | 5206 print STDERR "PASS\n"; |
| 4906 } | 5207 } |
| 4907 exit ($error_count); | 5208 exit ($error_count); |
| 4908 } | 5209 } |
| OLD | NEW |