1#! /usr/bin/env perl
2
3# Copyright (c) 1998-2007, Google Inc.
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions are
8# met:
9#
10#     * Redistributions of source code must retain the above copyright
11# notice, this list of conditions and the following disclaimer.
12#     * Redistributions in binary form must reproduce the above
13# copyright notice, this list of conditions and the following disclaimer
14# in the documentation and/or other materials provided with the
15# distribution.
16#     * Neither the name of Google Inc. nor the names of its
17# contributors may be used to endorse or promote products derived from
18# this software without specific prior written permission.
19#
20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32# ---
33# Program for printing the profile generated by common/profiler.cc,
34# or by the heap profiler (common/debugallocation.cc)
35#
36# The profile contains a sequence of entries of the form:
37#       <count> <stack trace>
38# This program parses the profile, and generates user-readable
39# output.
40#
41# Examples:
42#
43# % tools/jeprof "program" "profile"
44#   Enters "interactive" mode
45#
46# % tools/jeprof --text "program" "profile"
47#   Generates one line per procedure
48#
49# % tools/jeprof --gv "program" "profile"
50#   Generates annotated call-graph and displays via "gv"
51#
52# % tools/jeprof --gv --focus=Mutex "program" "profile"
53#   Restrict to code paths that involve an entry that matches "Mutex"
54#
55# % tools/jeprof --gv --focus=Mutex --ignore=string "program" "profile"
56#   Restrict to code paths that involve an entry that matches "Mutex"
57#   and does not match "string"
58#
59# % tools/jeprof --list=IBF_CheckDocid "program" "profile"
60#   Generates disassembly listing of all routines with at least one
61#   sample that match the --list=<regexp> pattern.  The listing is
62#   annotated with the flat and cumulative sample counts at each line.
63#
64# % tools/jeprof --disasm=IBF_CheckDocid "program" "profile"
65#   Generates disassembly listing of all routines with at least one
66#   sample that match the --disasm=<regexp> pattern.  The listing is
67#   annotated with the flat and cumulative sample counts at each PC value.
68#
69# TODO: Use color to indicate files?
70
71use strict;
72use warnings;
73use Getopt::Long;
74use Cwd;
75
76my $JEPROF_VERSION = "@jemalloc_version@";
77my $PPROF_VERSION = "2.0";
78
79# These are the object tools we use which can come from a
80# user-specified location using --tools, from the JEPROF_TOOLS
81# environment variable, or from the environment.
82my %obj_tool_map = (
83  "objdump" => "objdump",
84  "nm" => "nm",
85  "addr2line" => "addr2line",
86  "c++filt" => "c++filt",
87  ## ConfigureObjTools may add architecture-specific entries:
88  #"nm_pdb" => "nm-pdb",       # for reading windows (PDB-format) executables
89  #"addr2line_pdb" => "addr2line-pdb",                                # ditto
90  #"otool" => "otool",         # equivalent of objdump on OS X
91);
92# NOTE: these are lists, so you can put in commandline flags if you want.
93my @DOT = ("dot");          # leave non-absolute, since it may be in /usr/local
94my @GV = ("gv");
95my @EVINCE = ("evince");    # could also be xpdf or perhaps acroread
96my @KCACHEGRIND = ("kcachegrind");
97my @PS2PDF = ("ps2pdf");
98# These are used for dynamic profiles
99my @URL_FETCHER = ("curl", "-s", "--fail");
100
101# These are the web pages that servers need to support for dynamic profiles
102my $HEAP_PAGE = "/pprof/heap";
103my $PROFILE_PAGE = "/pprof/profile";   # must support cgi-param "?seconds=#"
104my $PMUPROFILE_PAGE = "/pprof/pmuprofile(?:\\?.*)?"; # must support cgi-param
105                                                # ?seconds=#&event=x&period=n
106my $GROWTH_PAGE = "/pprof/growth";
107my $CONTENTION_PAGE = "/pprof/contention";
108my $WALL_PAGE = "/pprof/wall(?:\\?.*)?";  # accepts options like namefilter
109my $FILTEREDPROFILE_PAGE = "/pprof/filteredprofile(?:\\?.*)?";
110my $CENSUSPROFILE_PAGE = "/pprof/censusprofile(?:\\?.*)?"; # must support cgi-param
111                                                       # "?seconds=#",
112                                                       # "?tags_regexp=#" and
113                                                       # "?type=#".
114my $SYMBOL_PAGE = "/pprof/symbol";     # must support symbol lookup via POST
115my $PROGRAM_NAME_PAGE = "/pprof/cmdline";
116
117# These are the web pages that can be named on the command line.
118# All the alternatives must begin with /.
119my $PROFILES = "($HEAP_PAGE|$PROFILE_PAGE|$PMUPROFILE_PAGE|" .
120               "$GROWTH_PAGE|$CONTENTION_PAGE|$WALL_PAGE|" .
121               "$FILTEREDPROFILE_PAGE|$CENSUSPROFILE_PAGE)";
122
123# default binary name
124my $UNKNOWN_BINARY = "(unknown)";
125
126# There is a pervasive dependency on the length (in hex characters,
127# i.e., nibbles) of an address, distinguishing between 32-bit and
128# 64-bit profiles.  To err on the safe size, default to 64-bit here:
129my $address_length = 16;
130
131my $dev_null = "/dev/null";
132if (! -e $dev_null && $^O =~ /MSWin/) {    # $^O is the OS perl was built for
133  $dev_null = "nul";
134}
135
136# A list of paths to search for shared object files
137my @prefix_list = ();
138
139# Special routine name that should not have any symbols.
140# Used as separator to parse "addr2line -i" output.
141my $sep_symbol = '_fini';
142my $sep_address = undef;
143
144##### Argument parsing #####
145
146sub usage_string {
147  return <<EOF;
148Usage:
149jeprof [options] <program> <profiles>
150   <profiles> is a space separated list of profile names.
151jeprof [options] <symbolized-profiles>
152   <symbolized-profiles> is a list of profile files where each file contains
153   the necessary symbol mappings  as well as profile data (likely generated
154   with --raw).
155jeprof [options] <profile>
156   <profile> is a remote form.  Symbols are obtained from host:port$SYMBOL_PAGE
157
158   Each name can be:
159   /path/to/profile        - a path to a profile file
160   host:port[/<service>]   - a location of a service to get profile from
161
162   The /<service> can be $HEAP_PAGE, $PROFILE_PAGE, /pprof/pmuprofile,
163                         $GROWTH_PAGE, $CONTENTION_PAGE, /pprof/wall,
164                         $CENSUSPROFILE_PAGE, or /pprof/filteredprofile.
165   For instance:
166     jeprof http://myserver.com:80$HEAP_PAGE
167   If /<service> is omitted, the service defaults to $PROFILE_PAGE (cpu profiling).
168jeprof --symbols <program>
169   Maps addresses to symbol names.  In this mode, stdin should be a
170   list of library mappings, in the same format as is found in the heap-
171   and cpu-profile files (this loosely matches that of /proc/self/maps
172   on linux), followed by a list of hex addresses to map, one per line.
173
174   For more help with querying remote servers, including how to add the
175   necessary server-side support code, see this filename (or one like it):
176
177   /usr/doc/gperftools-$PPROF_VERSION/pprof_remote_servers.html
178
179Options:
180   --cum               Sort by cumulative data
181   --base=<base>       Subtract <base> from <profile> before display
182   --interactive       Run in interactive mode (interactive "help" gives help) [default]
183   --seconds=<n>       Length of time for dynamic profiles [default=30 secs]
184   --add_lib=<file>    Read additional symbols and line info from the given library
185   --lib_prefix=<dir>  Comma separated list of library path prefixes
186
187Reporting Granularity:
188   --addresses         Report at address level
189   --lines             Report at source line level
190   --functions         Report at function level [default]
191   --files             Report at source file level
192
193Output type:
194   --text              Generate text report
195   --callgrind         Generate callgrind format to stdout
196   --gv                Generate Postscript and display
197   --evince            Generate PDF and display
198   --web               Generate SVG and display
199   --list=<regexp>     Generate source listing of matching routines
200   --disasm=<regexp>   Generate disassembly of matching routines
201   --symbols           Print demangled symbol names found at given addresses
202   --dot               Generate DOT file to stdout
203   --ps                Generate Postcript to stdout
204   --pdf               Generate PDF to stdout
205   --svg               Generate SVG to stdout
206   --gif               Generate GIF to stdout
207   --raw               Generate symbolized jeprof data (useful with remote fetch)
208
209Heap-Profile Options:
210   --inuse_space       Display in-use (mega)bytes [default]
211   --inuse_objects     Display in-use objects
212   --alloc_space       Display allocated (mega)bytes
213   --alloc_objects     Display allocated objects
214   --show_bytes        Display space in bytes
215   --drop_negative     Ignore negative differences
216
217Contention-profile options:
218   --total_delay       Display total delay at each region [default]
219   --contentions       Display number of delays at each region
220   --mean_delay        Display mean delay at each region
221
222Call-graph Options:
223   --nodecount=<n>     Show at most so many nodes [default=80]
224   --nodefraction=<f>  Hide nodes below <f>*total [default=.005]
225   --edgefraction=<f>  Hide edges below <f>*total [default=.001]
226   --maxdegree=<n>     Max incoming/outgoing edges per node [default=8]
227   --focus=<regexp>    Focus on backtraces with nodes matching <regexp>
228   --thread=<n>        Show profile for thread <n>
229   --ignore=<regexp>   Ignore backtraces with nodes matching <regexp>
230   --scale=<n>         Set GV scaling [default=0]
231   --heapcheck         Make nodes with non-0 object counts
232                       (i.e. direct leak generators) more visible
233   --retain=<regexp>   Retain only nodes that match <regexp>
234   --exclude=<regexp>  Exclude all nodes that match <regexp>
235
236Miscellaneous:
237   --tools=<prefix or binary:fullpath>[,...]   \$PATH for object tool pathnames
238   --test              Run unit tests
239   --help              This message
240   --version           Version information
241
242Environment Variables:
243   JEPROF_TMPDIR        Profiles directory. Defaults to \$HOME/jeprof
244   JEPROF_TOOLS         Prefix for object tools pathnames
245
246Examples:
247
248jeprof /bin/ls ls.prof
249                       Enters "interactive" mode
250jeprof --text /bin/ls ls.prof
251                       Outputs one line per procedure
252jeprof --web /bin/ls ls.prof
253                       Displays annotated call-graph in web browser
254jeprof --gv /bin/ls ls.prof
255                       Displays annotated call-graph via 'gv'
256jeprof --gv --focus=Mutex /bin/ls ls.prof
257                       Restricts to code paths including a .*Mutex.* entry
258jeprof --gv --focus=Mutex --ignore=string /bin/ls ls.prof
259                       Code paths including Mutex but not string
260jeprof --list=getdir /bin/ls ls.prof
261                       (Per-line) annotated source listing for getdir()
262jeprof --disasm=getdir /bin/ls ls.prof
263                       (Per-PC) annotated disassembly for getdir()
264
265jeprof http://localhost:1234/
266                       Enters "interactive" mode
267jeprof --text localhost:1234
268                       Outputs one line per procedure for localhost:1234
269jeprof --raw localhost:1234 > ./local.raw
270jeprof --text ./local.raw
271                       Fetches a remote profile for later analysis and then
272                       analyzes it in text mode.
273EOF
274}
275
276sub version_string {
277  return <<EOF
278jeprof (part of jemalloc $JEPROF_VERSION)
279based on pprof (part of gperftools $PPROF_VERSION)
280
281Copyright 1998-2007 Google Inc.
282
283This is BSD licensed software; see the source for copying conditions
284and license information.
285There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
286PARTICULAR PURPOSE.
287EOF
288}
289
290sub usage {
291  my $msg = shift;
292  print STDERR "$msg\n\n";
293  print STDERR usage_string();
294  print STDERR "\nFATAL ERROR: $msg\n";    # just as a reminder
295  exit(1);
296}
297
298sub Init() {
299  # Setup tmp-file name and handler to clean it up.
300  # We do this in the very beginning so that we can use
301  # error() and cleanup() function anytime here after.
302  $main::tmpfile_sym = "/tmp/jeprof$$.sym";
303  $main::tmpfile_ps = "/tmp/jeprof$$";
304  $main::next_tmpfile = 0;
305  $SIG{'INT'} = \&sighandler;
306
307  # Cache from filename/linenumber to source code
308  $main::source_cache = ();
309
310  $main::opt_help = 0;
311  $main::opt_version = 0;
312
313  $main::opt_cum = 0;
314  $main::opt_base = '';
315  $main::opt_addresses = 0;
316  $main::opt_lines = 0;
317  $main::opt_functions = 0;
318  $main::opt_files = 0;
319  $main::opt_lib_prefix = "";
320
321  $main::opt_text = 0;
322  $main::opt_callgrind = 0;
323  $main::opt_list = "";
324  $main::opt_disasm = "";
325  $main::opt_symbols = 0;
326  $main::opt_gv = 0;
327  $main::opt_evince = 0;
328  $main::opt_web = 0;
329  $main::opt_dot = 0;
330  $main::opt_ps = 0;
331  $main::opt_pdf = 0;
332  $main::opt_gif = 0;
333  $main::opt_svg = 0;
334  $main::opt_raw = 0;
335
336  $main::opt_nodecount = 80;
337  $main::opt_nodefraction = 0.005;
338  $main::opt_edgefraction = 0.001;
339  $main::opt_maxdegree = 8;
340  $main::opt_focus = '';
341  $main::opt_thread = undef;
342  $main::opt_ignore = '';
343  $main::opt_scale = 0;
344  $main::opt_heapcheck = 0;
345  $main::opt_retain = '';
346  $main::opt_exclude = '';
347  $main::opt_seconds = 30;
348  $main::opt_lib = "";
349
350  $main::opt_inuse_space   = 0;
351  $main::opt_inuse_objects = 0;
352  $main::opt_alloc_space   = 0;
353  $main::opt_alloc_objects = 0;
354  $main::opt_show_bytes    = 0;
355  $main::opt_drop_negative = 0;
356  $main::opt_interactive   = 0;
357
358  $main::opt_total_delay = 0;
359  $main::opt_contentions = 0;
360  $main::opt_mean_delay = 0;
361
362  $main::opt_tools   = "";
363  $main::opt_debug   = 0;
364  $main::opt_test    = 0;
365
366  # These are undocumented flags used only by unittests.
367  $main::opt_test_stride = 0;
368
369  # Are we using $SYMBOL_PAGE?
370  $main::use_symbol_page = 0;
371
372  # Files returned by TempName.
373  %main::tempnames = ();
374
375  # Type of profile we are dealing with
376  # Supported types:
377  #     cpu
378  #     heap
379  #     growth
380  #     contention
381  $main::profile_type = '';     # Empty type means "unknown"
382
383  GetOptions("help!"          => \$main::opt_help,
384             "version!"       => \$main::opt_version,
385             "cum!"           => \$main::opt_cum,
386             "base=s"         => \$main::opt_base,
387             "seconds=i"      => \$main::opt_seconds,
388             "add_lib=s"      => \$main::opt_lib,
389             "lib_prefix=s"   => \$main::opt_lib_prefix,
390             "functions!"     => \$main::opt_functions,
391             "lines!"         => \$main::opt_lines,
392             "addresses!"     => \$main::opt_addresses,
393             "files!"         => \$main::opt_files,
394             "text!"          => \$main::opt_text,
395             "callgrind!"     => \$main::opt_callgrind,
396             "list=s"         => \$main::opt_list,
397             "disasm=s"       => \$main::opt_disasm,
398             "symbols!"       => \$main::opt_symbols,
399             "gv!"            => \$main::opt_gv,
400             "evince!"        => \$main::opt_evince,
401             "web!"           => \$main::opt_web,
402             "dot!"           => \$main::opt_dot,
403             "ps!"            => \$main::opt_ps,
404             "pdf!"           => \$main::opt_pdf,
405             "svg!"           => \$main::opt_svg,
406             "gif!"           => \$main::opt_gif,
407             "raw!"           => \$main::opt_raw,
408             "interactive!"   => \$main::opt_interactive,
409             "nodecount=i"    => \$main::opt_nodecount,
410             "nodefraction=f" => \$main::opt_nodefraction,
411             "edgefraction=f" => \$main::opt_edgefraction,
412             "maxdegree=i"    => \$main::opt_maxdegree,
413             "focus=s"        => \$main::opt_focus,
414             "thread=s"       => \$main::opt_thread,
415             "ignore=s"       => \$main::opt_ignore,
416             "scale=i"        => \$main::opt_scale,
417             "heapcheck"      => \$main::opt_heapcheck,
418             "retain=s"       => \$main::opt_retain,
419             "exclude=s"      => \$main::opt_exclude,
420             "inuse_space!"   => \$main::opt_inuse_space,
421             "inuse_objects!" => \$main::opt_inuse_objects,
422             "alloc_space!"   => \$main::opt_alloc_space,
423             "alloc_objects!" => \$main::opt_alloc_objects,
424             "show_bytes!"    => \$main::opt_show_bytes,
425             "drop_negative!" => \$main::opt_drop_negative,
426             "total_delay!"   => \$main::opt_total_delay,
427             "contentions!"   => \$main::opt_contentions,
428             "mean_delay!"    => \$main::opt_mean_delay,
429             "tools=s"        => \$main::opt_tools,
430             "test!"          => \$main::opt_test,
431             "debug!"         => \$main::opt_debug,
432             # Undocumented flags used only by unittests:
433             "test_stride=i"  => \$main::opt_test_stride,
434      ) || usage("Invalid option(s)");
435
436  # Deal with the standard --help and --version
437  if ($main::opt_help) {
438    print usage_string();
439    exit(0);
440  }
441
442  if ($main::opt_version) {
443    print version_string();
444    exit(0);
445  }
446
447  # Disassembly/listing/symbols mode requires address-level info
448  if ($main::opt_disasm || $main::opt_list || $main::opt_symbols) {
449    $main::opt_functions = 0;
450    $main::opt_lines = 0;
451    $main::opt_addresses = 1;
452    $main::opt_files = 0;
453  }
454
455  # Check heap-profiling flags
456  if ($main::opt_inuse_space +
457      $main::opt_inuse_objects +
458      $main::opt_alloc_space +
459      $main::opt_alloc_objects > 1) {
460    usage("Specify at most on of --inuse/--alloc options");
461  }
462
463  # Check output granularities
464  my $grains =
465      $main::opt_functions +
466      $main::opt_lines +
467      $main::opt_addresses +
468      $main::opt_files +
469      0;
470  if ($grains > 1) {
471    usage("Only specify one output granularity option");
472  }
473  if ($grains == 0) {
474    $main::opt_functions = 1;
475  }
476
477  # Check output modes
478  my $modes =
479      $main::opt_text +
480      $main::opt_callgrind +
481      ($main::opt_list eq '' ? 0 : 1) +
482      ($main::opt_disasm eq '' ? 0 : 1) +
483      ($main::opt_symbols == 0 ? 0 : 1) +
484      $main::opt_gv +
485      $main::opt_evince +
486      $main::opt_web +
487      $main::opt_dot +
488      $main::opt_ps +
489      $main::opt_pdf +
490      $main::opt_svg +
491      $main::opt_gif +
492      $main::opt_raw +
493      $main::opt_interactive +
494      0;
495  if ($modes > 1) {
496    usage("Only specify one output mode");
497  }
498  if ($modes == 0) {
499    if (-t STDOUT) {  # If STDOUT is a tty, activate interactive mode
500      $main::opt_interactive = 1;
501    } else {
502      $main::opt_text = 1;
503    }
504  }
505
506  if ($main::opt_test) {
507    RunUnitTests();
508    # Should not return
509    exit(1);
510  }
511
512  # Binary name and profile arguments list
513  $main::prog = "";
514  @main::pfile_args = ();
515
516  # Remote profiling without a binary (using $SYMBOL_PAGE instead)
517  if (@ARGV > 0) {
518    if (IsProfileURL($ARGV[0])) {
519      $main::use_symbol_page = 1;
520    } elsif (IsSymbolizedProfileFile($ARGV[0])) {
521      $main::use_symbolized_profile = 1;
522      $main::prog = $UNKNOWN_BINARY;  # will be set later from the profile file
523    }
524  }
525
526  if ($main::use_symbol_page || $main::use_symbolized_profile) {
527    # We don't need a binary!
528    my %disabled = ('--lines' => $main::opt_lines,
529                    '--disasm' => $main::opt_disasm);
530    for my $option (keys %disabled) {
531      usage("$option cannot be used without a binary") if $disabled{$option};
532    }
533    # Set $main::prog later...
534    scalar(@ARGV) || usage("Did not specify profile file");
535  } elsif ($main::opt_symbols) {
536    # --symbols needs a binary-name (to run nm on, etc) but not profiles
537    $main::prog = shift(@ARGV) || usage("Did not specify program");
538  } else {
539    $main::prog = shift(@ARGV) || usage("Did not specify program");
540    scalar(@ARGV) || usage("Did not specify profile file");
541  }
542
543  # Parse profile file/location arguments
544  foreach my $farg (@ARGV) {
545    if ($farg =~ m/(.*)\@([0-9]+)(|\/.*)$/ ) {
546      my $machine = $1;
547      my $num_machines = $2;
548      my $path = $3;
549      for (my $i = 0; $i < $num_machines; $i++) {
550        unshift(@main::pfile_args, "$i.$machine$path");
551      }
552    } else {
553      unshift(@main::pfile_args, $farg);
554    }
555  }
556
557  if ($main::use_symbol_page) {
558    unless (IsProfileURL($main::pfile_args[0])) {
559      error("The first profile should be a remote form to use $SYMBOL_PAGE\n");
560    }
561    CheckSymbolPage();
562    $main::prog = FetchProgramName();
563  } elsif (!$main::use_symbolized_profile) {  # may not need objtools!
564    ConfigureObjTools($main::prog)
565  }
566
567  # Break the opt_lib_prefix into the prefix_list array
568  @prefix_list = split (',', $main::opt_lib_prefix);
569
570  # Remove trailing / from the prefixes, in the list to prevent
571  # searching things like /my/path//lib/mylib.so
572  foreach (@prefix_list) {
573    s|/+$||;
574  }
575}
576
577sub FilterAndPrint {
578  my ($profile, $symbols, $libs, $thread) = @_;
579
580  # Get total data in profile
581  my $total = TotalProfile($profile);
582
583  # Remove uniniteresting stack items
584  $profile = RemoveUninterestingFrames($symbols, $profile);
585
586  # Focus?
587  if ($main::opt_focus ne '') {
588    $profile = FocusProfile($symbols, $profile, $main::opt_focus);
589  }
590
591  # Ignore?
592  if ($main::opt_ignore ne '') {
593    $profile = IgnoreProfile($symbols, $profile, $main::opt_ignore);
594  }
595
596  my $calls = ExtractCalls($symbols, $profile);
597
598  # Reduce profiles to required output granularity, and also clean
599  # each stack trace so a given entry exists at most once.
600  my $reduced = ReduceProfile($symbols, $profile);
601
602  # Get derived profiles
603  my $flat = FlatProfile($reduced);
604  my $cumulative = CumulativeProfile($reduced);
605
606  # Print
607  if (!$main::opt_interactive) {
608    if ($main::opt_disasm) {
609      PrintDisassembly($libs, $flat, $cumulative, $main::opt_disasm);
610    } elsif ($main::opt_list) {
611      PrintListing($total, $libs, $flat, $cumulative, $main::opt_list, 0);
612    } elsif ($main::opt_text) {
613      # Make sure the output is empty when have nothing to report
614      # (only matters when --heapcheck is given but we must be
615      # compatible with old branches that did not pass --heapcheck always):
616      if ($total != 0) {
617        printf("Total%s: %s %s\n",
618               (defined($thread) ? " (t$thread)" : ""),
619               Unparse($total), Units());
620      }
621      PrintText($symbols, $flat, $cumulative, -1);
622    } elsif ($main::opt_raw) {
623      PrintSymbolizedProfile($symbols, $profile, $main::prog);
624    } elsif ($main::opt_callgrind) {
625      PrintCallgrind($calls);
626    } else {
627      if (PrintDot($main::prog, $symbols, $profile, $flat, $cumulative, $total)) {
628        if ($main::opt_gv) {
629          RunGV(TempName($main::next_tmpfile, "ps"), "");
630        } elsif ($main::opt_evince) {
631          RunEvince(TempName($main::next_tmpfile, "pdf"), "");
632        } elsif ($main::opt_web) {
633          my $tmp = TempName($main::next_tmpfile, "svg");
634          RunWeb($tmp);
635          # The command we run might hand the file name off
636          # to an already running browser instance and then exit.
637          # Normally, we'd remove $tmp on exit (right now),
638          # but fork a child to remove $tmp a little later, so that the
639          # browser has time to load it first.
640          delete $main::tempnames{$tmp};
641          if (fork() == 0) {
642            sleep 5;
643            unlink($tmp);
644            exit(0);
645          }
646        }
647      } else {
648        cleanup();
649        exit(1);
650      }
651    }
652  } else {
653    InteractiveMode($profile, $symbols, $libs, $total);
654  }
655}
656
657sub Main() {
658  Init();
659  $main::collected_profile = undef;
660  @main::profile_files = ();
661  $main::op_time = time();
662
663  # Printing symbols is special and requires a lot less info that most.
664  if ($main::opt_symbols) {
665    PrintSymbols(*STDIN);   # Get /proc/maps and symbols output from stdin
666    return;
667  }
668
669  # Fetch all profile data
670  FetchDynamicProfiles();
671
672  # this will hold symbols that we read from the profile files
673  my $symbol_map = {};
674
675  # Read one profile, pick the last item on the list
676  my $data = ReadProfile($main::prog, pop(@main::profile_files));
677  my $profile = $data->{profile};
678  my $pcs = $data->{pcs};
679  my $libs = $data->{libs};   # Info about main program and shared libraries
680  $symbol_map = MergeSymbols($symbol_map, $data->{symbols});
681
682  # Add additional profiles, if available.
683  if (scalar(@main::profile_files) > 0) {
684    foreach my $pname (@main::profile_files) {
685      my $data2 = ReadProfile($main::prog, $pname);
686      $profile = AddProfile($profile, $data2->{profile});
687      $pcs = AddPcs($pcs, $data2->{pcs});
688      $symbol_map = MergeSymbols($symbol_map, $data2->{symbols});
689    }
690  }
691
692  # Subtract base from profile, if specified
693  if ($main::opt_base ne '') {
694    my $base = ReadProfile($main::prog, $main::opt_base);
695    $profile = SubtractProfile($profile, $base->{profile});
696    $pcs = AddPcs($pcs, $base->{pcs});
697    $symbol_map = MergeSymbols($symbol_map, $base->{symbols});
698  }
699
700  # Collect symbols
701  my $symbols;
702  if ($main::use_symbolized_profile) {
703    $symbols = FetchSymbols($pcs, $symbol_map);
704  } elsif ($main::use_symbol_page) {
705    $symbols = FetchSymbols($pcs);
706  } else {
707    # TODO(csilvers): $libs uses the /proc/self/maps data from profile1,
708    # which may differ from the data from subsequent profiles, especially
709    # if they were run on different machines.  Use appropriate libs for
710    # each pc somehow.
711    $symbols = ExtractSymbols($libs, $pcs);
712  }
713
714  if (!defined($main::opt_thread)) {
715    FilterAndPrint($profile, $symbols, $libs);
716  }
717  if (defined($data->{threads})) {
718    foreach my $thread (sort { $a <=> $b } keys(%{$data->{threads}})) {
719      if (defined($main::opt_thread) &&
720          ($main::opt_thread eq '*' || $main::opt_thread == $thread)) {
721        my $thread_profile = $data->{threads}{$thread};
722        FilterAndPrint($thread_profile, $symbols, $libs, $thread);
723      }
724    }
725  }
726
727  cleanup();
728  exit(0);
729}
730
731##### Entry Point #####
732
733Main();
734
735# Temporary code to detect if we're running on a Goobuntu system.
736# These systems don't have the right stuff installed for the special
737# Readline libraries to work, so as a temporary workaround, we default
738# to using the normal stdio code, rather than the fancier readline-based
739# code
740sub ReadlineMightFail {
741  if (-e '/lib/libtermcap.so.2') {
742    return 0;  # libtermcap exists, so readline should be okay
743  } else {
744    return 1;
745  }
746}
747
748sub RunGV {
749  my $fname = shift;
750  my $bg = shift;       # "" or " &" if we should run in background
751  if (!system(ShellEscape(@GV, "--version") . " >$dev_null 2>&1")) {
752    # Options using double dash are supported by this gv version.
753    # Also, turn on noantialias to better handle bug in gv for
754    # postscript files with large dimensions.
755    # TODO: Maybe we should not pass the --noantialias flag
756    # if the gv version is known to work properly without the flag.
757    system(ShellEscape(@GV, "--scale=$main::opt_scale", "--noantialias", $fname)
758           . $bg);
759  } else {
760    # Old gv version - only supports options that use single dash.
761    print STDERR ShellEscape(@GV, "-scale", $main::opt_scale) . "\n";
762    system(ShellEscape(@GV, "-scale", "$main::opt_scale", $fname) . $bg);
763  }
764}
765
766sub RunEvince {
767  my $fname = shift;
768  my $bg = shift;       # "" or " &" if we should run in background
769  system(ShellEscape(@EVINCE, $fname) . $bg);
770}
771
772sub RunWeb {
773  my $fname = shift;
774  print STDERR "Loading web page file:///$fname\n";
775
776  if (`uname` =~ /Darwin/) {
777    # OS X: open will use standard preference for SVG files.
778    system("/usr/bin/open", $fname);
779    return;
780  }
781
782  # Some kind of Unix; try generic symlinks, then specific browsers.
783  # (Stop once we find one.)
784  # Works best if the browser is already running.
785  my @alt = (
786    "/etc/alternatives/gnome-www-browser",
787    "/etc/alternatives/x-www-browser",
788    "google-chrome",
789    "firefox",
790  );
791  foreach my $b (@alt) {
792    if (system($b, $fname) == 0) {
793      return;
794    }
795  }
796
797  print STDERR "Could not load web browser.\n";
798}
799
800sub RunKcachegrind {
801  my $fname = shift;
802  my $bg = shift;       # "" or " &" if we should run in background
803  print STDERR "Starting '@KCACHEGRIND " . $fname . $bg . "'\n";
804  system(ShellEscape(@KCACHEGRIND, $fname) . $bg);
805}
806
807
808##### Interactive helper routines #####
809
810sub InteractiveMode {
811  $| = 1;  # Make output unbuffered for interactive mode
812  my ($orig_profile, $symbols, $libs, $total) = @_;
813
814  print STDERR "Welcome to jeprof!  For help, type 'help'.\n";
815
816  # Use ReadLine if it's installed and input comes from a console.
817  if ( -t STDIN &&
818       !ReadlineMightFail() &&
819       defined(eval {require Term::ReadLine}) ) {
820    my $term = new Term::ReadLine 'jeprof';
821    while ( defined ($_ = $term->readline('(jeprof) '))) {
822      $term->addhistory($_) if /\S/;
823      if (!InteractiveCommand($orig_profile, $symbols, $libs, $total, $_)) {
824        last;    # exit when we get an interactive command to quit
825      }
826    }
827  } else {       # don't have readline
828    while (1) {
829      print STDERR "(jeprof) ";
830      $_ = <STDIN>;
831      last if ! defined $_ ;
832      s/\r//g;         # turn windows-looking lines into unix-looking lines
833
834      # Save some flags that might be reset by InteractiveCommand()
835      my $save_opt_lines = $main::opt_lines;
836
837      if (!InteractiveCommand($orig_profile, $symbols, $libs, $total, $_)) {
838        last;    # exit when we get an interactive command to quit
839      }
840
841      # Restore flags
842      $main::opt_lines = $save_opt_lines;
843    }
844  }
845}
846
847# Takes two args: orig profile, and command to run.
848# Returns 1 if we should keep going, or 0 if we were asked to quit
849sub InteractiveCommand {
850  my($orig_profile, $symbols, $libs, $total, $command) = @_;
851  $_ = $command;                # just to make future m//'s easier
852  if (!defined($_)) {
853    print STDERR "\n";
854    return 0;
855  }
856  if (m/^\s*quit/) {
857    return 0;
858  }
859  if (m/^\s*help/) {
860    InteractiveHelpMessage();
861    return 1;
862  }
863  # Clear all the mode options -- mode is controlled by "$command"
864  $main::opt_text = 0;
865  $main::opt_callgrind = 0;
866  $main::opt_disasm = 0;
867  $main::opt_list = 0;
868  $main::opt_gv = 0;
869  $main::opt_evince = 0;
870  $main::opt_cum = 0;
871
872  if (m/^\s*(text|top)(\d*)\s*(.*)/) {
873    $main::opt_text = 1;
874
875    my $line_limit = ($2 ne "") ? int($2) : 10;
876
877    my $routine;
878    my $ignore;
879    ($routine, $ignore) = ParseInteractiveArgs($3);
880
881    my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
882    my $reduced = ReduceProfile($symbols, $profile);
883
884    # Get derived profiles
885    my $flat = FlatProfile($reduced);
886    my $cumulative = CumulativeProfile($reduced);
887
888    PrintText($symbols, $flat, $cumulative, $line_limit);
889    return 1;
890  }
891  if (m/^\s*callgrind\s*([^ \n]*)/) {
892    $main::opt_callgrind = 1;
893
894    # Get derived profiles
895    my $calls = ExtractCalls($symbols, $orig_profile);
896    my $filename = $1;
897    if ( $1 eq '' ) {
898      $filename = TempName($main::next_tmpfile, "callgrind");
899    }
900    PrintCallgrind($calls, $filename);
901    if ( $1 eq '' ) {
902      RunKcachegrind($filename, " & ");
903      $main::next_tmpfile++;
904    }
905
906    return 1;
907  }
908  if (m/^\s*(web)?list\s*(.+)/) {
909    my $html = (defined($1) && ($1 eq "web"));
910    $main::opt_list = 1;
911
912    my $routine;
913    my $ignore;
914    ($routine, $ignore) = ParseInteractiveArgs($2);
915
916    my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
917    my $reduced = ReduceProfile($symbols, $profile);
918
919    # Get derived profiles
920    my $flat = FlatProfile($reduced);
921    my $cumulative = CumulativeProfile($reduced);
922
923    PrintListing($total, $libs, $flat, $cumulative, $routine, $html);
924    return 1;
925  }
926  if (m/^\s*disasm\s*(.+)/) {
927    $main::opt_disasm = 1;
928
929    my $routine;
930    my $ignore;
931    ($routine, $ignore) = ParseInteractiveArgs($1);
932
933    # Process current profile to account for various settings
934    my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
935    my $reduced = ReduceProfile($symbols, $profile);
936
937    # Get derived profiles
938    my $flat = FlatProfile($reduced);
939    my $cumulative = CumulativeProfile($reduced);
940
941    PrintDisassembly($libs, $flat, $cumulative, $routine);
942    return 1;
943  }
944  if (m/^\s*(gv|web|evince)\s*(.*)/) {
945    $main::opt_gv = 0;
946    $main::opt_evince = 0;
947    $main::opt_web = 0;
948    if ($1 eq "gv") {
949      $main::opt_gv = 1;
950    } elsif ($1 eq "evince") {
951      $main::opt_evince = 1;
952    } elsif ($1 eq "web") {
953      $main::opt_web = 1;
954    }
955
956    my $focus;
957    my $ignore;
958    ($focus, $ignore) = ParseInteractiveArgs($2);
959
960    # Process current profile to account for various settings
961    my $profile = ProcessProfile($total, $orig_profile, $symbols,
962                                 $focus, $ignore);
963    my $reduced = ReduceProfile($symbols, $profile);
964
965    # Get derived profiles
966    my $flat = FlatProfile($reduced);
967    my $cumulative = CumulativeProfile($reduced);
968
969    if (PrintDot($main::prog, $symbols, $profile, $flat, $cumulative, $total)) {
970      if ($main::opt_gv) {
971        RunGV(TempName($main::next_tmpfile, "ps"), " &");
972      } elsif ($main::opt_evince) {
973        RunEvince(TempName($main::next_tmpfile, "pdf"), " &");
974      } elsif ($main::opt_web) {
975        RunWeb(TempName($main::next_tmpfile, "svg"));
976      }
977      $main::next_tmpfile++;
978    }
979    return 1;
980  }
981  if (m/^\s*$/) {
982    return 1;
983  }
984  print STDERR "Unknown command: try 'help'.\n";
985  return 1;
986}
987
988
989sub ProcessProfile {
990  my $total_count = shift;
991  my $orig_profile = shift;
992  my $symbols = shift;
993  my $focus = shift;
994  my $ignore = shift;
995
996  # Process current profile to account for various settings
997  my $profile = $orig_profile;
998  printf("Total: %s %s\n", Unparse($total_count), Units());
999  if ($focus ne '') {
1000    $profile = FocusProfile($symbols, $profile, $focus);
1001    my $focus_count = TotalProfile($profile);
1002    printf("After focusing on '%s': %s %s of %s (%0.1f%%)\n",
1003           $focus,
1004           Unparse($focus_count), Units(),
1005           Unparse($total_count), ($focus_count*100.0) / $total_count);
1006  }
1007  if ($ignore ne '') {
1008    $profile = IgnoreProfile($symbols, $profile, $ignore);
1009    my $ignore_count = TotalProfile($profile);
1010    printf("After ignoring '%s': %s %s of %s (%0.1f%%)\n",
1011           $ignore,
1012           Unparse($ignore_count), Units(),
1013           Unparse($total_count),
1014           ($ignore_count*100.0) / $total_count);
1015  }
1016
1017  return $profile;
1018}
1019
1020sub InteractiveHelpMessage {
1021  print STDERR <<ENDOFHELP;
1022Interactive jeprof mode
1023
1024Commands:
1025  gv
1026  gv [focus] [-ignore1] [-ignore2]
1027      Show graphical hierarchical display of current profile.  Without
1028      any arguments, shows all samples in the profile.  With the optional
1029      "focus" argument, restricts the samples shown to just those where
1030      the "focus" regular expression matches a routine name on the stack
1031      trace.
1032
1033  web
1034  web [focus] [-ignore1] [-ignore2]
1035      Like GV, but displays profile in your web browser instead of using
1036      Ghostview. Works best if your web browser is already running.
1037      To change the browser that gets used:
1038      On Linux, set the /etc/alternatives/gnome-www-browser symlink.
1039      On OS X, change the Finder association for SVG files.
1040
1041  list [routine_regexp] [-ignore1] [-ignore2]
1042      Show source listing of routines whose names match "routine_regexp"
1043
1044  weblist [routine_regexp] [-ignore1] [-ignore2]
1045     Displays a source listing of routines whose names match "routine_regexp"
1046     in a web browser.  You can click on source lines to view the
1047     corresponding disassembly.
1048
1049  top [--cum] [-ignore1] [-ignore2]
1050  top20 [--cum] [-ignore1] [-ignore2]
1051  top37 [--cum] [-ignore1] [-ignore2]
1052      Show top lines ordered by flat profile count, or cumulative count
1053      if --cum is specified.  If a number is present after 'top', the
1054      top K routines will be shown (defaults to showing the top 10)
1055
1056  disasm [routine_regexp] [-ignore1] [-ignore2]
1057      Show disassembly of routines whose names match "routine_regexp",
1058      annotated with sample counts.
1059
1060  callgrind
1061  callgrind [filename]
1062      Generates callgrind file. If no filename is given, kcachegrind is called.
1063
1064  help - This listing
1065  quit or ^D - End jeprof
1066
1067For commands that accept optional -ignore tags, samples where any routine in
1068the stack trace matches the regular expression in any of the -ignore
1069parameters will be ignored.
1070
1071Further pprof details are available at this location (or one similar):
1072
1073 /usr/doc/gperftools-$PPROF_VERSION/cpu_profiler.html
1074 /usr/doc/gperftools-$PPROF_VERSION/heap_profiler.html
1075
1076ENDOFHELP
1077}
1078sub ParseInteractiveArgs {
1079  my $args = shift;
1080  my $focus = "";
1081  my $ignore = "";
1082  my @x = split(/ +/, $args);
1083  foreach $a (@x) {
1084    if ($a =~ m/^(--|-)lines$/) {
1085      $main::opt_lines = 1;
1086    } elsif ($a =~ m/^(--|-)cum$/) {
1087      $main::opt_cum = 1;
1088    } elsif ($a =~ m/^-(.*)/) {
1089      $ignore .= (($ignore ne "") ? "|" : "" ) . $1;
1090    } else {
1091      $focus .= (($focus ne "") ? "|" : "" ) . $a;
1092    }
1093  }
1094  if ($ignore ne "") {
1095    print STDERR "Ignoring samples in call stacks that match '$ignore'\n";
1096  }
1097  return ($focus, $ignore);
1098}
1099
1100##### Output code #####
1101
1102sub TempName {
1103  my $fnum = shift;
1104  my $ext = shift;
1105  my $file = "$main::tmpfile_ps.$fnum.$ext";
1106  $main::tempnames{$file} = 1;
1107  return $file;
1108}
1109
1110# Print profile data in packed binary format (64-bit) to standard out
1111sub PrintProfileData {
1112  my $profile = shift;
1113
1114  # print header (64-bit style)
1115  # (zero) (header-size) (version) (sample-period) (zero)
1116  print pack('L*', 0, 0, 3, 0, 0, 0, 1, 0, 0, 0);
1117
1118  foreach my $k (keys(%{$profile})) {
1119    my $count = $profile->{$k};
1120    my @addrs = split(/\n/, $k);
1121    if ($#addrs >= 0) {
1122      my $depth = $#addrs + 1;
1123      # int(foo / 2**32) is the only reliable way to get rid of bottom
1124      # 32 bits on both 32- and 64-bit systems.
1125      print pack('L*', $count & 0xFFFFFFFF, int($count / 2**32));
1126      print pack('L*', $depth & 0xFFFFFFFF, int($depth / 2**32));
1127
1128      foreach my $full_addr (@addrs) {
1129        my $addr = $full_addr;
1130        $addr =~ s/0x0*//;  # strip off leading 0x, zeroes
1131        if (length($addr) > 16) {
1132          print STDERR "Invalid address in profile: $full_addr\n";
1133          next;
1134        }
1135        my $low_addr = substr($addr, -8);       # get last 8 hex chars
1136        my $high_addr = substr($addr, -16, 8);  # get up to 8 more hex chars
1137        print pack('L*', hex('0x' . $low_addr), hex('0x' . $high_addr));
1138      }
1139    }
1140  }
1141}
1142
1143# Print symbols and profile data
1144sub PrintSymbolizedProfile {
1145  my $symbols = shift;
1146  my $profile = shift;
1147  my $prog = shift;
1148
1149  $SYMBOL_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
1150  my $symbol_marker = $&;
1151
1152  print '--- ', $symbol_marker, "\n";
1153  if (defined($prog)) {
1154    print 'binary=', $prog, "\n";
1155  }
1156  while (my ($pc, $name) = each(%{$symbols})) {
1157    my $sep = ' ';
1158    print '0x', $pc;
1159    # We have a list of function names, which include the inlined
1160    # calls.  They are separated (and terminated) by --, which is
1161    # illegal in function names.
1162    for (my $j = 2; $j <= $#{$name}; $j += 3) {
1163      print $sep, $name->[$j];
1164      $sep = '--';
1165    }
1166    print "\n";
1167  }
1168  print '---', "\n";
1169
1170  my $profile_marker;
1171  if ($main::profile_type eq 'heap') {
1172    $HEAP_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
1173    $profile_marker = $&;
1174  } elsif ($main::profile_type eq 'growth') {
1175    $GROWTH_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
1176    $profile_marker = $&;
1177  } elsif ($main::profile_type eq 'contention') {
1178    $CONTENTION_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
1179    $profile_marker = $&;
1180  } else { # elsif ($main::profile_type eq 'cpu')
1181    $PROFILE_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
1182    $profile_marker = $&;
1183  }
1184
1185  print '--- ', $profile_marker, "\n";
1186  if (defined($main::collected_profile)) {
1187    # if used with remote fetch, simply dump the collected profile to output.
1188    open(SRC, "<$main::collected_profile");
1189    while (<SRC>) {
1190      print $_;
1191    }
1192    close(SRC);
1193  } else {
1194    # --raw/http: For everything to work correctly for non-remote profiles, we
1195    # would need to extend PrintProfileData() to handle all possible profile
1196    # types, re-enable the code that is currently disabled in ReadCPUProfile()
1197    # and FixCallerAddresses(), and remove the remote profile dumping code in
1198    # the block above.
1199    die "--raw/http: jeprof can only dump remote profiles for --raw\n";
1200    # dump a cpu-format profile to standard out
1201    PrintProfileData($profile);
1202  }
1203}
1204
1205# Print text output
1206sub PrintText {
1207  my $symbols = shift;
1208  my $flat = shift;
1209  my $cumulative = shift;
1210  my $line_limit = shift;
1211
1212  my $total = TotalProfile($flat);
1213
1214  # Which profile to sort by?
1215  my $s = $main::opt_cum ? $cumulative : $flat;
1216
1217  my $running_sum = 0;
1218  my $lines = 0;
1219  foreach my $k (sort { GetEntry($s, $b) <=> GetEntry($s, $a) || $a cmp $b }
1220                 keys(%{$cumulative})) {
1221    my $f = GetEntry($flat, $k);
1222    my $c = GetEntry($cumulative, $k);
1223    $running_sum += $f;
1224
1225    my $sym = $k;
1226    if (exists($symbols->{$k})) {
1227      $sym = $symbols->{$k}->[0] . " " . $symbols->{$k}->[1];
1228      if ($main::opt_addresses) {
1229        $sym = $k . " " . $sym;
1230      }
1231    }
1232
1233    if ($f != 0 || $c != 0) {
1234      printf("%8s %6s %6s %8s %6s %s\n",
1235             Unparse($f),
1236             Percent($f, $total),
1237             Percent($running_sum, $total),
1238             Unparse($c),
1239             Percent($c, $total),
1240             $sym);
1241    }
1242    $lines++;
1243    last if ($line_limit >= 0 && $lines >= $line_limit);
1244  }
1245}
1246
1247# Callgrind format has a compression for repeated function and file
1248# names.  You show the name the first time, and just use its number
1249# subsequently.  This can cut down the file to about a third or a
1250# quarter of its uncompressed size.  $key and $val are the key/value
1251# pair that would normally be printed by callgrind; $map is a map from
1252# value to number.
1253sub CompressedCGName {
1254  my($key, $val, $map) = @_;
1255  my $idx = $map->{$val};
1256  # For very short keys, providing an index hurts rather than helps.
1257  if (length($val) <= 3) {
1258    return "$key=$val\n";
1259  } elsif (defined($idx)) {
1260    return "$key=($idx)\n";
1261  } else {
1262    # scalar(keys $map) gives the number of items in the map.
1263    $idx = scalar(keys(%{$map})) + 1;
1264    $map->{$val} = $idx;
1265    return "$key=($idx) $val\n";
1266  }
1267}
1268
1269# Print the call graph in a way that's suiteable for callgrind.
1270sub PrintCallgrind {
1271  my $calls = shift;
1272  my $filename;
1273  my %filename_to_index_map;
1274  my %fnname_to_index_map;
1275
1276  if ($main::opt_interactive) {
1277    $filename = shift;
1278    print STDERR "Writing callgrind file to '$filename'.\n"
1279  } else {
1280    $filename = "&STDOUT";
1281  }
1282  open(CG, ">$filename");
1283  printf CG ("events: Hits\n\n");
1284  foreach my $call ( map { $_->[0] }
1285                     sort { $a->[1] cmp $b ->[1] ||
1286                            $a->[2] <=> $b->[2] }
1287                     map { /([^:]+):(\d+):([^ ]+)( -> ([^:]+):(\d+):(.+))?/;
1288                           [$_, $1, $2] }
1289                     keys %$calls ) {
1290    my $count = int($calls->{$call});
1291    $call =~ /([^:]+):(\d+):([^ ]+)( -> ([^:]+):(\d+):(.+))?/;
1292    my ( $caller_file, $caller_line, $caller_function,
1293         $callee_file, $callee_line, $callee_function ) =
1294       ( $1, $2, $3, $5, $6, $7 );
1295
1296    # TODO(csilvers): for better compression, collect all the
1297    # caller/callee_files and functions first, before printing
1298    # anything, and only compress those referenced more than once.
1299    printf CG CompressedCGName("fl", $caller_file, \%filename_to_index_map);
1300    printf CG CompressedCGName("fn", $caller_function, \%fnname_to_index_map);
1301    if (defined $6) {
1302      printf CG CompressedCGName("cfl", $callee_file, \%filename_to_index_map);
1303      printf CG CompressedCGName("cfn", $callee_function, \%fnname_to_index_map);
1304      printf CG ("calls=$count $callee_line\n");
1305    }
1306    printf CG ("$caller_line $count\n\n");
1307  }
1308}
1309
1310# Print disassembly for all all routines that match $main::opt_disasm
1311sub PrintDisassembly {
1312  my $libs = shift;
1313  my $flat = shift;
1314  my $cumulative = shift;
1315  my $disasm_opts = shift;
1316
1317  my $total = TotalProfile($flat);
1318
1319  foreach my $lib (@{$libs}) {
1320    my $symbol_table = GetProcedureBoundaries($lib->[0], $disasm_opts);
1321    my $offset = AddressSub($lib->[1], $lib->[3]);
1322    foreach my $routine (sort ByName keys(%{$symbol_table})) {
1323      my $start_addr = $symbol_table->{$routine}->[0];
1324      my $end_addr = $symbol_table->{$routine}->[1];
1325      # See if there are any samples in this routine
1326      my $length = hex(AddressSub($end_addr, $start_addr));
1327      my $addr = AddressAdd($start_addr, $offset);
1328      for (my $i = 0; $i < $length; $i++) {
1329        if (defined($cumulative->{$addr})) {
1330          PrintDisassembledFunction($lib->[0], $offset,
1331                                    $routine, $flat, $cumulative,
1332                                    $start_addr, $end_addr, $total);
1333          last;
1334        }
1335        $addr = AddressInc($addr);
1336      }
1337    }
1338  }
1339}
1340
1341# Return reference to array of tuples of the form:
1342#       [start_address, filename, linenumber, instruction, limit_address]
1343# E.g.,
1344#       ["0x806c43d", "/foo/bar.cc", 131, "ret", "0x806c440"]
1345sub Disassemble {
1346  my $prog = shift;
1347  my $offset = shift;
1348  my $start_addr = shift;
1349  my $end_addr = shift;
1350
1351  my $objdump = $obj_tool_map{"objdump"};
1352  my $cmd = ShellEscape($objdump, "-C", "-d", "-l", "--no-show-raw-insn",
1353                        "--start-address=0x$start_addr",
1354                        "--stop-address=0x$end_addr", $prog);
1355  open(OBJDUMP, "$cmd |") || error("$cmd: $!\n");
1356  my @result = ();
1357  my $filename = "";
1358  my $linenumber = -1;
1359  my $last = ["", "", "", ""];
1360  while (<OBJDUMP>) {
1361    s/\r//g;         # turn windows-looking lines into unix-looking lines
1362    chop;
1363    if (m|\s*([^:\s]+):(\d+)\s*$|) {
1364      # Location line of the form:
1365      #   <filename>:<linenumber>
1366      $filename = $1;
1367      $linenumber = $2;
1368    } elsif (m/^ +([0-9a-f]+):\s*(.*)/) {
1369      # Disassembly line -- zero-extend address to full length
1370      my $addr = HexExtend($1);
1371      my $k = AddressAdd($addr, $offset);
1372      $last->[4] = $k;   # Store ending address for previous instruction
1373      $last = [$k, $filename, $linenumber, $2, $end_addr];
1374      push(@result, $last);
1375    }
1376  }
1377  close(OBJDUMP);
1378  return @result;
1379}
1380
1381# The input file should contain lines of the form /proc/maps-like
1382# output (same format as expected from the profiles) or that looks
1383# like hex addresses (like "0xDEADBEEF").  We will parse all
1384# /proc/maps output, and for all the hex addresses, we will output
1385# "short" symbol names, one per line, in the same order as the input.
1386sub PrintSymbols {
1387  my $maps_and_symbols_file = shift;
1388
1389  # ParseLibraries expects pcs to be in a set.  Fine by us...
1390  my @pclist = ();   # pcs in sorted order
1391  my $pcs = {};
1392  my $map = "";
1393  foreach my $line (<$maps_and_symbols_file>) {
1394    $line =~ s/\r//g;    # turn windows-looking lines into unix-looking lines
1395    if ($line =~ /\b(0x[0-9a-f]+)\b/i) {
1396      push(@pclist, HexExtend($1));
1397      $pcs->{$pclist[-1]} = 1;
1398    } else {
1399      $map .= $line;
1400    }
1401  }
1402
1403  my $libs = ParseLibraries($main::prog, $map, $pcs);
1404  my $symbols = ExtractSymbols($libs, $pcs);
1405
1406  foreach my $pc (@pclist) {
1407    # ->[0] is the shortname, ->[2] is the full name
1408    print(($symbols->{$pc}->[0] || "??") . "\n");
1409  }
1410}
1411
1412
1413# For sorting functions by name
1414sub ByName {
1415  return ShortFunctionName($a) cmp ShortFunctionName($b);
1416}
1417
1418# Print source-listing for all all routines that match $list_opts
1419sub PrintListing {
1420  my $total = shift;
1421  my $libs = shift;
1422  my $flat = shift;
1423  my $cumulative = shift;
1424  my $list_opts = shift;
1425  my $html = shift;
1426
1427  my $output = \*STDOUT;
1428  my $fname = "";
1429
1430  if ($html) {
1431    # Arrange to write the output to a temporary file
1432    $fname = TempName($main::next_tmpfile, "html");
1433    $main::next_tmpfile++;
1434    if (!open(TEMP, ">$fname")) {
1435      print STDERR "$fname: $!\n";
1436      return;
1437    }
1438    $output = \*TEMP;
1439    print $output HtmlListingHeader();
1440    printf $output ("<div class=\"legend\">%s<br>Total: %s %s</div>\n",
1441                    $main::prog, Unparse($total), Units());
1442  }
1443
1444  my $listed = 0;
1445  foreach my $lib (@{$libs}) {
1446    my $symbol_table = GetProcedureBoundaries($lib->[0], $list_opts);
1447    my $offset = AddressSub($lib->[1], $lib->[3]);
1448    foreach my $routine (sort ByName keys(%{$symbol_table})) {
1449      # Print if there are any samples in this routine
1450      my $start_addr = $symbol_table->{$routine}->[0];
1451      my $end_addr = $symbol_table->{$routine}->[1];
1452      my $length = hex(AddressSub($end_addr, $start_addr));
1453      my $addr = AddressAdd($start_addr, $offset);
1454      for (my $i = 0; $i < $length; $i++) {
1455        if (defined($cumulative->{$addr})) {
1456          $listed += PrintSource(
1457            $lib->[0], $offset,
1458            $routine, $flat, $cumulative,
1459            $start_addr, $end_addr,
1460            $html,
1461            $output);
1462          last;
1463        }
1464        $addr = AddressInc($addr);
1465      }
1466    }
1467  }
1468
1469  if ($html) {
1470    if ($listed > 0) {
1471      print $output HtmlListingFooter();
1472      close($output);
1473      RunWeb($fname);
1474    } else {
1475      close($output);
1476      unlink($fname);
1477    }
1478  }
1479}
1480
1481sub HtmlListingHeader {
1482  return <<'EOF';
1483<DOCTYPE html>
1484<html>
1485<head>
1486<title>Pprof listing</title>
1487<style type="text/css">
1488body {
1489  font-family: sans-serif;
1490}
1491h1 {
1492  font-size: 1.5em;
1493  margin-bottom: 4px;
1494}
1495.legend {
1496  font-size: 1.25em;
1497}
1498.line {
1499  color: #aaaaaa;
1500}
1501.nop {
1502  color: #aaaaaa;
1503}
1504.unimportant {
1505  color: #cccccc;
1506}
1507.disasmloc {
1508  color: #000000;
1509}
1510.deadsrc {
1511  cursor: pointer;
1512}
1513.deadsrc:hover {
1514  background-color: #eeeeee;
1515}
1516.livesrc {
1517  color: #0000ff;
1518  cursor: pointer;
1519}
1520.livesrc:hover {
1521  background-color: #eeeeee;
1522}
1523.asm {
1524  color: #008800;
1525  display: none;
1526}
1527</style>
1528<script type="text/javascript">
1529function jeprof_toggle_asm(e) {
1530  var target;
1531  if (!e) e = window.event;
1532  if (e.target) target = e.target;
1533  else if (e.srcElement) target = e.srcElement;
1534
1535  if (target) {
1536    var asm = target.nextSibling;
1537    if (asm && asm.className == "asm") {
1538      asm.style.display = (asm.style.display == "block" ? "" : "block");
1539      e.preventDefault();
1540      return false;
1541    }
1542  }
1543}
1544</script>
1545</head>
1546<body>
1547EOF
1548}
1549
1550sub HtmlListingFooter {
1551  return <<'EOF';
1552</body>
1553</html>
1554EOF
1555}
1556
1557sub HtmlEscape {
1558  my $text = shift;
1559  $text =~ s/&/&amp;/g;
1560  $text =~ s/</&lt;/g;
1561  $text =~ s/>/&gt;/g;
1562  return $text;
1563}
1564
1565# Returns the indentation of the line, if it has any non-whitespace
1566# characters.  Otherwise, returns -1.
1567sub Indentation {
1568  my $line = shift;
1569  if (m/^(\s*)\S/) {
1570    return length($1);
1571  } else {
1572    return -1;
1573  }
1574}
1575
1576# If the symbol table contains inlining info, Disassemble() may tag an
1577# instruction with a location inside an inlined function.  But for
1578# source listings, we prefer to use the location in the function we
1579# are listing.  So use MapToSymbols() to fetch full location
1580# information for each instruction and then pick out the first
1581# location from a location list (location list contains callers before
1582# callees in case of inlining).
1583#
1584# After this routine has run, each entry in $instructions contains:
1585#   [0] start address
1586#   [1] filename for function we are listing
1587#   [2] line number for function we are listing
1588#   [3] disassembly
1589#   [4] limit address
1590#   [5] most specific filename (may be different from [1] due to inlining)
1591#   [6] most specific line number (may be different from [2] due to inlining)
1592sub GetTopLevelLineNumbers {
1593  my ($lib, $offset, $instructions) = @_;
1594  my $pcs = [];
1595  for (my $i = 0; $i <= $#{$instructions}; $i++) {
1596    push(@{$pcs}, $instructions->[$i]->[0]);
1597  }
1598  my $symbols = {};
1599  MapToSymbols($lib, $offset, $pcs, $symbols);
1600  for (my $i = 0; $i <= $#{$instructions}; $i++) {
1601    my $e = $instructions->[$i];
1602    push(@{$e}, $e->[1]);
1603    push(@{$e}, $e->[2]);
1604    my $addr = $e->[0];
1605    my $sym = $symbols->{$addr};
1606    if (defined($sym)) {
1607      if ($#{$sym} >= 2 && $sym->[1] =~ m/^(.*):(\d+)$/) {
1608        $e->[1] = $1;  # File name
1609        $e->[2] = $2;  # Line number
1610      }
1611    }
1612  }
1613}
1614
1615# Print source-listing for one routine
1616sub PrintSource {
1617  my $prog = shift;
1618  my $offset = shift;
1619  my $routine = shift;
1620  my $flat = shift;
1621  my $cumulative = shift;
1622  my $start_addr = shift;
1623  my $end_addr = shift;
1624  my $html = shift;
1625  my $output = shift;
1626
1627  # Disassemble all instructions (just to get line numbers)
1628  my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr);
1629  GetTopLevelLineNumbers($prog, $offset, \@instructions);
1630
1631  # Hack 1: assume that the first source file encountered in the
1632  # disassembly contains the routine
1633  my $filename = undef;
1634  for (my $i = 0; $i <= $#instructions; $i++) {
1635    if ($instructions[$i]->[2] >= 0) {
1636      $filename = $instructions[$i]->[1];
1637      last;
1638    }
1639  }
1640  if (!defined($filename)) {
1641    print STDERR "no filename found in $routine\n";
1642    return 0;
1643  }
1644
1645  # Hack 2: assume that the largest line number from $filename is the
1646  # end of the procedure.  This is typically safe since if P1 contains
1647  # an inlined call to P2, then P2 usually occurs earlier in the
1648  # source file.  If this does not work, we might have to compute a
1649  # density profile or just print all regions we find.
1650  my $lastline = 0;
1651  for (my $i = 0; $i <= $#instructions; $i++) {
1652    my $f = $instructions[$i]->[1];
1653    my $l = $instructions[$i]->[2];
1654    if (($f eq $filename) && ($l > $lastline)) {
1655      $lastline = $l;
1656    }
1657  }
1658
1659  # Hack 3: assume the first source location from "filename" is the start of
1660  # the source code.
1661  my $firstline = 1;
1662  for (my $i = 0; $i <= $#instructions; $i++) {
1663    if ($instructions[$i]->[1] eq $filename) {
1664      $firstline = $instructions[$i]->[2];
1665      last;
1666    }
1667  }
1668
1669  # Hack 4: Extend last line forward until its indentation is less than
1670  # the indentation we saw on $firstline
1671  my $oldlastline = $lastline;
1672  {
1673    if (!open(FILE, "<$filename")) {
1674      print STDERR "$filename: $!\n";
1675      return 0;
1676    }
1677    my $l = 0;
1678    my $first_indentation = -1;
1679    while (<FILE>) {
1680      s/\r//g;         # turn windows-looking lines into unix-looking lines
1681      $l++;
1682      my $indent = Indentation($_);
1683      if ($l >= $firstline) {
1684        if ($first_indentation < 0 && $indent >= 0) {
1685          $first_indentation = $indent;
1686          last if ($first_indentation == 0);
1687        }
1688      }
1689      if ($l >= $lastline && $indent >= 0) {
1690        if ($indent >= $first_indentation) {
1691          $lastline = $l+1;
1692        } else {
1693          last;
1694        }
1695      }
1696    }
1697    close(FILE);
1698  }
1699
1700  # Assign all samples to the range $firstline,$lastline,
1701  # Hack 4: If an instruction does not occur in the range, its samples
1702  # are moved to the next instruction that occurs in the range.
1703  my $samples1 = {};        # Map from line number to flat count
1704  my $samples2 = {};        # Map from line number to cumulative count
1705  my $running1 = 0;         # Unassigned flat counts
1706  my $running2 = 0;         # Unassigned cumulative counts
1707  my $total1 = 0;           # Total flat counts
1708  my $total2 = 0;           # Total cumulative counts
1709  my %disasm = ();          # Map from line number to disassembly
1710  my $running_disasm = "";  # Unassigned disassembly
1711  my $skip_marker = "---\n";
1712  if ($html) {
1713    $skip_marker = "";
1714    for (my $l = $firstline; $l <= $lastline; $l++) {
1715      $disasm{$l} = "";
1716    }
1717  }
1718  my $last_dis_filename = '';
1719  my $last_dis_linenum = -1;
1720  my $last_touched_line = -1;  # To detect gaps in disassembly for a line
1721  foreach my $e (@instructions) {
1722    # Add up counts for all address that fall inside this instruction
1723    my $c1 = 0;
1724    my $c2 = 0;
1725    for (my $a = $e->[0]; $a lt $e->[4]; $a = AddressInc($a)) {
1726      $c1 += GetEntry($flat, $a);
1727      $c2 += GetEntry($cumulative, $a);
1728    }
1729
1730    if ($html) {
1731      my $dis = sprintf("      %6s %6s \t\t%8s: %s ",
1732                        HtmlPrintNumber($c1),
1733                        HtmlPrintNumber($c2),
1734                        UnparseAddress($offset, $e->[0]),
1735                        CleanDisassembly($e->[3]));
1736
1737      # Append the most specific source line associated with this instruction
1738      if (length($dis) < 80) { $dis .= (' ' x (80 - length($dis))) };
1739      $dis = HtmlEscape($dis);
1740      my $f = $e->[5];
1741      my $l = $e->[6];
1742      if ($f ne $last_dis_filename) {
1743        $dis .= sprintf("<span class=disasmloc>%s:%d</span>",
1744                        HtmlEscape(CleanFileName($f)), $l);
1745      } elsif ($l ne $last_dis_linenum) {
1746        # De-emphasize the unchanged file name portion
1747        $dis .= sprintf("<span class=unimportant>%s</span>" .
1748                        "<span class=disasmloc>:%d</span>",
1749                        HtmlEscape(CleanFileName($f)), $l);
1750      } else {
1751        # De-emphasize the entire location
1752        $dis .= sprintf("<span class=unimportant>%s:%d</span>",
1753                        HtmlEscape(CleanFileName($f)), $l);
1754      }
1755      $last_dis_filename = $f;
1756      $last_dis_linenum = $l;
1757      $running_disasm .= $dis;
1758      $running_disasm .= "\n";
1759    }
1760
1761    $running1 += $c1;
1762    $running2 += $c2;
1763    $total1 += $c1;
1764    $total2 += $c2;
1765    my $file = $e->[1];
1766    my $line = $e->[2];
1767    if (($file eq $filename) &&
1768        ($line >= $firstline) &&
1769        ($line <= $lastline)) {
1770      # Assign all accumulated samples to this line
1771      AddEntry($samples1, $line, $running1);
1772      AddEntry($samples2, $line, $running2);
1773      $running1 = 0;
1774      $running2 = 0;
1775      if ($html) {
1776        if ($line != $last_touched_line && $disasm{$line} ne '') {
1777          $disasm{$line} .= "\n";
1778        }
1779        $disasm{$line} .= $running_disasm;
1780        $running_disasm = '';
1781        $last_touched_line = $line;
1782      }
1783    }
1784  }
1785
1786  # Assign any leftover samples to $lastline
1787  AddEntry($samples1, $lastline, $running1);
1788  AddEntry($samples2, $lastline, $running2);
1789  if ($html) {
1790    if ($lastline != $last_touched_line && $disasm{$lastline} ne '') {
1791      $disasm{$lastline} .= "\n";
1792    }
1793    $disasm{$lastline} .= $running_disasm;
1794  }
1795
1796  if ($html) {
1797    printf $output (
1798      "<h1>%s</h1>%s\n<pre onClick=\"jeprof_toggle_asm()\">\n" .
1799      "Total:%6s %6s (flat / cumulative %s)\n",
1800      HtmlEscape(ShortFunctionName($routine)),
1801      HtmlEscape(CleanFileName($filename)),
1802      Unparse($total1),
1803      Unparse($total2),
1804      Units());
1805  } else {
1806    printf $output (
1807      "ROUTINE ====================== %s in %s\n" .
1808      "%6s %6s Total %s (flat / cumulative)\n",
1809      ShortFunctionName($routine),
1810      CleanFileName($filename),
1811      Unparse($total1),
1812      Unparse($total2),
1813      Units());
1814  }
1815  if (!open(FILE, "<$filename")) {
1816    print STDERR "$filename: $!\n";
1817    return 0;
1818  }
1819  my $l = 0;
1820  while (<FILE>) {
1821    s/\r//g;         # turn windows-looking lines into unix-looking lines
1822    $l++;
1823    if ($l >= $firstline - 5 &&
1824        (($l <= $oldlastline + 5) || ($l <= $lastline))) {
1825      chop;
1826      my $text = $_;
1827      if ($l == $firstline) { print $output $skip_marker; }
1828      my $n1 = GetEntry($samples1, $l);
1829      my $n2 = GetEntry($samples2, $l);
1830      if ($html) {
1831        # Emit a span that has one of the following classes:
1832        #    livesrc -- has samples
1833        #    deadsrc -- has disassembly, but with no samples
1834        #    nop     -- has no matching disasembly
1835        # Also emit an optional span containing disassembly.
1836        my $dis = $disasm{$l};
1837        my $asm = "";
1838        if (defined($dis) && $dis ne '') {
1839          $asm = "<span class=\"asm\">" . $dis . "</span>";
1840        }
1841        my $source_class = (($n1 + $n2 > 0)
1842                            ? "livesrc"
1843                            : (($asm ne "") ? "deadsrc" : "nop"));
1844        printf $output (
1845          "<span class=\"line\">%5d</span> " .
1846          "<span class=\"%s\">%6s %6s %s</span>%s\n",
1847          $l, $source_class,
1848          HtmlPrintNumber($n1),
1849          HtmlPrintNumber($n2),
1850          HtmlEscape($text),
1851          $asm);
1852      } else {
1853        printf $output(
1854          "%6s %6s %4d: %s\n",
1855          UnparseAlt($n1),
1856          UnparseAlt($n2),
1857          $l,
1858          $text);
1859      }
1860      if ($l == $lastline)  { print $output $skip_marker; }
1861    };
1862  }
1863  close(FILE);
1864  if ($html) {
1865    print $output "</pre>\n";
1866  }
1867  return 1;
1868}
1869
1870# Return the source line for the specified file/linenumber.
1871# Returns undef if not found.
1872sub SourceLine {
1873  my $file = shift;
1874  my $line = shift;
1875
1876  # Look in cache
1877  if (!defined($main::source_cache{$file})) {
1878    if (100 < scalar keys(%main::source_cache)) {
1879      # Clear the cache when it gets too big
1880      $main::source_cache = ();
1881    }
1882
1883    # Read all lines from the file
1884    if (!open(FILE, "<$file")) {
1885      print STDERR "$file: $!\n";
1886      $main::source_cache{$file} = [];  # Cache the negative result
1887      return undef;
1888    }
1889    my $lines = [];
1890    push(@{$lines}, "");        # So we can use 1-based line numbers as indices
1891    while (<FILE>) {
1892      push(@{$lines}, $_);
1893    }
1894    close(FILE);
1895
1896    # Save the lines in the cache
1897    $main::source_cache{$file} = $lines;
1898  }
1899
1900  my $lines = $main::source_cache{$file};
1901  if (($line < 0) || ($line > $#{$lines})) {
1902    return undef;
1903  } else {
1904    return $lines->[$line];
1905  }
1906}
1907
1908# Print disassembly for one routine with interspersed source if available
1909sub PrintDisassembledFunction {
1910  my $prog = shift;
1911  my $offset = shift;
1912  my $routine = shift;
1913  my $flat = shift;
1914  my $cumulative = shift;
1915  my $start_addr = shift;
1916  my $end_addr = shift;
1917  my $total = shift;
1918
1919  # Disassemble all instructions
1920  my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr);
1921
1922  # Make array of counts per instruction
1923  my @flat_count = ();
1924  my @cum_count = ();
1925  my $flat_total = 0;
1926  my $cum_total = 0;
1927  foreach my $e (@instructions) {
1928    # Add up counts for all address that fall inside this instruction
1929    my $c1 = 0;
1930    my $c2 = 0;
1931    for (my $a = $e->[0]; $a lt $e->[4]; $a = AddressInc($a)) {
1932      $c1 += GetEntry($flat, $a);
1933      $c2 += GetEntry($cumulative, $a);
1934    }
1935    push(@flat_count, $c1);
1936    push(@cum_count, $c2);
1937    $flat_total += $c1;
1938    $cum_total += $c2;
1939  }
1940
1941  # Print header with total counts
1942  printf("ROUTINE ====================== %s\n" .
1943         "%6s %6s %s (flat, cumulative) %.1f%% of total\n",
1944         ShortFunctionName($routine),
1945         Unparse($flat_total),
1946         Unparse($cum_total),
1947         Units(),
1948         ($cum_total * 100.0) / $total);
1949
1950  # Process instructions in order
1951  my $current_file = "";
1952  for (my $i = 0; $i <= $#instructions; ) {
1953    my $e = $instructions[$i];
1954
1955    # Print the new file name whenever we switch files
1956    if ($e->[1] ne $current_file) {
1957      $current_file = $e->[1];
1958      my $fname = $current_file;
1959      $fname =~ s|^\./||;   # Trim leading "./"
1960
1961      # Shorten long file names
1962      if (length($fname) >= 58) {
1963        $fname = "..." . substr($fname, -55);
1964      }
1965      printf("-------------------- %s\n", $fname);
1966    }
1967
1968    # TODO: Compute range of lines to print together to deal with
1969    # small reorderings.
1970    my $first_line = $e->[2];
1971    my $last_line = $first_line;
1972    my %flat_sum = ();
1973    my %cum_sum = ();
1974    for (my $l = $first_line; $l <= $last_line; $l++) {
1975      $flat_sum{$l} = 0;
1976      $cum_sum{$l} = 0;
1977    }
1978
1979    # Find run of instructions for this range of source lines
1980    my $first_inst = $i;
1981    while (($i <= $#instructions) &&
1982           ($instructions[$i]->[2] >= $first_line) &&
1983           ($instructions[$i]->[2] <= $last_line)) {
1984      $e = $instructions[$i];
1985      $flat_sum{$e->[2]} += $flat_count[$i];
1986      $cum_sum{$e->[2]} += $cum_count[$i];
1987      $i++;
1988    }
1989    my $last_inst = $i - 1;
1990
1991    # Print source lines
1992    for (my $l = $first_line; $l <= $last_line; $l++) {
1993      my $line = SourceLine($current_file, $l);
1994      if (!defined($line)) {
1995        $line = "?\n";
1996        next;
1997      } else {
1998        $line =~ s/^\s+//;
1999      }
2000      printf("%6s %6s %5d: %s",
2001             UnparseAlt($flat_sum{$l}),
2002             UnparseAlt($cum_sum{$l}),
2003             $l,
2004             $line);
2005    }
2006
2007    # Print disassembly
2008    for (my $x = $first_inst; $x <= $last_inst; $x++) {
2009      my $e = $instructions[$x];
2010      printf("%6s %6s    %8s: %6s\n",
2011             UnparseAlt($flat_count[$x]),
2012             UnparseAlt($cum_count[$x]),
2013             UnparseAddress($offset, $e->[0]),
2014             CleanDisassembly($e->[3]));
2015    }
2016  }
2017}
2018
2019# Print DOT graph
2020sub PrintDot {
2021  my $prog = shift;
2022  my $symbols = shift;
2023  my $raw = shift;
2024  my $flat = shift;
2025  my $cumulative = shift;
2026  my $overall_total = shift;
2027
2028  # Get total
2029  my $local_total = TotalProfile($flat);
2030  my $nodelimit = int($main::opt_nodefraction * $local_total);
2031  my $edgelimit = int($main::opt_edgefraction * $local_total);
2032  my $nodecount = $main::opt_nodecount;
2033
2034  # Find nodes to include
2035  my @list = (sort { abs(GetEntry($cumulative, $b)) <=>
2036                     abs(GetEntry($cumulative, $a))
2037                     || $a cmp $b }
2038              keys(%{$cumulative}));
2039  my $last = $nodecount - 1;
2040  if ($last > $#list) {
2041    $last = $#list;
2042  }
2043  while (($last >= 0) &&
2044         (abs(GetEntry($cumulative, $list[$last])) <= $nodelimit)) {
2045    $last--;
2046  }
2047  if ($last < 0) {
2048    print STDERR "No nodes to print\n";
2049    return 0;
2050  }
2051
2052  if ($nodelimit > 0 || $edgelimit > 0) {
2053    printf STDERR ("Dropping nodes with <= %s %s; edges with <= %s abs(%s)\n",
2054                   Unparse($nodelimit), Units(),
2055                   Unparse($edgelimit), Units());
2056  }
2057
2058  # Open DOT output file
2059  my $output;
2060  my $escaped_dot = ShellEscape(@DOT);
2061  my $escaped_ps2pdf = ShellEscape(@PS2PDF);
2062  if ($main::opt_gv) {
2063    my $escaped_outfile = ShellEscape(TempName($main::next_tmpfile, "ps"));
2064    $output = "| $escaped_dot -Tps2 >$escaped_outfile";
2065  } elsif ($main::opt_evince) {
2066    my $escaped_outfile = ShellEscape(TempName($main::next_tmpfile, "pdf"));
2067    $output = "| $escaped_dot -Tps2 | $escaped_ps2pdf - $escaped_outfile";
2068  } elsif ($main::opt_ps) {
2069    $output = "| $escaped_dot -Tps2";
2070  } elsif ($main::opt_pdf) {
2071    $output = "| $escaped_dot -Tps2 | $escaped_ps2pdf - -";
2072  } elsif ($main::opt_web || $main::opt_svg) {
2073    # We need to post-process the SVG, so write to a temporary file always.
2074    my $escaped_outfile = ShellEscape(TempName($main::next_tmpfile, "svg"));
2075    $output = "| $escaped_dot -Tsvg >$escaped_outfile";
2076  } elsif ($main::opt_gif) {
2077    $output = "| $escaped_dot -Tgif";
2078  } else {
2079    $output = ">&STDOUT";
2080  }
2081  open(DOT, $output) || error("$output: $!\n");
2082
2083  # Title
2084  printf DOT ("digraph \"%s; %s %s\" {\n",
2085              $prog,
2086              Unparse($overall_total),
2087              Units());
2088  if ($main::opt_pdf) {
2089    # The output is more printable if we set the page size for dot.
2090    printf DOT ("size=\"8,11\"\n");
2091  }
2092  printf DOT ("node [width=0.375,height=0.25];\n");
2093
2094  # Print legend
2095  printf DOT ("Legend [shape=box,fontsize=24,shape=plaintext," .
2096              "label=\"%s\\l%s\\l%s\\l%s\\l%s\\l\"];\n",
2097              $prog,
2098              sprintf("Total %s: %s", Units(), Unparse($overall_total)),
2099              sprintf("Focusing on: %s", Unparse($local_total)),
2100              sprintf("Dropped nodes with <= %s abs(%s)",
2101                      Unparse($nodelimit), Units()),
2102              sprintf("Dropped edges with <= %s %s",
2103                      Unparse($edgelimit), Units())
2104              );
2105
2106  # Print nodes
2107  my %node = ();
2108  my $nextnode = 1;
2109  foreach my $a (@list[0..$last]) {
2110    # Pick font size
2111    my $f = GetEntry($flat, $a);
2112    my $c = GetEntry($cumulative, $a);
2113
2114    my $fs = 8;
2115    if ($local_total > 0) {
2116      $fs = 8 + (50.0 * sqrt(abs($f * 1.0 / $local_total)));
2117    }
2118
2119    $node{$a} = $nextnode++;
2120    my $sym = $a;
2121    $sym =~ s/\s+/\\n/g;
2122    $sym =~ s/::/\\n/g;
2123
2124    # Extra cumulative info to print for non-leaves
2125    my $extra = "";
2126    if ($f != $c) {
2127      $extra = sprintf("\\rof %s (%s)",
2128                       Unparse($c),
2129                       Percent($c, $local_total));
2130    }
2131    my $style = "";
2132    if ($main::opt_heapcheck) {
2133      if ($f > 0) {
2134        # make leak-causing nodes more visible (add a background)
2135        $style = ",style=filled,fillcolor=gray"
2136      } elsif ($f < 0) {
2137        # make anti-leak-causing nodes (which almost never occur)
2138        # stand out as well (triple border)
2139        $style = ",peripheries=3"
2140      }
2141    }
2142
2143    printf DOT ("N%d [label=\"%s\\n%s (%s)%s\\r" .
2144                "\",shape=box,fontsize=%.1f%s];\n",
2145                $node{$a},
2146                $sym,
2147                Unparse($f),
2148                Percent($f, $local_total),
2149                $extra,
2150                $fs,
2151                $style,
2152               );
2153  }
2154
2155  # Get edges and counts per edge
2156  my %edge = ();
2157  my $n;
2158  my $fullname_to_shortname_map = {};
2159  FillFullnameToShortnameMap($symbols, $fullname_to_shortname_map);
2160  foreach my $k (keys(%{$raw})) {
2161    # TODO: omit low %age edges
2162    $n = $raw->{$k};
2163    my @translated = TranslateStack($symbols, $fullname_to_shortname_map, $k);
2164    for (my $i = 1; $i <= $#translated; $i++) {
2165      my $src = $translated[$i];
2166      my $dst = $translated[$i-1];
2167      #next if ($src eq $dst);  # Avoid self-edges?
2168      if (exists($node{$src}) && exists($node{$dst})) {
2169        my $edge_label = "$src\001$dst";
2170        if (!exists($edge{$edge_label})) {
2171          $edge{$edge_label} = 0;
2172        }
2173        $edge{$edge_label} += $n;
2174      }
2175    }
2176  }
2177
2178  # Print edges (process in order of decreasing counts)
2179  my %indegree = ();   # Number of incoming edges added per node so far
2180  my %outdegree = ();  # Number of outgoing edges added per node so far
2181  foreach my $e (sort { $edge{$b} <=> $edge{$a} } keys(%edge)) {
2182    my @x = split(/\001/, $e);
2183    $n = $edge{$e};
2184
2185    # Initialize degree of kept incoming and outgoing edges if necessary
2186    my $src = $x[0];
2187    my $dst = $x[1];
2188    if (!exists($outdegree{$src})) { $outdegree{$src} = 0; }
2189    if (!exists($indegree{$dst})) { $indegree{$dst} = 0; }
2190
2191    my $keep;
2192    if ($indegree{$dst} == 0) {
2193      # Keep edge if needed for reachability
2194      $keep = 1;
2195    } elsif (abs($n) <= $edgelimit) {
2196      # Drop if we are below --edgefraction
2197      $keep = 0;
2198    } elsif ($outdegree{$src} >= $main::opt_maxdegree ||
2199             $indegree{$dst} >= $main::opt_maxdegree) {
2200      # Keep limited number of in/out edges per node
2201      $keep = 0;
2202    } else {
2203      $keep = 1;
2204    }
2205
2206    if ($keep) {
2207      $outdegree{$src}++;
2208      $indegree{$dst}++;
2209
2210      # Compute line width based on edge count
2211      my $fraction = abs($local_total ? (3 * ($n / $local_total)) : 0);
2212      if ($fraction > 1) { $fraction = 1; }
2213      my $w = $fraction * 2;
2214      if ($w < 1 && ($main::opt_web || $main::opt_svg)) {
2215        # SVG output treats line widths < 1 poorly.
2216        $w = 1;
2217      }
2218
2219      # Dot sometimes segfaults if given edge weights that are too large, so
2220      # we cap the weights at a large value
2221      my $edgeweight = abs($n) ** 0.7;
2222      if ($edgeweight > 100000) { $edgeweight = 100000; }
2223      $edgeweight = int($edgeweight);
2224
2225      my $style = sprintf("setlinewidth(%f)", $w);
2226      if ($x[1] =~ m/\(inline\)/) {
2227        $style .= ",dashed";
2228      }
2229
2230      # Use a slightly squashed function of the edge count as the weight
2231      printf DOT ("N%s -> N%s [label=%s, weight=%d, style=\"%s\"];\n",
2232                  $node{$x[0]},
2233                  $node{$x[1]},
2234                  Unparse($n),
2235                  $edgeweight,
2236                  $style);
2237    }
2238  }
2239
2240  print DOT ("}\n");
2241  close(DOT);
2242
2243  if ($main::opt_web || $main::opt_svg) {
2244    # Rewrite SVG to be more usable inside web browser.
2245    RewriteSvg(TempName($main::next_tmpfile, "svg"));
2246  }
2247
2248  return 1;
2249}
2250
2251sub RewriteSvg {
2252  my $svgfile = shift;
2253
2254  open(SVG, $svgfile) || die "open temp svg: $!";
2255  my @svg = <SVG>;
2256  close(SVG);
2257  unlink $svgfile;
2258  my $svg = join('', @svg);
2259
2260  # Dot's SVG output is
2261  #
2262  #    <svg width="___" height="___"
2263  #     viewBox="___" xmlns=...>
2264  #    <g id="graph0" transform="...">
2265  #    ...
2266  #    </g>
2267  #    </svg>
2268  #
2269  # Change it to
2270  #
2271  #    <svg width="100%" height="100%"
2272  #     xmlns=...>
2273  #    $svg_javascript
2274  #    <g id="viewport" transform="translate(0,0)">
2275  #    <g id="graph0" transform="...">
2276  #    ...
2277  #    </g>
2278  #    </g>
2279  #    </svg>
2280
2281  # Fix width, height; drop viewBox.
2282  $svg =~ s/(?s)<svg width="[^"]+" height="[^"]+"(.*?)viewBox="[^"]+"/<svg width="100%" height="100%"$1/;
2283
2284  # Insert script, viewport <g> above first <g>
2285  my $svg_javascript = SvgJavascript();
2286  my $viewport = "<g id=\"viewport\" transform=\"translate(0,0)\">\n";
2287  $svg =~ s/<g id="graph\d"/$svg_javascript$viewport$&/;
2288
2289  # Insert final </g> above </svg>.
2290  $svg =~ s/(.*)(<\/svg>)/$1<\/g>$2/;
2291  $svg =~ s/<g id="graph\d"(.*?)/<g id="viewport"$1/;
2292
2293  if ($main::opt_svg) {
2294    # --svg: write to standard output.
2295    print $svg;
2296  } else {
2297    # Write back to temporary file.
2298    open(SVG, ">$svgfile") || die "open $svgfile: $!";
2299    print SVG $svg;
2300    close(SVG);
2301  }
2302}
2303
2304sub SvgJavascript {
2305  return <<'EOF';
2306<script type="text/ecmascript"><![CDATA[
2307// SVGPan
2308// http://www.cyberz.org/blog/2009/12/08/svgpan-a-javascript-svg-panzoomdrag-library/
2309// Local modification: if(true || ...) below to force panning, never moving.
2310
2311/**
2312 *  SVGPan library 1.2
2313 * ====================
2314 *
2315 * Given an unique existing element with id "viewport", including the
2316 * the library into any SVG adds the following capabilities:
2317 *
2318 *  - Mouse panning
2319 *  - Mouse zooming (using the wheel)
2320 *  - Object dargging
2321 *
2322 * Known issues:
2323 *
2324 *  - Zooming (while panning) on Safari has still some issues
2325 *
2326 * Releases:
2327 *
2328 * 1.2, Sat Mar 20 08:42:50 GMT 2010, Zeng Xiaohui
2329 *	Fixed a bug with browser mouse handler interaction
2330 *
2331 * 1.1, Wed Feb  3 17:39:33 GMT 2010, Zeng Xiaohui
2332 *	Updated the zoom code to support the mouse wheel on Safari/Chrome
2333 *
2334 * 1.0, Andrea Leofreddi
2335 *	First release
2336 *
2337 * This code is licensed under the following BSD license:
2338 *
2339 * Copyright 2009-2010 Andrea Leofreddi <a.leofreddi@itcharm.com>. All rights reserved.
2340 *
2341 * Redistribution and use in source and binary forms, with or without modification, are
2342 * permitted provided that the following conditions are met:
2343 *
2344 *    1. Redistributions of source code must retain the above copyright notice, this list of
2345 *       conditions and the following disclaimer.
2346 *
2347 *    2. Redistributions in binary form must reproduce the above copyright notice, this list
2348 *       of conditions and the following disclaimer in the documentation and/or other materials
2349 *       provided with the distribution.
2350 *
2351 * THIS SOFTWARE IS PROVIDED BY Andrea Leofreddi ``AS IS'' AND ANY EXPRESS OR IMPLIED
2352 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
2353 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Andrea Leofreddi OR
2354 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2355 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2356 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
2357 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
2358 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
2359 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2360 *
2361 * The views and conclusions contained in the software and documentation are those of the
2362 * authors and should not be interpreted as representing official policies, either expressed
2363 * or implied, of Andrea Leofreddi.
2364 */
2365
2366var root = document.documentElement;
2367
2368var state = 'none', stateTarget, stateOrigin, stateTf;
2369
2370setupHandlers(root);
2371
2372/**
2373 * Register handlers
2374 */
2375function setupHandlers(root){
2376	setAttributes(root, {
2377		"onmouseup" : "add(evt)",
2378		"onmousedown" : "handleMouseDown(evt)",
2379		"onmousemove" : "handleMouseMove(evt)",
2380		"onmouseup" : "handleMouseUp(evt)",
2381		//"onmouseout" : "handleMouseUp(evt)", // Decomment this to stop the pan functionality when dragging out of the SVG element
2382	});
2383
2384	if(navigator.userAgent.toLowerCase().indexOf('webkit') >= 0)
2385		window.addEventListener('mousewheel', handleMouseWheel, false); // Chrome/Safari
2386	else
2387		window.addEventListener('DOMMouseScroll', handleMouseWheel, false); // Others
2388
2389	var g = svgDoc.getElementById("svg");
2390	g.width = "100%";
2391	g.height = "100%";
2392}
2393
2394/**
2395 * Instance an SVGPoint object with given event coordinates.
2396 */
2397function getEventPoint(evt) {
2398	var p = root.createSVGPoint();
2399
2400	p.x = evt.clientX;
2401	p.y = evt.clientY;
2402
2403	return p;
2404}
2405
2406/**
2407 * Sets the current transform matrix of an element.
2408 */
2409function setCTM(element, matrix) {
2410	var s = "matrix(" + matrix.a + "," + matrix.b + "," + matrix.c + "," + matrix.d + "," + matrix.e + "," + matrix.f + ")";
2411
2412	element.setAttribute("transform", s);
2413}
2414
2415/**
2416 * Dumps a matrix to a string (useful for debug).
2417 */
2418function dumpMatrix(matrix) {
2419	var s = "[ " + matrix.a + ", " + matrix.c + ", " + matrix.e + "\n  " + matrix.b + ", " + matrix.d + ", " + matrix.f + "\n  0, 0, 1 ]";
2420
2421	return s;
2422}
2423
2424/**
2425 * Sets attributes of an element.
2426 */
2427function setAttributes(element, attributes){
2428	for (i in attributes)
2429		element.setAttributeNS(null, i, attributes[i]);
2430}
2431
2432/**
2433 * Handle mouse move event.
2434 */
2435function handleMouseWheel(evt) {
2436	if(evt.preventDefault)
2437		evt.preventDefault();
2438
2439	evt.returnValue = false;
2440
2441	var svgDoc = evt.target.ownerDocument;
2442
2443	var delta;
2444
2445	if(evt.wheelDelta)
2446		delta = evt.wheelDelta / 3600; // Chrome/Safari
2447	else
2448		delta = evt.detail / -90; // Mozilla
2449
2450	var z = 1 + delta; // Zoom factor: 0.9/1.1
2451
2452	var g = svgDoc.getElementById("viewport");
2453
2454	var p = getEventPoint(evt);
2455
2456	p = p.matrixTransform(g.getCTM().inverse());
2457
2458	// Compute new scale matrix in current mouse position
2459	var k = root.createSVGMatrix().translate(p.x, p.y).scale(z).translate(-p.x, -p.y);
2460
2461        setCTM(g, g.getCTM().multiply(k));
2462
2463	stateTf = stateTf.multiply(k.inverse());
2464}
2465
2466/**
2467 * Handle mouse move event.
2468 */
2469function handleMouseMove(evt) {
2470	if(evt.preventDefault)
2471		evt.preventDefault();
2472
2473	evt.returnValue = false;
2474
2475	var svgDoc = evt.target.ownerDocument;
2476
2477	var g = svgDoc.getElementById("viewport");
2478
2479	if(state == 'pan') {
2480		// Pan mode
2481		var p = getEventPoint(evt).matrixTransform(stateTf);
2482
2483		setCTM(g, stateTf.inverse().translate(p.x - stateOrigin.x, p.y - stateOrigin.y));
2484	} else if(state == 'move') {
2485		// Move mode
2486		var p = getEventPoint(evt).matrixTransform(g.getCTM().inverse());
2487
2488		setCTM(stateTarget, root.createSVGMatrix().translate(p.x - stateOrigin.x, p.y - stateOrigin.y).multiply(g.getCTM().inverse()).multiply(stateTarget.getCTM()));
2489
2490		stateOrigin = p;
2491	}
2492}
2493
2494/**
2495 * Handle click event.
2496 */
2497function handleMouseDown(evt) {
2498	if(evt.preventDefault)
2499		evt.preventDefault();
2500
2501	evt.returnValue = false;
2502
2503	var svgDoc = evt.target.ownerDocument;
2504
2505	var g = svgDoc.getElementById("viewport");
2506
2507	if(true || evt.target.tagName == "svg") {
2508		// Pan mode
2509		state = 'pan';
2510
2511		stateTf = g.getCTM().inverse();
2512
2513		stateOrigin = getEventPoint(evt).matrixTransform(stateTf);
2514	} else {
2515		// Move mode
2516		state = 'move';
2517
2518		stateTarget = evt.target;
2519
2520		stateTf = g.getCTM().inverse();
2521
2522		stateOrigin = getEventPoint(evt).matrixTransform(stateTf);
2523	}
2524}
2525
2526/**
2527 * Handle mouse button release event.
2528 */
2529function handleMouseUp(evt) {
2530	if(evt.preventDefault)
2531		evt.preventDefault();
2532
2533	evt.returnValue = false;
2534
2535	var svgDoc = evt.target.ownerDocument;
2536
2537	if(state == 'pan' || state == 'move') {
2538		// Quit pan mode
2539		state = '';
2540	}
2541}
2542
2543]]></script>
2544EOF
2545}
2546
2547# Provides a map from fullname to shortname for cases where the
2548# shortname is ambiguous.  The symlist has both the fullname and
2549# shortname for all symbols, which is usually fine, but sometimes --
2550# such as overloaded functions -- two different fullnames can map to
2551# the same shortname.  In that case, we use the address of the
2552# function to disambiguate the two.  This function fills in a map that
2553# maps fullnames to modified shortnames in such cases.  If a fullname
2554# is not present in the map, the 'normal' shortname provided by the
2555# symlist is the appropriate one to use.
2556sub FillFullnameToShortnameMap {
2557  my $symbols = shift;
2558  my $fullname_to_shortname_map = shift;
2559  my $shortnames_seen_once = {};
2560  my $shortnames_seen_more_than_once = {};
2561
2562  foreach my $symlist (values(%{$symbols})) {
2563    # TODO(csilvers): deal with inlined symbols too.
2564    my $shortname = $symlist->[0];
2565    my $fullname = $symlist->[2];
2566    if ($fullname !~ /<[0-9a-fA-F]+>$/) {  # fullname doesn't end in an address
2567      next;       # the only collisions we care about are when addresses differ
2568    }
2569    if (defined($shortnames_seen_once->{$shortname}) &&
2570        $shortnames_seen_once->{$shortname} ne $fullname) {
2571      $shortnames_seen_more_than_once->{$shortname} = 1;
2572    } else {
2573      $shortnames_seen_once->{$shortname} = $fullname;
2574    }
2575  }
2576
2577  foreach my $symlist (values(%{$symbols})) {
2578    my $shortname = $symlist->[0];
2579    my $fullname = $symlist->[2];
2580    # TODO(csilvers): take in a list of addresses we care about, and only
2581    # store in the map if $symlist->[1] is in that list.  Saves space.
2582    next if defined($fullname_to_shortname_map->{$fullname});
2583    if (defined($shortnames_seen_more_than_once->{$shortname})) {
2584      if ($fullname =~ /<0*([^>]*)>$/) {   # fullname has address at end of it
2585        $fullname_to_shortname_map->{$fullname} = "$shortname\@$1";
2586      }
2587    }
2588  }
2589}
2590
2591# Return a small number that identifies the argument.
2592# Multiple calls with the same argument will return the same number.
2593# Calls with different arguments will return different numbers.
2594sub ShortIdFor {
2595  my $key = shift;
2596  my $id = $main::uniqueid{$key};
2597  if (!defined($id)) {
2598    $id = keys(%main::uniqueid) + 1;
2599    $main::uniqueid{$key} = $id;
2600  }
2601  return $id;
2602}
2603
2604# Translate a stack of addresses into a stack of symbols
2605sub TranslateStack {
2606  my $symbols = shift;
2607  my $fullname_to_shortname_map = shift;
2608  my $k = shift;
2609
2610  my @addrs = split(/\n/, $k);
2611  my @result = ();
2612  for (my $i = 0; $i <= $#addrs; $i++) {
2613    my $a = $addrs[$i];
2614
2615    # Skip large addresses since they sometimes show up as fake entries on RH9
2616    if (length($a) > 8 && $a gt "7fffffffffffffff") {
2617      next;
2618    }
2619
2620    if ($main::opt_disasm || $main::opt_list) {
2621      # We want just the address for the key
2622      push(@result, $a);
2623      next;
2624    }
2625
2626    my $symlist = $symbols->{$a};
2627    if (!defined($symlist)) {
2628      $symlist = [$a, "", $a];
2629    }
2630
2631    # We can have a sequence of symbols for a particular entry
2632    # (more than one symbol in the case of inlining).  Callers
2633    # come before callees in symlist, so walk backwards since
2634    # the translated stack should contain callees before callers.
2635    for (my $j = $#{$symlist}; $j >= 2; $j -= 3) {
2636      my $func = $symlist->[$j-2];
2637      my $fileline = $symlist->[$j-1];
2638      my $fullfunc = $symlist->[$j];
2639      if (defined($fullname_to_shortname_map->{$fullfunc})) {
2640        $func = $fullname_to_shortname_map->{$fullfunc};
2641      }
2642      if ($j > 2) {
2643        $func = "$func (inline)";
2644      }
2645
2646      # Do not merge nodes corresponding to Callback::Run since that
2647      # causes confusing cycles in dot display.  Instead, we synthesize
2648      # a unique name for this frame per caller.
2649      if ($func =~ m/Callback.*::Run$/) {
2650        my $caller = ($i > 0) ? $addrs[$i-1] : 0;
2651        $func = "Run#" . ShortIdFor($caller);
2652      }
2653
2654      if ($main::opt_addresses) {
2655        push(@result, "$a $func $fileline");
2656      } elsif ($main::opt_lines) {
2657        if ($func eq '??' && $fileline eq '??:0') {
2658          push(@result, "$a");
2659        } else {
2660          push(@result, "$func $fileline");
2661        }
2662      } elsif ($main::opt_functions) {
2663        if ($func eq '??') {
2664          push(@result, "$a");
2665        } else {
2666          push(@result, $func);
2667        }
2668      } elsif ($main::opt_files) {
2669        if ($fileline eq '??:0' || $fileline eq '') {
2670          push(@result, "$a");
2671        } else {
2672          my $f = $fileline;
2673          $f =~ s/:\d+$//;
2674          push(@result, $f);
2675        }
2676      } else {
2677        push(@result, $a);
2678        last;  # Do not print inlined info
2679      }
2680    }
2681  }
2682
2683  # print join(",", @addrs), " => ", join(",", @result), "\n";
2684  return @result;
2685}
2686
2687# Generate percent string for a number and a total
2688sub Percent {
2689  my $num = shift;
2690  my $tot = shift;
2691  if ($tot != 0) {
2692    return sprintf("%.1f%%", $num * 100.0 / $tot);
2693  } else {
2694    return ($num == 0) ? "nan" : (($num > 0) ? "+inf" : "-inf");
2695  }
2696}
2697
2698# Generate pretty-printed form of number
2699sub Unparse {
2700  my $num = shift;
2701  if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {
2702    if ($main::opt_inuse_objects || $main::opt_alloc_objects) {
2703      return sprintf("%d", $num);
2704    } else {
2705      if ($main::opt_show_bytes) {
2706        return sprintf("%d", $num);
2707      } else {
2708        return sprintf("%.1f", $num / 1048576.0);
2709      }
2710    }
2711  } elsif ($main::profile_type eq 'contention' && !$main::opt_contentions) {
2712    return sprintf("%.3f", $num / 1e9); # Convert nanoseconds to seconds
2713  } else {
2714    return sprintf("%d", $num);
2715  }
2716}
2717
2718# Alternate pretty-printed form: 0 maps to "."
2719sub UnparseAlt {
2720  my $num = shift;
2721  if ($num == 0) {
2722    return ".";
2723  } else {
2724    return Unparse($num);
2725  }
2726}
2727
2728# Alternate pretty-printed form: 0 maps to ""
2729sub HtmlPrintNumber {
2730  my $num = shift;
2731  if ($num == 0) {
2732    return "";
2733  } else {
2734    return Unparse($num);
2735  }
2736}
2737
2738# Return output units
2739sub Units {
2740  if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {
2741    if ($main::opt_inuse_objects || $main::opt_alloc_objects) {
2742      return "objects";
2743    } else {
2744      if ($main::opt_show_bytes) {
2745        return "B";
2746      } else {
2747        return "MB";
2748      }
2749    }
2750  } elsif ($main::profile_type eq 'contention' && !$main::opt_contentions) {
2751    return "seconds";
2752  } else {
2753    return "samples";
2754  }
2755}
2756
2757##### Profile manipulation code #####
2758
2759# Generate flattened profile:
2760# If count is charged to stack [a,b,c,d], in generated profile,
2761# it will be charged to [a]
2762sub FlatProfile {
2763  my $profile = shift;
2764  my $result = {};
2765  foreach my $k (keys(%{$profile})) {
2766    my $count = $profile->{$k};
2767    my @addrs = split(/\n/, $k);
2768    if ($#addrs >= 0) {
2769      AddEntry($result, $addrs[0], $count);
2770    }
2771  }
2772  return $result;
2773}
2774
2775# Generate cumulative profile:
2776# If count is charged to stack [a,b,c,d], in generated profile,
2777# it will be charged to [a], [b], [c], [d]
2778sub CumulativeProfile {
2779  my $profile = shift;
2780  my $result = {};
2781  foreach my $k (keys(%{$profile})) {
2782    my $count = $profile->{$k};
2783    my @addrs = split(/\n/, $k);
2784    foreach my $a (@addrs) {
2785      AddEntry($result, $a, $count);
2786    }
2787  }
2788  return $result;
2789}
2790
2791# If the second-youngest PC on the stack is always the same, returns
2792# that pc.  Otherwise, returns undef.
2793sub IsSecondPcAlwaysTheSame {
2794  my $profile = shift;
2795
2796  my $second_pc = undef;
2797  foreach my $k (keys(%{$profile})) {
2798    my @addrs = split(/\n/, $k);
2799    if ($#addrs < 1) {
2800      return undef;
2801    }
2802    if (not defined $second_pc) {
2803      $second_pc = $addrs[1];
2804    } else {
2805      if ($second_pc ne $addrs[1]) {
2806        return undef;
2807      }
2808    }
2809  }
2810  return $second_pc;
2811}
2812
2813sub ExtractSymbolLocation {
2814  my $symbols = shift;
2815  my $address = shift;
2816  # 'addr2line' outputs "??:0" for unknown locations; we do the
2817  # same to be consistent.
2818  my $location = "??:0:unknown";
2819  if (exists $symbols->{$address}) {
2820    my $file = $symbols->{$address}->[1];
2821    if ($file eq "?") {
2822      $file = "??:0"
2823    }
2824    $location = $file . ":" . $symbols->{$address}->[0];
2825  }
2826  return $location;
2827}
2828
2829# Extracts a graph of calls.
2830sub ExtractCalls {
2831  my $symbols = shift;
2832  my $profile = shift;
2833
2834  my $calls = {};
2835  while( my ($stack_trace, $count) = each %$profile ) {
2836    my @address = split(/\n/, $stack_trace);
2837    my $destination = ExtractSymbolLocation($symbols, $address[0]);
2838    AddEntry($calls, $destination, $count);
2839    for (my $i = 1; $i <= $#address; $i++) {
2840      my $source = ExtractSymbolLocation($symbols, $address[$i]);
2841      my $call = "$source -> $destination";
2842      AddEntry($calls, $call, $count);
2843      $destination = $source;
2844    }
2845  }
2846
2847  return $calls;
2848}
2849
2850sub FilterFrames {
2851  my $symbols = shift;
2852  my $profile = shift;
2853
2854  if ($main::opt_retain eq '' && $main::opt_exclude eq '') {
2855    return $profile;
2856  }
2857
2858  my $result = {};
2859  foreach my $k (keys(%{$profile})) {
2860    my $count = $profile->{$k};
2861    my @addrs = split(/\n/, $k);
2862    my @path = ();
2863    foreach my $a (@addrs) {
2864      my $sym;
2865      if (exists($symbols->{$a})) {
2866        $sym = $symbols->{$a}->[0];
2867      } else {
2868        $sym = $a;
2869      }
2870      if ($main::opt_retain ne '' && $sym !~ m/$main::opt_retain/) {
2871        next;
2872      }
2873      if ($main::opt_exclude ne '' && $sym =~ m/$main::opt_exclude/) {
2874        next;
2875      }
2876      push(@path, $a);
2877    }
2878    if (scalar(@path) > 0) {
2879      my $reduced_path = join("\n", @path);
2880      AddEntry($result, $reduced_path, $count);
2881    }
2882  }
2883
2884  return $result;
2885}
2886
2887sub RemoveUninterestingFrames {
2888  my $symbols = shift;
2889  my $profile = shift;
2890
2891  # List of function names to skip
2892  my %skip = ();
2893  my $skip_regexp = 'NOMATCH';
2894  if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {
2895    foreach my $name ('@JEMALLOC_PREFIX@calloc',
2896                      'cfree',
2897                      '@JEMALLOC_PREFIX@malloc',
2898                      'newImpl',
2899                      'void* newImpl',
2900                      '@JEMALLOC_PREFIX@free',
2901                      '@JEMALLOC_PREFIX@memalign',
2902                      '@JEMALLOC_PREFIX@posix_memalign',
2903                      '@JEMALLOC_PREFIX@aligned_alloc',
2904                      'pvalloc',
2905                      '@JEMALLOC_PREFIX@valloc',
2906                      '@JEMALLOC_PREFIX@realloc',
2907                      '@JEMALLOC_PREFIX@mallocx',
2908                      '@JEMALLOC_PREFIX@rallocx',
2909                      '@JEMALLOC_PREFIX@xallocx',
2910                      '@JEMALLOC_PREFIX@dallocx',
2911                      '@JEMALLOC_PREFIX@sdallocx',
2912                      '@JEMALLOC_PREFIX@sdallocx_noflags',
2913                      'tc_calloc',
2914                      'tc_cfree',
2915                      'tc_malloc',
2916                      'tc_free',
2917                      'tc_memalign',
2918                      'tc_posix_memalign',
2919                      'tc_pvalloc',
2920                      'tc_valloc',
2921                      'tc_realloc',
2922                      'tc_new',
2923                      'tc_delete',
2924                      'tc_newarray',
2925                      'tc_deletearray',
2926                      'tc_new_nothrow',
2927                      'tc_newarray_nothrow',
2928                      'do_malloc',
2929                      '::do_malloc',   # new name -- got moved to an unnamed ns
2930                      '::do_malloc_or_cpp_alloc',
2931                      'DoSampledAllocation',
2932                      'simple_alloc::allocate',
2933                      '__malloc_alloc_template::allocate',
2934                      '__builtin_delete',
2935                      '__builtin_new',
2936                      '__builtin_vec_delete',
2937                      '__builtin_vec_new',
2938                      'operator new',
2939                      'operator new[]',
2940                      # The entry to our memory-allocation routines on OS X
2941                      'malloc_zone_malloc',
2942                      'malloc_zone_calloc',
2943                      'malloc_zone_valloc',
2944                      'malloc_zone_realloc',
2945                      'malloc_zone_memalign',
2946                      'malloc_zone_free',
2947                      # These mark the beginning/end of our custom sections
2948                      '__start_google_malloc',
2949                      '__stop_google_malloc',
2950                      '__start_malloc_hook',
2951                      '__stop_malloc_hook') {
2952      $skip{$name} = 1;
2953      $skip{"_" . $name} = 1;   # Mach (OS X) adds a _ prefix to everything
2954    }
2955    # TODO: Remove TCMalloc once everything has been
2956    # moved into the tcmalloc:: namespace and we have flushed
2957    # old code out of the system.
2958    $skip_regexp = "TCMalloc|^tcmalloc::";
2959  } elsif ($main::profile_type eq 'contention') {
2960    foreach my $vname ('base::RecordLockProfileData',
2961                       'base::SubmitMutexProfileData',
2962                       'base::SubmitSpinLockProfileData',
2963                       'Mutex::Unlock',
2964                       'Mutex::UnlockSlow',
2965                       'Mutex::ReaderUnlock',
2966                       'MutexLock::~MutexLock',
2967                       'SpinLock::Unlock',
2968                       'SpinLock::SlowUnlock',
2969                       'SpinLockHolder::~SpinLockHolder') {
2970      $skip{$vname} = 1;
2971    }
2972  } elsif ($main::profile_type eq 'cpu') {
2973    # Drop signal handlers used for CPU profile collection
2974    # TODO(dpeng): this should not be necessary; it's taken
2975    # care of by the general 2nd-pc mechanism below.
2976    foreach my $name ('ProfileData::Add',           # historical
2977                      'ProfileData::prof_handler',  # historical
2978                      'CpuProfiler::prof_handler',
2979                      '__FRAME_END__',
2980                      '__pthread_sighandler',
2981                      '__restore') {
2982      $skip{$name} = 1;
2983    }
2984  } else {
2985    # Nothing skipped for unknown types
2986  }
2987
2988  if ($main::profile_type eq 'cpu') {
2989    # If all the second-youngest program counters are the same,
2990    # this STRONGLY suggests that it is an artifact of measurement,
2991    # i.e., stack frames pushed by the CPU profiler signal handler.
2992    # Hence, we delete them.
2993    # (The topmost PC is read from the signal structure, not from
2994    # the stack, so it does not get involved.)
2995    while (my $second_pc = IsSecondPcAlwaysTheSame($profile)) {
2996      my $result = {};
2997      my $func = '';
2998      if (exists($symbols->{$second_pc})) {
2999        $second_pc = $symbols->{$second_pc}->[0];
3000      }
3001      print STDERR "Removing $second_pc from all stack traces.\n";
3002      foreach my $k (keys(%{$profile})) {
3003        my $count = $profile->{$k};
3004        my @addrs = split(/\n/, $k);
3005        splice @addrs, 1, 1;
3006        my $reduced_path = join("\n", @addrs);
3007        AddEntry($result, $reduced_path, $count);
3008      }
3009      $profile = $result;
3010    }
3011  }
3012
3013  my $result = {};
3014  foreach my $k (keys(%{$profile})) {
3015    my $count = $profile->{$k};
3016    my @addrs = split(/\n/, $k);
3017    my @path = ();
3018    foreach my $a (@addrs) {
3019      if (exists($symbols->{$a})) {
3020        my $func = $symbols->{$a}->[0];
3021        if ($skip{$func} || ($func =~ m/$skip_regexp/)) {
3022          # Throw away the portion of the backtrace seen so far, under the
3023          # assumption that previous frames were for functions internal to the
3024          # allocator.
3025          @path = ();
3026          next;
3027        }
3028      }
3029      push(@path, $a);
3030    }
3031    my $reduced_path = join("\n", @path);
3032    AddEntry($result, $reduced_path, $count);
3033  }
3034
3035  $result = FilterFrames($symbols, $result);
3036
3037  return $result;
3038}
3039
3040# Reduce profile to granularity given by user
3041sub ReduceProfile {
3042  my $symbols = shift;
3043  my $profile = shift;
3044  my $result = {};
3045  my $fullname_to_shortname_map = {};
3046  FillFullnameToShortnameMap($symbols, $fullname_to_shortname_map);
3047  foreach my $k (keys(%{$profile})) {
3048    my $count = $profile->{$k};
3049    my @translated = TranslateStack($symbols, $fullname_to_shortname_map, $k);
3050    my @path = ();
3051    my %seen = ();
3052    $seen{''} = 1;      # So that empty keys are skipped
3053    foreach my $e (@translated) {
3054      # To avoid double-counting due to recursion, skip a stack-trace
3055      # entry if it has already been seen
3056      if (!$seen{$e}) {
3057        $seen{$e} = 1;
3058        push(@path, $e);
3059      }
3060    }
3061    my $reduced_path = join("\n", @path);
3062    AddEntry($result, $reduced_path, $count);
3063  }
3064  return $result;
3065}
3066
3067# Does the specified symbol array match the regexp?
3068sub SymbolMatches {
3069  my $sym = shift;
3070  my $re = shift;
3071  if (defined($sym)) {
3072    for (my $i = 0; $i < $#{$sym}; $i += 3) {
3073      if ($sym->[$i] =~ m/$re/ || $sym->[$i+1] =~ m/$re/) {
3074        return 1;
3075      }
3076    }
3077  }
3078  return 0;
3079}
3080
3081# Focus only on paths involving specified regexps
3082sub FocusProfile {
3083  my $symbols = shift;
3084  my $profile = shift;
3085  my $focus = shift;
3086  my $result = {};
3087  foreach my $k (keys(%{$profile})) {
3088    my $count = $profile->{$k};
3089    my @addrs = split(/\n/, $k);
3090    foreach my $a (@addrs) {
3091      # Reply if it matches either the address/shortname/fileline
3092      if (($a =~ m/$focus/) || SymbolMatches($symbols->{$a}, $focus)) {
3093        AddEntry($result, $k, $count);
3094        last;
3095      }
3096    }
3097  }
3098  return $result;
3099}
3100
3101# Focus only on paths not involving specified regexps
3102sub IgnoreProfile {
3103  my $symbols = shift;
3104  my $profile = shift;
3105  my $ignore = shift;
3106  my $result = {};
3107  foreach my $k (keys(%{$profile})) {
3108    my $count = $profile->{$k};
3109    my @addrs = split(/\n/, $k);
3110    my $matched = 0;
3111    foreach my $a (@addrs) {
3112      # Reply if it matches either the address/shortname/fileline
3113      if (($a =~ m/$ignore/) || SymbolMatches($symbols->{$a}, $ignore)) {
3114        $matched = 1;
3115        last;
3116      }
3117    }
3118    if (!$matched) {
3119      AddEntry($result, $k, $count);
3120    }
3121  }
3122  return $result;
3123}
3124
3125# Get total count in profile
3126sub TotalProfile {
3127  my $profile = shift;
3128  my $result = 0;
3129  foreach my $k (keys(%{$profile})) {
3130    $result += $profile->{$k};
3131  }
3132  return $result;
3133}
3134
3135# Add A to B
3136sub AddProfile {
3137  my $A = shift;
3138  my $B = shift;
3139
3140  my $R = {};
3141  # add all keys in A
3142  foreach my $k (keys(%{$A})) {
3143    my $v = $A->{$k};
3144    AddEntry($R, $k, $v);
3145  }
3146  # add all keys in B
3147  foreach my $k (keys(%{$B})) {
3148    my $v = $B->{$k};
3149    AddEntry($R, $k, $v);
3150  }
3151  return $R;
3152}
3153
3154# Merges symbol maps
3155sub MergeSymbols {
3156  my $A = shift;
3157  my $B = shift;
3158
3159  my $R = {};
3160  foreach my $k (keys(%{$A})) {
3161    $R->{$k} = $A->{$k};
3162  }
3163  if (defined($B)) {
3164    foreach my $k (keys(%{$B})) {
3165      $R->{$k} = $B->{$k};
3166    }
3167  }
3168  return $R;
3169}
3170
3171
3172# Add A to B
3173sub AddPcs {
3174  my $A = shift;
3175  my $B = shift;
3176
3177  my $R = {};
3178  # add all keys in A
3179  foreach my $k (keys(%{$A})) {
3180    $R->{$k} = 1
3181  }
3182  # add all keys in B
3183  foreach my $k (keys(%{$B})) {
3184    $R->{$k} = 1
3185  }
3186  return $R;
3187}
3188
3189# Subtract B from A
3190sub SubtractProfile {
3191  my $A = shift;
3192  my $B = shift;
3193
3194  my $R = {};
3195  foreach my $k (keys(%{$A})) {
3196    my $v = $A->{$k} - GetEntry($B, $k);
3197    if ($v < 0 && $main::opt_drop_negative) {
3198      $v = 0;
3199    }
3200    AddEntry($R, $k, $v);
3201  }
3202  if (!$main::opt_drop_negative) {
3203    # Take care of when subtracted profile has more entries
3204    foreach my $k (keys(%{$B})) {
3205      if (!exists($A->{$k})) {
3206        AddEntry($R, $k, 0 - $B->{$k});
3207      }
3208    }
3209  }
3210  return $R;
3211}
3212
3213# Get entry from profile; zero if not present
3214sub GetEntry {
3215  my $profile = shift;
3216  my $k = shift;
3217  if (exists($profile->{$k})) {
3218    return $profile->{$k};
3219  } else {
3220    return 0;
3221  }
3222}
3223
3224# Add entry to specified profile
3225sub AddEntry {
3226  my $profile = shift;
3227  my $k = shift;
3228  my $n = shift;
3229  if (!exists($profile->{$k})) {
3230    $profile->{$k} = 0;
3231  }
3232  $profile->{$k} += $n;
3233}
3234
3235# Add a stack of entries to specified profile, and add them to the $pcs
3236# list.
3237sub AddEntries {
3238  my $profile = shift;
3239  my $pcs = shift;
3240  my $stack = shift;
3241  my $count = shift;
3242  my @k = ();
3243
3244  foreach my $e (split(/\s+/, $stack)) {
3245    my $pc = HexExtend($e);
3246    $pcs->{$pc} = 1;
3247    push @k, $pc;
3248  }
3249  AddEntry($profile, (join "\n", @k), $count);
3250}
3251
3252##### Code to profile a server dynamically #####
3253
3254sub CheckSymbolPage {
3255  my $url = SymbolPageURL();
3256  my $command = ShellEscape(@URL_FETCHER, $url);
3257  open(SYMBOL, "$command |") or error($command);
3258  my $line = <SYMBOL>;
3259  $line =~ s/\r//g;         # turn windows-looking lines into unix-looking lines
3260  close(SYMBOL);
3261  unless (defined($line)) {
3262    error("$url doesn't exist\n");
3263  }
3264
3265  if ($line =~ /^num_symbols:\s+(\d+)$/) {
3266    if ($1 == 0) {
3267      error("Stripped binary. No symbols available.\n");
3268    }
3269  } else {
3270    error("Failed to get the number of symbols from $url\n");
3271  }
3272}
3273
3274sub IsProfileURL {
3275  my $profile_name = shift;
3276  if (-f $profile_name) {
3277    printf STDERR "Using local file $profile_name.\n";
3278    return 0;
3279  }
3280  return 1;
3281}
3282
3283sub ParseProfileURL {
3284  my $profile_name = shift;
3285
3286  if (!defined($profile_name) || $profile_name eq "") {
3287    return ();
3288  }
3289
3290  # Split profile URL - matches all non-empty strings, so no test.
3291  $profile_name =~ m,^(https?://)?([^/]+)(.*?)(/|$PROFILES)?$,;
3292
3293  my $proto = $1 || "http://";
3294  my $hostport = $2;
3295  my $prefix = $3;
3296  my $profile = $4 || "/";
3297
3298  my $host = $hostport;
3299  $host =~ s/:.*//;
3300
3301  my $baseurl = "$proto$hostport$prefix";
3302  return ($host, $baseurl, $profile);
3303}
3304
3305# We fetch symbols from the first profile argument.
3306sub SymbolPageURL {
3307  my ($host, $baseURL, $path) = ParseProfileURL($main::pfile_args[0]);
3308  return "$baseURL$SYMBOL_PAGE";
3309}
3310
3311sub FetchProgramName() {
3312  my ($host, $baseURL, $path) = ParseProfileURL($main::pfile_args[0]);
3313  my $url = "$baseURL$PROGRAM_NAME_PAGE";
3314  my $command_line = ShellEscape(@URL_FETCHER, $url);
3315  open(CMDLINE, "$command_line |") or error($command_line);
3316  my $cmdline = <CMDLINE>;
3317  $cmdline =~ s/\r//g;   # turn windows-looking lines into unix-looking lines
3318  close(CMDLINE);
3319  error("Failed to get program name from $url\n") unless defined($cmdline);
3320  $cmdline =~ s/\x00.+//;  # Remove argv[1] and latters.
3321  $cmdline =~ s!\n!!g;  # Remove LFs.
3322  return $cmdline;
3323}
3324
3325# Gee, curl's -L (--location) option isn't reliable at least
3326# with its 7.12.3 version.  Curl will forget to post data if
3327# there is a redirection.  This function is a workaround for
3328# curl.  Redirection happens on borg hosts.
3329sub ResolveRedirectionForCurl {
3330  my $url = shift;
3331  my $command_line = ShellEscape(@URL_FETCHER, "--head", $url);
3332  open(CMDLINE, "$command_line |") or error($command_line);
3333  while (<CMDLINE>) {
3334    s/\r//g;         # turn windows-looking lines into unix-looking lines
3335    if (/^Location: (.*)/) {
3336      $url = $1;
3337    }
3338  }
3339  close(CMDLINE);
3340  return $url;
3341}
3342
3343# Add a timeout flat to URL_FETCHER.  Returns a new list.
3344sub AddFetchTimeout {
3345  my $timeout = shift;
3346  my @fetcher = @_;
3347  if (defined($timeout)) {
3348    if (join(" ", @fetcher) =~ m/\bcurl -s/) {
3349      push(@fetcher, "--max-time", sprintf("%d", $timeout));
3350    } elsif (join(" ", @fetcher) =~ m/\brpcget\b/) {
3351      push(@fetcher, sprintf("--deadline=%d", $timeout));
3352    }
3353  }
3354  return @fetcher;
3355}
3356
3357# Reads a symbol map from the file handle name given as $1, returning
3358# the resulting symbol map.  Also processes variables relating to symbols.
3359# Currently, the only variable processed is 'binary=<value>' which updates
3360# $main::prog to have the correct program name.
3361sub ReadSymbols {
3362  my $in = shift;
3363  my $map = {};
3364  while (<$in>) {
3365    s/\r//g;         # turn windows-looking lines into unix-looking lines
3366    # Removes all the leading zeroes from the symbols, see comment below.
3367    if (m/^0x0*([0-9a-f]+)\s+(.+)/) {
3368      $map->{$1} = $2;
3369    } elsif (m/^---/) {
3370      last;
3371    } elsif (m/^([a-z][^=]*)=(.*)$/ ) {
3372      my ($variable, $value) = ($1, $2);
3373      for ($variable, $value) {
3374        s/^\s+//;
3375        s/\s+$//;
3376      }
3377      if ($variable eq "binary") {
3378        if ($main::prog ne $UNKNOWN_BINARY && $main::prog ne $value) {
3379          printf STDERR ("Warning: Mismatched binary name '%s', using '%s'.\n",
3380                         $main::prog, $value);
3381        }
3382        $main::prog = $value;
3383      } else {
3384        printf STDERR ("Ignoring unknown variable in symbols list: " .
3385            "'%s' = '%s'\n", $variable, $value);
3386      }
3387    }
3388  }
3389  return $map;
3390}
3391
3392sub URLEncode {
3393  my $str = shift;
3394  $str =~ s/([^A-Za-z0-9\-_.!~*'()])/ sprintf "%%%02x", ord $1 /eg;
3395  return $str;
3396}
3397
3398sub AppendSymbolFilterParams {
3399  my $url = shift;
3400  my @params = ();
3401  if ($main::opt_retain ne '') {
3402    push(@params, sprintf("retain=%s", URLEncode($main::opt_retain)));
3403  }
3404  if ($main::opt_exclude ne '') {
3405    push(@params, sprintf("exclude=%s", URLEncode($main::opt_exclude)));
3406  }
3407  if (scalar @params > 0) {
3408    $url = sprintf("%s?%s", $url, join("&", @params));
3409  }
3410  return $url;
3411}
3412
3413# Fetches and processes symbols to prepare them for use in the profile output
3414# code.  If the optional 'symbol_map' arg is not given, fetches symbols from
3415# $SYMBOL_PAGE for all PC values found in profile.  Otherwise, the raw symbols
3416# are assumed to have already been fetched into 'symbol_map' and are simply
3417# extracted and processed.
3418sub FetchSymbols {
3419  my $pcset = shift;
3420  my $symbol_map = shift;
3421
3422  my %seen = ();
3423  my @pcs = grep { !$seen{$_}++ } keys(%$pcset);  # uniq
3424
3425  if (!defined($symbol_map)) {
3426    my $post_data = join("+", sort((map {"0x" . "$_"} @pcs)));
3427
3428    open(POSTFILE, ">$main::tmpfile_sym");
3429    print POSTFILE $post_data;
3430    close(POSTFILE);
3431
3432    my $url = SymbolPageURL();
3433
3434    my $command_line;
3435    if (join(" ", @URL_FETCHER) =~ m/\bcurl -s/) {
3436      $url = ResolveRedirectionForCurl($url);
3437      $url = AppendSymbolFilterParams($url);
3438      $command_line = ShellEscape(@URL_FETCHER, "-d", "\@$main::tmpfile_sym",
3439                                  $url);
3440    } else {
3441      $url = AppendSymbolFilterParams($url);
3442      $command_line = (ShellEscape(@URL_FETCHER, "--post", $url)
3443                       . " < " . ShellEscape($main::tmpfile_sym));
3444    }
3445    # We use c++filt in case $SYMBOL_PAGE gives us mangled symbols.
3446    my $escaped_cppfilt = ShellEscape($obj_tool_map{"c++filt"});
3447    open(SYMBOL, "$command_line | $escaped_cppfilt |") or error($command_line);
3448    $symbol_map = ReadSymbols(*SYMBOL{IO});
3449    close(SYMBOL);
3450  }
3451
3452  my $symbols = {};
3453  foreach my $pc (@pcs) {
3454    my $fullname;
3455    # For 64 bits binaries, symbols are extracted with 8 leading zeroes.
3456    # Then /symbol reads the long symbols in as uint64, and outputs
3457    # the result with a "0x%08llx" format which get rid of the zeroes.
3458    # By removing all the leading zeroes in both $pc and the symbols from
3459    # /symbol, the symbols match and are retrievable from the map.
3460    my $shortpc = $pc;
3461    $shortpc =~ s/^0*//;
3462    # Each line may have a list of names, which includes the function
3463    # and also other functions it has inlined.  They are separated (in
3464    # PrintSymbolizedProfile), by --, which is illegal in function names.
3465    my $fullnames;
3466    if (defined($symbol_map->{$shortpc})) {
3467      $fullnames = $symbol_map->{$shortpc};
3468    } else {
3469      $fullnames = "0x" . $pc;  # Just use addresses
3470    }
3471    my $sym = [];
3472    $symbols->{$pc} = $sym;
3473    foreach my $fullname (split("--", $fullnames)) {
3474      my $name = ShortFunctionName($fullname);
3475      push(@{$sym}, $name, "?", $fullname);
3476    }
3477  }
3478  return $symbols;
3479}
3480
3481sub BaseName {
3482  my $file_name = shift;
3483  $file_name =~ s!^.*/!!;  # Remove directory name
3484  return $file_name;
3485}
3486
3487sub MakeProfileBaseName {
3488  my ($binary_name, $profile_name) = @_;
3489  my ($host, $baseURL, $path) = ParseProfileURL($profile_name);
3490  my $binary_shortname = BaseName($binary_name);
3491  return sprintf("%s.%s.%s",
3492                 $binary_shortname, $main::op_time, $host);
3493}
3494
3495sub FetchDynamicProfile {
3496  my $binary_name = shift;
3497  my $profile_name = shift;
3498  my $fetch_name_only = shift;
3499  my $encourage_patience = shift;
3500
3501  if (!IsProfileURL($profile_name)) {
3502    return $profile_name;
3503  } else {
3504    my ($host, $baseURL, $path) = ParseProfileURL($profile_name);
3505    if ($path eq "" || $path eq "/") {
3506      # Missing type specifier defaults to cpu-profile
3507      $path = $PROFILE_PAGE;
3508    }
3509
3510    my $profile_file = MakeProfileBaseName($binary_name, $profile_name);
3511
3512    my $url = "$baseURL$path";
3513    my $fetch_timeout = undef;
3514    if ($path =~ m/$PROFILE_PAGE|$PMUPROFILE_PAGE/) {
3515      if ($path =~ m/[?]/) {
3516        $url .= "&";
3517      } else {
3518        $url .= "?";
3519      }
3520      $url .= sprintf("seconds=%d", $main::opt_seconds);
3521      $fetch_timeout = $main::opt_seconds * 1.01 + 60;
3522      # Set $profile_type for consumption by PrintSymbolizedProfile.
3523      $main::profile_type = 'cpu';
3524    } else {
3525      # For non-CPU profiles, we add a type-extension to
3526      # the target profile file name.
3527      my $suffix = $path;
3528      $suffix =~ s,/,.,g;
3529      $profile_file .= $suffix;
3530      # Set $profile_type for consumption by PrintSymbolizedProfile.
3531      if ($path =~ m/$HEAP_PAGE/) {
3532        $main::profile_type = 'heap';
3533      } elsif ($path =~ m/$GROWTH_PAGE/) {
3534        $main::profile_type = 'growth';
3535      } elsif ($path =~ m/$CONTENTION_PAGE/) {
3536        $main::profile_type = 'contention';
3537      }
3538    }
3539
3540    my $profile_dir = $ENV{"JEPROF_TMPDIR"} || ($ENV{HOME} . "/jeprof");
3541    if (! -d $profile_dir) {
3542      mkdir($profile_dir)
3543          || die("Unable to create profile directory $profile_dir: $!\n");
3544    }
3545    my $tmp_profile = "$profile_dir/.tmp.$profile_file";
3546    my $real_profile = "$profile_dir/$profile_file";
3547
3548    if ($fetch_name_only > 0) {
3549      return $real_profile;
3550    }
3551
3552    my @fetcher = AddFetchTimeout($fetch_timeout, @URL_FETCHER);
3553    my $cmd = ShellEscape(@fetcher, $url) . " > " . ShellEscape($tmp_profile);
3554    if ($path =~ m/$PROFILE_PAGE|$PMUPROFILE_PAGE|$CENSUSPROFILE_PAGE/){
3555      print STDERR "Gathering CPU profile from $url for $main::opt_seconds seconds to\n  ${real_profile}\n";
3556      if ($encourage_patience) {
3557        print STDERR "Be patient...\n";
3558      }
3559    } else {
3560      print STDERR "Fetching $path profile from $url to\n  ${real_profile}\n";
3561    }
3562
3563    (system($cmd) == 0) || error("Failed to get profile: $cmd: $!\n");
3564    (system("mv", $tmp_profile, $real_profile) == 0) || error("Unable to rename profile\n");
3565    print STDERR "Wrote profile to $real_profile\n";
3566    $main::collected_profile = $real_profile;
3567    return $main::collected_profile;
3568  }
3569}
3570
3571# Collect profiles in parallel
3572sub FetchDynamicProfiles {
3573  my $items = scalar(@main::pfile_args);
3574  my $levels = log($items) / log(2);
3575
3576  if ($items == 1) {
3577    $main::profile_files[0] = FetchDynamicProfile($main::prog, $main::pfile_args[0], 0, 1);
3578  } else {
3579    # math rounding issues
3580    if ((2 ** $levels) < $items) {
3581     $levels++;
3582    }
3583    my $count = scalar(@main::pfile_args);
3584    for (my $i = 0; $i < $count; $i++) {
3585      $main::profile_files[$i] = FetchDynamicProfile($main::prog, $main::pfile_args[$i], 1, 0);
3586    }
3587    print STDERR "Fetching $count profiles, Be patient...\n";
3588    FetchDynamicProfilesRecurse($levels, 0, 0);
3589    $main::collected_profile = join(" \\\n    ", @main::profile_files);
3590  }
3591}
3592
3593# Recursively fork a process to get enough processes
3594# collecting profiles
3595sub FetchDynamicProfilesRecurse {
3596  my $maxlevel = shift;
3597  my $level = shift;
3598  my $position = shift;
3599
3600  if (my $pid = fork()) {
3601    $position = 0 | ($position << 1);
3602    TryCollectProfile($maxlevel, $level, $position);
3603    wait;
3604  } else {
3605    $position = 1 | ($position << 1);
3606    TryCollectProfile($maxlevel, $level, $position);
3607    cleanup();
3608    exit(0);
3609  }
3610}
3611
3612# Collect a single profile
3613sub TryCollectProfile {
3614  my $maxlevel = shift;
3615  my $level = shift;
3616  my $position = shift;
3617
3618  if ($level >= ($maxlevel - 1)) {
3619    if ($position < scalar(@main::pfile_args)) {
3620      FetchDynamicProfile($main::prog, $main::pfile_args[$position], 0, 0);
3621    }
3622  } else {
3623    FetchDynamicProfilesRecurse($maxlevel, $level+1, $position);
3624  }
3625}
3626
3627##### Parsing code #####
3628
3629# Provide a small streaming-read module to handle very large
3630# cpu-profile files.  Stream in chunks along a sliding window.
3631# Provides an interface to get one 'slot', correctly handling
3632# endian-ness differences.  A slot is one 32-bit or 64-bit word
3633# (depending on the input profile).  We tell endianness and bit-size
3634# for the profile by looking at the first 8 bytes: in cpu profiles,
3635# the second slot is always 3 (we'll accept anything that's not 0).
3636BEGIN {
3637  package CpuProfileStream;
3638
3639  sub new {
3640    my ($class, $file, $fname) = @_;
3641    my $self = { file        => $file,
3642                 base        => 0,
3643                 stride      => 512 * 1024,   # must be a multiple of bitsize/8
3644                 slots       => [],
3645                 unpack_code => "",           # N for big-endian, V for little
3646                 perl_is_64bit => 1,          # matters if profile is 64-bit
3647    };
3648    bless $self, $class;
3649    # Let unittests adjust the stride
3650    if ($main::opt_test_stride > 0) {
3651      $self->{stride} = $main::opt_test_stride;
3652    }
3653    # Read the first two slots to figure out bitsize and endianness.
3654    my $slots = $self->{slots};
3655    my $str;
3656    read($self->{file}, $str, 8);
3657    # Set the global $address_length based on what we see here.
3658    # 8 is 32-bit (8 hexadecimal chars); 16 is 64-bit (16 hexadecimal chars).
3659    $address_length = ($str eq (chr(0)x8)) ? 16 : 8;
3660    if ($address_length == 8) {
3661      if (substr($str, 6, 2) eq chr(0)x2) {
3662        $self->{unpack_code} = 'V';  # Little-endian.
3663      } elsif (substr($str, 4, 2) eq chr(0)x2) {
3664        $self->{unpack_code} = 'N';  # Big-endian
3665      } else {
3666        ::error("$fname: header size >= 2**16\n");
3667      }
3668      @$slots = unpack($self->{unpack_code} . "*", $str);
3669    } else {
3670      # If we're a 64-bit profile, check if we're a 64-bit-capable
3671      # perl.  Otherwise, each slot will be represented as a float
3672      # instead of an int64, losing precision and making all the
3673      # 64-bit addresses wrong.  We won't complain yet, but will
3674      # later if we ever see a value that doesn't fit in 32 bits.
3675      my $has_q = 0;
3676      eval { $has_q = pack("Q", "1") ? 1 : 1; };
3677      if (!$has_q) {
3678        $self->{perl_is_64bit} = 0;
3679      }
3680      read($self->{file}, $str, 8);
3681      if (substr($str, 4, 4) eq chr(0)x4) {
3682        # We'd love to use 'Q', but it's a) not universal, b) not endian-proof.
3683        $self->{unpack_code} = 'V';  # Little-endian.
3684      } elsif (substr($str, 0, 4) eq chr(0)x4) {
3685        $self->{unpack_code} = 'N';  # Big-endian
3686      } else {
3687        ::error("$fname: header size >= 2**32\n");
3688      }
3689      my @pair = unpack($self->{unpack_code} . "*", $str);
3690      # Since we know one of the pair is 0, it's fine to just add them.
3691      @$slots = (0, $pair[0] + $pair[1]);
3692    }
3693    return $self;
3694  }
3695
3696  # Load more data when we access slots->get(X) which is not yet in memory.
3697  sub overflow {
3698    my ($self) = @_;
3699    my $slots = $self->{slots};
3700    $self->{base} += $#$slots + 1;   # skip over data we're replacing
3701    my $str;
3702    read($self->{file}, $str, $self->{stride});
3703    if ($address_length == 8) {      # the 32-bit case
3704      # This is the easy case: unpack provides 32-bit unpacking primitives.
3705      @$slots = unpack($self->{unpack_code} . "*", $str);
3706    } else {
3707      # We need to unpack 32 bits at a time and combine.
3708      my @b32_values = unpack($self->{unpack_code} . "*", $str);
3709      my @b64_values = ();
3710      for (my $i = 0; $i < $#b32_values; $i += 2) {
3711        # TODO(csilvers): if this is a 32-bit perl, the math below
3712        #    could end up in a too-large int, which perl will promote
3713        #    to a double, losing necessary precision.  Deal with that.
3714        #    Right now, we just die.
3715        my ($lo, $hi) = ($b32_values[$i], $b32_values[$i+1]);
3716        if ($self->{unpack_code} eq 'N') {    # big-endian
3717          ($lo, $hi) = ($hi, $lo);
3718        }
3719        my $value = $lo + $hi * (2**32);
3720        if (!$self->{perl_is_64bit} &&   # check value is exactly represented
3721            (($value % (2**32)) != $lo || int($value / (2**32)) != $hi)) {
3722          ::error("Need a 64-bit perl to process this 64-bit profile.\n");
3723        }
3724        push(@b64_values, $value);
3725      }
3726      @$slots = @b64_values;
3727    }
3728  }
3729
3730  # Access the i-th long in the file (logically), or -1 at EOF.
3731  sub get {
3732    my ($self, $idx) = @_;
3733    my $slots = $self->{slots};
3734    while ($#$slots >= 0) {
3735      if ($idx < $self->{base}) {
3736        # The only time we expect a reference to $slots[$i - something]
3737        # after referencing $slots[$i] is reading the very first header.
3738        # Since $stride > |header|, that shouldn't cause any lookback
3739        # errors.  And everything after the header is sequential.
3740        print STDERR "Unexpected look-back reading CPU profile";
3741        return -1;   # shrug, don't know what better to return
3742      } elsif ($idx > $self->{base} + $#$slots) {
3743        $self->overflow();
3744      } else {
3745        return $slots->[$idx - $self->{base}];
3746      }
3747    }
3748    # If we get here, $slots is [], which means we've reached EOF
3749    return -1;  # unique since slots is supposed to hold unsigned numbers
3750  }
3751}
3752
3753# Reads the top, 'header' section of a profile, and returns the last
3754# line of the header, commonly called a 'header line'.  The header
3755# section of a profile consists of zero or more 'command' lines that
3756# are instructions to jeprof, which jeprof executes when reading the
3757# header.  All 'command' lines start with a %.  After the command
3758# lines is the 'header line', which is a profile-specific line that
3759# indicates what type of profile it is, and perhaps other global
3760# information about the profile.  For instance, here's a header line
3761# for a heap profile:
3762#   heap profile:     53:    38236 [  5525:  1284029] @ heapprofile
3763# For historical reasons, the CPU profile does not contain a text-
3764# readable header line.  If the profile looks like a CPU profile,
3765# this function returns "".  If no header line could be found, this
3766# function returns undef.
3767#
3768# The following commands are recognized:
3769#   %warn -- emit the rest of this line to stderr, prefixed by 'WARNING:'
3770#
3771# The input file should be in binmode.
3772sub ReadProfileHeader {
3773  local *PROFILE = shift;
3774  my $firstchar = "";
3775  my $line = "";
3776  read(PROFILE, $firstchar, 1);
3777  seek(PROFILE, -1, 1);                    # unread the firstchar
3778  if ($firstchar !~ /[[:print:]]/) {       # is not a text character
3779    return "";
3780  }
3781  while (defined($line = <PROFILE>)) {
3782    $line =~ s/\r//g;   # turn windows-looking lines into unix-looking lines
3783    if ($line =~ /^%warn\s+(.*)/) {        # 'warn' command
3784      # Note this matches both '%warn blah\n' and '%warn\n'.
3785      print STDERR "WARNING: $1\n";        # print the rest of the line
3786    } elsif ($line =~ /^%/) {
3787      print STDERR "Ignoring unknown command from profile header: $line";
3788    } else {
3789      # End of commands, must be the header line.
3790      return $line;
3791    }
3792  }
3793  return undef;     # got to EOF without seeing a header line
3794}
3795
3796sub IsSymbolizedProfileFile {
3797  my $file_name = shift;
3798  if (!(-e $file_name) || !(-r $file_name)) {
3799    return 0;
3800  }
3801  # Check if the file contains a symbol-section marker.
3802  open(TFILE, "<$file_name");
3803  binmode TFILE;
3804  my $firstline = ReadProfileHeader(*TFILE);
3805  close(TFILE);
3806  if (!$firstline) {
3807    return 0;
3808  }
3809  $SYMBOL_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
3810  my $symbol_marker = $&;
3811  return $firstline =~ /^--- *$symbol_marker/;
3812}
3813
3814# Parse profile generated by common/profiler.cc and return a reference
3815# to a map:
3816#      $result->{version}     Version number of profile file
3817#      $result->{period}      Sampling period (in microseconds)
3818#      $result->{profile}     Profile object
3819#      $result->{threads}     Map of thread IDs to profile objects
3820#      $result->{map}         Memory map info from profile
3821#      $result->{pcs}         Hash of all PC values seen, key is hex address
3822sub ReadProfile {
3823  my $prog = shift;
3824  my $fname = shift;
3825  my $result;            # return value
3826
3827  $CONTENTION_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
3828  my $contention_marker = $&;
3829  $GROWTH_PAGE  =~ m,[^/]+$,;    # matches everything after the last slash
3830  my $growth_marker = $&;
3831  $SYMBOL_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
3832  my $symbol_marker = $&;
3833  $PROFILE_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
3834  my $profile_marker = $&;
3835  $HEAP_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
3836  my $heap_marker = $&;
3837
3838  # Look at first line to see if it is a heap or a CPU profile.
3839  # CPU profile may start with no header at all, and just binary data
3840  # (starting with \0\0\0\0) -- in that case, don't try to read the
3841  # whole firstline, since it may be gigabytes(!) of data.
3842  open(PROFILE, "<$fname") || error("$fname: $!\n");
3843  binmode PROFILE;      # New perls do UTF-8 processing
3844  my $header = ReadProfileHeader(*PROFILE);
3845  if (!defined($header)) {   # means "at EOF"
3846    error("Profile is empty.\n");
3847  }
3848
3849  my $symbols;
3850  if ($header =~ m/^--- *$symbol_marker/o) {
3851    # Verify that the user asked for a symbolized profile
3852    if (!$main::use_symbolized_profile) {
3853      # we have both a binary and symbolized profiles, abort
3854      error("FATAL ERROR: Symbolized profile\n   $fname\ncannot be used with " .
3855            "a binary arg. Try again without passing\n   $prog\n");
3856    }
3857    # Read the symbol section of the symbolized profile file.
3858    $symbols = ReadSymbols(*PROFILE{IO});
3859    # Read the next line to get the header for the remaining profile.
3860    $header = ReadProfileHeader(*PROFILE) || "";
3861  }
3862
3863  if ($header =~ m/^--- *($heap_marker|$growth_marker)/o) {
3864    # Skip "--- ..." line for profile types that have their own headers.
3865    $header = ReadProfileHeader(*PROFILE) || "";
3866  }
3867
3868  $main::profile_type = '';
3869
3870  if ($header =~ m/^heap profile:.*$growth_marker/o) {
3871    $main::profile_type = 'growth';
3872    $result =  ReadHeapProfile($prog, *PROFILE, $header);
3873  } elsif ($header =~ m/^heap profile:/) {
3874    $main::profile_type = 'heap';
3875    $result =  ReadHeapProfile($prog, *PROFILE, $header);
3876  } elsif ($header =~ m/^heap/) {
3877    $main::profile_type = 'heap';
3878    $result = ReadThreadedHeapProfile($prog, $fname, $header);
3879  } elsif ($header =~ m/^--- *$contention_marker/o) {
3880    $main::profile_type = 'contention';
3881    $result = ReadSynchProfile($prog, *PROFILE);
3882  } elsif ($header =~ m/^--- *Stacks:/) {
3883    print STDERR
3884      "Old format contention profile: mistakenly reports " .
3885      "condition variable signals as lock contentions.\n";
3886    $main::profile_type = 'contention';
3887    $result = ReadSynchProfile($prog, *PROFILE);
3888  } elsif ($header =~ m/^--- *$profile_marker/) {
3889    # the binary cpu profile data starts immediately after this line
3890    $main::profile_type = 'cpu';
3891    $result = ReadCPUProfile($prog, $fname, *PROFILE);
3892  } else {
3893    if (defined($symbols)) {
3894      # a symbolized profile contains a format we don't recognize, bail out
3895      error("$fname: Cannot recognize profile section after symbols.\n");
3896    }
3897    # no ascii header present -- must be a CPU profile
3898    $main::profile_type = 'cpu';
3899    $result = ReadCPUProfile($prog, $fname, *PROFILE);
3900  }
3901
3902  close(PROFILE);
3903
3904  # if we got symbols along with the profile, return those as well
3905  if (defined($symbols)) {
3906    $result->{symbols} = $symbols;
3907  }
3908
3909  return $result;
3910}
3911
3912# Subtract one from caller pc so we map back to call instr.
3913# However, don't do this if we're reading a symbolized profile
3914# file, in which case the subtract-one was done when the file
3915# was written.
3916#
3917# We apply the same logic to all readers, though ReadCPUProfile uses an
3918# independent implementation.
3919sub FixCallerAddresses {
3920  my $stack = shift;
3921  # --raw/http: Always subtract one from pc's, because PrintSymbolizedProfile()
3922  # dumps unadjusted profiles.
3923  {
3924    $stack =~ /(\s)/;
3925    my $delimiter = $1;
3926    my @addrs = split(' ', $stack);
3927    my @fixedaddrs;
3928    $#fixedaddrs = $#addrs;
3929    if ($#addrs >= 0) {
3930      $fixedaddrs[0] = $addrs[0];
3931    }
3932    for (my $i = 1; $i <= $#addrs; $i++) {
3933      $fixedaddrs[$i] = AddressSub($addrs[$i], "0x1");
3934    }
3935    return join $delimiter, @fixedaddrs;
3936  }
3937}
3938
3939# CPU profile reader
3940sub ReadCPUProfile {
3941  my $prog = shift;
3942  my $fname = shift;       # just used for logging
3943  local *PROFILE = shift;
3944  my $version;
3945  my $period;
3946  my $i;
3947  my $profile = {};
3948  my $pcs = {};
3949
3950  # Parse string into array of slots.
3951  my $slots = CpuProfileStream->new(*PROFILE, $fname);
3952
3953  # Read header.  The current header version is a 5-element structure
3954  # containing:
3955  #   0: header count (always 0)
3956  #   1: header "words" (after this one: 3)
3957  #   2: format version (0)
3958  #   3: sampling period (usec)
3959  #   4: unused padding (always 0)
3960  if ($slots->get(0) != 0 ) {
3961    error("$fname: not a profile file, or old format profile file\n");
3962  }
3963  $i = 2 + $slots->get(1);
3964  $version = $slots->get(2);
3965  $period = $slots->get(3);
3966  # Do some sanity checking on these header values.
3967  if ($version > (2**32) || $period > (2**32) || $i > (2**32) || $i < 5) {
3968    error("$fname: not a profile file, or corrupted profile file\n");
3969  }
3970
3971  # Parse profile
3972  while ($slots->get($i) != -1) {
3973    my $n = $slots->get($i++);
3974    my $d = $slots->get($i++);
3975    if ($d > (2**16)) {  # TODO(csilvers): what's a reasonable max-stack-depth?
3976      my $addr = sprintf("0%o", $i * ($address_length == 8 ? 4 : 8));
3977      print STDERR "At index $i (address $addr):\n";
3978      error("$fname: stack trace depth >= 2**32\n");
3979    }
3980    if ($slots->get($i) == 0) {
3981      # End of profile data marker
3982      $i += $d;
3983      last;
3984    }
3985
3986    # Make key out of the stack entries
3987    my @k = ();
3988    for (my $j = 0; $j < $d; $j++) {
3989      my $pc = $slots->get($i+$j);
3990      # Subtract one from caller pc so we map back to call instr.
3991      $pc--;
3992      $pc = sprintf("%0*x", $address_length, $pc);
3993      $pcs->{$pc} = 1;
3994      push @k, $pc;
3995    }
3996
3997    AddEntry($profile, (join "\n", @k), $n);
3998    $i += $d;
3999  }
4000
4001  # Parse map
4002  my $map = '';
4003  seek(PROFILE, $i * 4, 0);
4004  read(PROFILE, $map, (stat PROFILE)[7]);
4005
4006  my $r = {};
4007  $r->{version} = $version;
4008  $r->{period} = $period;
4009  $r->{profile} = $profile;
4010  $r->{libs} = ParseLibraries($prog, $map, $pcs);
4011  $r->{pcs} = $pcs;
4012
4013  return $r;
4014}
4015
4016sub HeapProfileIndex {
4017  my $index = 1;
4018  if ($main::opt_inuse_space) {
4019    $index = 1;
4020  } elsif ($main::opt_inuse_objects) {
4021    $index = 0;
4022  } elsif ($main::opt_alloc_space) {
4023    $index = 3;
4024  } elsif ($main::opt_alloc_objects) {
4025    $index = 2;
4026  }
4027  return $index;
4028}
4029
4030sub ReadMappedLibraries {
4031  my $fh = shift;
4032  my $map = "";
4033  # Read the /proc/self/maps data
4034  while (<$fh>) {
4035    s/\r//g;         # turn windows-looking lines into unix-looking lines
4036    $map .= $_;
4037  }
4038  return $map;
4039}
4040
4041sub ReadMemoryMap {
4042  my $fh = shift;
4043  my $map = "";
4044  # Read /proc/self/maps data as formatted by DumpAddressMap()
4045  my $buildvar = "";
4046  while (<PROFILE>) {
4047    s/\r//g;         # turn windows-looking lines into unix-looking lines
4048    # Parse "build=<dir>" specification if supplied
4049    if (m/^\s*build=(.*)\n/) {
4050      $buildvar = $1;
4051    }
4052
4053    # Expand "$build" variable if available
4054    $_ =~ s/\$build\b/$buildvar/g;
4055
4056    $map .= $_;
4057  }
4058  return $map;
4059}
4060
4061sub AdjustSamples {
4062  my ($sample_adjustment, $sampling_algorithm, $n1, $s1, $n2, $s2) = @_;
4063  if ($sample_adjustment) {
4064    if ($sampling_algorithm == 2) {
4065      # Remote-heap version 2
4066      # The sampling frequency is the rate of a Poisson process.
4067      # This means that the probability of sampling an allocation of
4068      # size X with sampling rate Y is 1 - exp(-X/Y)
4069      if ($n1 != 0) {
4070        my $ratio = (($s1*1.0)/$n1)/($sample_adjustment);
4071        my $scale_factor = 1/(1 - exp(-$ratio));
4072        $n1 *= $scale_factor;
4073        $s1 *= $scale_factor;
4074      }
4075      if ($n2 != 0) {
4076        my $ratio = (($s2*1.0)/$n2)/($sample_adjustment);
4077        my $scale_factor = 1/(1 - exp(-$ratio));
4078        $n2 *= $scale_factor;
4079        $s2 *= $scale_factor;
4080      }
4081    } else {
4082      # Remote-heap version 1
4083      my $ratio;
4084      $ratio = (($s1*1.0)/$n1)/($sample_adjustment);
4085      if ($ratio < 1) {
4086        $n1 /= $ratio;
4087        $s1 /= $ratio;
4088      }
4089      $ratio = (($s2*1.0)/$n2)/($sample_adjustment);
4090      if ($ratio < 1) {
4091        $n2 /= $ratio;
4092        $s2 /= $ratio;
4093      }
4094    }
4095  }
4096  return ($n1, $s1, $n2, $s2);
4097}
4098
4099sub ReadHeapProfile {
4100  my $prog = shift;
4101  local *PROFILE = shift;
4102  my $header = shift;
4103
4104  my $index = HeapProfileIndex();
4105
4106  # Find the type of this profile.  The header line looks like:
4107  #    heap profile:   1246:  8800744 [  1246:  8800744] @ <heap-url>/266053
4108  # There are two pairs <count: size>, the first inuse objects/space, and the
4109  # second allocated objects/space.  This is followed optionally by a profile
4110  # type, and if that is present, optionally by a sampling frequency.
4111  # For remote heap profiles (v1):
4112  # The interpretation of the sampling frequency is that the profiler, for
4113  # each sample, calculates a uniformly distributed random integer less than
4114  # the given value, and records the next sample after that many bytes have
4115  # been allocated.  Therefore, the expected sample interval is half of the
4116  # given frequency.  By default, if not specified, the expected sample
4117  # interval is 128KB.  Only remote-heap-page profiles are adjusted for
4118  # sample size.
4119  # For remote heap profiles (v2):
4120  # The sampling frequency is the rate of a Poisson process. This means that
4121  # the probability of sampling an allocation of size X with sampling rate Y
4122  # is 1 - exp(-X/Y)
4123  # For version 2, a typical header line might look like this:
4124  # heap profile:   1922: 127792360 [  1922: 127792360] @ <heap-url>_v2/524288
4125  # the trailing number (524288) is the sampling rate. (Version 1 showed
4126  # double the 'rate' here)
4127  my $sampling_algorithm = 0;
4128  my $sample_adjustment = 0;
4129  chomp($header);
4130  my $type = "unknown";
4131  if ($header =~ m"^heap profile:\s*(\d+):\s+(\d+)\s+\[\s*(\d+):\s+(\d+)\](\s*@\s*([^/]*)(/(\d+))?)?") {
4132    if (defined($6) && ($6 ne '')) {
4133      $type = $6;
4134      my $sample_period = $8;
4135      # $type is "heapprofile" for profiles generated by the
4136      # heap-profiler, and either "heap" or "heap_v2" for profiles
4137      # generated by sampling directly within tcmalloc.  It can also
4138      # be "growth" for heap-growth profiles.  The first is typically
4139      # found for profiles generated locally, and the others for
4140      # remote profiles.
4141      if (($type eq "heapprofile") || ($type !~ /heap/) ) {
4142        # No need to adjust for the sampling rate with heap-profiler-derived data
4143        $sampling_algorithm = 0;
4144      } elsif ($type =~ /_v2/) {
4145        $sampling_algorithm = 2;     # version 2 sampling
4146        if (defined($sample_period) && ($sample_period ne '')) {
4147          $sample_adjustment = int($sample_period);
4148        }
4149      } else {
4150        $sampling_algorithm = 1;     # version 1 sampling
4151        if (defined($sample_period) && ($sample_period ne '')) {
4152          $sample_adjustment = int($sample_period)/2;
4153        }
4154      }
4155    } else {
4156      # We detect whether or not this is a remote-heap profile by checking
4157      # that the total-allocated stats ($n2,$s2) are exactly the
4158      # same as the in-use stats ($n1,$s1).  It is remotely conceivable
4159      # that a non-remote-heap profile may pass this check, but it is hard
4160      # to imagine how that could happen.
4161      # In this case it's so old it's guaranteed to be remote-heap version 1.
4162      my ($n1, $s1, $n2, $s2) = ($1, $2, $3, $4);
4163      if (($n1 == $n2) && ($s1 == $s2)) {
4164        # This is likely to be a remote-heap based sample profile
4165        $sampling_algorithm = 1;
4166      }
4167    }
4168  }
4169
4170  if ($sampling_algorithm > 0) {
4171    # For remote-heap generated profiles, adjust the counts and sizes to
4172    # account for the sample rate (we sample once every 128KB by default).
4173    if ($sample_adjustment == 0) {
4174      # Turn on profile adjustment.
4175      $sample_adjustment = 128*1024;
4176      print STDERR "Adjusting heap profiles for 1-in-128KB sampling rate\n";
4177    } else {
4178      printf STDERR ("Adjusting heap profiles for 1-in-%d sampling rate\n",
4179                     $sample_adjustment);
4180    }
4181    if ($sampling_algorithm > 1) {
4182      # We don't bother printing anything for the original version (version 1)
4183      printf STDERR "Heap version $sampling_algorithm\n";
4184    }
4185  }
4186
4187  my $profile = {};
4188  my $pcs = {};
4189  my $map = "";
4190
4191  while (<PROFILE>) {
4192    s/\r//g;         # turn windows-looking lines into unix-looking lines
4193    if (/^MAPPED_LIBRARIES:/) {
4194      $map .= ReadMappedLibraries(*PROFILE);
4195      last;
4196    }
4197
4198    if (/^--- Memory map:/) {
4199      $map .= ReadMemoryMap(*PROFILE);
4200      last;
4201    }
4202
4203    # Read entry of the form:
4204    #  <count1>: <bytes1> [<count2>: <bytes2>] @ a1 a2 a3 ... an
4205    s/^\s*//;
4206    s/\s*$//;
4207    if (m/^\s*(\d+):\s+(\d+)\s+\[\s*(\d+):\s+(\d+)\]\s+@\s+(.*)$/) {
4208      my $stack = $5;
4209      my ($n1, $s1, $n2, $s2) = ($1, $2, $3, $4);
4210      my @counts = AdjustSamples($sample_adjustment, $sampling_algorithm,
4211                                 $n1, $s1, $n2, $s2);
4212      AddEntries($profile, $pcs, FixCallerAddresses($stack), $counts[$index]);
4213    }
4214  }
4215
4216  my $r = {};
4217  $r->{version} = "heap";
4218  $r->{period} = 1;
4219  $r->{profile} = $profile;
4220  $r->{libs} = ParseLibraries($prog, $map, $pcs);
4221  $r->{pcs} = $pcs;
4222  return $r;
4223}
4224
4225sub ReadThreadedHeapProfile {
4226  my ($prog, $fname, $header) = @_;
4227
4228  my $index = HeapProfileIndex();
4229  my $sampling_algorithm = 0;
4230  my $sample_adjustment = 0;
4231  chomp($header);
4232  my $type = "unknown";
4233  # Assuming a very specific type of header for now.
4234  if ($header =~ m"^heap_v2/(\d+)") {
4235    $type = "_v2";
4236    $sampling_algorithm = 2;
4237    $sample_adjustment = int($1);
4238  }
4239  if ($type ne "_v2" || !defined($sample_adjustment)) {
4240    die "Threaded heap profiles require v2 sampling with a sample rate\n";
4241  }
4242
4243  my $profile = {};
4244  my $thread_profiles = {};
4245  my $pcs = {};
4246  my $map = "";
4247  my $stack = "";
4248
4249  while (<PROFILE>) {
4250    s/\r//g;
4251    if (/^MAPPED_LIBRARIES:/) {
4252      $map .= ReadMappedLibraries(*PROFILE);
4253      last;
4254    }
4255
4256    if (/^--- Memory map:/) {
4257      $map .= ReadMemoryMap(*PROFILE);
4258      last;
4259    }
4260
4261    # Read entry of the form:
4262    # @ a1 a2 ... an
4263    #   t*: <count1>: <bytes1> [<count2>: <bytes2>]
4264    #   t1: <count1>: <bytes1> [<count2>: <bytes2>]
4265    #     ...
4266    #   tn: <count1>: <bytes1> [<count2>: <bytes2>]
4267    s/^\s*//;
4268    s/\s*$//;
4269    if (m/^@\s+(.*)$/) {
4270      $stack = $1;
4271    } elsif (m/^\s*(t(\*|\d+)):\s+(\d+):\s+(\d+)\s+\[\s*(\d+):\s+(\d+)\]$/) {
4272      if ($stack eq "") {
4273        # Still in the header, so this is just a per-thread summary.
4274        next;
4275      }
4276      my $thread = $2;
4277      my ($n1, $s1, $n2, $s2) = ($3, $4, $5, $6);
4278      my @counts = AdjustSamples($sample_adjustment, $sampling_algorithm,
4279                                 $n1, $s1, $n2, $s2);
4280      if ($thread eq "*") {
4281        AddEntries($profile, $pcs, FixCallerAddresses($stack), $counts[$index]);
4282      } else {
4283        if (!exists($thread_profiles->{$thread})) {
4284          $thread_profiles->{$thread} = {};
4285        }
4286        AddEntries($thread_profiles->{$thread}, $pcs,
4287                   FixCallerAddresses($stack), $counts[$index]);
4288      }
4289    }
4290  }
4291
4292  my $r = {};
4293  $r->{version} = "heap";
4294  $r->{period} = 1;
4295  $r->{profile} = $profile;
4296  $r->{threads} = $thread_profiles;
4297  $r->{libs} = ParseLibraries($prog, $map, $pcs);
4298  $r->{pcs} = $pcs;
4299  return $r;
4300}
4301
4302sub ReadSynchProfile {
4303  my $prog = shift;
4304  local *PROFILE = shift;
4305  my $header = shift;
4306
4307  my $map = '';
4308  my $profile = {};
4309  my $pcs = {};
4310  my $sampling_period = 1;
4311  my $cyclespernanosec = 2.8;   # Default assumption for old binaries
4312  my $seen_clockrate = 0;
4313  my $line;
4314
4315  my $index = 0;
4316  if ($main::opt_total_delay) {
4317    $index = 0;
4318  } elsif ($main::opt_contentions) {
4319    $index = 1;
4320  } elsif ($main::opt_mean_delay) {
4321    $index = 2;
4322  }
4323
4324  while ( $line = <PROFILE> ) {
4325    $line =~ s/\r//g;      # turn windows-looking lines into unix-looking lines
4326    if ( $line =~ /^\s*(\d+)\s+(\d+) \@\s*(.*?)\s*$/ ) {
4327      my ($cycles, $count, $stack) = ($1, $2, $3);
4328
4329      # Convert cycles to nanoseconds
4330      $cycles /= $cyclespernanosec;
4331
4332      # Adjust for sampling done by application
4333      $cycles *= $sampling_period;
4334      $count *= $sampling_period;
4335
4336      my @values = ($cycles, $count, $cycles / $count);
4337      AddEntries($profile, $pcs, FixCallerAddresses($stack), $values[$index]);
4338
4339    } elsif ( $line =~ /^(slow release).*thread \d+  \@\s*(.*?)\s*$/ ||
4340              $line =~ /^\s*(\d+) \@\s*(.*?)\s*$/ ) {
4341      my ($cycles, $stack) = ($1, $2);
4342      if ($cycles !~ /^\d+$/) {
4343        next;
4344      }
4345
4346      # Convert cycles to nanoseconds
4347      $cycles /= $cyclespernanosec;
4348
4349      # Adjust for sampling done by application
4350      $cycles *= $sampling_period;
4351
4352      AddEntries($profile, $pcs, FixCallerAddresses($stack), $cycles);
4353
4354    } elsif ( $line =~ m/^([a-z][^=]*)=(.*)$/ ) {
4355      my ($variable, $value) = ($1,$2);
4356      for ($variable, $value) {
4357        s/^\s+//;
4358        s/\s+$//;
4359      }
4360      if ($variable eq "cycles/second") {
4361        $cyclespernanosec = $value / 1e9;
4362        $seen_clockrate = 1;
4363      } elsif ($variable eq "sampling period") {
4364        $sampling_period = $value;
4365      } elsif ($variable eq "ms since reset") {
4366        # Currently nothing is done with this value in jeprof
4367        # So we just silently ignore it for now
4368      } elsif ($variable eq "discarded samples") {
4369        # Currently nothing is done with this value in jeprof
4370        # So we just silently ignore it for now
4371      } else {
4372        printf STDERR ("Ignoring unnknown variable in /contention output: " .
4373                       "'%s' = '%s'\n",$variable,$value);
4374      }
4375    } else {
4376      # Memory map entry
4377      $map .= $line;
4378    }
4379  }
4380
4381  if (!$seen_clockrate) {
4382    printf STDERR ("No cycles/second entry in profile; Guessing %.1f GHz\n",
4383                   $cyclespernanosec);
4384  }
4385
4386  my $r = {};
4387  $r->{version} = 0;
4388  $r->{period} = $sampling_period;
4389  $r->{profile} = $profile;
4390  $r->{libs} = ParseLibraries($prog, $map, $pcs);
4391  $r->{pcs} = $pcs;
4392  return $r;
4393}
4394
4395# Given a hex value in the form "0x1abcd" or "1abcd", return either
4396# "0001abcd" or "000000000001abcd", depending on the current (global)
4397# address length.
4398sub HexExtend {
4399  my $addr = shift;
4400
4401  $addr =~ s/^(0x)?0*//;
4402  my $zeros_needed = $address_length - length($addr);
4403  if ($zeros_needed < 0) {
4404    printf STDERR "Warning: address $addr is longer than address length $address_length\n";
4405    return $addr;
4406  }
4407  return ("0" x $zeros_needed) . $addr;
4408}
4409
4410##### Symbol extraction #####
4411
4412# Aggressively search the lib_prefix values for the given library
4413# If all else fails, just return the name of the library unmodified.
4414# If the lib_prefix is "/my/path,/other/path" and $file is "/lib/dir/mylib.so"
4415# it will search the following locations in this order, until it finds a file:
4416#   /my/path/lib/dir/mylib.so
4417#   /other/path/lib/dir/mylib.so
4418#   /my/path/dir/mylib.so
4419#   /other/path/dir/mylib.so
4420#   /my/path/mylib.so
4421#   /other/path/mylib.so
4422#   /lib/dir/mylib.so              (returned as last resort)
4423sub FindLibrary {
4424  my $file = shift;
4425  my $suffix = $file;
4426
4427  # Search for the library as described above
4428  do {
4429    foreach my $prefix (@prefix_list) {
4430      my $fullpath = $prefix . $suffix;
4431      if (-e $fullpath) {
4432        return $fullpath;
4433      }
4434    }
4435  } while ($suffix =~ s|^/[^/]+/|/|);
4436  return $file;
4437}
4438
4439# Return path to library with debugging symbols.
4440# For libc libraries, the copy in /usr/lib/debug contains debugging symbols
4441sub DebuggingLibrary {
4442  my $file = shift;
4443  if ($file =~ m|^/|) {
4444      if (-f "/usr/lib/debug$file") {
4445        return "/usr/lib/debug$file";
4446      } elsif (-f "/usr/lib/debug$file.debug") {
4447        return "/usr/lib/debug$file.debug";
4448      }
4449  }
4450  return undef;
4451}
4452
4453# Parse text section header of a library using objdump
4454sub ParseTextSectionHeaderFromObjdump {
4455  my $lib = shift;
4456
4457  my $size = undef;
4458  my $vma;
4459  my $file_offset;
4460  # Get objdump output from the library file to figure out how to
4461  # map between mapped addresses and addresses in the library.
4462  my $cmd = ShellEscape($obj_tool_map{"objdump"}, "-h", $lib);
4463  open(OBJDUMP, "$cmd |") || error("$cmd: $!\n");
4464  while (<OBJDUMP>) {
4465    s/\r//g;         # turn windows-looking lines into unix-looking lines
4466    # Idx Name          Size      VMA       LMA       File off  Algn
4467    #  10 .text         00104b2c  420156f0  420156f0  000156f0  2**4
4468    # For 64-bit objects, VMA and LMA will be 16 hex digits, size and file
4469    # offset may still be 8.  But AddressSub below will still handle that.
4470    my @x = split;
4471    if (($#x >= 6) && ($x[1] eq '.text')) {
4472      $size = $x[2];
4473      $vma = $x[3];
4474      $file_offset = $x[5];
4475      last;
4476    }
4477  }
4478  close(OBJDUMP);
4479
4480  if (!defined($size)) {
4481    return undef;
4482  }
4483
4484  my $r = {};
4485  $r->{size} = $size;
4486  $r->{vma} = $vma;
4487  $r->{file_offset} = $file_offset;
4488
4489  return $r;
4490}
4491
4492# Parse text section header of a library using otool (on OS X)
4493sub ParseTextSectionHeaderFromOtool {
4494  my $lib = shift;
4495
4496  my $size = undef;
4497  my $vma = undef;
4498  my $file_offset = undef;
4499  # Get otool output from the library file to figure out how to
4500  # map between mapped addresses and addresses in the library.
4501  my $command = ShellEscape($obj_tool_map{"otool"}, "-l", $lib);
4502  open(OTOOL, "$command |") || error("$command: $!\n");
4503  my $cmd = "";
4504  my $sectname = "";
4505  my $segname = "";
4506  foreach my $line (<OTOOL>) {
4507    $line =~ s/\r//g;      # turn windows-looking lines into unix-looking lines
4508    # Load command <#>
4509    #       cmd LC_SEGMENT
4510    # [...]
4511    # Section
4512    #   sectname __text
4513    #    segname __TEXT
4514    #       addr 0x000009f8
4515    #       size 0x00018b9e
4516    #     offset 2552
4517    #      align 2^2 (4)
4518    # We will need to strip off the leading 0x from the hex addresses,
4519    # and convert the offset into hex.
4520    if ($line =~ /Load command/) {
4521      $cmd = "";
4522      $sectname = "";
4523      $segname = "";
4524    } elsif ($line =~ /Section/) {
4525      $sectname = "";
4526      $segname = "";
4527    } elsif ($line =~ /cmd (\w+)/) {
4528      $cmd = $1;
4529    } elsif ($line =~ /sectname (\w+)/) {
4530      $sectname = $1;
4531    } elsif ($line =~ /segname (\w+)/) {
4532      $segname = $1;
4533    } elsif (!(($cmd eq "LC_SEGMENT" || $cmd eq "LC_SEGMENT_64") &&
4534               $sectname eq "__text" &&
4535               $segname eq "__TEXT")) {
4536      next;
4537    } elsif ($line =~ /\baddr 0x([0-9a-fA-F]+)/) {
4538      $vma = $1;
4539    } elsif ($line =~ /\bsize 0x([0-9a-fA-F]+)/) {
4540      $size = $1;
4541    } elsif ($line =~ /\boffset ([0-9]+)/) {
4542      $file_offset = sprintf("%016x", $1);
4543    }
4544    if (defined($vma) && defined($size) && defined($file_offset)) {
4545      last;
4546    }
4547  }
4548  close(OTOOL);
4549
4550  if (!defined($vma) || !defined($size) || !defined($file_offset)) {
4551     return undef;
4552  }
4553
4554  my $r = {};
4555  $r->{size} = $size;
4556  $r->{vma} = $vma;
4557  $r->{file_offset} = $file_offset;
4558
4559  return $r;
4560}
4561
4562sub ParseTextSectionHeader {
4563  # obj_tool_map("otool") is only defined if we're in a Mach-O environment
4564  if (defined($obj_tool_map{"otool"})) {
4565    my $r = ParseTextSectionHeaderFromOtool(@_);
4566    if (defined($r)){
4567      return $r;
4568    }
4569  }
4570  # If otool doesn't work, or we don't have it, fall back to objdump
4571  return ParseTextSectionHeaderFromObjdump(@_);
4572}
4573
4574# Split /proc/pid/maps dump into a list of libraries
4575sub ParseLibraries {
4576  return if $main::use_symbol_page;  # We don't need libraries info.
4577  my $prog = Cwd::abs_path(shift);
4578  my $map = shift;
4579  my $pcs = shift;
4580
4581  my $result = [];
4582  my $h = "[a-f0-9]+";
4583  my $zero_offset = HexExtend("0");
4584
4585  my $buildvar = "";
4586  foreach my $l (split("\n", $map)) {
4587    if ($l =~ m/^\s*build=(.*)$/) {
4588      $buildvar = $1;
4589    }
4590
4591    my $start;
4592    my $finish;
4593    my $offset;
4594    my $lib;
4595    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) {
4596      # Full line from /proc/self/maps.  Example:
4597      #   40000000-40015000 r-xp 00000000 03:01 12845071   /lib/ld-2.3.2.so
4598      $start = HexExtend($1);
4599      $finish = HexExtend($2);
4600      $offset = HexExtend($3);
4601      $lib = $4;
4602      $lib =~ s|\\|/|g;     # turn windows-style paths into unix-style paths
4603    } elsif ($l =~ /^\s*($h)-($h):\s*(\S+\.so(\.\d+)*)/) {
4604      # Cooked line from DumpAddressMap.  Example:
4605      #   40000000-40015000: /lib/ld-2.3.2.so
4606      $start = HexExtend($1);
4607      $finish = HexExtend($2);
4608      $offset = $zero_offset;
4609      $lib = $3;
4610    } elsif (($l =~ /^($h)-($h)\s+..x.\s+($h)\s+\S+:\S+\s+\d+\s+(\S+)$/i) && ($4 eq $prog)) {
4611      # PIEs and address space randomization do not play well with our
4612      # default assumption that main executable is at lowest
4613      # addresses. So we're detecting main executable in
4614      # /proc/self/maps as well.
4615      $start = HexExtend($1);
4616      $finish = HexExtend($2);
4617      $offset = HexExtend($3);
4618      $lib = $4;
4619      $lib =~ s|\\|/|g;     # turn windows-style paths into unix-style paths
4620    }
4621    # FreeBSD 10.0 virtual memory map /proc/curproc/map as defined in
4622    # function procfs_doprocmap (sys/fs/procfs/procfs_map.c)
4623    #
4624    # Example:
4625    # 0x800600000 0x80061a000 26 0 0xfffff800035a0000 r-x 75 33 0x1004 COW NC vnode /libexec/ld-elf.s
4626    # o.1 NCH -1
4627    elsif ($l =~ /^(0x$h)\s(0x$h)\s\d+\s\d+\s0x$h\sr-x\s\d+\s\d+\s0x\d+\s(COW|NCO)\s(NC|NNC)\svnode\s(\S+\.so(\.\d+)*)/) {
4628      $start = HexExtend($1);
4629      $finish = HexExtend($2);
4630      $offset = $zero_offset;
4631      $lib = FindLibrary($5);
4632
4633    } else {
4634      next;
4635    }
4636
4637    # Expand "$build" variable if available
4638    $lib =~ s/\$build\b/$buildvar/g;
4639
4640    $lib = FindLibrary($lib);
4641
4642    # Check for pre-relocated libraries, which use pre-relocated symbol tables
4643    # and thus require adjusting the offset that we'll use to translate
4644    # VM addresses into symbol table addresses.
4645    # Only do this if we're not going to fetch the symbol table from a
4646    # debugging copy of the library.
4647    if (!DebuggingLibrary($lib)) {
4648      my $text = ParseTextSectionHeader($lib);
4649      if (defined($text)) {
4650         my $vma_offset = AddressSub($text->{vma}, $text->{file_offset});
4651         $offset = AddressAdd($offset, $vma_offset);
4652      }
4653    }
4654
4655    if($main::opt_debug) { printf STDERR "$start:$finish ($offset) $lib\n"; }
4656    push(@{$result}, [$lib, $start, $finish, $offset]);
4657  }
4658
4659  # Append special entry for additional library (not relocated)
4660  if ($main::opt_lib ne "") {
4661    my $text = ParseTextSectionHeader($main::opt_lib);
4662    if (defined($text)) {
4663       my $start = $text->{vma};
4664       my $finish = AddressAdd($start, $text->{size});
4665
4666       push(@{$result}, [$main::opt_lib, $start, $finish, $start]);
4667    }
4668  }
4669
4670  # Append special entry for the main program.  This covers
4671  # 0..max_pc_value_seen, so that we assume pc values not found in one
4672  # of the library ranges will be treated as coming from the main
4673  # program binary.
4674  my $min_pc = HexExtend("0");
4675  my $max_pc = $min_pc;          # find the maximal PC value in any sample
4676  foreach my $pc (keys(%{$pcs})) {
4677    if (HexExtend($pc) gt $max_pc) { $max_pc = HexExtend($pc); }
4678  }
4679  push(@{$result}, [$prog, $min_pc, $max_pc, $zero_offset]);
4680
4681  return $result;
4682}
4683
4684# Add two hex addresses of length $address_length.
4685# Run jeprof --test for unit test if this is changed.
4686sub AddressAdd {
4687  my $addr1 = shift;
4688  my $addr2 = shift;
4689  my $sum;
4690
4691  if ($address_length == 8) {
4692    # Perl doesn't cope with wraparound arithmetic, so do it explicitly:
4693    $sum = (hex($addr1)+hex($addr2)) % (0x10000000 * 16);
4694    return sprintf("%08x", $sum);
4695
4696  } else {
4697    # Do the addition in 7-nibble chunks to trivialize carry handling.
4698
4699    if ($main::opt_debug and $main::opt_test) {
4700      print STDERR "AddressAdd $addr1 + $addr2 = ";
4701    }
4702
4703    my $a1 = substr($addr1,-7);
4704    $addr1 = substr($addr1,0,-7);
4705    my $a2 = substr($addr2,-7);
4706    $addr2 = substr($addr2,0,-7);
4707    $sum = hex($a1) + hex($a2);
4708    my $c = 0;
4709    if ($sum > 0xfffffff) {
4710      $c = 1;
4711      $sum -= 0x10000000;
4712    }
4713    my $r = sprintf("%07x", $sum);
4714
4715    $a1 = substr($addr1,-7);
4716    $addr1 = substr($addr1,0,-7);
4717    $a2 = substr($addr2,-7);
4718    $addr2 = substr($addr2,0,-7);
4719    $sum = hex($a1) + hex($a2) + $c;
4720    $c = 0;
4721    if ($sum > 0xfffffff) {
4722      $c = 1;
4723      $sum -= 0x10000000;
4724    }
4725    $r = sprintf("%07x", $sum) . $r;
4726
4727    $sum = hex($addr1) + hex($addr2) + $c;
4728    if ($sum > 0xff) { $sum -= 0x100; }
4729    $r = sprintf("%02x", $sum) . $r;
4730
4731    if ($main::opt_debug and $main::opt_test) { print STDERR "$r\n"; }
4732
4733    return $r;
4734  }
4735}
4736
4737
4738# Subtract two hex addresses of length $address_length.
4739# Run jeprof --test for unit test if this is changed.
4740sub AddressSub {
4741  my $addr1 = shift;
4742  my $addr2 = shift;
4743  my $diff;
4744
4745  if ($address_length == 8) {
4746    # Perl doesn't cope with wraparound arithmetic, so do it explicitly:
4747    $diff = (hex($addr1)-hex($addr2)) % (0x10000000 * 16);
4748    return sprintf("%08x", $diff);
4749
4750  } else {
4751    # Do the addition in 7-nibble chunks to trivialize borrow handling.
4752    # if ($main::opt_debug) { print STDERR "AddressSub $addr1 - $addr2 = "; }
4753
4754    my $a1 = hex(substr($addr1,-7));
4755    $addr1 = substr($addr1,0,-7);
4756    my $a2 = hex(substr($addr2,-7));
4757    $addr2 = substr($addr2,0,-7);
4758    my $b = 0;
4759    if ($a2 > $a1) {
4760      $b = 1;
4761      $a1 += 0x10000000;
4762    }
4763    $diff = $a1 - $a2;
4764    my $r = sprintf("%07x", $diff);
4765
4766    $a1 = hex(substr($addr1,-7));
4767    $addr1 = substr($addr1,0,-7);
4768    $a2 = hex(substr($addr2,-7)) + $b;
4769    $addr2 = substr($addr2,0,-7);
4770    $b = 0;
4771    if ($a2 > $a1) {
4772      $b = 1;
4773      $a1 += 0x10000000;
4774    }
4775    $diff = $a1 - $a2;
4776    $r = sprintf("%07x", $diff) . $r;
4777
4778    $a1 = hex($addr1);
4779    $a2 = hex($addr2) + $b;
4780    if ($a2 > $a1) { $a1 += 0x100; }
4781    $diff = $a1 - $a2;
4782    $r = sprintf("%02x", $diff) . $r;
4783
4784    # if ($main::opt_debug) { print STDERR "$r\n"; }
4785
4786    return $r;
4787  }
4788}
4789
4790# Increment a hex addresses of length $address_length.
4791# Run jeprof --test for unit test if this is changed.
4792sub AddressInc {
4793  my $addr = shift;
4794  my $sum;
4795
4796  if ($address_length == 8) {
4797    # Perl doesn't cope with wraparound arithmetic, so do it explicitly:
4798    $sum = (hex($addr)+1) % (0x10000000 * 16);
4799    return sprintf("%08x", $sum);
4800
4801  } else {
4802    # Do the addition in 7-nibble chunks to trivialize carry handling.
4803    # We are always doing this to step through the addresses in a function,
4804    # and will almost never overflow the first chunk, so we check for this
4805    # case and exit early.
4806
4807    # if ($main::opt_debug) { print STDERR "AddressInc $addr1 = "; }
4808
4809    my $a1 = substr($addr,-7);
4810    $addr = substr($addr,0,-7);
4811    $sum = hex($a1) + 1;
4812    my $r = sprintf("%07x", $sum);
4813    if ($sum <= 0xfffffff) {
4814      $r = $addr . $r;
4815      # if ($main::opt_debug) { print STDERR "$r\n"; }
4816      return HexExtend($r);
4817    } else {
4818      $r = "0000000";
4819    }
4820
4821    $a1 = substr($addr,-7);
4822    $addr = substr($addr,0,-7);
4823    $sum = hex($a1) + 1;
4824    $r = sprintf("%07x", $sum) . $r;
4825    if ($sum <= 0xfffffff) {
4826      $r = $addr . $r;
4827      # if ($main::opt_debug) { print STDERR "$r\n"; }
4828      return HexExtend($r);
4829    } else {
4830      $r = "00000000000000";
4831    }
4832
4833    $sum = hex($addr) + 1;
4834    if ($sum > 0xff) { $sum -= 0x100; }
4835    $r = sprintf("%02x", $sum) . $r;
4836
4837    # if ($main::opt_debug) { print STDERR "$r\n"; }
4838    return $r;
4839  }
4840}
4841
4842# Extract symbols for all PC values found in profile
4843sub ExtractSymbols {
4844  my $libs = shift;
4845  my $pcset = shift;
4846
4847  my $symbols = {};
4848
4849  # Map each PC value to the containing library.  To make this faster,
4850  # we sort libraries by their starting pc value (highest first), and
4851  # advance through the libraries as we advance the pc.  Sometimes the
4852  # addresses of libraries may overlap with the addresses of the main
4853  # binary, so to make sure the libraries 'win', we iterate over the
4854  # libraries in reverse order (which assumes the binary doesn't start
4855  # in the middle of a library, which seems a fair assumption).
4856  my @pcs = (sort { $a cmp $b } keys(%{$pcset}));  # pcset is 0-extended strings
4857  foreach my $lib (sort {$b->[1] cmp $a->[1]} @{$libs}) {
4858    my $libname = $lib->[0];
4859    my $start = $lib->[1];
4860    my $finish = $lib->[2];
4861    my $offset = $lib->[3];
4862
4863    # Use debug library if it exists
4864    my $debug_libname = DebuggingLibrary($libname);
4865    if ($debug_libname) {
4866        $libname = $debug_libname;
4867    }
4868
4869    # Get list of pcs that belong in this library.
4870    my $contained = [];
4871    my ($start_pc_index, $finish_pc_index);
4872    # Find smallest finish_pc_index such that $finish < $pc[$finish_pc_index].
4873    for ($finish_pc_index = $#pcs + 1; $finish_pc_index > 0;
4874         $finish_pc_index--) {
4875      last if $pcs[$finish_pc_index - 1] le $finish;
4876    }
4877    # Find smallest start_pc_index such that $start <= $pc[$start_pc_index].
4878    for ($start_pc_index = $finish_pc_index; $start_pc_index > 0;
4879         $start_pc_index--) {
4880      last if $pcs[$start_pc_index - 1] lt $start;
4881    }
4882    # This keeps PC values higher than $pc[$finish_pc_index] in @pcs,
4883    # in case there are overlaps in libraries and the main binary.
4884    @{$contained} = splice(@pcs, $start_pc_index,
4885                           $finish_pc_index - $start_pc_index);
4886    # Map to symbols
4887    MapToSymbols($libname, AddressSub($start, $offset), $contained, $symbols);
4888  }
4889
4890  return $symbols;
4891}
4892
4893# Map list of PC values to symbols for a given image
4894sub MapToSymbols {
4895  my $image = shift;
4896  my $offset = shift;
4897  my $pclist = shift;
4898  my $symbols = shift;
4899
4900  my $debug = 0;
4901
4902  # Ignore empty binaries
4903  if ($#{$pclist} < 0) { return; }
4904
4905  # Figure out the addr2line command to use
4906  my $addr2line = $obj_tool_map{"addr2line"};
4907  my $cmd = ShellEscape($addr2line, "-f", "-C", "-e", $image);
4908  if (exists $obj_tool_map{"addr2line_pdb"}) {
4909    $addr2line = $obj_tool_map{"addr2line_pdb"};
4910    $cmd = ShellEscape($addr2line, "--demangle", "-f", "-C", "-e", $image);
4911  }
4912
4913  # If "addr2line" isn't installed on the system at all, just use
4914  # nm to get what info we can (function names, but not line numbers).
4915  if (system(ShellEscape($addr2line, "--help") . " >$dev_null 2>&1") != 0) {
4916    MapSymbolsWithNM($image, $offset, $pclist, $symbols);
4917    return;
4918  }
4919
4920  # "addr2line -i" can produce a variable number of lines per input
4921  # address, with no separator that allows us to tell when data for
4922  # the next address starts.  So we find the address for a special
4923  # symbol (_fini) and interleave this address between all real
4924  # addresses passed to addr2line.  The name of this special symbol
4925  # can then be used as a separator.
4926  $sep_address = undef;  # May be filled in by MapSymbolsWithNM()
4927  my $nm_symbols = {};
4928  MapSymbolsWithNM($image, $offset, $pclist, $nm_symbols);
4929  if (defined($sep_address)) {
4930    # Only add " -i" to addr2line if the binary supports it.
4931    # addr2line --help returns 0, but not if it sees an unknown flag first.
4932    if (system("$cmd -i --help >$dev_null 2>&1") == 0) {
4933      $cmd .= " -i";
4934    } else {
4935      $sep_address = undef;   # no need for sep_address if we don't support -i
4936    }
4937  }
4938
4939  # Make file with all PC values with intervening 'sep_address' so
4940  # that we can reliably detect the end of inlined function list
4941  open(ADDRESSES, ">$main::tmpfile_sym") || error("$main::tmpfile_sym: $!\n");
4942  if ($debug) { print("---- $image ---\n"); }
4943  for (my $i = 0; $i <= $#{$pclist}; $i++) {
4944    # addr2line always reads hex addresses, and does not need '0x' prefix.
4945    if ($debug) { printf STDERR ("%s\n", $pclist->[$i]); }
4946    printf ADDRESSES ("%s\n", AddressSub($pclist->[$i], $offset));
4947    if (defined($sep_address)) {
4948      printf ADDRESSES ("%s\n", $sep_address);
4949    }
4950  }
4951  close(ADDRESSES);
4952  if ($debug) {
4953    print("----\n");
4954    system("cat", $main::tmpfile_sym);
4955    print("----\n");
4956    system("$cmd < " . ShellEscape($main::tmpfile_sym));
4957    print("----\n");
4958  }
4959
4960  open(SYMBOLS, "$cmd <" . ShellEscape($main::tmpfile_sym) . " |")
4961      || error("$cmd: $!\n");
4962  my $count = 0;   # Index in pclist
4963  while (<SYMBOLS>) {
4964    # Read fullfunction and filelineinfo from next pair of lines
4965    s/\r?\n$//g;
4966    my $fullfunction = $_;
4967    $_ = <SYMBOLS>;
4968    s/\r?\n$//g;
4969    my $filelinenum = $_;
4970
4971    if (defined($sep_address) && $fullfunction eq $sep_symbol) {
4972      # Terminating marker for data for this address
4973      $count++;
4974      next;
4975    }
4976
4977    $filelinenum =~ s|\\|/|g; # turn windows-style paths into unix-style paths
4978
4979    my $pcstr = $pclist->[$count];
4980    my $function = ShortFunctionName($fullfunction);
4981    my $nms = $nm_symbols->{$pcstr};
4982    if (defined($nms)) {
4983      if ($fullfunction eq '??') {
4984        # nm found a symbol for us.
4985        $function = $nms->[0];
4986        $fullfunction = $nms->[2];
4987      } else {
4988	# MapSymbolsWithNM tags each routine with its starting address,
4989	# useful in case the image has multiple occurrences of this
4990	# routine.  (It uses a syntax that resembles template paramters,
4991	# that are automatically stripped out by ShortFunctionName().)
4992	# addr2line does not provide the same information.  So we check
4993	# if nm disambiguated our symbol, and if so take the annotated
4994	# (nm) version of the routine-name.  TODO(csilvers): this won't
4995	# catch overloaded, inlined symbols, which nm doesn't see.
4996	# Better would be to do a check similar to nm's, in this fn.
4997	if ($nms->[2] =~ m/^\Q$function\E/) {  # sanity check it's the right fn
4998	  $function = $nms->[0];
4999	  $fullfunction = $nms->[2];
5000	}
5001      }
5002    }
5003
5004    # Prepend to accumulated symbols for pcstr
5005    # (so that caller comes before callee)
5006    my $sym = $symbols->{$pcstr};
5007    if (!defined($sym)) {
5008      $sym = [];
5009      $symbols->{$pcstr} = $sym;
5010    }
5011    unshift(@{$sym}, $function, $filelinenum, $fullfunction);
5012    if ($debug) { printf STDERR ("%s => [%s]\n", $pcstr, join(" ", @{$sym})); }
5013    if (!defined($sep_address)) {
5014      # Inlining is off, so this entry ends immediately
5015      $count++;
5016    }
5017  }
5018  close(SYMBOLS);
5019}
5020
5021# Use nm to map the list of referenced PCs to symbols.  Return true iff we
5022# are able to read procedure information via nm.
5023sub MapSymbolsWithNM {
5024  my $image = shift;
5025  my $offset = shift;
5026  my $pclist = shift;
5027  my $symbols = shift;
5028
5029  # Get nm output sorted by increasing address
5030  my $symbol_table = GetProcedureBoundaries($image, ".");
5031  if (!%{$symbol_table}) {
5032    return 0;
5033  }
5034  # Start addresses are already the right length (8 or 16 hex digits).
5035  my @names = sort { $symbol_table->{$a}->[0] cmp $symbol_table->{$b}->[0] }
5036    keys(%{$symbol_table});
5037
5038  if ($#names < 0) {
5039    # No symbols: just use addresses
5040    foreach my $pc (@{$pclist}) {
5041      my $pcstr = "0x" . $pc;
5042      $symbols->{$pc} = [$pcstr, "?", $pcstr];
5043    }
5044    return 0;
5045  }
5046
5047  # Sort addresses so we can do a join against nm output
5048  my $index = 0;
5049  my $fullname = $names[0];
5050  my $name = ShortFunctionName($fullname);
5051  foreach my $pc (sort { $a cmp $b } @{$pclist}) {
5052    # Adjust for mapped offset
5053    my $mpc = AddressSub($pc, $offset);
5054    while (($index < $#names) && ($mpc ge $symbol_table->{$fullname}->[1])){
5055      $index++;
5056      $fullname = $names[$index];
5057      $name = ShortFunctionName($fullname);
5058    }
5059    if ($mpc lt $symbol_table->{$fullname}->[1]) {
5060      $symbols->{$pc} = [$name, "?", $fullname];
5061    } else {
5062      my $pcstr = "0x" . $pc;
5063      $symbols->{$pc} = [$pcstr, "?", $pcstr];
5064    }
5065  }
5066  return 1;
5067}
5068
5069sub ShortFunctionName {
5070  my $function = shift;
5071  while ($function =~ s/\([^()]*\)(\s*const)?//g) { }   # Argument types
5072  while ($function =~ s/<[^<>]*>//g)  { }    # Remove template arguments
5073  $function =~ s/^.*\s+(\w+::)/$1/;          # Remove leading type
5074  return $function;
5075}
5076
5077# Trim overly long symbols found in disassembler output
5078sub CleanDisassembly {
5079  my $d = shift;
5080  while ($d =~ s/\([^()%]*\)(\s*const)?//g) { } # Argument types, not (%rax)
5081  while ($d =~ s/(\w+)<[^<>]*>/$1/g)  { }       # Remove template arguments
5082  return $d;
5083}
5084
5085# Clean file name for display
5086sub CleanFileName {
5087  my ($f) = @_;
5088  $f =~ s|^/proc/self/cwd/||;
5089  $f =~ s|^\./||;
5090  return $f;
5091}
5092
5093# Make address relative to section and clean up for display
5094sub UnparseAddress {
5095  my ($offset, $address) = @_;
5096  $address = AddressSub($address, $offset);
5097  $address =~ s/^0x//;
5098  $address =~ s/^0*//;
5099  return $address;
5100}
5101
5102##### Miscellaneous #####
5103
5104# Find the right versions of the above object tools to use.  The
5105# argument is the program file being analyzed, and should be an ELF
5106# 32-bit or ELF 64-bit executable file.  The location of the tools
5107# is determined by considering the following options in this order:
5108#   1) --tools option, if set
5109#   2) JEPROF_TOOLS environment variable, if set
5110#   3) the environment
5111sub ConfigureObjTools {
5112  my $prog_file = shift;
5113
5114  # Check for the existence of $prog_file because /usr/bin/file does not
5115  # predictably return error status in prod.
5116  (-e $prog_file)  || error("$prog_file does not exist.\n");
5117
5118  my $file_type = undef;
5119  if (-e "/usr/bin/file") {
5120    # Follow symlinks (at least for systems where "file" supports that).
5121    my $escaped_prog_file = ShellEscape($prog_file);
5122    $file_type = `/usr/bin/file -L $escaped_prog_file 2>$dev_null ||
5123                  /usr/bin/file $escaped_prog_file`;
5124  } elsif ($^O == "MSWin32") {
5125    $file_type = "MS Windows";
5126  } else {
5127    print STDERR "WARNING: Can't determine the file type of $prog_file";
5128  }
5129
5130  if ($file_type =~ /64-bit/) {
5131    # Change $address_length to 16 if the program file is ELF 64-bit.
5132    # We can't detect this from many (most?) heap or lock contention
5133    # profiles, since the actual addresses referenced are generally in low
5134    # memory even for 64-bit programs.
5135    $address_length = 16;
5136  }
5137
5138  if ($file_type =~ /MS Windows/) {
5139    # For windows, we provide a version of nm and addr2line as part of
5140    # the opensource release, which is capable of parsing
5141    # Windows-style PDB executables.  It should live in the path, or
5142    # in the same directory as jeprof.
5143    $obj_tool_map{"nm_pdb"} = "nm-pdb";
5144    $obj_tool_map{"addr2line_pdb"} = "addr2line-pdb";
5145  }
5146
5147  if ($file_type =~ /Mach-O/) {
5148    # OS X uses otool to examine Mach-O files, rather than objdump.
5149    $obj_tool_map{"otool"} = "otool";
5150    $obj_tool_map{"addr2line"} = "false";  # no addr2line
5151    $obj_tool_map{"objdump"} = "false";  # no objdump
5152  }
5153
5154  # Go fill in %obj_tool_map with the pathnames to use:
5155  foreach my $tool (keys %obj_tool_map) {
5156    $obj_tool_map{$tool} = ConfigureTool($obj_tool_map{$tool});
5157  }
5158}
5159
5160# Returns the path of a caller-specified object tool.  If --tools or
5161# JEPROF_TOOLS are specified, then returns the full path to the tool
5162# with that prefix.  Otherwise, returns the path unmodified (which
5163# means we will look for it on PATH).
5164sub ConfigureTool {
5165  my $tool = shift;
5166  my $path;
5167
5168  # --tools (or $JEPROF_TOOLS) is a comma separated list, where each
5169  # item is either a) a pathname prefix, or b) a map of the form
5170  # <tool>:<path>.  First we look for an entry of type (b) for our
5171  # tool.  If one is found, we use it.  Otherwise, we consider all the
5172  # pathname prefixes in turn, until one yields an existing file.  If
5173  # none does, we use a default path.
5174  my $tools = $main::opt_tools || $ENV{"JEPROF_TOOLS"} || "";
5175  if ($tools =~ m/(,|^)\Q$tool\E:([^,]*)/) {
5176    $path = $2;
5177    # TODO(csilvers): sanity-check that $path exists?  Hard if it's relative.
5178  } elsif ($tools ne '') {
5179    foreach my $prefix (split(',', $tools)) {
5180      next if ($prefix =~ /:/);    # ignore "tool:fullpath" entries in the list
5181      if (-x $prefix . $tool) {
5182        $path = $prefix . $tool;
5183        last;
5184      }
5185    }
5186    if (!$path) {
5187      error("No '$tool' found with prefix specified by " .
5188            "--tools (or \$JEPROF_TOOLS) '$tools'\n");
5189    }
5190  } else {
5191    # ... otherwise use the version that exists in the same directory as
5192    # jeprof.  If there's nothing there, use $PATH.
5193    $0 =~ m,[^/]*$,;     # this is everything after the last slash
5194    my $dirname = $`;    # this is everything up to and including the last slash
5195    if (-x "$dirname$tool") {
5196      $path = "$dirname$tool";
5197    } else {
5198      $path = $tool;
5199    }
5200  }
5201  if ($main::opt_debug) { print STDERR "Using '$path' for '$tool'.\n"; }
5202  return $path;
5203}
5204
5205sub ShellEscape {
5206  my @escaped_words = ();
5207  foreach my $word (@_) {
5208    my $escaped_word = $word;
5209    if ($word =~ m![^a-zA-Z0-9/.,_=-]!) {  # check for anything not in whitelist
5210      $escaped_word =~ s/'/'\\''/;
5211      $escaped_word = "'$escaped_word'";
5212    }
5213    push(@escaped_words, $escaped_word);
5214  }
5215  return join(" ", @escaped_words);
5216}
5217
5218sub cleanup {
5219  unlink($main::tmpfile_sym);
5220  unlink(keys %main::tempnames);
5221
5222  # We leave any collected profiles in $HOME/jeprof in case the user wants
5223  # to look at them later.  We print a message informing them of this.
5224  if ((scalar(@main::profile_files) > 0) &&
5225      defined($main::collected_profile)) {
5226    if (scalar(@main::profile_files) == 1) {
5227      print STDERR "Dynamically gathered profile is in $main::collected_profile\n";
5228    }
5229    print STDERR "If you want to investigate this profile further, you can do:\n";
5230    print STDERR "\n";
5231    print STDERR "  jeprof \\\n";
5232    print STDERR "    $main::prog \\\n";
5233    print STDERR "    $main::collected_profile\n";
5234    print STDERR "\n";
5235  }
5236}
5237
5238sub sighandler {
5239  cleanup();
5240  exit(1);
5241}
5242
5243sub error {
5244  my $msg = shift;
5245  print STDERR $msg;
5246  cleanup();
5247  exit(1);
5248}
5249
5250
5251# Run $nm_command and get all the resulting procedure boundaries whose
5252# names match "$regexp" and returns them in a hashtable mapping from
5253# procedure name to a two-element vector of [start address, end address]
5254sub GetProcedureBoundariesViaNm {
5255  my $escaped_nm_command = shift;    # shell-escaped
5256  my $regexp = shift;
5257
5258  my $symbol_table = {};
5259  open(NM, "$escaped_nm_command |") || error("$escaped_nm_command: $!\n");
5260  my $last_start = "0";
5261  my $routine = "";
5262  while (<NM>) {
5263    s/\r//g;         # turn windows-looking lines into unix-looking lines
5264    if (m/^\s*([0-9a-f]+) (.) (..*)/) {
5265      my $start_val = $1;
5266      my $type = $2;
5267      my $this_routine = $3;
5268
5269      # It's possible for two symbols to share the same address, if
5270      # one is a zero-length variable (like __start_google_malloc) or
5271      # one symbol is a weak alias to another (like __libc_malloc).
5272      # In such cases, we want to ignore all values except for the
5273      # actual symbol, which in nm-speak has type "T".  The logic
5274      # below does this, though it's a bit tricky: what happens when
5275      # we have a series of lines with the same address, is the first
5276      # one gets queued up to be processed.  However, it won't
5277      # *actually* be processed until later, when we read a line with
5278      # a different address.  That means that as long as we're reading
5279      # lines with the same address, we have a chance to replace that
5280      # item in the queue, which we do whenever we see a 'T' entry --
5281      # that is, a line with type 'T'.  If we never see a 'T' entry,
5282      # we'll just go ahead and process the first entry (which never
5283      # got touched in the queue), and ignore the others.
5284      if ($start_val eq $last_start && $type =~ /t/i) {
5285        # We are the 'T' symbol at this address, replace previous symbol.
5286        $routine = $this_routine;
5287        next;
5288      } elsif ($start_val eq $last_start) {
5289        # We're not the 'T' symbol at this address, so ignore us.
5290        next;
5291      }
5292
5293      if ($this_routine eq $sep_symbol) {
5294        $sep_address = HexExtend($start_val);
5295      }
5296
5297      # Tag this routine with the starting address in case the image
5298      # has multiple occurrences of this routine.  We use a syntax
5299      # that resembles template parameters that are automatically
5300      # stripped out by ShortFunctionName()
5301      $this_routine .= "<$start_val>";
5302
5303      if (defined($routine) && $routine =~ m/$regexp/) {
5304        $symbol_table->{$routine} = [HexExtend($last_start),
5305                                     HexExtend($start_val)];
5306      }
5307      $last_start = $start_val;
5308      $routine = $this_routine;
5309    } elsif (m/^Loaded image name: (.+)/) {
5310      # The win32 nm workalike emits information about the binary it is using.
5311      if ($main::opt_debug) { print STDERR "Using Image $1\n"; }
5312    } elsif (m/^PDB file name: (.+)/) {
5313      # The win32 nm workalike emits information about the pdb it is using.
5314      if ($main::opt_debug) { print STDERR "Using PDB $1\n"; }
5315    }
5316  }
5317  close(NM);
5318  # Handle the last line in the nm output.  Unfortunately, we don't know
5319  # how big this last symbol is, because we don't know how big the file
5320  # is.  For now, we just give it a size of 0.
5321  # TODO(csilvers): do better here.
5322  if (defined($routine) && $routine =~ m/$regexp/) {
5323    $symbol_table->{$routine} = [HexExtend($last_start),
5324                                 HexExtend($last_start)];
5325  }
5326  return $symbol_table;
5327}
5328
5329# Gets the procedure boundaries for all routines in "$image" whose names
5330# match "$regexp" and returns them in a hashtable mapping from procedure
5331# name to a two-element vector of [start address, end address].
5332# Will return an empty map if nm is not installed or not working properly.
5333sub GetProcedureBoundaries {
5334  my $image = shift;
5335  my $regexp = shift;
5336
5337  # If $image doesn't start with /, then put ./ in front of it.  This works
5338  # around an obnoxious bug in our probing of nm -f behavior.
5339  # "nm -f $image" is supposed to fail on GNU nm, but if:
5340  #
5341  # a. $image starts with [BbSsPp] (for example, bin/foo/bar), AND
5342  # b. you have a.out in your current directory (a not uncommon occurence)
5343  #
5344  # then "nm -f $image" succeeds because -f only looks at the first letter of
5345  # the argument, which looks valid because it's [BbSsPp], and then since
5346  # there's no image provided, it looks for a.out and finds it.
5347  #
5348  # This regex makes sure that $image starts with . or /, forcing the -f
5349  # parsing to fail since . and / are not valid formats.
5350  $image =~ s#^[^/]#./$&#;
5351
5352  # For libc libraries, the copy in /usr/lib/debug contains debugging symbols
5353  my $debugging = DebuggingLibrary($image);
5354  if ($debugging) {
5355    $image = $debugging;
5356  }
5357
5358  my $nm = $obj_tool_map{"nm"};
5359  my $cppfilt = $obj_tool_map{"c++filt"};
5360
5361  # nm can fail for two reasons: 1) $image isn't a debug library; 2) nm
5362  # binary doesn't support --demangle.  In addition, for OS X we need
5363  # to use the -f flag to get 'flat' nm output (otherwise we don't sort
5364  # properly and get incorrect results).  Unfortunately, GNU nm uses -f
5365  # in an incompatible way.  So first we test whether our nm supports
5366  # --demangle and -f.
5367  my $demangle_flag = "";
5368  my $cppfilt_flag = "";
5369  my $to_devnull = ">$dev_null 2>&1";
5370  if (system(ShellEscape($nm, "--demangle", $image) . $to_devnull) == 0) {
5371    # In this mode, we do "nm --demangle <foo>"
5372    $demangle_flag = "--demangle";
5373    $cppfilt_flag = "";
5374  } elsif (system(ShellEscape($cppfilt, $image) . $to_devnull) == 0) {
5375    # In this mode, we do "nm <foo> | c++filt"
5376    $cppfilt_flag = " | " . ShellEscape($cppfilt);
5377  };
5378  my $flatten_flag = "";
5379  if (system(ShellEscape($nm, "-f", $image) . $to_devnull) == 0) {
5380    $flatten_flag = "-f";
5381  }
5382
5383  # Finally, in the case $imagie isn't a debug library, we try again with
5384  # -D to at least get *exported* symbols.  If we can't use --demangle,
5385  # we use c++filt instead, if it exists on this system.
5386  my @nm_commands = (ShellEscape($nm, "-n", $flatten_flag, $demangle_flag,
5387                                 $image) . " 2>$dev_null $cppfilt_flag",
5388                     ShellEscape($nm, "-D", "-n", $flatten_flag, $demangle_flag,
5389                                 $image) . " 2>$dev_null $cppfilt_flag",
5390                     # 6nm is for Go binaries
5391                     ShellEscape("6nm", "$image") . " 2>$dev_null | sort",
5392                     );
5393
5394  # If the executable is an MS Windows PDB-format executable, we'll
5395  # have set up obj_tool_map("nm_pdb").  In this case, we actually
5396  # want to use both unix nm and windows-specific nm_pdb, since
5397  # PDB-format executables can apparently include dwarf .o files.
5398  if (exists $obj_tool_map{"nm_pdb"}) {
5399    push(@nm_commands,
5400         ShellEscape($obj_tool_map{"nm_pdb"}, "--demangle", $image)
5401         . " 2>$dev_null");
5402  }
5403
5404  foreach my $nm_command (@nm_commands) {
5405    my $symbol_table = GetProcedureBoundariesViaNm($nm_command, $regexp);
5406    return $symbol_table if (%{$symbol_table});
5407  }
5408  my $symbol_table = {};
5409  return $symbol_table;
5410}
5411
5412
5413# The test vectors for AddressAdd/Sub/Inc are 8-16-nibble hex strings.
5414# To make them more readable, we add underscores at interesting places.
5415# This routine removes the underscores, producing the canonical representation
5416# used by jeprof to represent addresses, particularly in the tested routines.
5417sub CanonicalHex {
5418  my $arg = shift;
5419  return join '', (split '_',$arg);
5420}
5421
5422
5423# Unit test for AddressAdd:
5424sub AddressAddUnitTest {
5425  my $test_data_8 = shift;
5426  my $test_data_16 = shift;
5427  my $error_count = 0;
5428  my $fail_count = 0;
5429  my $pass_count = 0;
5430  # print STDERR "AddressAddUnitTest: ", 1+$#{$test_data_8}, " tests\n";
5431
5432  # First a few 8-nibble addresses.  Note that this implementation uses
5433  # plain old arithmetic, so a quick sanity check along with verifying what
5434  # happens to overflow (we want it to wrap):
5435  $address_length = 8;
5436  foreach my $row (@{$test_data_8}) {
5437    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
5438    my $sum = AddressAdd ($row->[0], $row->[1]);
5439    if ($sum ne $row->[2]) {
5440      printf STDERR "ERROR: %s != %s + %s = %s\n", $sum,
5441             $row->[0], $row->[1], $row->[2];
5442      ++$fail_count;
5443    } else {
5444      ++$pass_count;
5445    }
5446  }
5447  printf STDERR "AddressAdd 32-bit tests: %d passes, %d failures\n",
5448         $pass_count, $fail_count;
5449  $error_count = $fail_count;
5450  $fail_count = 0;
5451  $pass_count = 0;
5452
5453  # Now 16-nibble addresses.
5454  $address_length = 16;
5455  foreach my $row (@{$test_data_16}) {
5456    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
5457    my $sum = AddressAdd (CanonicalHex($row->[0]), CanonicalHex($row->[1]));
5458    my $expected = join '', (split '_',$row->[2]);
5459    if ($sum ne CanonicalHex($row->[2])) {
5460      printf STDERR "ERROR: %s != %s + %s = %s\n", $sum,
5461             $row->[0], $row->[1], $row->[2];
5462      ++$fail_count;
5463    } else {
5464      ++$pass_count;
5465    }
5466  }
5467  printf STDERR "AddressAdd 64-bit tests: %d passes, %d failures\n",
5468         $pass_count, $fail_count;
5469  $error_count += $fail_count;
5470
5471  return $error_count;
5472}
5473
5474
5475# Unit test for AddressSub:
5476sub AddressSubUnitTest {
5477  my $test_data_8 = shift;
5478  my $test_data_16 = shift;
5479  my $error_count = 0;
5480  my $fail_count = 0;
5481  my $pass_count = 0;
5482  # print STDERR "AddressSubUnitTest: ", 1+$#{$test_data_8}, " tests\n";
5483
5484  # First a few 8-nibble addresses.  Note that this implementation uses
5485  # plain old arithmetic, so a quick sanity check along with verifying what
5486  # happens to overflow (we want it to wrap):
5487  $address_length = 8;
5488  foreach my $row (@{$test_data_8}) {
5489    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
5490    my $sum = AddressSub ($row->[0], $row->[1]);
5491    if ($sum ne $row->[3]) {
5492      printf STDERR "ERROR: %s != %s - %s = %s\n", $sum,
5493             $row->[0], $row->[1], $row->[3];
5494      ++$fail_count;
5495    } else {
5496      ++$pass_count;
5497    }
5498  }
5499  printf STDERR "AddressSub 32-bit tests: %d passes, %d failures\n",
5500         $pass_count, $fail_count;
5501  $error_count = $fail_count;
5502  $fail_count = 0;
5503  $pass_count = 0;
5504
5505  # Now 16-nibble addresses.
5506  $address_length = 16;
5507  foreach my $row (@{$test_data_16}) {
5508    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
5509    my $sum = AddressSub (CanonicalHex($row->[0]), CanonicalHex($row->[1]));
5510    if ($sum ne CanonicalHex($row->[3])) {
5511      printf STDERR "ERROR: %s != %s - %s = %s\n", $sum,
5512             $row->[0], $row->[1], $row->[3];
5513      ++$fail_count;
5514    } else {
5515      ++$pass_count;
5516    }
5517  }
5518  printf STDERR "AddressSub 64-bit tests: %d passes, %d failures\n",
5519         $pass_count, $fail_count;
5520  $error_count += $fail_count;
5521
5522  return $error_count;
5523}
5524
5525
5526# Unit test for AddressInc:
5527sub AddressIncUnitTest {
5528  my $test_data_8 = shift;
5529  my $test_data_16 = shift;
5530  my $error_count = 0;
5531  my $fail_count = 0;
5532  my $pass_count = 0;
5533  # print STDERR "AddressIncUnitTest: ", 1+$#{$test_data_8}, " tests\n";
5534
5535  # First a few 8-nibble addresses.  Note that this implementation uses
5536  # plain old arithmetic, so a quick sanity check along with verifying what
5537  # happens to overflow (we want it to wrap):
5538  $address_length = 8;
5539  foreach my $row (@{$test_data_8}) {
5540    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
5541    my $sum = AddressInc ($row->[0]);
5542    if ($sum ne $row->[4]) {
5543      printf STDERR "ERROR: %s != %s + 1 = %s\n", $sum,
5544             $row->[0], $row->[4];
5545      ++$fail_count;
5546    } else {
5547      ++$pass_count;
5548    }
5549  }
5550  printf STDERR "AddressInc 32-bit tests: %d passes, %d failures\n",
5551         $pass_count, $fail_count;
5552  $error_count = $fail_count;
5553  $fail_count = 0;
5554  $pass_count = 0;
5555
5556  # Now 16-nibble addresses.
5557  $address_length = 16;
5558  foreach my $row (@{$test_data_16}) {
5559    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
5560    my $sum = AddressInc (CanonicalHex($row->[0]));
5561    if ($sum ne CanonicalHex($row->[4])) {
5562      printf STDERR "ERROR: %s != %s + 1 = %s\n", $sum,
5563             $row->[0], $row->[4];
5564      ++$fail_count;
5565    } else {
5566      ++$pass_count;
5567    }
5568  }
5569  printf STDERR "AddressInc 64-bit tests: %d passes, %d failures\n",
5570         $pass_count, $fail_count;
5571  $error_count += $fail_count;
5572
5573  return $error_count;
5574}
5575
5576
5577# Driver for unit tests.
5578# Currently just the address add/subtract/increment routines for 64-bit.
5579sub RunUnitTests {
5580  my $error_count = 0;
5581
5582  # This is a list of tuples [a, b, a+b, a-b, a+1]
5583  my $unit_test_data_8 = [
5584    [qw(aaaaaaaa 50505050 fafafafa 5a5a5a5a aaaaaaab)],
5585    [qw(50505050 aaaaaaaa fafafafa a5a5a5a6 50505051)],
5586    [qw(ffffffff aaaaaaaa aaaaaaa9 55555555 00000000)],
5587    [qw(00000001 ffffffff 00000000 00000002 00000002)],
5588    [qw(00000001 fffffff0 fffffff1 00000011 00000002)],
5589  ];
5590  my $unit_test_data_16 = [
5591    # The implementation handles data in 7-nibble chunks, so those are the
5592    # interesting boundaries.
5593    [qw(aaaaaaaa 50505050
5594        00_000000f_afafafa 00_0000005_a5a5a5a 00_000000a_aaaaaab)],
5595    [qw(50505050 aaaaaaaa
5596        00_000000f_afafafa ff_ffffffa_5a5a5a6 00_0000005_0505051)],
5597    [qw(ffffffff aaaaaaaa
5598        00_000001a_aaaaaa9 00_0000005_5555555 00_0000010_0000000)],
5599    [qw(00000001 ffffffff
5600        00_0000010_0000000 ff_ffffff0_0000002 00_0000000_0000002)],
5601    [qw(00000001 fffffff0
5602        00_000000f_ffffff1 ff_ffffff0_0000011 00_0000000_0000002)],
5603
5604    [qw(00_a00000a_aaaaaaa 50505050
5605        00_a00000f_afafafa 00_a000005_a5a5a5a 00_a00000a_aaaaaab)],
5606    [qw(0f_fff0005_0505050 aaaaaaaa
5607        0f_fff000f_afafafa 0f_ffefffa_5a5a5a6 0f_fff0005_0505051)],
5608    [qw(00_000000f_fffffff 01_800000a_aaaaaaa
5609        01_800001a_aaaaaa9 fe_8000005_5555555 00_0000010_0000000)],
5610    [qw(00_0000000_0000001 ff_fffffff_fffffff
5611        00_0000000_0000000 00_0000000_0000002 00_0000000_0000002)],
5612    [qw(00_0000000_0000001 ff_fffffff_ffffff0
5613        ff_fffffff_ffffff1 00_0000000_0000011 00_0000000_0000002)],
5614  ];
5615
5616  $error_count += AddressAddUnitTest($unit_test_data_8, $unit_test_data_16);
5617  $error_count += AddressSubUnitTest($unit_test_data_8, $unit_test_data_16);
5618  $error_count += AddressIncUnitTest($unit_test_data_8, $unit_test_data_16);
5619  if ($error_count > 0) {
5620    print STDERR $error_count, " errors: FAILED\n";
5621  } else {
5622    print STDERR "PASS\n";
5623  }
5624  exit ($error_count);
5625}
5626