Index: third_party/tcmalloc/chromium/src/pprof |
diff --git a/third_party/tcmalloc/chromium/src/pprof b/third_party/tcmalloc/chromium/src/pprof |
index 03bafa4446a1e520542cb953b04b314a4c1b1fe4..2d889d0e08efcc5e944624157c5e3812ff14b441 100755 |
--- a/third_party/tcmalloc/chromium/src/pprof |
+++ b/third_party/tcmalloc/chromium/src/pprof |
@@ -72,7 +72,7 @@ use strict; |
use warnings; |
use Getopt::Long; |
-my $PPROF_VERSION = "1.7"; |
+my $PPROF_VERSION = "1.8.2"; |
# These are the object tools we use which can come from a |
# user-specified location using --tools, from the PPROF_TOOLS |
@@ -156,7 +156,8 @@ pprof [options] <profile> |
The /<service> can be $HEAP_PAGE, $PROFILE_PAGE, /pprof/pmuprofile, |
$GROWTH_PAGE, $CONTENTION_PAGE, /pprof/wall, |
$CENSUSPROFILE_PAGE, or /pprof/filteredprofile. |
- For instance: "pprof http://myserver.com:80$HEAP_PAGE". |
+ For instance: |
+ pprof http://myserver.com:80$HEAP_PAGE |
If /<service> is omitted, the service defaults to $PROFILE_PAGE (cpu profiling). |
pprof --symbols <program> |
Maps addresses to symbol names. In this mode, stdin should be a |
@@ -545,7 +546,7 @@ sub Init() { |
ConfigureObjTools($main::prog) |
} |
- # Break the opt_list_prefix into the prefix_list array |
+ # Break the opt_lib_prefix into the prefix_list array |
@prefix_list = split (',', $main::opt_lib_prefix); |
# Remove trailing / from the prefixes, in the list to prevent |
@@ -643,7 +644,7 @@ sub Main() { |
if ($main::opt_disasm) { |
PrintDisassembly($libs, $flat, $cumulative, $main::opt_disasm); |
} elsif ($main::opt_list) { |
- PrintListing($libs, $flat, $cumulative, $main::opt_list); |
+ PrintListing($total, $libs, $flat, $cumulative, $main::opt_list, 0); |
} elsif ($main::opt_text) { |
# Make sure the output is empty when have nothing to report |
# (only matters when --heapcheck is given but we must be |
@@ -839,7 +840,7 @@ sub InteractiveCommand { |
my $ignore; |
($routine, $ignore) = ParseInteractiveArgs($3); |
- my $profile = ProcessProfile($orig_profile, $symbols, "", $ignore); |
+ my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore); |
my $reduced = ReduceProfile($symbols, $profile); |
# Get derived profiles |
@@ -866,21 +867,22 @@ sub InteractiveCommand { |
return 1; |
} |
- if (m/^\s*list\s*(.+)/) { |
+ if (m/^\s*(web)?list\s*(.+)/) { |
+ my $html = (defined($1) && ($1 eq "web")); |
$main::opt_list = 1; |
my $routine; |
my $ignore; |
- ($routine, $ignore) = ParseInteractiveArgs($1); |
+ ($routine, $ignore) = ParseInteractiveArgs($2); |
- my $profile = ProcessProfile($orig_profile, $symbols, "", $ignore); |
+ my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore); |
my $reduced = ReduceProfile($symbols, $profile); |
# Get derived profiles |
my $flat = FlatProfile($reduced); |
my $cumulative = CumulativeProfile($reduced); |
- PrintListing($libs, $flat, $cumulative, $routine); |
+ PrintListing($total, $libs, $flat, $cumulative, $routine, $html); |
return 1; |
} |
if (m/^\s*disasm\s*(.+)/) { |
@@ -891,7 +893,7 @@ sub InteractiveCommand { |
($routine, $ignore) = ParseInteractiveArgs($1); |
# Process current profile to account for various settings |
- my $profile = ProcessProfile($orig_profile, $symbols, "", $ignore); |
+ my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore); |
my $reduced = ReduceProfile($symbols, $profile); |
# Get derived profiles |
@@ -918,7 +920,8 @@ sub InteractiveCommand { |
($focus, $ignore) = ParseInteractiveArgs($2); |
# Process current profile to account for various settings |
- my $profile = ProcessProfile($orig_profile, $symbols, $focus, $ignore); |
+ my $profile = ProcessProfile($total, $orig_profile, $symbols, |
+ $focus, $ignore); |
my $reduced = ReduceProfile($symbols, $profile); |
# Get derived profiles |
@@ -946,6 +949,7 @@ sub InteractiveCommand { |
sub ProcessProfile { |
+ my $total_count = shift; |
my $orig_profile = shift; |
my $symbols = shift; |
my $focus = shift; |
@@ -953,7 +957,6 @@ sub ProcessProfile { |
# Process current profile to account for various settings |
my $profile = $orig_profile; |
- my $total_count = TotalProfile($profile); |
printf("Total: %s %s\n", Unparse($total_count), Units()); |
if ($focus ne '') { |
$profile = FocusProfile($symbols, $profile, $focus); |
@@ -1000,6 +1003,11 @@ Commands: |
list [routine_regexp] [-ignore1] [-ignore2] |
Show source listing of routines whose names match "routine_regexp" |
+ weblist [routine_regexp] [-ignore1] [-ignore2] |
+ Displays a source listing of routines whose names match "routine_regexp" |
+ in a web browser. You can click on source lines to view the |
+ corresponding disassembly. |
+ |
top [--cum] [-ignore1] [-ignore2] |
top20 [--cum] [-ignore1] [-ignore2] |
top37 [--cum] [-ignore1] [-ignore2] |
@@ -1175,7 +1183,7 @@ sub PrintText { |
$sym); |
} |
$lines++; |
- last if ($line_limit >= 0 && $lines > $line_limit); |
+ last if ($line_limit >= 0 && $lines >= $line_limit); |
} |
} |
@@ -1322,13 +1330,33 @@ sub ByName { |
return ShortFunctionName($a) cmp ShortFunctionName($b); |
} |
-# Print source-listing for all all routines that match $main::opt_list |
+# Print source-listing for all all routines that match $list_opts |
sub PrintListing { |
+ my $total = shift; |
my $libs = shift; |
my $flat = shift; |
my $cumulative = shift; |
my $list_opts = shift; |
+ my $html = shift; |
+ my $output = \*STDOUT; |
+ my $fname = ""; |
+ |
+ if ($html) { |
+ # Arrange to write the output to a temporary file |
+ $fname = TempName($main::next_tmpfile, "html"); |
+ $main::next_tmpfile++; |
+ if (!open(TEMP, ">$fname")) { |
+ print STDERR "$fname: $!\n"; |
+ return; |
+ } |
+ $output = \*TEMP; |
+ print $output HtmlListingHeader(); |
+ printf $output ("<div class=\"legend\">%s<br>Total: %s %s</div>\n", |
+ $main::prog, Unparse($total), Units()); |
+ } |
+ |
+ my $listed = 0; |
foreach my $lib (@{$libs}) { |
my $symbol_table = GetProcedureBoundaries($lib->[0], $list_opts); |
my $offset = AddressSub($lib->[1], $lib->[3]); |
@@ -1340,15 +1368,113 @@ sub PrintListing { |
my $addr = AddressAdd($start_addr, $offset); |
for (my $i = 0; $i < $length; $i++) { |
if (defined($cumulative->{$addr})) { |
- PrintSource($lib->[0], $offset, |
- $routine, $flat, $cumulative, |
- $start_addr, $end_addr); |
+ $listed += PrintSource( |
+ $lib->[0], $offset, |
+ $routine, $flat, $cumulative, |
+ $start_addr, $end_addr, |
+ $html, |
+ $output); |
last; |
} |
$addr = AddressInc($addr); |
} |
} |
} |
+ |
+ if ($html) { |
+ if ($listed > 0) { |
+ print $output HtmlListingFooter(); |
+ close($output); |
+ RunWeb($fname); |
+ } else { |
+ close($output); |
+ unlink($fname); |
+ } |
+ } |
+} |
+ |
+sub HtmlListingHeader { |
+ return <<'EOF'; |
+<DOCTYPE html> |
+<html> |
+<head> |
+<title>Pprof listing</title> |
+<style type="text/css"> |
+body { |
+ font-family: sans-serif; |
+} |
+h1 { |
+ font-size: 1.5em; |
+ margin-bottom: 4px; |
+} |
+.legend { |
+ font-size: 1.25em; |
+} |
+.line { |
+ color: #aaaaaa; |
+} |
+.nop { |
+ color: #aaaaaa; |
+} |
+.unimportant { |
+ color: #cccccc; |
+} |
+.disasmloc { |
+ color: #000000; |
+} |
+.deadsrc { |
+ cursor: pointer; |
+} |
+.deadsrc:hover { |
+ background-color: #eeeeee; |
+} |
+.livesrc { |
+ color: #0000ff; |
+ cursor: pointer; |
+} |
+.livesrc:hover { |
+ background-color: #eeeeee; |
+} |
+.asm { |
+ color: #008800; |
+ display: none; |
+} |
+</style> |
+<script type="text/javascript"> |
+function pprof_toggle_asm(e) { |
+ var target; |
+ if (!e) e = window.event; |
+ if (e.target) target = e.target; |
+ else if (e.srcElement) target = e.srcElement; |
+ |
+ if (target) { |
+ var asm = target.nextSibling; |
+ if (asm && asm.className == "asm") { |
+ asm.style.display = (asm.style.display == "block" ? "" : "block"); |
+ e.preventDefault(); |
+ return false; |
+ } |
+ } |
+} |
+</script> |
+</head> |
+<body> |
+EOF |
+} |
+ |
+sub HtmlListingFooter { |
+ return <<'EOF'; |
+</body> |
+</html> |
+EOF |
+} |
+ |
+sub HtmlEscape { |
+ my $text = shift; |
+ $text =~ s/&/&/g; |
+ $text =~ s/</</g; |
+ $text =~ s/>/>/g; |
+ return $text; |
} |
# Returns the indentation of the line, if it has any non-whitespace |
@@ -1362,6 +1488,45 @@ sub Indentation { |
} |
} |
+# If the symbol table contains inlining info, Disassemble() may tag an |
+# instruction with a location inside an inlined function. But for |
+# source listings, we prefer to use the location in the function we |
+# are listing. So use MapToSymbols() to fetch full location |
+# information for each instruction and then pick out the first |
+# location from a location list (location list contains callers before |
+# callees in case of inlining). |
+# |
+# After this routine has run, each entry in $instructions contains: |
+# [0] start address |
+# [1] filename for function we are listing |
+# [2] line number for function we are listing |
+# [3] disassembly |
+# [4] limit address |
+# [5] most specific filename (may be different from [1] due to inlining) |
+# [6] most specific line number (may be different from [2] due to inlining) |
+sub GetTopLevelLineNumbers { |
+ my ($lib, $offset, $instructions) = @_; |
+ my $pcs = []; |
+ for (my $i = 0; $i <= $#{$instructions}; $i++) { |
+ push(@{$pcs}, $instructions->[$i]->[0]); |
+ } |
+ my $symbols = {}; |
+ MapToSymbols($lib, $offset, $pcs, $symbols); |
+ for (my $i = 0; $i <= $#{$instructions}; $i++) { |
+ my $e = $instructions->[$i]; |
+ push(@{$e}, $e->[1]); |
+ push(@{$e}, $e->[2]); |
+ my $addr = $e->[0]; |
+ my $sym = $symbols->{$addr}; |
+ if (defined($sym)) { |
+ if ($#{$sym} >= 2 && $sym->[1] =~ m/^(.*):(\d+)$/) { |
+ $e->[1] = $1; # File name |
+ $e->[2] = $2; # Line number |
+ } |
+ } |
+ } |
+} |
+ |
# Print source-listing for one routine |
sub PrintSource { |
my $prog = shift; |
@@ -1371,9 +1536,12 @@ sub PrintSource { |
my $cumulative = shift; |
my $start_addr = shift; |
my $end_addr = shift; |
+ my $html = shift; |
+ my $output = shift; |
# Disassemble all instructions (just to get line numbers) |
my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr); |
+ GetTopLevelLineNumbers($prog, $offset, \@instructions); |
# Hack 1: assume that the first source file encountered in the |
# disassembly contains the routine |
@@ -1386,7 +1554,7 @@ sub PrintSource { |
} |
if (!defined($filename)) { |
print STDERR "no filename found in $routine\n"; |
- return; |
+ return 0; |
} |
# Hack 2: assume that the largest line number from $filename is the |
@@ -1419,7 +1587,7 @@ sub PrintSource { |
{ |
if (!open(FILE, "<$filename")) { |
print STDERR "$filename: $!\n"; |
- return; |
+ return 0; |
} |
my $l = 0; |
my $first_indentation = -1; |
@@ -1447,12 +1615,24 @@ sub PrintSource { |
# Assign all samples to the range $firstline,$lastline, |
# Hack 4: If an instruction does not occur in the range, its samples |
# are moved to the next instruction that occurs in the range. |
- my $samples1 = {}; |
- my $samples2 = {}; |
- my $running1 = 0; # Unassigned flat counts |
- my $running2 = 0; # Unassigned cumulative counts |
- my $total1 = 0; # Total flat counts |
- my $total2 = 0; # Total cumulative counts |
+ my $samples1 = {}; # Map from line number to flat count |
+ my $samples2 = {}; # Map from line number to cumulative count |
+ my $running1 = 0; # Unassigned flat counts |
+ my $running2 = 0; # Unassigned cumulative counts |
+ my $total1 = 0; # Total flat counts |
+ my $total2 = 0; # Total cumulative counts |
+ my %disasm = (); # Map from line number to disassembly |
+ my $running_disasm = ""; # Unassigned disassembly |
+ my $skip_marker = "---\n"; |
+ if ($html) { |
+ $skip_marker = ""; |
+ for (my $l = $firstline; $l <= $lastline; $l++) { |
+ $disasm{$l} = ""; |
+ } |
+ } |
+ my $last_dis_filename = ''; |
+ my $last_dis_linenum = -1; |
+ my $last_touched_line = -1; # To detect gaps in disassembly for a line |
foreach my $e (@instructions) { |
# Add up counts for all address that fall inside this instruction |
my $c1 = 0; |
@@ -1461,6 +1641,38 @@ sub PrintSource { |
$c1 += GetEntry($flat, $a); |
$c2 += GetEntry($cumulative, $a); |
} |
+ |
+ if ($html) { |
+ my $dis = sprintf(" %6s %6s \t\t%8s: %s ", |
+ HtmlPrintNumber($c1), |
+ HtmlPrintNumber($c2), |
+ UnparseAddress($offset, $e->[0]), |
+ CleanDisassembly($e->[3])); |
+ |
+ # Append the most specific source line associated with this instruction |
+ if (length($dis) < 80) { $dis .= (' ' x (80 - length($dis))) }; |
+ $dis = HtmlEscape($dis); |
+ my $f = $e->[5]; |
+ my $l = $e->[6]; |
+ if ($f ne $last_dis_filename) { |
+ $dis .= sprintf("<span class=disasmloc>%s:%d</span>", |
+ HtmlEscape(CleanFileName($f)), $l); |
+ } elsif ($l ne $last_dis_linenum) { |
+ # De-emphasize the unchanged file name portion |
+ $dis .= sprintf("<span class=unimportant>%s</span>" . |
+ "<span class=disasmloc>:%d</span>", |
+ HtmlEscape(CleanFileName($f)), $l); |
+ } else { |
+ # De-emphasize the entire location |
+ $dis .= sprintf("<span class=unimportant>%s:%d</span>", |
+ HtmlEscape(CleanFileName($f)), $l); |
+ } |
+ $last_dis_filename = $f; |
+ $last_dis_linenum = $l; |
+ $running_disasm .= $dis; |
+ $running_disasm .= "\n"; |
+ } |
+ |
$running1 += $c1; |
$running2 += $c2; |
$total1 += $c1; |
@@ -1475,23 +1687,49 @@ sub PrintSource { |
AddEntry($samples2, $line, $running2); |
$running1 = 0; |
$running2 = 0; |
+ if ($html) { |
+ if ($line != $last_touched_line && $disasm{$line} ne '') { |
+ $disasm{$line} .= "\n"; |
+ } |
+ $disasm{$line} .= $running_disasm; |
+ $running_disasm = ''; |
+ $last_touched_line = $line; |
+ } |
} |
} |
# Assign any leftover samples to $lastline |
AddEntry($samples1, $lastline, $running1); |
AddEntry($samples2, $lastline, $running2); |
- |
- printf("ROUTINE ====================== %s in %s\n" . |
- "%6s %6s Total %s (flat / cumulative)\n", |
- ShortFunctionName($routine), |
- $filename, |
- Units(), |
- Unparse($total1), |
- Unparse($total2)); |
+ if ($html) { |
+ if ($lastline != $last_touched_line && $disasm{$lastline} ne '') { |
+ $disasm{$lastline} .= "\n"; |
+ } |
+ $disasm{$lastline} .= $running_disasm; |
+ } |
+ |
+ if ($html) { |
+ printf $output ( |
+ "<h1>%s</h1>%s\n<pre onClick=\"pprof_toggle_asm()\">\n" . |
+ "Total:%6s %6s (flat / cumulative %s)\n", |
+ HtmlEscape(ShortFunctionName($routine)), |
+ HtmlEscape(CleanFileName($filename)), |
+ Unparse($total1), |
+ Unparse($total2), |
+ Units()); |
+ } else { |
+ printf $output ( |
+ "ROUTINE ====================== %s in %s\n" . |
+ "%6s %6s Total %s (flat / cumulative)\n", |
+ ShortFunctionName($routine), |
+ CleanFileName($filename), |
+ Unparse($total1), |
+ Unparse($total2), |
+ Units()); |
+ } |
if (!open(FILE, "<$filename")) { |
print STDERR "$filename: $!\n"; |
- return; |
+ return 0; |
} |
my $l = 0; |
while (<FILE>) { |
@@ -1501,16 +1739,47 @@ sub PrintSource { |
(($l <= $oldlastline + 5) || ($l <= $lastline))) { |
chop; |
my $text = $_; |
- if ($l == $firstline) { printf("---\n"); } |
- printf("%6s %6s %4d: %s\n", |
- UnparseAlt(GetEntry($samples1, $l)), |
- UnparseAlt(GetEntry($samples2, $l)), |
- $l, |
- $text); |
- if ($l == $lastline) { printf("---\n"); } |
+ if ($l == $firstline) { print $output $skip_marker; } |
+ my $n1 = GetEntry($samples1, $l); |
+ my $n2 = GetEntry($samples2, $l); |
+ if ($html) { |
+ # Emit a span that has one of the following classes: |
+ # livesrc -- has samples |
+ # deadsrc -- has disassembly, but with no samples |
+ # nop -- has no matching disasembly |
+ # Also emit an optional span containing disassembly. |
+ my $dis = $disasm{$l}; |
+ my $asm = ""; |
+ if (defined($dis) && $dis ne '') { |
+ $asm = "<span class=\"asm\">" . $dis . "</span>"; |
+ } |
+ my $source_class = (($n1 + $n2 > 0) |
+ ? "livesrc" |
+ : (($asm ne "") ? "deadsrc" : "nop")); |
+ printf $output ( |
+ "<span class=\"line\">%5d</span> " . |
+ "<span class=\"%s\">%6s %6s %s</span>%s\n", |
+ $l, $source_class, |
+ HtmlPrintNumber($n1), |
+ HtmlPrintNumber($n2), |
+ HtmlEscape($text), |
+ $asm); |
+ } else { |
+ printf $output( |
+ "%6s %6s %4d: %s\n", |
+ UnparseAlt($n1), |
+ UnparseAlt($n2), |
+ $l, |
+ $text); |
+ } |
+ if ($l == $lastline) { print $output $skip_marker; } |
}; |
} |
close(FILE); |
+ if ($html) { |
+ print $output "</pre>\n"; |
+ } |
+ return 1; |
} |
# Return the source line for the specified file/linenumber. |
@@ -1653,21 +1922,11 @@ sub PrintDisassembledFunction { |
# Print disassembly |
for (my $x = $first_inst; $x <= $last_inst; $x++) { |
my $e = $instructions[$x]; |
- my $address = $e->[0]; |
- $address = AddressSub($address, $offset); # Make relative to section |
- $address =~ s/^0x//; |
- $address =~ s/^0*//; |
- |
- # Trim symbols |
- my $d = $e->[3]; |
- while ($d =~ s/\([^()%]*\)(\s*const)?//g) { } # Argument types, not (%rax) |
- while ($d =~ s/(\w+)<[^<>]*>/$1/g) { } # Remove template arguments |
- |
printf("%6s %6s %8s: %6s\n", |
UnparseAlt($flat_count[$x]), |
UnparseAlt($cum_count[$x]), |
- $address, |
- $d); |
+ UnparseAddress($offset, $e->[0]), |
+ CleanDisassembly($e->[3])); |
} |
} |
} |
@@ -2326,6 +2585,16 @@ sub UnparseAlt { |
} |
} |
+# Alternate pretty-printed form: 0 maps to "" |
+sub HtmlPrintNumber { |
+ my $num = shift; |
+ if ($num == 0) { |
+ return ""; |
+ } else { |
+ return Unparse($num); |
+ } |
+} |
+ |
# Return output units |
sub Units { |
if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') { |
@@ -2482,6 +2751,13 @@ sub RemoveUninterestingFrames { |
'__builtin_vec_new', |
'operator new', |
'operator new[]', |
+ # The entry to our memory-allocation routines on OS X |
+ 'malloc_zone_malloc', |
+ 'malloc_zone_calloc', |
+ 'malloc_zone_valloc', |
+ 'malloc_zone_realloc', |
+ 'malloc_zone_memalign', |
+ 'malloc_zone_free', |
# These mark the beginning/end of our custom sections |
'__start_google_malloc', |
'__stop_google_malloc', |
@@ -3976,7 +4252,7 @@ sub ParseLibraries { |
my $finish; |
my $offset; |
my $lib; |
- if ($l =~ /^($h)-($h)\s+..x.\s+($h)\s+\S+:\S+\s+\d+\s+(\S+\.(so|dll|dylib|bundle)((\.\d+)+\w*(\.\d+){0,3})?)$/i) { |
+ if ($l =~ /^($h)-($h)\s+..x.\s+($h)\s+\S+:\S+\s+\d+\s+(\S+(\.(so|dll|dylib|bundle)|chrome)((\.\d+)+\w*(\.\d+){0,3})?)$/i) { |
# Full line from /proc/self/maps. Example: |
# 40000000-40015000 r-xp 00000000 03:01 12845071 /lib/ld-2.3.2.so |
$start = HexExtend($1); |
@@ -3997,7 +4273,6 @@ sub ParseLibraries { |
# Expand "$build" variable if available |
$lib =~ s/\$build\b/$buildvar/g; |
- |
$lib = FindLibrary($lib); |
# Check for pre-relocated libraries, which use pre-relocated symbol tables |
@@ -4414,6 +4689,31 @@ sub ShortFunctionName { |
return $function; |
} |
+# Trim overly long symbols found in disassembler output |
+sub CleanDisassembly { |
+ my $d = shift; |
+ while ($d =~ s/\([^()%]*\)(\s*const)?//g) { } # Argument types, not (%rax) |
+ while ($d =~ s/(\w+)<[^<>]*>/$1/g) { } # Remove template arguments |
+ return $d; |
+} |
+ |
+# Clean file name for display |
+sub CleanFileName { |
+ my ($f) = @_; |
+ $f =~ s|^/proc/self/cwd/||; |
+ $f =~ s|^\./||; |
+ return $f; |
+} |
+ |
+# Make address relative to section and clean up for display |
+sub UnparseAddress { |
+ my ($offset, $address) = @_; |
+ $address = AddressSub($address, $offset); |
+ $address =~ s/^0x//; |
+ $address =~ s/^0*//; |
+ return $address; |
+} |
+ |
##### Miscellaneous ##### |
# Find the right versions of the above object tools to use. The |