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