xref: /freebsd/contrib/jemalloc/bin/jeprof.in (revision c43cad87172039ccf38172129c79755ea79e6102)
1<<<<<<< HEAD
2#! /usr/bin/env perl
3
4# Copyright (c) 1998-2007, Google Inc.
5# All rights reserved.
6#
7# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that the following conditions are
9# met:
10#
11#     * Redistributions of source code must retain the above copyright
12# notice, this list of conditions and the following disclaimer.
13#     * Redistributions in binary form must reproduce the above
14# copyright notice, this list of conditions and the following disclaimer
15# in the documentation and/or other materials provided with the
16# distribution.
17#     * Neither the name of Google Inc. nor the names of its
18# contributors may be used to endorse or promote products derived from
19# this software without specific prior written permission.
20#
21# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
33# ---
34# Program for printing the profile generated by common/profiler.cc,
35# or by the heap profiler (common/debugallocation.cc)
36#
37# The profile contains a sequence of entries of the form:
38#       <count> <stack trace>
39# This program parses the profile, and generates user-readable
40# output.
41#
42# Examples:
43#
44# % tools/jeprof "program" "profile"
45#   Enters "interactive" mode
46#
47# % tools/jeprof --text "program" "profile"
48#   Generates one line per procedure
49#
50# % tools/jeprof --gv "program" "profile"
51#   Generates annotated call-graph and displays via "gv"
52#
53# % tools/jeprof --gv --focus=Mutex "program" "profile"
54#   Restrict to code paths that involve an entry that matches "Mutex"
55#
56# % tools/jeprof --gv --focus=Mutex --ignore=string "program" "profile"
57#   Restrict to code paths that involve an entry that matches "Mutex"
58#   and does not match "string"
59#
60# % tools/jeprof --list=IBF_CheckDocid "program" "profile"
61#   Generates disassembly listing of all routines with at least one
62#   sample that match the --list=<regexp> pattern.  The listing is
63#   annotated with the flat and cumulative sample counts at each line.
64#
65# % tools/jeprof --disasm=IBF_CheckDocid "program" "profile"
66#   Generates disassembly listing of all routines with at least one
67#   sample that match the --disasm=<regexp> pattern.  The listing is
68#   annotated with the flat and cumulative sample counts at each PC value.
69#
70# TODO: Use color to indicate files?
71
72use strict;
73use warnings;
74use Getopt::Long;
75use Cwd;
76
77my $JEPROF_VERSION = "@jemalloc_version@";
78my $PPROF_VERSION = "2.0";
79
80# These are the object tools we use which can come from a
81# user-specified location using --tools, from the JEPROF_TOOLS
82# environment variable, or from the environment.
83my %obj_tool_map = (
84  "objdump" => "objdump",
85  "nm" => "nm",
86  "addr2line" => "addr2line",
87  "c++filt" => "c++filt",
88  ## ConfigureObjTools may add architecture-specific entries:
89  #"nm_pdb" => "nm-pdb",       # for reading windows (PDB-format) executables
90  #"addr2line_pdb" => "addr2line-pdb",                                # ditto
91  #"otool" => "otool",         # equivalent of objdump on OS X
92);
93# NOTE: these are lists, so you can put in commandline flags if you want.
94my @DOT = ("dot");          # leave non-absolute, since it may be in /usr/local
95my @GV = ("gv");
96my @EVINCE = ("evince");    # could also be xpdf or perhaps acroread
97my @KCACHEGRIND = ("kcachegrind");
98my @PS2PDF = ("ps2pdf");
99# These are used for dynamic profiles
100my @URL_FETCHER = ("curl", "-s", "--fail");
101
102# These are the web pages that servers need to support for dynamic profiles
103my $HEAP_PAGE = "/pprof/heap";
104my $PROFILE_PAGE = "/pprof/profile";   # must support cgi-param "?seconds=#"
105my $PMUPROFILE_PAGE = "/pprof/pmuprofile(?:\\?.*)?"; # must support cgi-param
106                                                # ?seconds=#&event=x&period=n
107my $GROWTH_PAGE = "/pprof/growth";
108my $CONTENTION_PAGE = "/pprof/contention";
109my $WALL_PAGE = "/pprof/wall(?:\\?.*)?";  # accepts options like namefilter
110my $FILTEREDPROFILE_PAGE = "/pprof/filteredprofile(?:\\?.*)?";
111my $CENSUSPROFILE_PAGE = "/pprof/censusprofile(?:\\?.*)?"; # must support cgi-param
112                                                       # "?seconds=#",
113                                                       # "?tags_regexp=#" and
114                                                       # "?type=#".
115my $SYMBOL_PAGE = "/pprof/symbol";     # must support symbol lookup via POST
116my $PROGRAM_NAME_PAGE = "/pprof/cmdline";
117
118# These are the web pages that can be named on the command line.
119# All the alternatives must begin with /.
120my $PROFILES = "($HEAP_PAGE|$PROFILE_PAGE|$PMUPROFILE_PAGE|" .
121               "$GROWTH_PAGE|$CONTENTION_PAGE|$WALL_PAGE|" .
122               "$FILTEREDPROFILE_PAGE|$CENSUSPROFILE_PAGE)";
123
124# default binary name
125my $UNKNOWN_BINARY = "(unknown)";
126
127# There is a pervasive dependency on the length (in hex characters,
128# i.e., nibbles) of an address, distinguishing between 32-bit and
129# 64-bit profiles.  To err on the safe size, default to 64-bit here:
130my $address_length = 16;
131
132my $dev_null = "/dev/null";
133if (! -e $dev_null && $^O =~ /MSWin/) {    # $^O is the OS perl was built for
134  $dev_null = "nul";
135}
136
137# A list of paths to search for shared object files
138my @prefix_list = ();
139
140# Special routine name that should not have any symbols.
141# Used as separator to parse "addr2line -i" output.
142my $sep_symbol = '_fini';
143my $sep_address = undef;
144
145##### Argument parsing #####
146
147sub usage_string {
148  return <<EOF;
149Usage:
150jeprof [options] <program> <profiles>
151   <profiles> is a space separated list of profile names.
152jeprof [options] <symbolized-profiles>
153   <symbolized-profiles> is a list of profile files where each file contains
154   the necessary symbol mappings  as well as profile data (likely generated
155   with --raw).
156jeprof [options] <profile>
157   <profile> is a remote form.  Symbols are obtained from host:port$SYMBOL_PAGE
158
159   Each name can be:
160   /path/to/profile        - a path to a profile file
161   host:port[/<service>]   - a location of a service to get profile from
162
163   The /<service> can be $HEAP_PAGE, $PROFILE_PAGE, /pprof/pmuprofile,
164                         $GROWTH_PAGE, $CONTENTION_PAGE, /pprof/wall,
165                         $CENSUSPROFILE_PAGE, or /pprof/filteredprofile.
166   For instance:
167     jeprof http://myserver.com:80$HEAP_PAGE
168   If /<service> is omitted, the service defaults to $PROFILE_PAGE (cpu profiling).
169jeprof --symbols <program>
170   Maps addresses to symbol names.  In this mode, stdin should be a
171   list of library mappings, in the same format as is found in the heap-
172   and cpu-profile files (this loosely matches that of /proc/self/maps
173   on linux), followed by a list of hex addresses to map, one per line.
174
175   For more help with querying remote servers, including how to add the
176   necessary server-side support code, see this filename (or one like it):
177
178   /usr/doc/gperftools-$PPROF_VERSION/pprof_remote_servers.html
179
180Options:
181   --cum               Sort by cumulative data
182   --base=<base>       Subtract <base> from <profile> before display
183   --interactive       Run in interactive mode (interactive "help" gives help) [default]
184   --seconds=<n>       Length of time for dynamic profiles [default=30 secs]
185   --add_lib=<file>    Read additional symbols and line info from the given library
186   --lib_prefix=<dir>  Comma separated list of library path prefixes
187
188Reporting Granularity:
189   --addresses         Report at address level
190   --lines             Report at source line level
191   --functions         Report at function level [default]
192   --files             Report at source file level
193
194Output type:
195   --text              Generate text report
196   --callgrind         Generate callgrind format to stdout
197   --gv                Generate Postscript and display
198   --evince            Generate PDF and display
199   --web               Generate SVG and display
200   --list=<regexp>     Generate source listing of matching routines
201   --disasm=<regexp>   Generate disassembly of matching routines
202   --symbols           Print demangled symbol names found at given addresses
203   --dot               Generate DOT file to stdout
204   --ps                Generate Postcript to stdout
205   --pdf               Generate PDF to stdout
206   --svg               Generate SVG to stdout
207   --gif               Generate GIF to stdout
208   --raw               Generate symbolized jeprof data (useful with remote fetch)
209   --collapsed         Generate collapsed stacks for building flame graphs
210                       (see http://www.brendangregg.com/flamegraphs.html)
211
212Heap-Profile Options:
213   --inuse_space       Display in-use (mega)bytes [default]
214   --inuse_objects     Display in-use objects
215   --alloc_space       Display allocated (mega)bytes
216   --alloc_objects     Display allocated objects
217   --show_bytes        Display space in bytes
218   --drop_negative     Ignore negative differences
219
220Contention-profile options:
221   --total_delay       Display total delay at each region [default]
222   --contentions       Display number of delays at each region
223   --mean_delay        Display mean delay at each region
224
225Call-graph Options:
226   --nodecount=<n>     Show at most so many nodes [default=80]
227   --nodefraction=<f>  Hide nodes below <f>*total [default=.005]
228   --edgefraction=<f>  Hide edges below <f>*total [default=.001]
229   --maxdegree=<n>     Max incoming/outgoing edges per node [default=8]
230   --focus=<regexp>    Focus on backtraces with nodes matching <regexp>
231   --thread=<n>        Show profile for thread <n>
232   --ignore=<regexp>   Ignore backtraces with nodes matching <regexp>
233   --scale=<n>         Set GV scaling [default=0]
234   --heapcheck         Make nodes with non-0 object counts
235                       (i.e. direct leak generators) more visible
236   --retain=<regexp>   Retain only nodes that match <regexp>
237   --exclude=<regexp>  Exclude all nodes that match <regexp>
238
239Miscellaneous:
240   --tools=<prefix or binary:fullpath>[,...]   \$PATH for object tool pathnames
241   --test              Run unit tests
242   --help              This message
243   --version           Version information
244   --debug-syms-by-id  (Linux only) Find debug symbol files by build ID as well as by name
245
246Environment Variables:
247   JEPROF_TMPDIR        Profiles directory. Defaults to \$HOME/jeprof
248   JEPROF_TOOLS         Prefix for object tools pathnames
249
250Examples:
251
252jeprof /bin/ls ls.prof
253                       Enters "interactive" mode
254jeprof --text /bin/ls ls.prof
255                       Outputs one line per procedure
256jeprof --web /bin/ls ls.prof
257                       Displays annotated call-graph in web browser
258jeprof --gv /bin/ls ls.prof
259                       Displays annotated call-graph via 'gv'
260jeprof --gv --focus=Mutex /bin/ls ls.prof
261                       Restricts to code paths including a .*Mutex.* entry
262jeprof --gv --focus=Mutex --ignore=string /bin/ls ls.prof
263                       Code paths including Mutex but not string
264jeprof --list=getdir /bin/ls ls.prof
265                       (Per-line) annotated source listing for getdir()
266jeprof --disasm=getdir /bin/ls ls.prof
267                       (Per-PC) annotated disassembly for getdir()
268
269jeprof http://localhost:1234/
270                       Enters "interactive" mode
271jeprof --text localhost:1234
272                       Outputs one line per procedure for localhost:1234
273jeprof --raw localhost:1234 > ./local.raw
274jeprof --text ./local.raw
275                       Fetches a remote profile for later analysis and then
276                       analyzes it in text mode.
277EOF
278}
279
280sub version_string {
281  return <<EOF
282jeprof (part of jemalloc $JEPROF_VERSION)
283based on pprof (part of gperftools $PPROF_VERSION)
284
285Copyright 1998-2007 Google Inc.
286
287This is BSD licensed software; see the source for copying conditions
288and license information.
289There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
290PARTICULAR PURPOSE.
291EOF
292}
293
294sub usage {
295  my $msg = shift;
296  print STDERR "$msg\n\n";
297  print STDERR usage_string();
298  print STDERR "\nFATAL ERROR: $msg\n";    # just as a reminder
299  exit(1);
300}
301
302sub Init() {
303  # Setup tmp-file name and handler to clean it up.
304  # We do this in the very beginning so that we can use
305  # error() and cleanup() function anytime here after.
306  $main::tmpfile_sym = "/tmp/jeprof$$.sym";
307  $main::tmpfile_ps = "/tmp/jeprof$$";
308  $main::next_tmpfile = 0;
309  $SIG{'INT'} = \&sighandler;
310
311  # Cache from filename/linenumber to source code
312  $main::source_cache = ();
313
314  $main::opt_help = 0;
315  $main::opt_version = 0;
316
317  $main::opt_cum = 0;
318  $main::opt_base = '';
319  $main::opt_addresses = 0;
320  $main::opt_lines = 0;
321  $main::opt_functions = 0;
322  $main::opt_files = 0;
323  $main::opt_lib_prefix = "";
324
325  $main::opt_text = 0;
326  $main::opt_callgrind = 0;
327  $main::opt_list = "";
328  $main::opt_disasm = "";
329  $main::opt_symbols = 0;
330  $main::opt_gv = 0;
331  $main::opt_evince = 0;
332  $main::opt_web = 0;
333  $main::opt_dot = 0;
334  $main::opt_ps = 0;
335  $main::opt_pdf = 0;
336  $main::opt_gif = 0;
337  $main::opt_svg = 0;
338  $main::opt_raw = 0;
339  $main::opt_collapsed = 0;
340
341  $main::opt_nodecount = 80;
342  $main::opt_nodefraction = 0.005;
343  $main::opt_edgefraction = 0.001;
344  $main::opt_maxdegree = 8;
345  $main::opt_focus = '';
346  $main::opt_thread = undef;
347  $main::opt_ignore = '';
348  $main::opt_scale = 0;
349  $main::opt_heapcheck = 0;
350  $main::opt_retain = '';
351  $main::opt_exclude = '';
352  $main::opt_seconds = 30;
353  $main::opt_lib = "";
354
355  $main::opt_inuse_space   = 0;
356  $main::opt_inuse_objects = 0;
357  $main::opt_alloc_space   = 0;
358  $main::opt_alloc_objects = 0;
359  $main::opt_show_bytes    = 0;
360  $main::opt_drop_negative = 0;
361  $main::opt_interactive   = 0;
362
363  $main::opt_total_delay = 0;
364  $main::opt_contentions = 0;
365  $main::opt_mean_delay = 0;
366
367  $main::opt_tools   = "";
368  $main::opt_debug   = 0;
369  $main::opt_test    = 0;
370  $main::opt_debug_syms_by_id = 0;
371
372  # These are undocumented flags used only by unittests.
373  $main::opt_test_stride = 0;
374
375  # Are we using $SYMBOL_PAGE?
376  $main::use_symbol_page = 0;
377
378  # Files returned by TempName.
379  %main::tempnames = ();
380
381  # Type of profile we are dealing with
382  # Supported types:
383  #     cpu
384  #     heap
385  #     growth
386  #     contention
387  $main::profile_type = '';     # Empty type means "unknown"
388
389  GetOptions("help!"          => \$main::opt_help,
390             "version!"       => \$main::opt_version,
391             "cum!"           => \$main::opt_cum,
392             "base=s"         => \$main::opt_base,
393             "seconds=i"      => \$main::opt_seconds,
394             "add_lib=s"      => \$main::opt_lib,
395             "lib_prefix=s"   => \$main::opt_lib_prefix,
396             "functions!"     => \$main::opt_functions,
397             "lines!"         => \$main::opt_lines,
398             "addresses!"     => \$main::opt_addresses,
399             "files!"         => \$main::opt_files,
400             "text!"          => \$main::opt_text,
401             "callgrind!"     => \$main::opt_callgrind,
402             "list=s"         => \$main::opt_list,
403             "disasm=s"       => \$main::opt_disasm,
404             "symbols!"       => \$main::opt_symbols,
405             "gv!"            => \$main::opt_gv,
406             "evince!"        => \$main::opt_evince,
407             "web!"           => \$main::opt_web,
408             "dot!"           => \$main::opt_dot,
409             "ps!"            => \$main::opt_ps,
410             "pdf!"           => \$main::opt_pdf,
411             "svg!"           => \$main::opt_svg,
412             "gif!"           => \$main::opt_gif,
413             "raw!"           => \$main::opt_raw,
414             "collapsed!"     => \$main::opt_collapsed,
415             "interactive!"   => \$main::opt_interactive,
416             "nodecount=i"    => \$main::opt_nodecount,
417             "nodefraction=f" => \$main::opt_nodefraction,
418             "edgefraction=f" => \$main::opt_edgefraction,
419             "maxdegree=i"    => \$main::opt_maxdegree,
420             "focus=s"        => \$main::opt_focus,
421             "thread=s"       => \$main::opt_thread,
422             "ignore=s"       => \$main::opt_ignore,
423             "scale=i"        => \$main::opt_scale,
424             "heapcheck"      => \$main::opt_heapcheck,
425             "retain=s"       => \$main::opt_retain,
426             "exclude=s"      => \$main::opt_exclude,
427             "inuse_space!"   => \$main::opt_inuse_space,
428             "inuse_objects!" => \$main::opt_inuse_objects,
429             "alloc_space!"   => \$main::opt_alloc_space,
430             "alloc_objects!" => \$main::opt_alloc_objects,
431             "show_bytes!"    => \$main::opt_show_bytes,
432             "drop_negative!" => \$main::opt_drop_negative,
433             "total_delay!"   => \$main::opt_total_delay,
434             "contentions!"   => \$main::opt_contentions,
435             "mean_delay!"    => \$main::opt_mean_delay,
436             "tools=s"        => \$main::opt_tools,
437             "test!"          => \$main::opt_test,
438             "debug!"         => \$main::opt_debug,
439             "debug-syms-by-id!" => \$main::opt_debug_syms_by_id,
440             # Undocumented flags used only by unittests:
441             "test_stride=i"  => \$main::opt_test_stride,
442      ) || usage("Invalid option(s)");
443
444  # Deal with the standard --help and --version
445  if ($main::opt_help) {
446    print usage_string();
447    exit(0);
448  }
449
450  if ($main::opt_version) {
451    print version_string();
452    exit(0);
453  }
454
455  # Disassembly/listing/symbols mode requires address-level info
456  if ($main::opt_disasm || $main::opt_list || $main::opt_symbols) {
457    $main::opt_functions = 0;
458    $main::opt_lines = 0;
459    $main::opt_addresses = 1;
460    $main::opt_files = 0;
461  }
462
463  # Check heap-profiling flags
464  if ($main::opt_inuse_space +
465      $main::opt_inuse_objects +
466      $main::opt_alloc_space +
467      $main::opt_alloc_objects > 1) {
468    usage("Specify at most on of --inuse/--alloc options");
469  }
470
471  # Check output granularities
472  my $grains =
473      $main::opt_functions +
474      $main::opt_lines +
475      $main::opt_addresses +
476      $main::opt_files +
477      0;
478  if ($grains > 1) {
479    usage("Only specify one output granularity option");
480  }
481  if ($grains == 0) {
482    $main::opt_functions = 1;
483  }
484
485  # Check output modes
486  my $modes =
487      $main::opt_text +
488      $main::opt_callgrind +
489      ($main::opt_list eq '' ? 0 : 1) +
490      ($main::opt_disasm eq '' ? 0 : 1) +
491      ($main::opt_symbols == 0 ? 0 : 1) +
492      $main::opt_gv +
493      $main::opt_evince +
494      $main::opt_web +
495      $main::opt_dot +
496      $main::opt_ps +
497      $main::opt_pdf +
498      $main::opt_svg +
499      $main::opt_gif +
500      $main::opt_raw +
501      $main::opt_collapsed +
502      $main::opt_interactive +
503      0;
504  if ($modes > 1) {
505    usage("Only specify one output mode");
506  }
507  if ($modes == 0) {
508    if (-t STDOUT) {  # If STDOUT is a tty, activate interactive mode
509      $main::opt_interactive = 1;
510    } else {
511      $main::opt_text = 1;
512    }
513  }
514
515  if ($main::opt_test) {
516    RunUnitTests();
517    # Should not return
518    exit(1);
519  }
520
521  # Binary name and profile arguments list
522  $main::prog = "";
523  @main::pfile_args = ();
524
525  # Remote profiling without a binary (using $SYMBOL_PAGE instead)
526  if (@ARGV > 0) {
527    if (IsProfileURL($ARGV[0])) {
528      $main::use_symbol_page = 1;
529    } elsif (IsSymbolizedProfileFile($ARGV[0])) {
530      $main::use_symbolized_profile = 1;
531      $main::prog = $UNKNOWN_BINARY;  # will be set later from the profile file
532    }
533  }
534
535  if ($main::use_symbol_page || $main::use_symbolized_profile) {
536    # We don't need a binary!
537    my %disabled = ('--lines' => $main::opt_lines,
538                    '--disasm' => $main::opt_disasm);
539    for my $option (keys %disabled) {
540      usage("$option cannot be used without a binary") if $disabled{$option};
541    }
542    # Set $main::prog later...
543    scalar(@ARGV) || usage("Did not specify profile file");
544  } elsif ($main::opt_symbols) {
545    # --symbols needs a binary-name (to run nm on, etc) but not profiles
546    $main::prog = shift(@ARGV) || usage("Did not specify program");
547  } else {
548    $main::prog = shift(@ARGV) || usage("Did not specify program");
549    scalar(@ARGV) || usage("Did not specify profile file");
550  }
551
552  # Parse profile file/location arguments
553  foreach my $farg (@ARGV) {
554    if ($farg =~ m/(.*)\@([0-9]+)(|\/.*)$/ ) {
555      my $machine = $1;
556      my $num_machines = $2;
557      my $path = $3;
558      for (my $i = 0; $i < $num_machines; $i++) {
559        unshift(@main::pfile_args, "$i.$machine$path");
560      }
561    } else {
562      unshift(@main::pfile_args, $farg);
563    }
564  }
565
566  if ($main::use_symbol_page) {
567    unless (IsProfileURL($main::pfile_args[0])) {
568      error("The first profile should be a remote form to use $SYMBOL_PAGE\n");
569    }
570    CheckSymbolPage();
571    $main::prog = FetchProgramName();
572  } elsif (!$main::use_symbolized_profile) {  # may not need objtools!
573    ConfigureObjTools($main::prog)
574  }
575
576  # Break the opt_lib_prefix into the prefix_list array
577  @prefix_list = split (',', $main::opt_lib_prefix);
578
579  # Remove trailing / from the prefixes, in the list to prevent
580  # searching things like /my/path//lib/mylib.so
581  foreach (@prefix_list) {
582    s|/+$||;
583  }
584
585  # Flag to prevent us from trying over and over to use
586  #  elfutils if it's not installed (used only with
587  #  --debug-syms-by-id option).
588  $main::gave_up_on_elfutils = 0;
589}
590
591sub FilterAndPrint {
592  my ($profile, $symbols, $libs, $thread) = @_;
593
594  # Get total data in profile
595  my $total = TotalProfile($profile);
596
597  # Remove uniniteresting stack items
598  $profile = RemoveUninterestingFrames($symbols, $profile);
599
600  # Focus?
601  if ($main::opt_focus ne '') {
602    $profile = FocusProfile($symbols, $profile, $main::opt_focus);
603  }
604
605  # Ignore?
606  if ($main::opt_ignore ne '') {
607    $profile = IgnoreProfile($symbols, $profile, $main::opt_ignore);
608  }
609
610  my $calls = ExtractCalls($symbols, $profile);
611
612  # Reduce profiles to required output granularity, and also clean
613  # each stack trace so a given entry exists at most once.
614  my $reduced = ReduceProfile($symbols, $profile);
615
616  # Get derived profiles
617  my $flat = FlatProfile($reduced);
618  my $cumulative = CumulativeProfile($reduced);
619
620  # Print
621  if (!$main::opt_interactive) {
622    if ($main::opt_disasm) {
623      PrintDisassembly($libs, $flat, $cumulative, $main::opt_disasm);
624    } elsif ($main::opt_list) {
625      PrintListing($total, $libs, $flat, $cumulative, $main::opt_list, 0);
626    } elsif ($main::opt_text) {
627      # Make sure the output is empty when have nothing to report
628      # (only matters when --heapcheck is given but we must be
629      # compatible with old branches that did not pass --heapcheck always):
630      if ($total != 0) {
631        printf("Total%s: %s %s\n",
632               (defined($thread) ? " (t$thread)" : ""),
633               Unparse($total), Units());
634      }
635      PrintText($symbols, $flat, $cumulative, -1);
636    } elsif ($main::opt_raw) {
637      PrintSymbolizedProfile($symbols, $profile, $main::prog);
638    } elsif ($main::opt_collapsed) {
639      PrintCollapsedStacks($symbols, $profile);
640    } elsif ($main::opt_callgrind) {
641      PrintCallgrind($calls);
642    } else {
643      if (PrintDot($main::prog, $symbols, $profile, $flat, $cumulative, $total)) {
644        if ($main::opt_gv) {
645          RunGV(TempName($main::next_tmpfile, "ps"), "");
646        } elsif ($main::opt_evince) {
647          RunEvince(TempName($main::next_tmpfile, "pdf"), "");
648        } elsif ($main::opt_web) {
649          my $tmp = TempName($main::next_tmpfile, "svg");
650          RunWeb($tmp);
651          # The command we run might hand the file name off
652          # to an already running browser instance and then exit.
653          # Normally, we'd remove $tmp on exit (right now),
654          # but fork a child to remove $tmp a little later, so that the
655          # browser has time to load it first.
656          delete $main::tempnames{$tmp};
657          if (fork() == 0) {
658            sleep 5;
659            unlink($tmp);
660            exit(0);
661          }
662        }
663      } else {
664        cleanup();
665        exit(1);
666      }
667    }
668  } else {
669    InteractiveMode($profile, $symbols, $libs, $total);
670  }
671}
672
673sub Main() {
674  Init();
675  $main::collected_profile = undef;
676  @main::profile_files = ();
677  $main::op_time = time();
678
679  # Printing symbols is special and requires a lot less info that most.
680  if ($main::opt_symbols) {
681    PrintSymbols(*STDIN);   # Get /proc/maps and symbols output from stdin
682    return;
683  }
684
685  # Fetch all profile data
686  FetchDynamicProfiles();
687
688  # this will hold symbols that we read from the profile files
689  my $symbol_map = {};
690
691  # Read one profile, pick the last item on the list
692  my $data = ReadProfile($main::prog, pop(@main::profile_files));
693  my $profile = $data->{profile};
694  my $pcs = $data->{pcs};
695  my $libs = $data->{libs};   # Info about main program and shared libraries
696  $symbol_map = MergeSymbols($symbol_map, $data->{symbols});
697
698  # Add additional profiles, if available.
699  if (scalar(@main::profile_files) > 0) {
700    foreach my $pname (@main::profile_files) {
701      my $data2 = ReadProfile($main::prog, $pname);
702      $profile = AddProfile($profile, $data2->{profile});
703      $pcs = AddPcs($pcs, $data2->{pcs});
704      $symbol_map = MergeSymbols($symbol_map, $data2->{symbols});
705    }
706  }
707
708  # Subtract base from profile, if specified
709  if ($main::opt_base ne '') {
710    my $base = ReadProfile($main::prog, $main::opt_base);
711    $profile = SubtractProfile($profile, $base->{profile});
712    $pcs = AddPcs($pcs, $base->{pcs});
713    $symbol_map = MergeSymbols($symbol_map, $base->{symbols});
714  }
715
716  # Collect symbols
717  my $symbols;
718  if ($main::use_symbolized_profile) {
719    $symbols = FetchSymbols($pcs, $symbol_map);
720  } elsif ($main::use_symbol_page) {
721    $symbols = FetchSymbols($pcs);
722  } else {
723    # TODO(csilvers): $libs uses the /proc/self/maps data from profile1,
724    # which may differ from the data from subsequent profiles, especially
725    # if they were run on different machines.  Use appropriate libs for
726    # each pc somehow.
727    $symbols = ExtractSymbols($libs, $pcs);
728  }
729
730  if (!defined($main::opt_thread)) {
731    FilterAndPrint($profile, $symbols, $libs);
732  }
733  if (defined($data->{threads})) {
734    foreach my $thread (sort { $a <=> $b } keys(%{$data->{threads}})) {
735      if (defined($main::opt_thread) &&
736          ($main::opt_thread eq '*' || $main::opt_thread == $thread)) {
737        my $thread_profile = $data->{threads}{$thread};
738        FilterAndPrint($thread_profile, $symbols, $libs, $thread);
739      }
740    }
741  }
742
743  cleanup();
744  exit(0);
745}
746
747##### Entry Point #####
748
749Main();
750
751# Temporary code to detect if we're running on a Goobuntu system.
752# These systems don't have the right stuff installed for the special
753# Readline libraries to work, so as a temporary workaround, we default
754# to using the normal stdio code, rather than the fancier readline-based
755# code
756sub ReadlineMightFail {
757  if (-e '/lib/libtermcap.so.2') {
758    return 0;  # libtermcap exists, so readline should be okay
759  } else {
760    return 1;
761  }
762}
763
764sub RunGV {
765  my $fname = shift;
766  my $bg = shift;       # "" or " &" if we should run in background
767  if (!system(ShellEscape(@GV, "--version") . " >$dev_null 2>&1")) {
768    # Options using double dash are supported by this gv version.
769    # Also, turn on noantialias to better handle bug in gv for
770    # postscript files with large dimensions.
771    # TODO: Maybe we should not pass the --noantialias flag
772    # if the gv version is known to work properly without the flag.
773    system(ShellEscape(@GV, "--scale=$main::opt_scale", "--noantialias", $fname)
774           . $bg);
775  } else {
776    # Old gv version - only supports options that use single dash.
777    print STDERR ShellEscape(@GV, "-scale", $main::opt_scale) . "\n";
778    system(ShellEscape(@GV, "-scale", "$main::opt_scale", $fname) . $bg);
779  }
780}
781
782sub RunEvince {
783  my $fname = shift;
784  my $bg = shift;       # "" or " &" if we should run in background
785  system(ShellEscape(@EVINCE, $fname) . $bg);
786}
787
788sub RunWeb {
789  my $fname = shift;
790  print STDERR "Loading web page file:///$fname\n";
791
792  if (`uname` =~ /Darwin/) {
793    # OS X: open will use standard preference for SVG files.
794    system("/usr/bin/open", $fname);
795    return;
796  }
797
798  # Some kind of Unix; try generic symlinks, then specific browsers.
799  # (Stop once we find one.)
800  # Works best if the browser is already running.
801  my @alt = (
802    "/etc/alternatives/gnome-www-browser",
803    "/etc/alternatives/x-www-browser",
804    "google-chrome",
805    "firefox",
806  );
807  foreach my $b (@alt) {
808    if (system($b, $fname) == 0) {
809      return;
810    }
811  }
812
813  print STDERR "Could not load web browser.\n";
814}
815
816sub RunKcachegrind {
817  my $fname = shift;
818  my $bg = shift;       # "" or " &" if we should run in background
819  print STDERR "Starting '@KCACHEGRIND " . $fname . $bg . "'\n";
820  system(ShellEscape(@KCACHEGRIND, $fname) . $bg);
821}
822
823
824##### Interactive helper routines #####
825
826sub InteractiveMode {
827  $| = 1;  # Make output unbuffered for interactive mode
828  my ($orig_profile, $symbols, $libs, $total) = @_;
829
830  print STDERR "Welcome to jeprof!  For help, type 'help'.\n";
831
832  # Use ReadLine if it's installed and input comes from a console.
833  if ( -t STDIN &&
834       !ReadlineMightFail() &&
835       defined(eval {require Term::ReadLine}) ) {
836    my $term = new Term::ReadLine 'jeprof';
837    while ( defined ($_ = $term->readline('(jeprof) '))) {
838      $term->addhistory($_) if /\S/;
839      if (!InteractiveCommand($orig_profile, $symbols, $libs, $total, $_)) {
840        last;    # exit when we get an interactive command to quit
841      }
842    }
843  } else {       # don't have readline
844    while (1) {
845      print STDERR "(jeprof) ";
846      $_ = <STDIN>;
847      last if ! defined $_ ;
848      s/\r//g;         # turn windows-looking lines into unix-looking lines
849
850      # Save some flags that might be reset by InteractiveCommand()
851      my $save_opt_lines = $main::opt_lines;
852
853      if (!InteractiveCommand($orig_profile, $symbols, $libs, $total, $_)) {
854        last;    # exit when we get an interactive command to quit
855      }
856
857      # Restore flags
858      $main::opt_lines = $save_opt_lines;
859    }
860  }
861}
862
863# Takes two args: orig profile, and command to run.
864# Returns 1 if we should keep going, or 0 if we were asked to quit
865sub InteractiveCommand {
866  my($orig_profile, $symbols, $libs, $total, $command) = @_;
867  $_ = $command;                # just to make future m//'s easier
868  if (!defined($_)) {
869    print STDERR "\n";
870    return 0;
871  }
872  if (m/^\s*quit/) {
873    return 0;
874  }
875  if (m/^\s*help/) {
876    InteractiveHelpMessage();
877    return 1;
878  }
879  # Clear all the mode options -- mode is controlled by "$command"
880  $main::opt_text = 0;
881  $main::opt_callgrind = 0;
882  $main::opt_disasm = 0;
883  $main::opt_list = 0;
884  $main::opt_gv = 0;
885  $main::opt_evince = 0;
886  $main::opt_cum = 0;
887
888  if (m/^\s*(text|top)(\d*)\s*(.*)/) {
889    $main::opt_text = 1;
890
891    my $line_limit = ($2 ne "") ? int($2) : 10;
892
893    my $routine;
894    my $ignore;
895    ($routine, $ignore) = ParseInteractiveArgs($3);
896
897    my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
898    my $reduced = ReduceProfile($symbols, $profile);
899
900    # Get derived profiles
901    my $flat = FlatProfile($reduced);
902    my $cumulative = CumulativeProfile($reduced);
903
904    PrintText($symbols, $flat, $cumulative, $line_limit);
905    return 1;
906  }
907  if (m/^\s*callgrind\s*([^ \n]*)/) {
908    $main::opt_callgrind = 1;
909
910    # Get derived profiles
911    my $calls = ExtractCalls($symbols, $orig_profile);
912    my $filename = $1;
913    if ( $1 eq '' ) {
914      $filename = TempName($main::next_tmpfile, "callgrind");
915    }
916    PrintCallgrind($calls, $filename);
917    if ( $1 eq '' ) {
918      RunKcachegrind($filename, " & ");
919      $main::next_tmpfile++;
920    }
921
922    return 1;
923  }
924  if (m/^\s*(web)?list\s*(.+)/) {
925    my $html = (defined($1) && ($1 eq "web"));
926    $main::opt_list = 1;
927
928    my $routine;
929    my $ignore;
930    ($routine, $ignore) = ParseInteractiveArgs($2);
931
932    my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
933    my $reduced = ReduceProfile($symbols, $profile);
934
935    # Get derived profiles
936    my $flat = FlatProfile($reduced);
937    my $cumulative = CumulativeProfile($reduced);
938
939    PrintListing($total, $libs, $flat, $cumulative, $routine, $html);
940    return 1;
941  }
942  if (m/^\s*disasm\s*(.+)/) {
943    $main::opt_disasm = 1;
944
945    my $routine;
946    my $ignore;
947    ($routine, $ignore) = ParseInteractiveArgs($1);
948
949    # Process current profile to account for various settings
950    my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
951    my $reduced = ReduceProfile($symbols, $profile);
952
953    # Get derived profiles
954    my $flat = FlatProfile($reduced);
955    my $cumulative = CumulativeProfile($reduced);
956
957    PrintDisassembly($libs, $flat, $cumulative, $routine);
958    return 1;
959  }
960  if (m/^\s*(gv|web|evince)\s*(.*)/) {
961    $main::opt_gv = 0;
962    $main::opt_evince = 0;
963    $main::opt_web = 0;
964    if ($1 eq "gv") {
965      $main::opt_gv = 1;
966    } elsif ($1 eq "evince") {
967      $main::opt_evince = 1;
968    } elsif ($1 eq "web") {
969      $main::opt_web = 1;
970    }
971
972    my $focus;
973    my $ignore;
974    ($focus, $ignore) = ParseInteractiveArgs($2);
975
976    # Process current profile to account for various settings
977    my $profile = ProcessProfile($total, $orig_profile, $symbols,
978                                 $focus, $ignore);
979    my $reduced = ReduceProfile($symbols, $profile);
980
981    # Get derived profiles
982    my $flat = FlatProfile($reduced);
983    my $cumulative = CumulativeProfile($reduced);
984
985    if (PrintDot($main::prog, $symbols, $profile, $flat, $cumulative, $total)) {
986      if ($main::opt_gv) {
987        RunGV(TempName($main::next_tmpfile, "ps"), " &");
988      } elsif ($main::opt_evince) {
989        RunEvince(TempName($main::next_tmpfile, "pdf"), " &");
990      } elsif ($main::opt_web) {
991        RunWeb(TempName($main::next_tmpfile, "svg"));
992      }
993      $main::next_tmpfile++;
994    }
995    return 1;
996  }
997  if (m/^\s*$/) {
998    return 1;
999  }
1000  print STDERR "Unknown command: try 'help'.\n";
1001  return 1;
1002}
1003
1004
1005sub ProcessProfile {
1006  my $total_count = shift;
1007  my $orig_profile = shift;
1008  my $symbols = shift;
1009  my $focus = shift;
1010  my $ignore = shift;
1011
1012  # Process current profile to account for various settings
1013  my $profile = $orig_profile;
1014  printf("Total: %s %s\n", Unparse($total_count), Units());
1015  if ($focus ne '') {
1016    $profile = FocusProfile($symbols, $profile, $focus);
1017    my $focus_count = TotalProfile($profile);
1018    printf("After focusing on '%s': %s %s of %s (%0.1f%%)\n",
1019           $focus,
1020           Unparse($focus_count), Units(),
1021           Unparse($total_count), ($focus_count*100.0) / $total_count);
1022  }
1023  if ($ignore ne '') {
1024    $profile = IgnoreProfile($symbols, $profile, $ignore);
1025    my $ignore_count = TotalProfile($profile);
1026    printf("After ignoring '%s': %s %s of %s (%0.1f%%)\n",
1027           $ignore,
1028           Unparse($ignore_count), Units(),
1029           Unparse($total_count),
1030           ($ignore_count*100.0) / $total_count);
1031  }
1032
1033  return $profile;
1034}
1035
1036sub InteractiveHelpMessage {
1037  print STDERR <<ENDOFHELP;
1038Interactive jeprof mode
1039
1040Commands:
1041  gv
1042  gv [focus] [-ignore1] [-ignore2]
1043      Show graphical hierarchical display of current profile.  Without
1044      any arguments, shows all samples in the profile.  With the optional
1045      "focus" argument, restricts the samples shown to just those where
1046      the "focus" regular expression matches a routine name on the stack
1047      trace.
1048
1049  web
1050  web [focus] [-ignore1] [-ignore2]
1051      Like GV, but displays profile in your web browser instead of using
1052      Ghostview. Works best if your web browser is already running.
1053      To change the browser that gets used:
1054      On Linux, set the /etc/alternatives/gnome-www-browser symlink.
1055      On OS X, change the Finder association for SVG files.
1056
1057  list [routine_regexp] [-ignore1] [-ignore2]
1058      Show source listing of routines whose names match "routine_regexp"
1059
1060  weblist [routine_regexp] [-ignore1] [-ignore2]
1061     Displays a source listing of routines whose names match "routine_regexp"
1062     in a web browser.  You can click on source lines to view the
1063     corresponding disassembly.
1064
1065  top [--cum] [-ignore1] [-ignore2]
1066  top20 [--cum] [-ignore1] [-ignore2]
1067  top37 [--cum] [-ignore1] [-ignore2]
1068      Show top lines ordered by flat profile count, or cumulative count
1069      if --cum is specified.  If a number is present after 'top', the
1070      top K routines will be shown (defaults to showing the top 10)
1071
1072  disasm [routine_regexp] [-ignore1] [-ignore2]
1073      Show disassembly of routines whose names match "routine_regexp",
1074      annotated with sample counts.
1075
1076  callgrind
1077  callgrind [filename]
1078      Generates callgrind file. If no filename is given, kcachegrind is called.
1079
1080  help - This listing
1081  quit or ^D - End jeprof
1082
1083For commands that accept optional -ignore tags, samples where any routine in
1084the stack trace matches the regular expression in any of the -ignore
1085parameters will be ignored.
1086
1087Further pprof details are available at this location (or one similar):
1088
1089 /usr/doc/gperftools-$PPROF_VERSION/cpu_profiler.html
1090 /usr/doc/gperftools-$PPROF_VERSION/heap_profiler.html
1091
1092ENDOFHELP
1093}
1094sub ParseInteractiveArgs {
1095  my $args = shift;
1096  my $focus = "";
1097  my $ignore = "";
1098  my @x = split(/ +/, $args);
1099  foreach $a (@x) {
1100    if ($a =~ m/^(--|-)lines$/) {
1101      $main::opt_lines = 1;
1102    } elsif ($a =~ m/^(--|-)cum$/) {
1103      $main::opt_cum = 1;
1104    } elsif ($a =~ m/^-(.*)/) {
1105      $ignore .= (($ignore ne "") ? "|" : "" ) . $1;
1106    } else {
1107      $focus .= (($focus ne "") ? "|" : "" ) . $a;
1108    }
1109  }
1110  if ($ignore ne "") {
1111    print STDERR "Ignoring samples in call stacks that match '$ignore'\n";
1112  }
1113  return ($focus, $ignore);
1114}
1115
1116##### Output code #####
1117
1118sub TempName {
1119  my $fnum = shift;
1120  my $ext = shift;
1121  my $file = "$main::tmpfile_ps.$fnum.$ext";
1122  $main::tempnames{$file} = 1;
1123  return $file;
1124}
1125
1126# Print profile data in packed binary format (64-bit) to standard out
1127sub PrintProfileData {
1128  my $profile = shift;
1129
1130  # print header (64-bit style)
1131  # (zero) (header-size) (version) (sample-period) (zero)
1132  print pack('L*', 0, 0, 3, 0, 0, 0, 1, 0, 0, 0);
1133
1134  foreach my $k (keys(%{$profile})) {
1135    my $count = $profile->{$k};
1136    my @addrs = split(/\n/, $k);
1137    if ($#addrs >= 0) {
1138      my $depth = $#addrs + 1;
1139      # int(foo / 2**32) is the only reliable way to get rid of bottom
1140      # 32 bits on both 32- and 64-bit systems.
1141      print pack('L*', $count & 0xFFFFFFFF, int($count / 2**32));
1142      print pack('L*', $depth & 0xFFFFFFFF, int($depth / 2**32));
1143
1144      foreach my $full_addr (@addrs) {
1145        my $addr = $full_addr;
1146        $addr =~ s/0x0*//;  # strip off leading 0x, zeroes
1147        if (length($addr) > 16) {
1148          print STDERR "Invalid address in profile: $full_addr\n";
1149          next;
1150        }
1151        my $low_addr = substr($addr, -8);       # get last 8 hex chars
1152        my $high_addr = substr($addr, -16, 8);  # get up to 8 more hex chars
1153        print pack('L*', hex('0x' . $low_addr), hex('0x' . $high_addr));
1154      }
1155    }
1156  }
1157}
1158
1159# Print symbols and profile data
1160sub PrintSymbolizedProfile {
1161  my $symbols = shift;
1162  my $profile = shift;
1163  my $prog = shift;
1164
1165  $SYMBOL_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
1166  my $symbol_marker = $&;
1167
1168  print '--- ', $symbol_marker, "\n";
1169  if (defined($prog)) {
1170    print 'binary=', $prog, "\n";
1171  }
1172  while (my ($pc, $name) = each(%{$symbols})) {
1173    my $sep = ' ';
1174    print '0x', $pc;
1175    # We have a list of function names, which include the inlined
1176    # calls.  They are separated (and terminated) by --, which is
1177    # illegal in function names.
1178    for (my $j = 2; $j <= $#{$name}; $j += 3) {
1179      print $sep, $name->[$j];
1180      $sep = '--';
1181    }
1182    print "\n";
1183  }
1184  print '---', "\n";
1185
1186  my $profile_marker;
1187  if ($main::profile_type eq 'heap') {
1188    $HEAP_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
1189    $profile_marker = $&;
1190  } elsif ($main::profile_type eq 'growth') {
1191    $GROWTH_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
1192    $profile_marker = $&;
1193  } elsif ($main::profile_type eq 'contention') {
1194    $CONTENTION_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
1195    $profile_marker = $&;
1196  } else { # elsif ($main::profile_type eq 'cpu')
1197    $PROFILE_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
1198    $profile_marker = $&;
1199  }
1200
1201  print '--- ', $profile_marker, "\n";
1202  if (defined($main::collected_profile)) {
1203    # if used with remote fetch, simply dump the collected profile to output.
1204    open(SRC, "<$main::collected_profile");
1205    while (<SRC>) {
1206      print $_;
1207    }
1208    close(SRC);
1209  } else {
1210    # --raw/http: For everything to work correctly for non-remote profiles, we
1211    # would need to extend PrintProfileData() to handle all possible profile
1212    # types, re-enable the code that is currently disabled in ReadCPUProfile()
1213    # and FixCallerAddresses(), and remove the remote profile dumping code in
1214    # the block above.
1215    die "--raw/http: jeprof can only dump remote profiles for --raw\n";
1216    # dump a cpu-format profile to standard out
1217    PrintProfileData($profile);
1218  }
1219}
1220
1221# Print text output
1222sub PrintText {
1223  my $symbols = shift;
1224  my $flat = shift;
1225  my $cumulative = shift;
1226  my $line_limit = shift;
1227
1228  my $total = TotalProfile($flat);
1229
1230  # Which profile to sort by?
1231  my $s = $main::opt_cum ? $cumulative : $flat;
1232
1233  my $running_sum = 0;
1234  my $lines = 0;
1235  foreach my $k (sort { GetEntry($s, $b) <=> GetEntry($s, $a) || $a cmp $b }
1236                 keys(%{$cumulative})) {
1237    my $f = GetEntry($flat, $k);
1238    my $c = GetEntry($cumulative, $k);
1239    $running_sum += $f;
1240
1241    my $sym = $k;
1242    if (exists($symbols->{$k})) {
1243      $sym = $symbols->{$k}->[0] . " " . $symbols->{$k}->[1];
1244      if ($main::opt_addresses) {
1245        $sym = $k . " " . $sym;
1246      }
1247    }
1248
1249    if ($f != 0 || $c != 0) {
1250      printf("%8s %6s %6s %8s %6s %s\n",
1251             Unparse($f),
1252             Percent($f, $total),
1253             Percent($running_sum, $total),
1254             Unparse($c),
1255             Percent($c, $total),
1256             $sym);
1257    }
1258    $lines++;
1259    last if ($line_limit >= 0 && $lines >= $line_limit);
1260  }
1261}
1262
1263# Callgrind format has a compression for repeated function and file
1264# names.  You show the name the first time, and just use its number
1265# subsequently.  This can cut down the file to about a third or a
1266# quarter of its uncompressed size.  $key and $val are the key/value
1267# pair that would normally be printed by callgrind; $map is a map from
1268# value to number.
1269sub CompressedCGName {
1270  my($key, $val, $map) = @_;
1271  my $idx = $map->{$val};
1272  # For very short keys, providing an index hurts rather than helps.
1273  if (length($val) <= 3) {
1274    return "$key=$val\n";
1275  } elsif (defined($idx)) {
1276    return "$key=($idx)\n";
1277  } else {
1278    # scalar(keys $map) gives the number of items in the map.
1279    $idx = scalar(keys(%{$map})) + 1;
1280    $map->{$val} = $idx;
1281    return "$key=($idx) $val\n";
1282  }
1283}
1284
1285# Print the call graph in a way that's suiteable for callgrind.
1286sub PrintCallgrind {
1287  my $calls = shift;
1288  my $filename;
1289  my %filename_to_index_map;
1290  my %fnname_to_index_map;
1291
1292  if ($main::opt_interactive) {
1293    $filename = shift;
1294    print STDERR "Writing callgrind file to '$filename'.\n"
1295  } else {
1296    $filename = "&STDOUT";
1297  }
1298  open(CG, ">$filename");
1299  printf CG ("events: Hits\n\n");
1300  foreach my $call ( map { $_->[0] }
1301                     sort { $a->[1] cmp $b ->[1] ||
1302                            $a->[2] <=> $b->[2] }
1303                     map { /([^:]+):(\d+):([^ ]+)( -> ([^:]+):(\d+):(.+))?/;
1304                           [$_, $1, $2] }
1305                     keys %$calls ) {
1306    my $count = int($calls->{$call});
1307    $call =~ /([^:]+):(\d+):([^ ]+)( -> ([^:]+):(\d+):(.+))?/;
1308    my ( $caller_file, $caller_line, $caller_function,
1309         $callee_file, $callee_line, $callee_function ) =
1310       ( $1, $2, $3, $5, $6, $7 );
1311
1312    # TODO(csilvers): for better compression, collect all the
1313    # caller/callee_files and functions first, before printing
1314    # anything, and only compress those referenced more than once.
1315    printf CG CompressedCGName("fl", $caller_file, \%filename_to_index_map);
1316    printf CG CompressedCGName("fn", $caller_function, \%fnname_to_index_map);
1317    if (defined $6) {
1318      printf CG CompressedCGName("cfl", $callee_file, \%filename_to_index_map);
1319      printf CG CompressedCGName("cfn", $callee_function, \%fnname_to_index_map);
1320      printf CG ("calls=$count $callee_line\n");
1321    }
1322    printf CG ("$caller_line $count\n\n");
1323  }
1324}
1325
1326# Print disassembly for all all routines that match $main::opt_disasm
1327sub PrintDisassembly {
1328  my $libs = shift;
1329  my $flat = shift;
1330  my $cumulative = shift;
1331  my $disasm_opts = shift;
1332
1333  my $total = TotalProfile($flat);
1334
1335  foreach my $lib (@{$libs}) {
1336    my $symbol_table = GetProcedureBoundaries($lib->[0], $disasm_opts);
1337    my $offset = AddressSub($lib->[1], $lib->[3]);
1338    foreach my $routine (sort ByName keys(%{$symbol_table})) {
1339      my $start_addr = $symbol_table->{$routine}->[0];
1340      my $end_addr = $symbol_table->{$routine}->[1];
1341      # See if there are any samples in this routine
1342      my $length = hex(AddressSub($end_addr, $start_addr));
1343      my $addr = AddressAdd($start_addr, $offset);
1344      for (my $i = 0; $i < $length; $i++) {
1345        if (defined($cumulative->{$addr})) {
1346          PrintDisassembledFunction($lib->[0], $offset,
1347                                    $routine, $flat, $cumulative,
1348                                    $start_addr, $end_addr, $total);
1349          last;
1350        }
1351        $addr = AddressInc($addr);
1352      }
1353    }
1354  }
1355}
1356
1357# Return reference to array of tuples of the form:
1358#       [start_address, filename, linenumber, instruction, limit_address]
1359# E.g.,
1360#       ["0x806c43d", "/foo/bar.cc", 131, "ret", "0x806c440"]
1361sub Disassemble {
1362  my $prog = shift;
1363  my $offset = shift;
1364  my $start_addr = shift;
1365  my $end_addr = shift;
1366
1367  my $objdump = $obj_tool_map{"objdump"};
1368  my $cmd = ShellEscape($objdump, "-C", "-d", "-l", "--no-show-raw-insn",
1369                        "--start-address=0x$start_addr",
1370                        "--stop-address=0x$end_addr", $prog);
1371  open(OBJDUMP, "$cmd |") || error("$cmd: $!\n");
1372  my @result = ();
1373  my $filename = "";
1374  my $linenumber = -1;
1375  my $last = ["", "", "", ""];
1376  while (<OBJDUMP>) {
1377    s/\r//g;         # turn windows-looking lines into unix-looking lines
1378    chop;
1379    if (m|\s*([^:\s]+):(\d+)\s*$|) {
1380      # Location line of the form:
1381      #   <filename>:<linenumber>
1382      $filename = $1;
1383      $linenumber = $2;
1384    } elsif (m/^ +([0-9a-f]+):\s*(.*)/) {
1385      # Disassembly line -- zero-extend address to full length
1386      my $addr = HexExtend($1);
1387      my $k = AddressAdd($addr, $offset);
1388      $last->[4] = $k;   # Store ending address for previous instruction
1389      $last = [$k, $filename, $linenumber, $2, $end_addr];
1390      push(@result, $last);
1391    }
1392  }
1393  close(OBJDUMP);
1394  return @result;
1395}
1396
1397# The input file should contain lines of the form /proc/maps-like
1398# output (same format as expected from the profiles) or that looks
1399# like hex addresses (like "0xDEADBEEF").  We will parse all
1400# /proc/maps output, and for all the hex addresses, we will output
1401# "short" symbol names, one per line, in the same order as the input.
1402sub PrintSymbols {
1403  my $maps_and_symbols_file = shift;
1404
1405  # ParseLibraries expects pcs to be in a set.  Fine by us...
1406  my @pclist = ();   # pcs in sorted order
1407  my $pcs = {};
1408  my $map = "";
1409  foreach my $line (<$maps_and_symbols_file>) {
1410    $line =~ s/\r//g;    # turn windows-looking lines into unix-looking lines
1411    if ($line =~ /\b(0x[0-9a-f]+)\b/i) {
1412      push(@pclist, HexExtend($1));
1413      $pcs->{$pclist[-1]} = 1;
1414    } else {
1415      $map .= $line;
1416    }
1417  }
1418
1419  my $libs = ParseLibraries($main::prog, $map, $pcs);
1420  my $symbols = ExtractSymbols($libs, $pcs);
1421
1422  foreach my $pc (@pclist) {
1423    # ->[0] is the shortname, ->[2] is the full name
1424    print(($symbols->{$pc}->[0] || "??") . "\n");
1425  }
1426}
1427
1428
1429# For sorting functions by name
1430sub ByName {
1431  return ShortFunctionName($a) cmp ShortFunctionName($b);
1432}
1433
1434# Print source-listing for all all routines that match $list_opts
1435sub PrintListing {
1436  my $total = shift;
1437  my $libs = shift;
1438  my $flat = shift;
1439  my $cumulative = shift;
1440  my $list_opts = shift;
1441  my $html = shift;
1442
1443  my $output = \*STDOUT;
1444  my $fname = "";
1445
1446  if ($html) {
1447    # Arrange to write the output to a temporary file
1448    $fname = TempName($main::next_tmpfile, "html");
1449    $main::next_tmpfile++;
1450    if (!open(TEMP, ">$fname")) {
1451      print STDERR "$fname: $!\n";
1452      return;
1453    }
1454    $output = \*TEMP;
1455    print $output HtmlListingHeader();
1456    printf $output ("<div class=\"legend\">%s<br>Total: %s %s</div>\n",
1457                    $main::prog, Unparse($total), Units());
1458  }
1459
1460  my $listed = 0;
1461  foreach my $lib (@{$libs}) {
1462    my $symbol_table = GetProcedureBoundaries($lib->[0], $list_opts);
1463    my $offset = AddressSub($lib->[1], $lib->[3]);
1464    foreach my $routine (sort ByName keys(%{$symbol_table})) {
1465      # Print if there are any samples in this routine
1466      my $start_addr = $symbol_table->{$routine}->[0];
1467      my $end_addr = $symbol_table->{$routine}->[1];
1468      my $length = hex(AddressSub($end_addr, $start_addr));
1469      my $addr = AddressAdd($start_addr, $offset);
1470      for (my $i = 0; $i < $length; $i++) {
1471        if (defined($cumulative->{$addr})) {
1472          $listed += PrintSource(
1473            $lib->[0], $offset,
1474            $routine, $flat, $cumulative,
1475            $start_addr, $end_addr,
1476            $html,
1477            $output);
1478          last;
1479        }
1480        $addr = AddressInc($addr);
1481      }
1482    }
1483  }
1484
1485  if ($html) {
1486    if ($listed > 0) {
1487      print $output HtmlListingFooter();
1488      close($output);
1489      RunWeb($fname);
1490    } else {
1491      close($output);
1492      unlink($fname);
1493    }
1494  }
1495}
1496
1497sub HtmlListingHeader {
1498  return <<'EOF';
1499<DOCTYPE html>
1500<html>
1501<head>
1502<title>Pprof listing</title>
1503<style type="text/css">
1504body {
1505  font-family: sans-serif;
1506}
1507h1 {
1508  font-size: 1.5em;
1509  margin-bottom: 4px;
1510}
1511.legend {
1512  font-size: 1.25em;
1513}
1514.line {
1515  color: #aaaaaa;
1516}
1517.nop {
1518  color: #aaaaaa;
1519}
1520.unimportant {
1521  color: #cccccc;
1522}
1523.disasmloc {
1524  color: #000000;
1525}
1526.deadsrc {
1527  cursor: pointer;
1528}
1529.deadsrc:hover {
1530  background-color: #eeeeee;
1531}
1532.livesrc {
1533  color: #0000ff;
1534  cursor: pointer;
1535}
1536.livesrc:hover {
1537  background-color: #eeeeee;
1538}
1539.asm {
1540  color: #008800;
1541  display: none;
1542}
1543</style>
1544<script type="text/javascript">
1545function jeprof_toggle_asm(e) {
1546  var target;
1547  if (!e) e = window.event;
1548  if (e.target) target = e.target;
1549  else if (e.srcElement) target = e.srcElement;
1550
1551  if (target) {
1552    var asm = target.nextSibling;
1553    if (asm && asm.className == "asm") {
1554      asm.style.display = (asm.style.display == "block" ? "" : "block");
1555      e.preventDefault();
1556      return false;
1557    }
1558  }
1559}
1560</script>
1561</head>
1562<body>
1563EOF
1564}
1565
1566sub HtmlListingFooter {
1567  return <<'EOF';
1568</body>
1569</html>
1570EOF
1571}
1572
1573sub HtmlEscape {
1574  my $text = shift;
1575  $text =~ s/&/&amp;/g;
1576  $text =~ s/</&lt;/g;
1577  $text =~ s/>/&gt;/g;
1578  return $text;
1579}
1580
1581# Returns the indentation of the line, if it has any non-whitespace
1582# characters.  Otherwise, returns -1.
1583sub Indentation {
1584  my $line = shift;
1585  if (m/^(\s*)\S/) {
1586    return length($1);
1587  } else {
1588    return -1;
1589  }
1590}
1591
1592# If the symbol table contains inlining info, Disassemble() may tag an
1593# instruction with a location inside an inlined function.  But for
1594# source listings, we prefer to use the location in the function we
1595# are listing.  So use MapToSymbols() to fetch full location
1596# information for each instruction and then pick out the first
1597# location from a location list (location list contains callers before
1598# callees in case of inlining).
1599#
1600# After this routine has run, each entry in $instructions contains:
1601#   [0] start address
1602#   [1] filename for function we are listing
1603#   [2] line number for function we are listing
1604#   [3] disassembly
1605#   [4] limit address
1606#   [5] most specific filename (may be different from [1] due to inlining)
1607#   [6] most specific line number (may be different from [2] due to inlining)
1608sub GetTopLevelLineNumbers {
1609  my ($lib, $offset, $instructions) = @_;
1610  my $pcs = [];
1611  for (my $i = 0; $i <= $#{$instructions}; $i++) {
1612    push(@{$pcs}, $instructions->[$i]->[0]);
1613  }
1614  my $symbols = {};
1615  MapToSymbols($lib, $offset, $pcs, $symbols);
1616  for (my $i = 0; $i <= $#{$instructions}; $i++) {
1617    my $e = $instructions->[$i];
1618    push(@{$e}, $e->[1]);
1619    push(@{$e}, $e->[2]);
1620    my $addr = $e->[0];
1621    my $sym = $symbols->{$addr};
1622    if (defined($sym)) {
1623      if ($#{$sym} >= 2 && $sym->[1] =~ m/^(.*):(\d+)$/) {
1624        $e->[1] = $1;  # File name
1625        $e->[2] = $2;  # Line number
1626      }
1627    }
1628  }
1629}
1630
1631# Print source-listing for one routine
1632sub PrintSource {
1633  my $prog = shift;
1634  my $offset = shift;
1635  my $routine = shift;
1636  my $flat = shift;
1637  my $cumulative = shift;
1638  my $start_addr = shift;
1639  my $end_addr = shift;
1640  my $html = shift;
1641  my $output = shift;
1642
1643  # Disassemble all instructions (just to get line numbers)
1644  my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr);
1645  GetTopLevelLineNumbers($prog, $offset, \@instructions);
1646
1647  # Hack 1: assume that the first source file encountered in the
1648  # disassembly contains the routine
1649  my $filename = undef;
1650  for (my $i = 0; $i <= $#instructions; $i++) {
1651    if ($instructions[$i]->[2] >= 0) {
1652      $filename = $instructions[$i]->[1];
1653      last;
1654    }
1655  }
1656  if (!defined($filename)) {
1657    print STDERR "no filename found in $routine\n";
1658    return 0;
1659  }
1660
1661  # Hack 2: assume that the largest line number from $filename is the
1662  # end of the procedure.  This is typically safe since if P1 contains
1663  # an inlined call to P2, then P2 usually occurs earlier in the
1664  # source file.  If this does not work, we might have to compute a
1665  # density profile or just print all regions we find.
1666  my $lastline = 0;
1667  for (my $i = 0; $i <= $#instructions; $i++) {
1668    my $f = $instructions[$i]->[1];
1669    my $l = $instructions[$i]->[2];
1670    if (($f eq $filename) && ($l > $lastline)) {
1671      $lastline = $l;
1672    }
1673  }
1674
1675  # Hack 3: assume the first source location from "filename" is the start of
1676  # the source code.
1677  my $firstline = 1;
1678  for (my $i = 0; $i <= $#instructions; $i++) {
1679    if ($instructions[$i]->[1] eq $filename) {
1680      $firstline = $instructions[$i]->[2];
1681      last;
1682    }
1683  }
1684
1685  # Hack 4: Extend last line forward until its indentation is less than
1686  # the indentation we saw on $firstline
1687  my $oldlastline = $lastline;
1688  {
1689    if (!open(FILE, "<$filename")) {
1690      print STDERR "$filename: $!\n";
1691      return 0;
1692    }
1693    my $l = 0;
1694    my $first_indentation = -1;
1695    while (<FILE>) {
1696      s/\r//g;         # turn windows-looking lines into unix-looking lines
1697      $l++;
1698      my $indent = Indentation($_);
1699      if ($l >= $firstline) {
1700        if ($first_indentation < 0 && $indent >= 0) {
1701          $first_indentation = $indent;
1702          last if ($first_indentation == 0);
1703        }
1704      }
1705      if ($l >= $lastline && $indent >= 0) {
1706        if ($indent >= $first_indentation) {
1707          $lastline = $l+1;
1708        } else {
1709          last;
1710        }
1711      }
1712    }
1713    close(FILE);
1714  }
1715
1716  # Assign all samples to the range $firstline,$lastline,
1717  # Hack 4: If an instruction does not occur in the range, its samples
1718  # are moved to the next instruction that occurs in the range.
1719  my $samples1 = {};        # Map from line number to flat count
1720  my $samples2 = {};        # Map from line number to cumulative count
1721  my $running1 = 0;         # Unassigned flat counts
1722  my $running2 = 0;         # Unassigned cumulative counts
1723  my $total1 = 0;           # Total flat counts
1724  my $total2 = 0;           # Total cumulative counts
1725  my %disasm = ();          # Map from line number to disassembly
1726  my $running_disasm = "";  # Unassigned disassembly
1727  my $skip_marker = "---\n";
1728  if ($html) {
1729    $skip_marker = "";
1730    for (my $l = $firstline; $l <= $lastline; $l++) {
1731      $disasm{$l} = "";
1732    }
1733  }
1734  my $last_dis_filename = '';
1735  my $last_dis_linenum = -1;
1736  my $last_touched_line = -1;  # To detect gaps in disassembly for a line
1737  foreach my $e (@instructions) {
1738    # Add up counts for all address that fall inside this instruction
1739    my $c1 = 0;
1740    my $c2 = 0;
1741    for (my $a = $e->[0]; $a lt $e->[4]; $a = AddressInc($a)) {
1742      $c1 += GetEntry($flat, $a);
1743      $c2 += GetEntry($cumulative, $a);
1744    }
1745
1746    if ($html) {
1747      my $dis = sprintf("      %6s %6s \t\t%8s: %s ",
1748                        HtmlPrintNumber($c1),
1749                        HtmlPrintNumber($c2),
1750                        UnparseAddress($offset, $e->[0]),
1751                        CleanDisassembly($e->[3]));
1752
1753      # Append the most specific source line associated with this instruction
1754      if (length($dis) < 80) { $dis .= (' ' x (80 - length($dis))) };
1755      $dis = HtmlEscape($dis);
1756      my $f = $e->[5];
1757      my $l = $e->[6];
1758      if ($f ne $last_dis_filename) {
1759        $dis .= sprintf("<span class=disasmloc>%s:%d</span>",
1760                        HtmlEscape(CleanFileName($f)), $l);
1761      } elsif ($l ne $last_dis_linenum) {
1762        # De-emphasize the unchanged file name portion
1763        $dis .= sprintf("<span class=unimportant>%s</span>" .
1764                        "<span class=disasmloc>:%d</span>",
1765                        HtmlEscape(CleanFileName($f)), $l);
1766      } else {
1767        # De-emphasize the entire location
1768        $dis .= sprintf("<span class=unimportant>%s:%d</span>",
1769                        HtmlEscape(CleanFileName($f)), $l);
1770      }
1771      $last_dis_filename = $f;
1772      $last_dis_linenum = $l;
1773      $running_disasm .= $dis;
1774      $running_disasm .= "\n";
1775    }
1776
1777    $running1 += $c1;
1778    $running2 += $c2;
1779    $total1 += $c1;
1780    $total2 += $c2;
1781    my $file = $e->[1];
1782    my $line = $e->[2];
1783    if (($file eq $filename) &&
1784        ($line >= $firstline) &&
1785        ($line <= $lastline)) {
1786      # Assign all accumulated samples to this line
1787      AddEntry($samples1, $line, $running1);
1788      AddEntry($samples2, $line, $running2);
1789      $running1 = 0;
1790      $running2 = 0;
1791      if ($html) {
1792        if ($line != $last_touched_line && $disasm{$line} ne '') {
1793          $disasm{$line} .= "\n";
1794        }
1795        $disasm{$line} .= $running_disasm;
1796        $running_disasm = '';
1797        $last_touched_line = $line;
1798      }
1799    }
1800  }
1801
1802  # Assign any leftover samples to $lastline
1803  AddEntry($samples1, $lastline, $running1);
1804  AddEntry($samples2, $lastline, $running2);
1805  if ($html) {
1806    if ($lastline != $last_touched_line && $disasm{$lastline} ne '') {
1807      $disasm{$lastline} .= "\n";
1808    }
1809    $disasm{$lastline} .= $running_disasm;
1810  }
1811
1812  if ($html) {
1813    printf $output (
1814      "<h1>%s</h1>%s\n<pre onClick=\"jeprof_toggle_asm()\">\n" .
1815      "Total:%6s %6s (flat / cumulative %s)\n",
1816      HtmlEscape(ShortFunctionName($routine)),
1817      HtmlEscape(CleanFileName($filename)),
1818      Unparse($total1),
1819      Unparse($total2),
1820      Units());
1821  } else {
1822    printf $output (
1823      "ROUTINE ====================== %s in %s\n" .
1824      "%6s %6s Total %s (flat / cumulative)\n",
1825      ShortFunctionName($routine),
1826      CleanFileName($filename),
1827      Unparse($total1),
1828      Unparse($total2),
1829      Units());
1830  }
1831  if (!open(FILE, "<$filename")) {
1832    print STDERR "$filename: $!\n";
1833    return 0;
1834  }
1835  my $l = 0;
1836  while (<FILE>) {
1837    s/\r//g;         # turn windows-looking lines into unix-looking lines
1838    $l++;
1839    if ($l >= $firstline - 5 &&
1840        (($l <= $oldlastline + 5) || ($l <= $lastline))) {
1841      chop;
1842      my $text = $_;
1843      if ($l == $firstline) { print $output $skip_marker; }
1844      my $n1 = GetEntry($samples1, $l);
1845      my $n2 = GetEntry($samples2, $l);
1846      if ($html) {
1847        # Emit a span that has one of the following classes:
1848        #    livesrc -- has samples
1849        #    deadsrc -- has disassembly, but with no samples
1850        #    nop     -- has no matching disasembly
1851        # Also emit an optional span containing disassembly.
1852        my $dis = $disasm{$l};
1853        my $asm = "";
1854        if (defined($dis) && $dis ne '') {
1855          $asm = "<span class=\"asm\">" . $dis . "</span>";
1856        }
1857        my $source_class = (($n1 + $n2 > 0)
1858                            ? "livesrc"
1859                            : (($asm ne "") ? "deadsrc" : "nop"));
1860        printf $output (
1861          "<span class=\"line\">%5d</span> " .
1862          "<span class=\"%s\">%6s %6s %s</span>%s\n",
1863          $l, $source_class,
1864          HtmlPrintNumber($n1),
1865          HtmlPrintNumber($n2),
1866          HtmlEscape($text),
1867          $asm);
1868      } else {
1869        printf $output(
1870          "%6s %6s %4d: %s\n",
1871          UnparseAlt($n1),
1872          UnparseAlt($n2),
1873          $l,
1874          $text);
1875      }
1876      if ($l == $lastline)  { print $output $skip_marker; }
1877    };
1878  }
1879  close(FILE);
1880  if ($html) {
1881    print $output "</pre>\n";
1882  }
1883  return 1;
1884}
1885
1886# Return the source line for the specified file/linenumber.
1887# Returns undef if not found.
1888sub SourceLine {
1889  my $file = shift;
1890  my $line = shift;
1891
1892  # Look in cache
1893  if (!defined($main::source_cache{$file})) {
1894    if (100 < scalar keys(%main::source_cache)) {
1895      # Clear the cache when it gets too big
1896      $main::source_cache = ();
1897    }
1898
1899    # Read all lines from the file
1900    if (!open(FILE, "<$file")) {
1901      print STDERR "$file: $!\n";
1902      $main::source_cache{$file} = [];  # Cache the negative result
1903      return undef;
1904    }
1905    my $lines = [];
1906    push(@{$lines}, "");        # So we can use 1-based line numbers as indices
1907    while (<FILE>) {
1908      push(@{$lines}, $_);
1909    }
1910    close(FILE);
1911
1912    # Save the lines in the cache
1913    $main::source_cache{$file} = $lines;
1914  }
1915
1916  my $lines = $main::source_cache{$file};
1917  if (($line < 0) || ($line > $#{$lines})) {
1918    return undef;
1919  } else {
1920    return $lines->[$line];
1921  }
1922}
1923
1924# Print disassembly for one routine with interspersed source if available
1925sub PrintDisassembledFunction {
1926  my $prog = shift;
1927  my $offset = shift;
1928  my $routine = shift;
1929  my $flat = shift;
1930  my $cumulative = shift;
1931  my $start_addr = shift;
1932  my $end_addr = shift;
1933  my $total = shift;
1934
1935  # Disassemble all instructions
1936  my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr);
1937
1938  # Make array of counts per instruction
1939  my @flat_count = ();
1940  my @cum_count = ();
1941  my $flat_total = 0;
1942  my $cum_total = 0;
1943  foreach my $e (@instructions) {
1944    # Add up counts for all address that fall inside this instruction
1945    my $c1 = 0;
1946    my $c2 = 0;
1947    for (my $a = $e->[0]; $a lt $e->[4]; $a = AddressInc($a)) {
1948      $c1 += GetEntry($flat, $a);
1949      $c2 += GetEntry($cumulative, $a);
1950    }
1951    push(@flat_count, $c1);
1952    push(@cum_count, $c2);
1953    $flat_total += $c1;
1954    $cum_total += $c2;
1955  }
1956
1957  # Print header with total counts
1958  printf("ROUTINE ====================== %s\n" .
1959         "%6s %6s %s (flat, cumulative) %.1f%% of total\n",
1960         ShortFunctionName($routine),
1961         Unparse($flat_total),
1962         Unparse($cum_total),
1963         Units(),
1964         ($cum_total * 100.0) / $total);
1965
1966  # Process instructions in order
1967  my $current_file = "";
1968  for (my $i = 0; $i <= $#instructions; ) {
1969    my $e = $instructions[$i];
1970
1971    # Print the new file name whenever we switch files
1972    if ($e->[1] ne $current_file) {
1973      $current_file = $e->[1];
1974      my $fname = $current_file;
1975      $fname =~ s|^\./||;   # Trim leading "./"
1976
1977      # Shorten long file names
1978      if (length($fname) >= 58) {
1979        $fname = "..." . substr($fname, -55);
1980      }
1981      printf("-------------------- %s\n", $fname);
1982    }
1983
1984    # TODO: Compute range of lines to print together to deal with
1985    # small reorderings.
1986    my $first_line = $e->[2];
1987    my $last_line = $first_line;
1988    my %flat_sum = ();
1989    my %cum_sum = ();
1990    for (my $l = $first_line; $l <= $last_line; $l++) {
1991      $flat_sum{$l} = 0;
1992      $cum_sum{$l} = 0;
1993    }
1994
1995    # Find run of instructions for this range of source lines
1996    my $first_inst = $i;
1997    while (($i <= $#instructions) &&
1998           ($instructions[$i]->[2] >= $first_line) &&
1999           ($instructions[$i]->[2] <= $last_line)) {
2000      $e = $instructions[$i];
2001      $flat_sum{$e->[2]} += $flat_count[$i];
2002      $cum_sum{$e->[2]} += $cum_count[$i];
2003      $i++;
2004    }
2005    my $last_inst = $i - 1;
2006
2007    # Print source lines
2008    for (my $l = $first_line; $l <= $last_line; $l++) {
2009      my $line = SourceLine($current_file, $l);
2010      if (!defined($line)) {
2011        $line = "?\n";
2012        next;
2013      } else {
2014        $line =~ s/^\s+//;
2015      }
2016      printf("%6s %6s %5d: %s",
2017             UnparseAlt($flat_sum{$l}),
2018             UnparseAlt($cum_sum{$l}),
2019             $l,
2020             $line);
2021    }
2022
2023    # Print disassembly
2024    for (my $x = $first_inst; $x <= $last_inst; $x++) {
2025      my $e = $instructions[$x];
2026      printf("%6s %6s    %8s: %6s\n",
2027             UnparseAlt($flat_count[$x]),
2028             UnparseAlt($cum_count[$x]),
2029             UnparseAddress($offset, $e->[0]),
2030             CleanDisassembly($e->[3]));
2031    }
2032  }
2033}
2034
2035# Print DOT graph
2036sub PrintDot {
2037  my $prog = shift;
2038  my $symbols = shift;
2039  my $raw = shift;
2040  my $flat = shift;
2041  my $cumulative = shift;
2042  my $overall_total = shift;
2043
2044  # Get total
2045  my $local_total = TotalProfile($flat);
2046  my $nodelimit = int($main::opt_nodefraction * $local_total);
2047  my $edgelimit = int($main::opt_edgefraction * $local_total);
2048  my $nodecount = $main::opt_nodecount;
2049
2050  # Find nodes to include
2051  my @list = (sort { abs(GetEntry($cumulative, $b)) <=>
2052                     abs(GetEntry($cumulative, $a))
2053                     || $a cmp $b }
2054              keys(%{$cumulative}));
2055  my $last = $nodecount - 1;
2056  if ($last > $#list) {
2057    $last = $#list;
2058  }
2059  while (($last >= 0) &&
2060         (abs(GetEntry($cumulative, $list[$last])) <= $nodelimit)) {
2061    $last--;
2062  }
2063  if ($last < 0) {
2064    print STDERR "No nodes to print\n";
2065    return 0;
2066  }
2067
2068  if ($nodelimit > 0 || $edgelimit > 0) {
2069    printf STDERR ("Dropping nodes with <= %s %s; edges with <= %s abs(%s)\n",
2070                   Unparse($nodelimit), Units(),
2071                   Unparse($edgelimit), Units());
2072  }
2073
2074  # Open DOT output file
2075  my $output;
2076  my $escaped_dot = ShellEscape(@DOT);
2077  my $escaped_ps2pdf = ShellEscape(@PS2PDF);
2078  if ($main::opt_gv) {
2079    my $escaped_outfile = ShellEscape(TempName($main::next_tmpfile, "ps"));
2080    $output = "| $escaped_dot -Tps2 >$escaped_outfile";
2081  } elsif ($main::opt_evince) {
2082    my $escaped_outfile = ShellEscape(TempName($main::next_tmpfile, "pdf"));
2083    $output = "| $escaped_dot -Tps2 | $escaped_ps2pdf - $escaped_outfile";
2084  } elsif ($main::opt_ps) {
2085    $output = "| $escaped_dot -Tps2";
2086  } elsif ($main::opt_pdf) {
2087    $output = "| $escaped_dot -Tps2 | $escaped_ps2pdf - -";
2088  } elsif ($main::opt_web || $main::opt_svg) {
2089    # We need to post-process the SVG, so write to a temporary file always.
2090    my $escaped_outfile = ShellEscape(TempName($main::next_tmpfile, "svg"));
2091    $output = "| $escaped_dot -Tsvg >$escaped_outfile";
2092  } elsif ($main::opt_gif) {
2093    $output = "| $escaped_dot -Tgif";
2094  } else {
2095    $output = ">&STDOUT";
2096  }
2097  open(DOT, $output) || error("$output: $!\n");
2098
2099  # Title
2100  printf DOT ("digraph \"%s; %s %s\" {\n",
2101              $prog,
2102              Unparse($overall_total),
2103              Units());
2104  if ($main::opt_pdf) {
2105    # The output is more printable if we set the page size for dot.
2106    printf DOT ("size=\"8,11\"\n");
2107  }
2108  printf DOT ("node [width=0.375,height=0.25];\n");
2109
2110  # Print legend
2111  printf DOT ("Legend [shape=box,fontsize=24,shape=plaintext," .
2112              "label=\"%s\\l%s\\l%s\\l%s\\l%s\\l\"];\n",
2113              $prog,
2114              sprintf("Total %s: %s", Units(), Unparse($overall_total)),
2115              sprintf("Focusing on: %s", Unparse($local_total)),
2116              sprintf("Dropped nodes with <= %s abs(%s)",
2117                      Unparse($nodelimit), Units()),
2118              sprintf("Dropped edges with <= %s %s",
2119                      Unparse($edgelimit), Units())
2120              );
2121
2122  # Print nodes
2123  my %node = ();
2124  my $nextnode = 1;
2125  foreach my $a (@list[0..$last]) {
2126    # Pick font size
2127    my $f = GetEntry($flat, $a);
2128    my $c = GetEntry($cumulative, $a);
2129
2130    my $fs = 8;
2131    if ($local_total > 0) {
2132      $fs = 8 + (50.0 * sqrt(abs($f * 1.0 / $local_total)));
2133    }
2134
2135    $node{$a} = $nextnode++;
2136    my $sym = $a;
2137    $sym =~ s/\s+/\\n/g;
2138    $sym =~ s/::/\\n/g;
2139
2140    # Extra cumulative info to print for non-leaves
2141    my $extra = "";
2142    if ($f != $c) {
2143      $extra = sprintf("\\rof %s (%s)",
2144                       Unparse($c),
2145                       Percent($c, $local_total));
2146    }
2147    my $style = "";
2148    if ($main::opt_heapcheck) {
2149      if ($f > 0) {
2150        # make leak-causing nodes more visible (add a background)
2151        $style = ",style=filled,fillcolor=gray"
2152      } elsif ($f < 0) {
2153        # make anti-leak-causing nodes (which almost never occur)
2154        # stand out as well (triple border)
2155        $style = ",peripheries=3"
2156      }
2157    }
2158
2159    printf DOT ("N%d [label=\"%s\\n%s (%s)%s\\r" .
2160                "\",shape=box,fontsize=%.1f%s];\n",
2161                $node{$a},
2162                $sym,
2163                Unparse($f),
2164                Percent($f, $local_total),
2165                $extra,
2166                $fs,
2167                $style,
2168               );
2169  }
2170
2171  # Get edges and counts per edge
2172  my %edge = ();
2173  my $n;
2174  my $fullname_to_shortname_map = {};
2175  FillFullnameToShortnameMap($symbols, $fullname_to_shortname_map);
2176  foreach my $k (keys(%{$raw})) {
2177    # TODO: omit low %age edges
2178    $n = $raw->{$k};
2179    my @translated = TranslateStack($symbols, $fullname_to_shortname_map, $k);
2180    for (my $i = 1; $i <= $#translated; $i++) {
2181      my $src = $translated[$i];
2182      my $dst = $translated[$i-1];
2183      #next if ($src eq $dst);  # Avoid self-edges?
2184      if (exists($node{$src}) && exists($node{$dst})) {
2185        my $edge_label = "$src\001$dst";
2186        if (!exists($edge{$edge_label})) {
2187          $edge{$edge_label} = 0;
2188        }
2189        $edge{$edge_label} += $n;
2190      }
2191    }
2192  }
2193
2194  # Print edges (process in order of decreasing counts)
2195  my %indegree = ();   # Number of incoming edges added per node so far
2196  my %outdegree = ();  # Number of outgoing edges added per node so far
2197  foreach my $e (sort { $edge{$b} <=> $edge{$a} } keys(%edge)) {
2198    my @x = split(/\001/, $e);
2199    $n = $edge{$e};
2200
2201    # Initialize degree of kept incoming and outgoing edges if necessary
2202    my $src = $x[0];
2203    my $dst = $x[1];
2204    if (!exists($outdegree{$src})) { $outdegree{$src} = 0; }
2205    if (!exists($indegree{$dst})) { $indegree{$dst} = 0; }
2206
2207    my $keep;
2208    if ($indegree{$dst} == 0) {
2209      # Keep edge if needed for reachability
2210      $keep = 1;
2211    } elsif (abs($n) <= $edgelimit) {
2212      # Drop if we are below --edgefraction
2213      $keep = 0;
2214    } elsif ($outdegree{$src} >= $main::opt_maxdegree ||
2215             $indegree{$dst} >= $main::opt_maxdegree) {
2216      # Keep limited number of in/out edges per node
2217      $keep = 0;
2218    } else {
2219      $keep = 1;
2220    }
2221
2222    if ($keep) {
2223      $outdegree{$src}++;
2224      $indegree{$dst}++;
2225
2226      # Compute line width based on edge count
2227      my $fraction = abs($local_total ? (3 * ($n / $local_total)) : 0);
2228      if ($fraction > 1) { $fraction = 1; }
2229      my $w = $fraction * 2;
2230      if ($w < 1 && ($main::opt_web || $main::opt_svg)) {
2231        # SVG output treats line widths < 1 poorly.
2232        $w = 1;
2233      }
2234
2235      # Dot sometimes segfaults if given edge weights that are too large, so
2236      # we cap the weights at a large value
2237      my $edgeweight = abs($n) ** 0.7;
2238      if ($edgeweight > 100000) { $edgeweight = 100000; }
2239      $edgeweight = int($edgeweight);
2240
2241      my $style = sprintf("setlinewidth(%f)", $w);
2242      if ($x[1] =~ m/\(inline\)/) {
2243        $style .= ",dashed";
2244      }
2245
2246      # Use a slightly squashed function of the edge count as the weight
2247      printf DOT ("N%s -> N%s [label=%s, weight=%d, style=\"%s\"];\n",
2248                  $node{$x[0]},
2249                  $node{$x[1]},
2250                  Unparse($n),
2251                  $edgeweight,
2252                  $style);
2253    }
2254  }
2255
2256  print DOT ("}\n");
2257  close(DOT);
2258
2259  if ($main::opt_web || $main::opt_svg) {
2260    # Rewrite SVG to be more usable inside web browser.
2261    RewriteSvg(TempName($main::next_tmpfile, "svg"));
2262  }
2263
2264  return 1;
2265}
2266
2267sub RewriteSvg {
2268  my $svgfile = shift;
2269
2270  open(SVG, $svgfile) || die "open temp svg: $!";
2271  my @svg = <SVG>;
2272  close(SVG);
2273  unlink $svgfile;
2274  my $svg = join('', @svg);
2275
2276  # Dot's SVG output is
2277  #
2278  #    <svg width="___" height="___"
2279  #     viewBox="___" xmlns=...>
2280  #    <g id="graph0" transform="...">
2281  #    ...
2282  #    </g>
2283  #    </svg>
2284  #
2285  # Change it to
2286  #
2287  #    <svg width="100%" height="100%"
2288  #     xmlns=...>
2289  #    $svg_javascript
2290  #    <g id="viewport" transform="translate(0,0)">
2291  #    <g id="graph0" transform="...">
2292  #    ...
2293  #    </g>
2294  #    </g>
2295  #    </svg>
2296
2297  # Fix width, height; drop viewBox.
2298  $svg =~ s/(?s)<svg width="[^"]+" height="[^"]+"(.*?)viewBox="[^"]+"/<svg width="100%" height="100%"$1/;
2299
2300  # Insert script, viewport <g> above first <g>
2301  my $svg_javascript = SvgJavascript();
2302  my $viewport = "<g id=\"viewport\" transform=\"translate(0,0)\">\n";
2303  $svg =~ s/<g id="graph\d"/$svg_javascript$viewport$&/;
2304
2305  # Insert final </g> above </svg>.
2306  $svg =~ s/(.*)(<\/svg>)/$1<\/g>$2/;
2307  $svg =~ s/<g id="graph\d"(.*?)/<g id="viewport"$1/;
2308
2309  if ($main::opt_svg) {
2310    # --svg: write to standard output.
2311    print $svg;
2312  } else {
2313    # Write back to temporary file.
2314    open(SVG, ">$svgfile") || die "open $svgfile: $!";
2315    print SVG $svg;
2316    close(SVG);
2317  }
2318}
2319
2320sub SvgJavascript {
2321  return <<'EOF';
2322<script type="text/ecmascript"><![CDATA[
2323// SVGPan
2324// http://www.cyberz.org/blog/2009/12/08/svgpan-a-javascript-svg-panzoomdrag-library/
2325// Local modification: if(true || ...) below to force panning, never moving.
2326
2327/**
2328 *  SVGPan library 1.2
2329 * ====================
2330 *
2331 * Given an unique existing element with id "viewport", including the
2332 * the library into any SVG adds the following capabilities:
2333 *
2334 *  - Mouse panning
2335 *  - Mouse zooming (using the wheel)
2336 *  - Object dargging
2337 *
2338 * Known issues:
2339 *
2340 *  - Zooming (while panning) on Safari has still some issues
2341 *
2342 * Releases:
2343 *
2344 * 1.2, Sat Mar 20 08:42:50 GMT 2010, Zeng Xiaohui
2345 *	Fixed a bug with browser mouse handler interaction
2346 *
2347 * 1.1, Wed Feb  3 17:39:33 GMT 2010, Zeng Xiaohui
2348 *	Updated the zoom code to support the mouse wheel on Safari/Chrome
2349 *
2350 * 1.0, Andrea Leofreddi
2351 *	First release
2352 *
2353 * This code is licensed under the following BSD license:
2354 *
2355 * Copyright 2009-2010 Andrea Leofreddi <a.leofreddi@itcharm.com>. All rights reserved.
2356 *
2357 * Redistribution and use in source and binary forms, with or without modification, are
2358 * permitted provided that the following conditions are met:
2359 *
2360 *    1. Redistributions of source code must retain the above copyright notice, this list of
2361 *       conditions and the following disclaimer.
2362 *
2363 *    2. Redistributions in binary form must reproduce the above copyright notice, this list
2364 *       of conditions and the following disclaimer in the documentation and/or other materials
2365 *       provided with the distribution.
2366 *
2367 * THIS SOFTWARE IS PROVIDED BY Andrea Leofreddi ``AS IS'' AND ANY EXPRESS OR IMPLIED
2368 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
2369 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Andrea Leofreddi OR
2370 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2371 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2372 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
2373 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
2374 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
2375 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2376 *
2377 * The views and conclusions contained in the software and documentation are those of the
2378 * authors and should not be interpreted as representing official policies, either expressed
2379 * or implied, of Andrea Leofreddi.
2380 */
2381
2382var root = document.documentElement;
2383
2384var state = 'none', stateTarget, stateOrigin, stateTf;
2385
2386setupHandlers(root);
2387
2388/**
2389 * Register handlers
2390 */
2391function setupHandlers(root){
2392	setAttributes(root, {
2393		"onmouseup" : "add(evt)",
2394		"onmousedown" : "handleMouseDown(evt)",
2395		"onmousemove" : "handleMouseMove(evt)",
2396		"onmouseup" : "handleMouseUp(evt)",
2397		//"onmouseout" : "handleMouseUp(evt)", // Decomment this to stop the pan functionality when dragging out of the SVG element
2398	});
2399
2400	if(navigator.userAgent.toLowerCase().indexOf('webkit') >= 0)
2401		window.addEventListener('mousewheel', handleMouseWheel, false); // Chrome/Safari
2402	else
2403		window.addEventListener('DOMMouseScroll', handleMouseWheel, false); // Others
2404
2405	var g = svgDoc.getElementById("svg");
2406	g.width = "100%";
2407	g.height = "100%";
2408}
2409
2410/**
2411 * Instance an SVGPoint object with given event coordinates.
2412 */
2413function getEventPoint(evt) {
2414	var p = root.createSVGPoint();
2415
2416	p.x = evt.clientX;
2417	p.y = evt.clientY;
2418
2419	return p;
2420}
2421
2422/**
2423 * Sets the current transform matrix of an element.
2424 */
2425function setCTM(element, matrix) {
2426	var s = "matrix(" + matrix.a + "," + matrix.b + "," + matrix.c + "," + matrix.d + "," + matrix.e + "," + matrix.f + ")";
2427
2428	element.setAttribute("transform", s);
2429}
2430
2431/**
2432 * Dumps a matrix to a string (useful for debug).
2433 */
2434function dumpMatrix(matrix) {
2435	var s = "[ " + matrix.a + ", " + matrix.c + ", " + matrix.e + "\n  " + matrix.b + ", " + matrix.d + ", " + matrix.f + "\n  0, 0, 1 ]";
2436
2437	return s;
2438}
2439
2440/**
2441 * Sets attributes of an element.
2442 */
2443function setAttributes(element, attributes){
2444	for (i in attributes)
2445		element.setAttributeNS(null, i, attributes[i]);
2446}
2447
2448/**
2449 * Handle mouse move event.
2450 */
2451function handleMouseWheel(evt) {
2452	if(evt.preventDefault)
2453		evt.preventDefault();
2454
2455	evt.returnValue = false;
2456
2457	var svgDoc = evt.target.ownerDocument;
2458
2459	var delta;
2460
2461	if(evt.wheelDelta)
2462		delta = evt.wheelDelta / 3600; // Chrome/Safari
2463	else
2464		delta = evt.detail / -90; // Mozilla
2465
2466	var z = 1 + delta; // Zoom factor: 0.9/1.1
2467
2468	var g = svgDoc.getElementById("viewport");
2469
2470	var p = getEventPoint(evt);
2471
2472	p = p.matrixTransform(g.getCTM().inverse());
2473
2474	// Compute new scale matrix in current mouse position
2475	var k = root.createSVGMatrix().translate(p.x, p.y).scale(z).translate(-p.x, -p.y);
2476
2477        setCTM(g, g.getCTM().multiply(k));
2478
2479	stateTf = stateTf.multiply(k.inverse());
2480}
2481
2482/**
2483 * Handle mouse move event.
2484 */
2485function handleMouseMove(evt) {
2486	if(evt.preventDefault)
2487		evt.preventDefault();
2488
2489	evt.returnValue = false;
2490
2491	var svgDoc = evt.target.ownerDocument;
2492
2493	var g = svgDoc.getElementById("viewport");
2494
2495	if(state == 'pan') {
2496		// Pan mode
2497		var p = getEventPoint(evt).matrixTransform(stateTf);
2498
2499		setCTM(g, stateTf.inverse().translate(p.x - stateOrigin.x, p.y - stateOrigin.y));
2500	} else if(state == 'move') {
2501		// Move mode
2502		var p = getEventPoint(evt).matrixTransform(g.getCTM().inverse());
2503
2504		setCTM(stateTarget, root.createSVGMatrix().translate(p.x - stateOrigin.x, p.y - stateOrigin.y).multiply(g.getCTM().inverse()).multiply(stateTarget.getCTM()));
2505
2506		stateOrigin = p;
2507	}
2508}
2509
2510/**
2511 * Handle click event.
2512 */
2513function handleMouseDown(evt) {
2514	if(evt.preventDefault)
2515		evt.preventDefault();
2516
2517	evt.returnValue = false;
2518
2519	var svgDoc = evt.target.ownerDocument;
2520
2521	var g = svgDoc.getElementById("viewport");
2522
2523	if(true || evt.target.tagName == "svg") {
2524		// Pan mode
2525		state = 'pan';
2526
2527		stateTf = g.getCTM().inverse();
2528
2529		stateOrigin = getEventPoint(evt).matrixTransform(stateTf);
2530	} else {
2531		// Move mode
2532		state = 'move';
2533
2534		stateTarget = evt.target;
2535
2536		stateTf = g.getCTM().inverse();
2537
2538		stateOrigin = getEventPoint(evt).matrixTransform(stateTf);
2539	}
2540}
2541
2542/**
2543 * Handle mouse button release event.
2544 */
2545function handleMouseUp(evt) {
2546	if(evt.preventDefault)
2547		evt.preventDefault();
2548
2549	evt.returnValue = false;
2550
2551	var svgDoc = evt.target.ownerDocument;
2552
2553	if(state == 'pan' || state == 'move') {
2554		// Quit pan mode
2555		state = '';
2556	}
2557}
2558
2559]]></script>
2560EOF
2561}
2562
2563# Provides a map from fullname to shortname for cases where the
2564# shortname is ambiguous.  The symlist has both the fullname and
2565# shortname for all symbols, which is usually fine, but sometimes --
2566# such as overloaded functions -- two different fullnames can map to
2567# the same shortname.  In that case, we use the address of the
2568# function to disambiguate the two.  This function fills in a map that
2569# maps fullnames to modified shortnames in such cases.  If a fullname
2570# is not present in the map, the 'normal' shortname provided by the
2571# symlist is the appropriate one to use.
2572sub FillFullnameToShortnameMap {
2573  my $symbols = shift;
2574  my $fullname_to_shortname_map = shift;
2575  my $shortnames_seen_once = {};
2576  my $shortnames_seen_more_than_once = {};
2577
2578  foreach my $symlist (values(%{$symbols})) {
2579    # TODO(csilvers): deal with inlined symbols too.
2580    my $shortname = $symlist->[0];
2581    my $fullname = $symlist->[2];
2582    if ($fullname !~ /<[0-9a-fA-F]+>$/) {  # fullname doesn't end in an address
2583      next;       # the only collisions we care about are when addresses differ
2584    }
2585    if (defined($shortnames_seen_once->{$shortname}) &&
2586        $shortnames_seen_once->{$shortname} ne $fullname) {
2587      $shortnames_seen_more_than_once->{$shortname} = 1;
2588    } else {
2589      $shortnames_seen_once->{$shortname} = $fullname;
2590    }
2591  }
2592
2593  foreach my $symlist (values(%{$symbols})) {
2594    my $shortname = $symlist->[0];
2595    my $fullname = $symlist->[2];
2596    # TODO(csilvers): take in a list of addresses we care about, and only
2597    # store in the map if $symlist->[1] is in that list.  Saves space.
2598    next if defined($fullname_to_shortname_map->{$fullname});
2599    if (defined($shortnames_seen_more_than_once->{$shortname})) {
2600      if ($fullname =~ /<0*([^>]*)>$/) {   # fullname has address at end of it
2601        $fullname_to_shortname_map->{$fullname} = "$shortname\@$1";
2602      }
2603    }
2604  }
2605}
2606
2607# Return a small number that identifies the argument.
2608# Multiple calls with the same argument will return the same number.
2609# Calls with different arguments will return different numbers.
2610sub ShortIdFor {
2611  my $key = shift;
2612  my $id = $main::uniqueid{$key};
2613  if (!defined($id)) {
2614    $id = keys(%main::uniqueid) + 1;
2615    $main::uniqueid{$key} = $id;
2616  }
2617  return $id;
2618}
2619
2620# Translate a stack of addresses into a stack of symbols
2621sub TranslateStack {
2622  my $symbols = shift;
2623  my $fullname_to_shortname_map = shift;
2624  my $k = shift;
2625
2626  my @addrs = split(/\n/, $k);
2627  my @result = ();
2628  for (my $i = 0; $i <= $#addrs; $i++) {
2629    my $a = $addrs[$i];
2630
2631    # Skip large addresses since they sometimes show up as fake entries on RH9
2632    if (length($a) > 8 && $a gt "7fffffffffffffff") {
2633      next;
2634    }
2635
2636    if ($main::opt_disasm || $main::opt_list) {
2637      # We want just the address for the key
2638      push(@result, $a);
2639      next;
2640    }
2641
2642    my $symlist = $symbols->{$a};
2643    if (!defined($symlist)) {
2644      $symlist = [$a, "", $a];
2645    }
2646
2647    # We can have a sequence of symbols for a particular entry
2648    # (more than one symbol in the case of inlining).  Callers
2649    # come before callees in symlist, so walk backwards since
2650    # the translated stack should contain callees before callers.
2651    for (my $j = $#{$symlist}; $j >= 2; $j -= 3) {
2652      my $func = $symlist->[$j-2];
2653      my $fileline = $symlist->[$j-1];
2654      my $fullfunc = $symlist->[$j];
2655      if (defined($fullname_to_shortname_map->{$fullfunc})) {
2656        $func = $fullname_to_shortname_map->{$fullfunc};
2657      }
2658      if ($j > 2) {
2659        $func = "$func (inline)";
2660      }
2661
2662      # Do not merge nodes corresponding to Callback::Run since that
2663      # causes confusing cycles in dot display.  Instead, we synthesize
2664      # a unique name for this frame per caller.
2665      if ($func =~ m/Callback.*::Run$/) {
2666        my $caller = ($i > 0) ? $addrs[$i-1] : 0;
2667        $func = "Run#" . ShortIdFor($caller);
2668      }
2669
2670      if ($main::opt_addresses) {
2671        push(@result, "$a $func $fileline");
2672      } elsif ($main::opt_lines) {
2673        if ($func eq '??' && $fileline eq '??:0') {
2674          push(@result, "$a");
2675        } else {
2676          push(@result, "$func $fileline");
2677        }
2678      } elsif ($main::opt_functions) {
2679        if ($func eq '??') {
2680          push(@result, "$a");
2681        } else {
2682          push(@result, $func);
2683        }
2684      } elsif ($main::opt_files) {
2685        if ($fileline eq '??:0' || $fileline eq '') {
2686          push(@result, "$a");
2687        } else {
2688          my $f = $fileline;
2689          $f =~ s/:\d+$//;
2690          push(@result, $f);
2691        }
2692      } else {
2693        push(@result, $a);
2694        last;  # Do not print inlined info
2695      }
2696    }
2697  }
2698
2699  # print join(",", @addrs), " => ", join(",", @result), "\n";
2700  return @result;
2701}
2702
2703# Generate percent string for a number and a total
2704sub Percent {
2705  my $num = shift;
2706  my $tot = shift;
2707  if ($tot != 0) {
2708    return sprintf("%.1f%%", $num * 100.0 / $tot);
2709  } else {
2710    return ($num == 0) ? "nan" : (($num > 0) ? "+inf" : "-inf");
2711  }
2712}
2713
2714# Generate pretty-printed form of number
2715sub Unparse {
2716  my $num = shift;
2717  if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {
2718    if ($main::opt_inuse_objects || $main::opt_alloc_objects) {
2719      return sprintf("%d", $num);
2720    } else {
2721      if ($main::opt_show_bytes) {
2722        return sprintf("%d", $num);
2723      } else {
2724        return sprintf("%.1f", $num / 1048576.0);
2725      }
2726    }
2727  } elsif ($main::profile_type eq 'contention' && !$main::opt_contentions) {
2728    return sprintf("%.3f", $num / 1e9); # Convert nanoseconds to seconds
2729  } else {
2730    return sprintf("%d", $num);
2731  }
2732}
2733
2734# Alternate pretty-printed form: 0 maps to "."
2735sub UnparseAlt {
2736  my $num = shift;
2737  if ($num == 0) {
2738    return ".";
2739  } else {
2740    return Unparse($num);
2741  }
2742}
2743
2744# Alternate pretty-printed form: 0 maps to ""
2745sub HtmlPrintNumber {
2746  my $num = shift;
2747  if ($num == 0) {
2748    return "";
2749  } else {
2750    return Unparse($num);
2751  }
2752}
2753
2754# Return output units
2755sub Units {
2756  if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {
2757    if ($main::opt_inuse_objects || $main::opt_alloc_objects) {
2758      return "objects";
2759    } else {
2760      if ($main::opt_show_bytes) {
2761        return "B";
2762      } else {
2763        return "MB";
2764      }
2765    }
2766  } elsif ($main::profile_type eq 'contention' && !$main::opt_contentions) {
2767    return "seconds";
2768  } else {
2769    return "samples";
2770  }
2771}
2772
2773##### Profile manipulation code #####
2774
2775# Generate flattened profile:
2776# If count is charged to stack [a,b,c,d], in generated profile,
2777# it will be charged to [a]
2778sub FlatProfile {
2779  my $profile = shift;
2780  my $result = {};
2781  foreach my $k (keys(%{$profile})) {
2782    my $count = $profile->{$k};
2783    my @addrs = split(/\n/, $k);
2784    if ($#addrs >= 0) {
2785      AddEntry($result, $addrs[0], $count);
2786    }
2787  }
2788  return $result;
2789}
2790
2791# Generate cumulative profile:
2792# If count is charged to stack [a,b,c,d], in generated profile,
2793# it will be charged to [a], [b], [c], [d]
2794sub CumulativeProfile {
2795  my $profile = shift;
2796  my $result = {};
2797  foreach my $k (keys(%{$profile})) {
2798    my $count = $profile->{$k};
2799    my @addrs = split(/\n/, $k);
2800    foreach my $a (@addrs) {
2801      AddEntry($result, $a, $count);
2802    }
2803  }
2804  return $result;
2805}
2806
2807# If the second-youngest PC on the stack is always the same, returns
2808# that pc.  Otherwise, returns undef.
2809sub IsSecondPcAlwaysTheSame {
2810  my $profile = shift;
2811
2812  my $second_pc = undef;
2813  foreach my $k (keys(%{$profile})) {
2814    my @addrs = split(/\n/, $k);
2815    if ($#addrs < 1) {
2816      return undef;
2817    }
2818    if (not defined $second_pc) {
2819      $second_pc = $addrs[1];
2820    } else {
2821      if ($second_pc ne $addrs[1]) {
2822        return undef;
2823      }
2824    }
2825  }
2826  return $second_pc;
2827}
2828
2829sub ExtractSymbolNameInlineStack {
2830  my $symbols = shift;
2831  my $address = shift;
2832
2833  my @stack = ();
2834
2835  if (exists $symbols->{$address}) {
2836    my @localinlinestack = @{$symbols->{$address}};
2837    for (my $i = $#localinlinestack; $i > 0; $i-=3) {
2838      my $file = $localinlinestack[$i-1];
2839      my $fn = $localinlinestack[$i-0];
2840
2841      if ($file eq "?" || $file eq ":0") {
2842        $file = "??:0";
2843      }
2844      if ($fn eq '??') {
2845        # If we can't get the symbol name, at least use the file information.
2846        $fn = $file;
2847      }
2848      my $suffix = "[inline]";
2849      if ($i == 2) {
2850        $suffix = "";
2851      }
2852      push (@stack, $fn.$suffix);
2853    }
2854  }
2855  else {
2856    # If we can't get a symbol name, at least fill in the address.
2857    push (@stack, $address);
2858  }
2859
2860  return @stack;
2861}
2862
2863sub ExtractSymbolLocation {
2864  my $symbols = shift;
2865  my $address = shift;
2866  # 'addr2line' outputs "??:0" for unknown locations; we do the
2867  # same to be consistent.
2868  my $location = "??:0:unknown";
2869  if (exists $symbols->{$address}) {
2870    my $file = $symbols->{$address}->[1];
2871    if ($file eq "?") {
2872      $file = "??:0"
2873    }
2874    $location = $file . ":" . $symbols->{$address}->[0];
2875  }
2876  return $location;
2877}
2878
2879# Extracts a graph of calls.
2880sub ExtractCalls {
2881  my $symbols = shift;
2882  my $profile = shift;
2883
2884  my $calls = {};
2885  while( my ($stack_trace, $count) = each %$profile ) {
2886    my @address = split(/\n/, $stack_trace);
2887    my $destination = ExtractSymbolLocation($symbols, $address[0]);
2888    AddEntry($calls, $destination, $count);
2889    for (my $i = 1; $i <= $#address; $i++) {
2890      my $source = ExtractSymbolLocation($symbols, $address[$i]);
2891      my $call = "$source -> $destination";
2892      AddEntry($calls, $call, $count);
2893      $destination = $source;
2894    }
2895  }
2896
2897  return $calls;
2898}
2899
2900sub FilterFrames {
2901  my $symbols = shift;
2902  my $profile = shift;
2903
2904  if ($main::opt_retain eq '' && $main::opt_exclude eq '') {
2905    return $profile;
2906  }
2907
2908  my $result = {};
2909  foreach my $k (keys(%{$profile})) {
2910    my $count = $profile->{$k};
2911    my @addrs = split(/\n/, $k);
2912    my @path = ();
2913    foreach my $a (@addrs) {
2914      my $sym;
2915      if (exists($symbols->{$a})) {
2916        $sym = $symbols->{$a}->[0];
2917      } else {
2918        $sym = $a;
2919      }
2920      if ($main::opt_retain ne '' && $sym !~ m/$main::opt_retain/) {
2921        next;
2922      }
2923      if ($main::opt_exclude ne '' && $sym =~ m/$main::opt_exclude/) {
2924        next;
2925      }
2926      push(@path, $a);
2927    }
2928    if (scalar(@path) > 0) {
2929      my $reduced_path = join("\n", @path);
2930      AddEntry($result, $reduced_path, $count);
2931    }
2932  }
2933
2934  return $result;
2935}
2936
2937sub PrintCollapsedStacks {
2938  my $symbols = shift;
2939  my $profile = shift;
2940
2941  while (my ($stack_trace, $count) = each %$profile) {
2942    my @address = split(/\n/, $stack_trace);
2943    my @names = reverse ( map { ExtractSymbolNameInlineStack($symbols, $_) } @address );
2944    printf("%s %d\n", join(";", @names), $count);
2945  }
2946}
2947
2948sub RemoveUninterestingFrames {
2949  my $symbols = shift;
2950  my $profile = shift;
2951
2952  # List of function names to skip
2953  my %skip = ();
2954  my $skip_regexp = 'NOMATCH';
2955  if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {
2956    foreach my $name ('@JEMALLOC_PREFIX@calloc',
2957                      'cfree',
2958                      '@JEMALLOC_PREFIX@malloc',
2959                      'newImpl',
2960                      'void* newImpl',
2961                      '@JEMALLOC_PREFIX@free',
2962                      '@JEMALLOC_PREFIX@memalign',
2963                      '@JEMALLOC_PREFIX@posix_memalign',
2964                      '@JEMALLOC_PREFIX@aligned_alloc',
2965                      'pvalloc',
2966                      '@JEMALLOC_PREFIX@valloc',
2967                      '@JEMALLOC_PREFIX@realloc',
2968                      '@JEMALLOC_PREFIX@mallocx',
2969                      '@JEMALLOC_PREFIX@rallocx',
2970                      '@JEMALLOC_PREFIX@xallocx',
2971                      '@JEMALLOC_PREFIX@dallocx',
2972                      '@JEMALLOC_PREFIX@sdallocx',
2973                      '@JEMALLOC_PREFIX@sdallocx_noflags',
2974                      'tc_calloc',
2975                      'tc_cfree',
2976                      'tc_malloc',
2977                      'tc_free',
2978                      'tc_memalign',
2979                      'tc_posix_memalign',
2980                      'tc_pvalloc',
2981                      'tc_valloc',
2982                      'tc_realloc',
2983                      'tc_new',
2984                      'tc_delete',
2985                      'tc_newarray',
2986                      'tc_deletearray',
2987                      'tc_new_nothrow',
2988                      'tc_newarray_nothrow',
2989                      'do_malloc',
2990                      '::do_malloc',   # new name -- got moved to an unnamed ns
2991                      '::do_malloc_or_cpp_alloc',
2992                      'DoSampledAllocation',
2993                      'simple_alloc::allocate',
2994                      '__malloc_alloc_template::allocate',
2995                      '__builtin_delete',
2996                      '__builtin_new',
2997                      '__builtin_vec_delete',
2998                      '__builtin_vec_new',
2999                      'operator new',
3000                      'operator new[]',
3001                      # The entry to our memory-allocation routines on OS X
3002                      'malloc_zone_malloc',
3003                      'malloc_zone_calloc',
3004                      'malloc_zone_valloc',
3005                      'malloc_zone_realloc',
3006                      'malloc_zone_memalign',
3007                      'malloc_zone_free',
3008                      # These mark the beginning/end of our custom sections
3009                      '__start_google_malloc',
3010                      '__stop_google_malloc',
3011                      '__start_malloc_hook',
3012                      '__stop_malloc_hook') {
3013      $skip{$name} = 1;
3014      $skip{"_" . $name} = 1;   # Mach (OS X) adds a _ prefix to everything
3015    }
3016    # TODO: Remove TCMalloc once everything has been
3017    # moved into the tcmalloc:: namespace and we have flushed
3018    # old code out of the system.
3019    $skip_regexp = "TCMalloc|^tcmalloc::";
3020  } elsif ($main::profile_type eq 'contention') {
3021    foreach my $vname ('base::RecordLockProfileData',
3022                       'base::SubmitMutexProfileData',
3023                       'base::SubmitSpinLockProfileData',
3024                       'Mutex::Unlock',
3025                       'Mutex::UnlockSlow',
3026                       'Mutex::ReaderUnlock',
3027                       'MutexLock::~MutexLock',
3028                       'SpinLock::Unlock',
3029                       'SpinLock::SlowUnlock',
3030                       'SpinLockHolder::~SpinLockHolder') {
3031      $skip{$vname} = 1;
3032    }
3033  } elsif ($main::profile_type eq 'cpu') {
3034    # Drop signal handlers used for CPU profile collection
3035    # TODO(dpeng): this should not be necessary; it's taken
3036    # care of by the general 2nd-pc mechanism below.
3037    foreach my $name ('ProfileData::Add',           # historical
3038                      'ProfileData::prof_handler',  # historical
3039                      'CpuProfiler::prof_handler',
3040                      '__FRAME_END__',
3041                      '__pthread_sighandler',
3042                      '__restore') {
3043      $skip{$name} = 1;
3044    }
3045  } else {
3046    # Nothing skipped for unknown types
3047  }
3048
3049  if ($main::profile_type eq 'cpu') {
3050    # If all the second-youngest program counters are the same,
3051    # this STRONGLY suggests that it is an artifact of measurement,
3052    # i.e., stack frames pushed by the CPU profiler signal handler.
3053    # Hence, we delete them.
3054    # (The topmost PC is read from the signal structure, not from
3055    # the stack, so it does not get involved.)
3056    while (my $second_pc = IsSecondPcAlwaysTheSame($profile)) {
3057      my $result = {};
3058      my $func = '';
3059      if (exists($symbols->{$second_pc})) {
3060        $second_pc = $symbols->{$second_pc}->[0];
3061      }
3062      print STDERR "Removing $second_pc from all stack traces.\n";
3063      foreach my $k (keys(%{$profile})) {
3064        my $count = $profile->{$k};
3065        my @addrs = split(/\n/, $k);
3066        splice @addrs, 1, 1;
3067        my $reduced_path = join("\n", @addrs);
3068        AddEntry($result, $reduced_path, $count);
3069      }
3070      $profile = $result;
3071    }
3072  }
3073
3074  my $result = {};
3075  foreach my $k (keys(%{$profile})) {
3076    my $count = $profile->{$k};
3077    my @addrs = split(/\n/, $k);
3078    my @path = ();
3079    foreach my $a (@addrs) {
3080      if (exists($symbols->{$a})) {
3081        my $func = $symbols->{$a}->[0];
3082        if ($skip{$func} || ($func =~ m/$skip_regexp/)) {
3083          # Throw away the portion of the backtrace seen so far, under the
3084          # assumption that previous frames were for functions internal to the
3085          # allocator.
3086          @path = ();
3087          next;
3088        }
3089      }
3090      push(@path, $a);
3091    }
3092    my $reduced_path = join("\n", @path);
3093    AddEntry($result, $reduced_path, $count);
3094  }
3095
3096  $result = FilterFrames($symbols, $result);
3097
3098  return $result;
3099}
3100
3101# Reduce profile to granularity given by user
3102sub ReduceProfile {
3103  my $symbols = shift;
3104  my $profile = shift;
3105  my $result = {};
3106  my $fullname_to_shortname_map = {};
3107  FillFullnameToShortnameMap($symbols, $fullname_to_shortname_map);
3108  foreach my $k (keys(%{$profile})) {
3109    my $count = $profile->{$k};
3110    my @translated = TranslateStack($symbols, $fullname_to_shortname_map, $k);
3111    my @path = ();
3112    my %seen = ();
3113    $seen{''} = 1;      # So that empty keys are skipped
3114    foreach my $e (@translated) {
3115      # To avoid double-counting due to recursion, skip a stack-trace
3116      # entry if it has already been seen
3117      if (!$seen{$e}) {
3118        $seen{$e} = 1;
3119        push(@path, $e);
3120      }
3121    }
3122    my $reduced_path = join("\n", @path);
3123    AddEntry($result, $reduced_path, $count);
3124  }
3125  return $result;
3126}
3127
3128# Does the specified symbol array match the regexp?
3129sub SymbolMatches {
3130  my $sym = shift;
3131  my $re = shift;
3132  if (defined($sym)) {
3133    for (my $i = 0; $i < $#{$sym}; $i += 3) {
3134      if ($sym->[$i] =~ m/$re/ || $sym->[$i+1] =~ m/$re/) {
3135        return 1;
3136      }
3137    }
3138  }
3139  return 0;
3140}
3141
3142# Focus only on paths involving specified regexps
3143sub FocusProfile {
3144  my $symbols = shift;
3145  my $profile = shift;
3146  my $focus = shift;
3147  my $result = {};
3148  foreach my $k (keys(%{$profile})) {
3149    my $count = $profile->{$k};
3150    my @addrs = split(/\n/, $k);
3151    foreach my $a (@addrs) {
3152      # Reply if it matches either the address/shortname/fileline
3153      if (($a =~ m/$focus/) || SymbolMatches($symbols->{$a}, $focus)) {
3154        AddEntry($result, $k, $count);
3155        last;
3156      }
3157    }
3158  }
3159  return $result;
3160}
3161
3162# Focus only on paths not involving specified regexps
3163sub IgnoreProfile {
3164  my $symbols = shift;
3165  my $profile = shift;
3166  my $ignore = shift;
3167  my $result = {};
3168  foreach my $k (keys(%{$profile})) {
3169    my $count = $profile->{$k};
3170    my @addrs = split(/\n/, $k);
3171    my $matched = 0;
3172    foreach my $a (@addrs) {
3173      # Reply if it matches either the address/shortname/fileline
3174      if (($a =~ m/$ignore/) || SymbolMatches($symbols->{$a}, $ignore)) {
3175        $matched = 1;
3176        last;
3177      }
3178    }
3179    if (!$matched) {
3180      AddEntry($result, $k, $count);
3181    }
3182  }
3183  return $result;
3184}
3185
3186# Get total count in profile
3187sub TotalProfile {
3188  my $profile = shift;
3189  my $result = 0;
3190  foreach my $k (keys(%{$profile})) {
3191    $result += $profile->{$k};
3192  }
3193  return $result;
3194}
3195
3196# Add A to B
3197sub AddProfile {
3198  my $A = shift;
3199  my $B = shift;
3200
3201  my $R = {};
3202  # add all keys in A
3203  foreach my $k (keys(%{$A})) {
3204    my $v = $A->{$k};
3205    AddEntry($R, $k, $v);
3206  }
3207  # add all keys in B
3208  foreach my $k (keys(%{$B})) {
3209    my $v = $B->{$k};
3210    AddEntry($R, $k, $v);
3211  }
3212  return $R;
3213}
3214
3215# Merges symbol maps
3216sub MergeSymbols {
3217  my $A = shift;
3218  my $B = shift;
3219
3220  my $R = {};
3221  foreach my $k (keys(%{$A})) {
3222    $R->{$k} = $A->{$k};
3223  }
3224  if (defined($B)) {
3225    foreach my $k (keys(%{$B})) {
3226      $R->{$k} = $B->{$k};
3227    }
3228  }
3229  return $R;
3230}
3231
3232
3233# Add A to B
3234sub AddPcs {
3235  my $A = shift;
3236  my $B = shift;
3237
3238  my $R = {};
3239  # add all keys in A
3240  foreach my $k (keys(%{$A})) {
3241    $R->{$k} = 1
3242  }
3243  # add all keys in B
3244  foreach my $k (keys(%{$B})) {
3245    $R->{$k} = 1
3246  }
3247  return $R;
3248}
3249
3250# Subtract B from A
3251sub SubtractProfile {
3252  my $A = shift;
3253  my $B = shift;
3254
3255  my $R = {};
3256  foreach my $k (keys(%{$A})) {
3257    my $v = $A->{$k} - GetEntry($B, $k);
3258    if ($v < 0 && $main::opt_drop_negative) {
3259      $v = 0;
3260    }
3261    AddEntry($R, $k, $v);
3262  }
3263  if (!$main::opt_drop_negative) {
3264    # Take care of when subtracted profile has more entries
3265    foreach my $k (keys(%{$B})) {
3266      if (!exists($A->{$k})) {
3267        AddEntry($R, $k, 0 - $B->{$k});
3268      }
3269    }
3270  }
3271  return $R;
3272}
3273
3274# Get entry from profile; zero if not present
3275sub GetEntry {
3276  my $profile = shift;
3277  my $k = shift;
3278  if (exists($profile->{$k})) {
3279    return $profile->{$k};
3280  } else {
3281    return 0;
3282  }
3283}
3284
3285# Add entry to specified profile
3286sub AddEntry {
3287  my $profile = shift;
3288  my $k = shift;
3289  my $n = shift;
3290  if (!exists($profile->{$k})) {
3291    $profile->{$k} = 0;
3292  }
3293  $profile->{$k} += $n;
3294}
3295
3296# Add a stack of entries to specified profile, and add them to the $pcs
3297# list.
3298sub AddEntries {
3299  my $profile = shift;
3300  my $pcs = shift;
3301  my $stack = shift;
3302  my $count = shift;
3303  my @k = ();
3304
3305  foreach my $e (split(/\s+/, $stack)) {
3306    my $pc = HexExtend($e);
3307    $pcs->{$pc} = 1;
3308    push @k, $pc;
3309  }
3310  AddEntry($profile, (join "\n", @k), $count);
3311}
3312
3313##### Code to profile a server dynamically #####
3314
3315sub CheckSymbolPage {
3316  my $url = SymbolPageURL();
3317  my $command = ShellEscape(@URL_FETCHER, $url);
3318  open(SYMBOL, "$command |") or error($command);
3319  my $line = <SYMBOL>;
3320  $line =~ s/\r//g;         # turn windows-looking lines into unix-looking lines
3321  close(SYMBOL);
3322  unless (defined($line)) {
3323    error("$url doesn't exist\n");
3324  }
3325
3326  if ($line =~ /^num_symbols:\s+(\d+)$/) {
3327    if ($1 == 0) {
3328      error("Stripped binary. No symbols available.\n");
3329    }
3330  } else {
3331    error("Failed to get the number of symbols from $url\n");
3332  }
3333}
3334
3335sub IsProfileURL {
3336  my $profile_name = shift;
3337  if (-f $profile_name) {
3338    printf STDERR "Using local file $profile_name.\n";
3339    return 0;
3340  }
3341  return 1;
3342}
3343
3344sub ParseProfileURL {
3345  my $profile_name = shift;
3346
3347  if (!defined($profile_name) || $profile_name eq "") {
3348    return ();
3349  }
3350
3351  # Split profile URL - matches all non-empty strings, so no test.
3352  $profile_name =~ m,^(https?://)?([^/]+)(.*?)(/|$PROFILES)?$,;
3353
3354  my $proto = $1 || "http://";
3355  my $hostport = $2;
3356  my $prefix = $3;
3357  my $profile = $4 || "/";
3358
3359  my $host = $hostport;
3360  $host =~ s/:.*//;
3361
3362  my $baseurl = "$proto$hostport$prefix";
3363  return ($host, $baseurl, $profile);
3364}
3365
3366# We fetch symbols from the first profile argument.
3367sub SymbolPageURL {
3368  my ($host, $baseURL, $path) = ParseProfileURL($main::pfile_args[0]);
3369  return "$baseURL$SYMBOL_PAGE";
3370}
3371
3372sub FetchProgramName() {
3373  my ($host, $baseURL, $path) = ParseProfileURL($main::pfile_args[0]);
3374  my $url = "$baseURL$PROGRAM_NAME_PAGE";
3375  my $command_line = ShellEscape(@URL_FETCHER, $url);
3376  open(CMDLINE, "$command_line |") or error($command_line);
3377  my $cmdline = <CMDLINE>;
3378  $cmdline =~ s/\r//g;   # turn windows-looking lines into unix-looking lines
3379  close(CMDLINE);
3380  error("Failed to get program name from $url\n") unless defined($cmdline);
3381  $cmdline =~ s/\x00.+//;  # Remove argv[1] and latters.
3382  $cmdline =~ s!\n!!g;  # Remove LFs.
3383  return $cmdline;
3384}
3385
3386# Gee, curl's -L (--location) option isn't reliable at least
3387# with its 7.12.3 version.  Curl will forget to post data if
3388# there is a redirection.  This function is a workaround for
3389# curl.  Redirection happens on borg hosts.
3390sub ResolveRedirectionForCurl {
3391  my $url = shift;
3392  my $command_line = ShellEscape(@URL_FETCHER, "--head", $url);
3393  open(CMDLINE, "$command_line |") or error($command_line);
3394  while (<CMDLINE>) {
3395    s/\r//g;         # turn windows-looking lines into unix-looking lines
3396    if (/^Location: (.*)/) {
3397      $url = $1;
3398    }
3399  }
3400  close(CMDLINE);
3401  return $url;
3402}
3403
3404# Add a timeout flat to URL_FETCHER.  Returns a new list.
3405sub AddFetchTimeout {
3406  my $timeout = shift;
3407  my @fetcher = @_;
3408  if (defined($timeout)) {
3409    if (join(" ", @fetcher) =~ m/\bcurl -s/) {
3410      push(@fetcher, "--max-time", sprintf("%d", $timeout));
3411    } elsif (join(" ", @fetcher) =~ m/\brpcget\b/) {
3412      push(@fetcher, sprintf("--deadline=%d", $timeout));
3413    }
3414  }
3415  return @fetcher;
3416}
3417
3418# Reads a symbol map from the file handle name given as $1, returning
3419# the resulting symbol map.  Also processes variables relating to symbols.
3420# Currently, the only variable processed is 'binary=<value>' which updates
3421# $main::prog to have the correct program name.
3422sub ReadSymbols {
3423  my $in = shift;
3424  my $map = {};
3425  while (<$in>) {
3426    s/\r//g;         # turn windows-looking lines into unix-looking lines
3427    # Removes all the leading zeroes from the symbols, see comment below.
3428    if (m/^0x0*([0-9a-f]+)\s+(.+)/) {
3429      $map->{$1} = $2;
3430    } elsif (m/^---/) {
3431      last;
3432    } elsif (m/^([a-z][^=]*)=(.*)$/ ) {
3433      my ($variable, $value) = ($1, $2);
3434      for ($variable, $value) {
3435        s/^\s+//;
3436        s/\s+$//;
3437      }
3438      if ($variable eq "binary") {
3439        if ($main::prog ne $UNKNOWN_BINARY && $main::prog ne $value) {
3440          printf STDERR ("Warning: Mismatched binary name '%s', using '%s'.\n",
3441                         $main::prog, $value);
3442        }
3443        $main::prog = $value;
3444      } else {
3445        printf STDERR ("Ignoring unknown variable in symbols list: " .
3446            "'%s' = '%s'\n", $variable, $value);
3447      }
3448    }
3449  }
3450  return $map;
3451}
3452
3453sub URLEncode {
3454  my $str = shift;
3455  $str =~ s/([^A-Za-z0-9\-_.!~*'()])/ sprintf "%%%02x", ord $1 /eg;
3456  return $str;
3457}
3458
3459sub AppendSymbolFilterParams {
3460  my $url = shift;
3461  my @params = ();
3462  if ($main::opt_retain ne '') {
3463    push(@params, sprintf("retain=%s", URLEncode($main::opt_retain)));
3464  }
3465  if ($main::opt_exclude ne '') {
3466    push(@params, sprintf("exclude=%s", URLEncode($main::opt_exclude)));
3467  }
3468  if (scalar @params > 0) {
3469    $url = sprintf("%s?%s", $url, join("&", @params));
3470  }
3471  return $url;
3472}
3473
3474# Fetches and processes symbols to prepare them for use in the profile output
3475# code.  If the optional 'symbol_map' arg is not given, fetches symbols from
3476# $SYMBOL_PAGE for all PC values found in profile.  Otherwise, the raw symbols
3477# are assumed to have already been fetched into 'symbol_map' and are simply
3478# extracted and processed.
3479sub FetchSymbols {
3480  my $pcset = shift;
3481  my $symbol_map = shift;
3482
3483  my %seen = ();
3484  my @pcs = grep { !$seen{$_}++ } keys(%$pcset);  # uniq
3485
3486  if (!defined($symbol_map)) {
3487    my $post_data = join("+", sort((map {"0x" . "$_"} @pcs)));
3488
3489    open(POSTFILE, ">$main::tmpfile_sym");
3490    print POSTFILE $post_data;
3491    close(POSTFILE);
3492
3493    my $url = SymbolPageURL();
3494
3495    my $command_line;
3496    if (join(" ", @URL_FETCHER) =~ m/\bcurl -s/) {
3497      $url = ResolveRedirectionForCurl($url);
3498      $url = AppendSymbolFilterParams($url);
3499      $command_line = ShellEscape(@URL_FETCHER, "-d", "\@$main::tmpfile_sym",
3500                                  $url);
3501    } else {
3502      $url = AppendSymbolFilterParams($url);
3503      $command_line = (ShellEscape(@URL_FETCHER, "--post", $url)
3504                       . " < " . ShellEscape($main::tmpfile_sym));
3505    }
3506    # We use c++filt in case $SYMBOL_PAGE gives us mangled symbols.
3507    my $escaped_cppfilt = ShellEscape($obj_tool_map{"c++filt"});
3508    open(SYMBOL, "$command_line | $escaped_cppfilt |") or error($command_line);
3509    $symbol_map = ReadSymbols(*SYMBOL{IO});
3510    close(SYMBOL);
3511  }
3512
3513  my $symbols = {};
3514  foreach my $pc (@pcs) {
3515    my $fullname;
3516    # For 64 bits binaries, symbols are extracted with 8 leading zeroes.
3517    # Then /symbol reads the long symbols in as uint64, and outputs
3518    # the result with a "0x%08llx" format which get rid of the zeroes.
3519    # By removing all the leading zeroes in both $pc and the symbols from
3520    # /symbol, the symbols match and are retrievable from the map.
3521    my $shortpc = $pc;
3522    $shortpc =~ s/^0*//;
3523    # Each line may have a list of names, which includes the function
3524    # and also other functions it has inlined.  They are separated (in
3525    # PrintSymbolizedProfile), by --, which is illegal in function names.
3526    my $fullnames;
3527    if (defined($symbol_map->{$shortpc})) {
3528      $fullnames = $symbol_map->{$shortpc};
3529    } else {
3530      $fullnames = "0x" . $pc;  # Just use addresses
3531    }
3532    my $sym = [];
3533    $symbols->{$pc} = $sym;
3534    foreach my $fullname (split("--", $fullnames)) {
3535      my $name = ShortFunctionName($fullname);
3536      push(@{$sym}, $name, "?", $fullname);
3537    }
3538  }
3539  return $symbols;
3540}
3541
3542sub BaseName {
3543  my $file_name = shift;
3544  $file_name =~ s!^.*/!!;  # Remove directory name
3545  return $file_name;
3546}
3547
3548sub MakeProfileBaseName {
3549  my ($binary_name, $profile_name) = @_;
3550  my ($host, $baseURL, $path) = ParseProfileURL($profile_name);
3551  my $binary_shortname = BaseName($binary_name);
3552  return sprintf("%s.%s.%s",
3553                 $binary_shortname, $main::op_time, $host);
3554}
3555
3556sub FetchDynamicProfile {
3557  my $binary_name = shift;
3558  my $profile_name = shift;
3559  my $fetch_name_only = shift;
3560  my $encourage_patience = shift;
3561
3562  if (!IsProfileURL($profile_name)) {
3563    return $profile_name;
3564  } else {
3565    my ($host, $baseURL, $path) = ParseProfileURL($profile_name);
3566    if ($path eq "" || $path eq "/") {
3567      # Missing type specifier defaults to cpu-profile
3568      $path = $PROFILE_PAGE;
3569    }
3570
3571    my $profile_file = MakeProfileBaseName($binary_name, $profile_name);
3572
3573    my $url = "$baseURL$path";
3574    my $fetch_timeout = undef;
3575    if ($path =~ m/$PROFILE_PAGE|$PMUPROFILE_PAGE/) {
3576      if ($path =~ m/[?]/) {
3577        $url .= "&";
3578      } else {
3579        $url .= "?";
3580      }
3581      $url .= sprintf("seconds=%d", $main::opt_seconds);
3582      $fetch_timeout = $main::opt_seconds * 1.01 + 60;
3583      # Set $profile_type for consumption by PrintSymbolizedProfile.
3584      $main::profile_type = 'cpu';
3585    } else {
3586      # For non-CPU profiles, we add a type-extension to
3587      # the target profile file name.
3588      my $suffix = $path;
3589      $suffix =~ s,/,.,g;
3590      $profile_file .= $suffix;
3591      # Set $profile_type for consumption by PrintSymbolizedProfile.
3592      if ($path =~ m/$HEAP_PAGE/) {
3593        $main::profile_type = 'heap';
3594      } elsif ($path =~ m/$GROWTH_PAGE/) {
3595        $main::profile_type = 'growth';
3596      } elsif ($path =~ m/$CONTENTION_PAGE/) {
3597        $main::profile_type = 'contention';
3598      }
3599    }
3600
3601    my $profile_dir = $ENV{"JEPROF_TMPDIR"} || ($ENV{HOME} . "/jeprof");
3602    if (! -d $profile_dir) {
3603      mkdir($profile_dir)
3604          || die("Unable to create profile directory $profile_dir: $!\n");
3605    }
3606    my $tmp_profile = "$profile_dir/.tmp.$profile_file";
3607    my $real_profile = "$profile_dir/$profile_file";
3608
3609    if ($fetch_name_only > 0) {
3610      return $real_profile;
3611    }
3612
3613    my @fetcher = AddFetchTimeout($fetch_timeout, @URL_FETCHER);
3614    my $cmd = ShellEscape(@fetcher, $url) . " > " . ShellEscape($tmp_profile);
3615    if ($path =~ m/$PROFILE_PAGE|$PMUPROFILE_PAGE|$CENSUSPROFILE_PAGE/){
3616      print STDERR "Gathering CPU profile from $url for $main::opt_seconds seconds to\n  ${real_profile}\n";
3617      if ($encourage_patience) {
3618        print STDERR "Be patient...\n";
3619      }
3620    } else {
3621      print STDERR "Fetching $path profile from $url to\n  ${real_profile}\n";
3622    }
3623
3624    (system($cmd) == 0) || error("Failed to get profile: $cmd: $!\n");
3625    (system("mv", $tmp_profile, $real_profile) == 0) || error("Unable to rename profile\n");
3626    print STDERR "Wrote profile to $real_profile\n";
3627    $main::collected_profile = $real_profile;
3628    return $main::collected_profile;
3629  }
3630}
3631
3632# Collect profiles in parallel
3633sub FetchDynamicProfiles {
3634  my $items = scalar(@main::pfile_args);
3635  my $levels = log($items) / log(2);
3636
3637  if ($items == 1) {
3638    $main::profile_files[0] = FetchDynamicProfile($main::prog, $main::pfile_args[0], 0, 1);
3639  } else {
3640    # math rounding issues
3641    if ((2 ** $levels) < $items) {
3642     $levels++;
3643    }
3644    my $count = scalar(@main::pfile_args);
3645    for (my $i = 0; $i < $count; $i++) {
3646      $main::profile_files[$i] = FetchDynamicProfile($main::prog, $main::pfile_args[$i], 1, 0);
3647    }
3648    print STDERR "Fetching $count profiles, Be patient...\n";
3649    FetchDynamicProfilesRecurse($levels, 0, 0);
3650    $main::collected_profile = join(" \\\n    ", @main::profile_files);
3651  }
3652}
3653
3654# Recursively fork a process to get enough processes
3655# collecting profiles
3656sub FetchDynamicProfilesRecurse {
3657  my $maxlevel = shift;
3658  my $level = shift;
3659  my $position = shift;
3660
3661  if (my $pid = fork()) {
3662    $position = 0 | ($position << 1);
3663    TryCollectProfile($maxlevel, $level, $position);
3664    wait;
3665  } else {
3666    $position = 1 | ($position << 1);
3667    TryCollectProfile($maxlevel, $level, $position);
3668    cleanup();
3669    exit(0);
3670  }
3671}
3672
3673# Collect a single profile
3674sub TryCollectProfile {
3675  my $maxlevel = shift;
3676  my $level = shift;
3677  my $position = shift;
3678
3679  if ($level >= ($maxlevel - 1)) {
3680    if ($position < scalar(@main::pfile_args)) {
3681      FetchDynamicProfile($main::prog, $main::pfile_args[$position], 0, 0);
3682    }
3683  } else {
3684    FetchDynamicProfilesRecurse($maxlevel, $level+1, $position);
3685  }
3686}
3687
3688##### Parsing code #####
3689
3690# Provide a small streaming-read module to handle very large
3691# cpu-profile files.  Stream in chunks along a sliding window.
3692# Provides an interface to get one 'slot', correctly handling
3693# endian-ness differences.  A slot is one 32-bit or 64-bit word
3694# (depending on the input profile).  We tell endianness and bit-size
3695# for the profile by looking at the first 8 bytes: in cpu profiles,
3696# the second slot is always 3 (we'll accept anything that's not 0).
3697BEGIN {
3698  package CpuProfileStream;
3699
3700  sub new {
3701    my ($class, $file, $fname) = @_;
3702    my $self = { file        => $file,
3703                 base        => 0,
3704                 stride      => 512 * 1024,   # must be a multiple of bitsize/8
3705                 slots       => [],
3706                 unpack_code => "",           # N for big-endian, V for little
3707                 perl_is_64bit => 1,          # matters if profile is 64-bit
3708    };
3709    bless $self, $class;
3710    # Let unittests adjust the stride
3711    if ($main::opt_test_stride > 0) {
3712      $self->{stride} = $main::opt_test_stride;
3713    }
3714    # Read the first two slots to figure out bitsize and endianness.
3715    my $slots = $self->{slots};
3716    my $str;
3717    read($self->{file}, $str, 8);
3718    # Set the global $address_length based on what we see here.
3719    # 8 is 32-bit (8 hexadecimal chars); 16 is 64-bit (16 hexadecimal chars).
3720    $address_length = ($str eq (chr(0)x8)) ? 16 : 8;
3721    if ($address_length == 8) {
3722      if (substr($str, 6, 2) eq chr(0)x2) {
3723        $self->{unpack_code} = 'V';  # Little-endian.
3724      } elsif (substr($str, 4, 2) eq chr(0)x2) {
3725        $self->{unpack_code} = 'N';  # Big-endian
3726      } else {
3727        ::error("$fname: header size >= 2**16\n");
3728      }
3729      @$slots = unpack($self->{unpack_code} . "*", $str);
3730    } else {
3731      # If we're a 64-bit profile, check if we're a 64-bit-capable
3732      # perl.  Otherwise, each slot will be represented as a float
3733      # instead of an int64, losing precision and making all the
3734      # 64-bit addresses wrong.  We won't complain yet, but will
3735      # later if we ever see a value that doesn't fit in 32 bits.
3736      my $has_q = 0;
3737      eval { $has_q = pack("Q", "1") ? 1 : 1; };
3738      if (!$has_q) {
3739        $self->{perl_is_64bit} = 0;
3740      }
3741      read($self->{file}, $str, 8);
3742      if (substr($str, 4, 4) eq chr(0)x4) {
3743        # We'd love to use 'Q', but it's a) not universal, b) not endian-proof.
3744        $self->{unpack_code} = 'V';  # Little-endian.
3745      } elsif (substr($str, 0, 4) eq chr(0)x4) {
3746        $self->{unpack_code} = 'N';  # Big-endian
3747      } else {
3748        ::error("$fname: header size >= 2**32\n");
3749      }
3750      my @pair = unpack($self->{unpack_code} . "*", $str);
3751      # Since we know one of the pair is 0, it's fine to just add them.
3752      @$slots = (0, $pair[0] + $pair[1]);
3753    }
3754    return $self;
3755  }
3756
3757  # Load more data when we access slots->get(X) which is not yet in memory.
3758  sub overflow {
3759    my ($self) = @_;
3760    my $slots = $self->{slots};
3761    $self->{base} += $#$slots + 1;   # skip over data we're replacing
3762    my $str;
3763    read($self->{file}, $str, $self->{stride});
3764    if ($address_length == 8) {      # the 32-bit case
3765      # This is the easy case: unpack provides 32-bit unpacking primitives.
3766      @$slots = unpack($self->{unpack_code} . "*", $str);
3767    } else {
3768      # We need to unpack 32 bits at a time and combine.
3769      my @b32_values = unpack($self->{unpack_code} . "*", $str);
3770      my @b64_values = ();
3771      for (my $i = 0; $i < $#b32_values; $i += 2) {
3772        # TODO(csilvers): if this is a 32-bit perl, the math below
3773        #    could end up in a too-large int, which perl will promote
3774        #    to a double, losing necessary precision.  Deal with that.
3775        #    Right now, we just die.
3776        my ($lo, $hi) = ($b32_values[$i], $b32_values[$i+1]);
3777        if ($self->{unpack_code} eq 'N') {    # big-endian
3778          ($lo, $hi) = ($hi, $lo);
3779        }
3780        my $value = $lo + $hi * (2**32);
3781        if (!$self->{perl_is_64bit} &&   # check value is exactly represented
3782            (($value % (2**32)) != $lo || int($value / (2**32)) != $hi)) {
3783          ::error("Need a 64-bit perl to process this 64-bit profile.\n");
3784        }
3785        push(@b64_values, $value);
3786      }
3787      @$slots = @b64_values;
3788    }
3789  }
3790
3791  # Access the i-th long in the file (logically), or -1 at EOF.
3792  sub get {
3793    my ($self, $idx) = @_;
3794    my $slots = $self->{slots};
3795    while ($#$slots >= 0) {
3796      if ($idx < $self->{base}) {
3797        # The only time we expect a reference to $slots[$i - something]
3798        # after referencing $slots[$i] is reading the very first header.
3799        # Since $stride > |header|, that shouldn't cause any lookback
3800        # errors.  And everything after the header is sequential.
3801        print STDERR "Unexpected look-back reading CPU profile";
3802        return -1;   # shrug, don't know what better to return
3803      } elsif ($idx > $self->{base} + $#$slots) {
3804        $self->overflow();
3805      } else {
3806        return $slots->[$idx - $self->{base}];
3807      }
3808    }
3809    # If we get here, $slots is [], which means we've reached EOF
3810    return -1;  # unique since slots is supposed to hold unsigned numbers
3811  }
3812}
3813
3814# Reads the top, 'header' section of a profile, and returns the last
3815# line of the header, commonly called a 'header line'.  The header
3816# section of a profile consists of zero or more 'command' lines that
3817# are instructions to jeprof, which jeprof executes when reading the
3818# header.  All 'command' lines start with a %.  After the command
3819# lines is the 'header line', which is a profile-specific line that
3820# indicates what type of profile it is, and perhaps other global
3821# information about the profile.  For instance, here's a header line
3822# for a heap profile:
3823#   heap profile:     53:    38236 [  5525:  1284029] @ heapprofile
3824# For historical reasons, the CPU profile does not contain a text-
3825# readable header line.  If the profile looks like a CPU profile,
3826# this function returns "".  If no header line could be found, this
3827# function returns undef.
3828#
3829# The following commands are recognized:
3830#   %warn -- emit the rest of this line to stderr, prefixed by 'WARNING:'
3831#
3832# The input file should be in binmode.
3833sub ReadProfileHeader {
3834  local *PROFILE = shift;
3835  my $firstchar = "";
3836  my $line = "";
3837  read(PROFILE, $firstchar, 1);
3838  seek(PROFILE, -1, 1);                    # unread the firstchar
3839  if ($firstchar !~ /[[:print:]]/) {       # is not a text character
3840    return "";
3841  }
3842  while (defined($line = <PROFILE>)) {
3843    $line =~ s/\r//g;   # turn windows-looking lines into unix-looking lines
3844    if ($line =~ /^%warn\s+(.*)/) {        # 'warn' command
3845      # Note this matches both '%warn blah\n' and '%warn\n'.
3846      print STDERR "WARNING: $1\n";        # print the rest of the line
3847    } elsif ($line =~ /^%/) {
3848      print STDERR "Ignoring unknown command from profile header: $line";
3849    } else {
3850      # End of commands, must be the header line.
3851      return $line;
3852    }
3853  }
3854  return undef;     # got to EOF without seeing a header line
3855}
3856
3857sub IsSymbolizedProfileFile {
3858  my $file_name = shift;
3859  if (!(-e $file_name) || !(-r $file_name)) {
3860    return 0;
3861  }
3862  # Check if the file contains a symbol-section marker.
3863  open(TFILE, "<$file_name");
3864  binmode TFILE;
3865  my $firstline = ReadProfileHeader(*TFILE);
3866  close(TFILE);
3867  if (!$firstline) {
3868    return 0;
3869  }
3870  $SYMBOL_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
3871  my $symbol_marker = $&;
3872  return $firstline =~ /^--- *$symbol_marker/;
3873}
3874
3875# Parse profile generated by common/profiler.cc and return a reference
3876# to a map:
3877#      $result->{version}     Version number of profile file
3878#      $result->{period}      Sampling period (in microseconds)
3879#      $result->{profile}     Profile object
3880#      $result->{threads}     Map of thread IDs to profile objects
3881#      $result->{map}         Memory map info from profile
3882#      $result->{pcs}         Hash of all PC values seen, key is hex address
3883sub ReadProfile {
3884  my $prog = shift;
3885  my $fname = shift;
3886  my $result;            # return value
3887
3888  $CONTENTION_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
3889  my $contention_marker = $&;
3890  $GROWTH_PAGE  =~ m,[^/]+$,;    # matches everything after the last slash
3891  my $growth_marker = $&;
3892  $SYMBOL_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
3893  my $symbol_marker = $&;
3894  $PROFILE_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
3895  my $profile_marker = $&;
3896  $HEAP_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
3897  my $heap_marker = $&;
3898
3899  # Look at first line to see if it is a heap or a CPU profile.
3900  # CPU profile may start with no header at all, and just binary data
3901  # (starting with \0\0\0\0) -- in that case, don't try to read the
3902  # whole firstline, since it may be gigabytes(!) of data.
3903  open(PROFILE, "<$fname") || error("$fname: $!\n");
3904  binmode PROFILE;      # New perls do UTF-8 processing
3905  my $header = ReadProfileHeader(*PROFILE);
3906  if (!defined($header)) {   # means "at EOF"
3907    error("Profile is empty.\n");
3908  }
3909
3910  my $symbols;
3911  if ($header =~ m/^--- *$symbol_marker/o) {
3912    # Verify that the user asked for a symbolized profile
3913    if (!$main::use_symbolized_profile) {
3914      # we have both a binary and symbolized profiles, abort
3915      error("FATAL ERROR: Symbolized profile\n   $fname\ncannot be used with " .
3916            "a binary arg. Try again without passing\n   $prog\n");
3917    }
3918    # Read the symbol section of the symbolized profile file.
3919    $symbols = ReadSymbols(*PROFILE{IO});
3920    # Read the next line to get the header for the remaining profile.
3921    $header = ReadProfileHeader(*PROFILE) || "";
3922  }
3923
3924  if ($header =~ m/^--- *($heap_marker|$growth_marker)/o) {
3925    # Skip "--- ..." line for profile types that have their own headers.
3926    $header = ReadProfileHeader(*PROFILE) || "";
3927  }
3928
3929  $main::profile_type = '';
3930
3931  if ($header =~ m/^heap profile:.*$growth_marker/o) {
3932    $main::profile_type = 'growth';
3933    $result =  ReadHeapProfile($prog, *PROFILE, $header);
3934  } elsif ($header =~ m/^heap profile:/) {
3935    $main::profile_type = 'heap';
3936    $result =  ReadHeapProfile($prog, *PROFILE, $header);
3937  } elsif ($header =~ m/^heap/) {
3938    $main::profile_type = 'heap';
3939    $result = ReadThreadedHeapProfile($prog, $fname, $header);
3940  } elsif ($header =~ m/^--- *$contention_marker/o) {
3941    $main::profile_type = 'contention';
3942    $result = ReadSynchProfile($prog, *PROFILE);
3943  } elsif ($header =~ m/^--- *Stacks:/) {
3944    print STDERR
3945      "Old format contention profile: mistakenly reports " .
3946      "condition variable signals as lock contentions.\n";
3947    $main::profile_type = 'contention';
3948    $result = ReadSynchProfile($prog, *PROFILE);
3949  } elsif ($header =~ m/^--- *$profile_marker/) {
3950    # the binary cpu profile data starts immediately after this line
3951    $main::profile_type = 'cpu';
3952    $result = ReadCPUProfile($prog, $fname, *PROFILE);
3953  } else {
3954    if (defined($symbols)) {
3955      # a symbolized profile contains a format we don't recognize, bail out
3956      error("$fname: Cannot recognize profile section after symbols.\n");
3957    }
3958    # no ascii header present -- must be a CPU profile
3959    $main::profile_type = 'cpu';
3960    $result = ReadCPUProfile($prog, $fname, *PROFILE);
3961  }
3962
3963  close(PROFILE);
3964
3965  # if we got symbols along with the profile, return those as well
3966  if (defined($symbols)) {
3967    $result->{symbols} = $symbols;
3968  }
3969
3970  return $result;
3971}
3972
3973# Subtract one from caller pc so we map back to call instr.
3974# However, don't do this if we're reading a symbolized profile
3975# file, in which case the subtract-one was done when the file
3976# was written.
3977#
3978# We apply the same logic to all readers, though ReadCPUProfile uses an
3979# independent implementation.
3980sub FixCallerAddresses {
3981  my $stack = shift;
3982  # --raw/http: Always subtract one from pc's, because PrintSymbolizedProfile()
3983  # dumps unadjusted profiles.
3984  {
3985    $stack =~ /(\s)/;
3986    my $delimiter = $1;
3987    my @addrs = split(' ', $stack);
3988    my @fixedaddrs;
3989    $#fixedaddrs = $#addrs;
3990    if ($#addrs >= 0) {
3991      $fixedaddrs[0] = $addrs[0];
3992    }
3993    for (my $i = 1; $i <= $#addrs; $i++) {
3994      $fixedaddrs[$i] = AddressSub($addrs[$i], "0x1");
3995    }
3996    return join $delimiter, @fixedaddrs;
3997  }
3998}
3999
4000# CPU profile reader
4001sub ReadCPUProfile {
4002  my $prog = shift;
4003  my $fname = shift;       # just used for logging
4004  local *PROFILE = shift;
4005  my $version;
4006  my $period;
4007  my $i;
4008  my $profile = {};
4009  my $pcs = {};
4010
4011  # Parse string into array of slots.
4012  my $slots = CpuProfileStream->new(*PROFILE, $fname);
4013
4014  # Read header.  The current header version is a 5-element structure
4015  # containing:
4016  #   0: header count (always 0)
4017  #   1: header "words" (after this one: 3)
4018  #   2: format version (0)
4019  #   3: sampling period (usec)
4020  #   4: unused padding (always 0)
4021  if ($slots->get(0) != 0 ) {
4022    error("$fname: not a profile file, or old format profile file\n");
4023  }
4024  $i = 2 + $slots->get(1);
4025  $version = $slots->get(2);
4026  $period = $slots->get(3);
4027  # Do some sanity checking on these header values.
4028  if ($version > (2**32) || $period > (2**32) || $i > (2**32) || $i < 5) {
4029    error("$fname: not a profile file, or corrupted profile file\n");
4030  }
4031
4032  # Parse profile
4033  while ($slots->get($i) != -1) {
4034    my $n = $slots->get($i++);
4035    my $d = $slots->get($i++);
4036    if ($d > (2**16)) {  # TODO(csilvers): what's a reasonable max-stack-depth?
4037      my $addr = sprintf("0%o", $i * ($address_length == 8 ? 4 : 8));
4038      print STDERR "At index $i (address $addr):\n";
4039      error("$fname: stack trace depth >= 2**32\n");
4040    }
4041    if ($slots->get($i) == 0) {
4042      # End of profile data marker
4043      $i += $d;
4044      last;
4045    }
4046
4047    # Make key out of the stack entries
4048    my @k = ();
4049    for (my $j = 0; $j < $d; $j++) {
4050      my $pc = $slots->get($i+$j);
4051      # Subtract one from caller pc so we map back to call instr.
4052      $pc--;
4053      $pc = sprintf("%0*x", $address_length, $pc);
4054      $pcs->{$pc} = 1;
4055      push @k, $pc;
4056    }
4057
4058    AddEntry($profile, (join "\n", @k), $n);
4059    $i += $d;
4060  }
4061
4062  # Parse map
4063  my $map = '';
4064  seek(PROFILE, $i * 4, 0);
4065  read(PROFILE, $map, (stat PROFILE)[7]);
4066
4067  my $r = {};
4068  $r->{version} = $version;
4069  $r->{period} = $period;
4070  $r->{profile} = $profile;
4071  $r->{libs} = ParseLibraries($prog, $map, $pcs);
4072  $r->{pcs} = $pcs;
4073
4074  return $r;
4075}
4076
4077sub HeapProfileIndex {
4078  my $index = 1;
4079  if ($main::opt_inuse_space) {
4080    $index = 1;
4081  } elsif ($main::opt_inuse_objects) {
4082    $index = 0;
4083  } elsif ($main::opt_alloc_space) {
4084    $index = 3;
4085  } elsif ($main::opt_alloc_objects) {
4086    $index = 2;
4087  }
4088  return $index;
4089}
4090
4091sub ReadMappedLibraries {
4092  my $fh = shift;
4093  my $map = "";
4094  # Read the /proc/self/maps data
4095  while (<$fh>) {
4096    s/\r//g;         # turn windows-looking lines into unix-looking lines
4097    $map .= $_;
4098  }
4099  return $map;
4100}
4101
4102sub ReadMemoryMap {
4103  my $fh = shift;
4104  my $map = "";
4105  # Read /proc/self/maps data as formatted by DumpAddressMap()
4106  my $buildvar = "";
4107  while (<PROFILE>) {
4108    s/\r//g;         # turn windows-looking lines into unix-looking lines
4109    # Parse "build=<dir>" specification if supplied
4110    if (m/^\s*build=(.*)\n/) {
4111      $buildvar = $1;
4112    }
4113
4114    # Expand "$build" variable if available
4115    $_ =~ s/\$build\b/$buildvar/g;
4116
4117    $map .= $_;
4118  }
4119  return $map;
4120}
4121
4122sub AdjustSamples {
4123  my ($sample_adjustment, $sampling_algorithm, $n1, $s1, $n2, $s2) = @_;
4124  if ($sample_adjustment) {
4125    if ($sampling_algorithm == 2) {
4126      # Remote-heap version 2
4127      # The sampling frequency is the rate of a Poisson process.
4128      # This means that the probability of sampling an allocation of
4129      # size X with sampling rate Y is 1 - exp(-X/Y)
4130      if ($n1 != 0) {
4131        my $ratio = (($s1*1.0)/$n1)/($sample_adjustment);
4132        my $scale_factor = 1/(1 - exp(-$ratio));
4133        $n1 *= $scale_factor;
4134        $s1 *= $scale_factor;
4135      }
4136      if ($n2 != 0) {
4137        my $ratio = (($s2*1.0)/$n2)/($sample_adjustment);
4138        my $scale_factor = 1/(1 - exp(-$ratio));
4139        $n2 *= $scale_factor;
4140        $s2 *= $scale_factor;
4141      }
4142    } else {
4143      # Remote-heap version 1
4144      my $ratio;
4145      $ratio = (($s1*1.0)/$n1)/($sample_adjustment);
4146      if ($ratio < 1) {
4147        $n1 /= $ratio;
4148        $s1 /= $ratio;
4149      }
4150      $ratio = (($s2*1.0)/$n2)/($sample_adjustment);
4151      if ($ratio < 1) {
4152        $n2 /= $ratio;
4153        $s2 /= $ratio;
4154      }
4155    }
4156  }
4157  return ($n1, $s1, $n2, $s2);
4158}
4159
4160sub ReadHeapProfile {
4161  my $prog = shift;
4162  local *PROFILE = shift;
4163  my $header = shift;
4164
4165  my $index = HeapProfileIndex();
4166
4167  # Find the type of this profile.  The header line looks like:
4168  #    heap profile:   1246:  8800744 [  1246:  8800744] @ <heap-url>/266053
4169  # There are two pairs <count: size>, the first inuse objects/space, and the
4170  # second allocated objects/space.  This is followed optionally by a profile
4171  # type, and if that is present, optionally by a sampling frequency.
4172  # For remote heap profiles (v1):
4173  # The interpretation of the sampling frequency is that the profiler, for
4174  # each sample, calculates a uniformly distributed random integer less than
4175  # the given value, and records the next sample after that many bytes have
4176  # been allocated.  Therefore, the expected sample interval is half of the
4177  # given frequency.  By default, if not specified, the expected sample
4178  # interval is 128KB.  Only remote-heap-page profiles are adjusted for
4179  # sample size.
4180  # For remote heap profiles (v2):
4181  # The sampling frequency is the rate of a Poisson process. This means that
4182  # the probability of sampling an allocation of size X with sampling rate Y
4183  # is 1 - exp(-X/Y)
4184  # For version 2, a typical header line might look like this:
4185  # heap profile:   1922: 127792360 [  1922: 127792360] @ <heap-url>_v2/524288
4186  # the trailing number (524288) is the sampling rate. (Version 1 showed
4187  # double the 'rate' here)
4188  my $sampling_algorithm = 0;
4189  my $sample_adjustment = 0;
4190  chomp($header);
4191  my $type = "unknown";
4192  if ($header =~ m"^heap profile:\s*(\d+):\s+(\d+)\s+\[\s*(\d+):\s+(\d+)\](\s*@\s*([^/]*)(/(\d+))?)?") {
4193    if (defined($6) && ($6 ne '')) {
4194      $type = $6;
4195      my $sample_period = $8;
4196      # $type is "heapprofile" for profiles generated by the
4197      # heap-profiler, and either "heap" or "heap_v2" for profiles
4198      # generated by sampling directly within tcmalloc.  It can also
4199      # be "growth" for heap-growth profiles.  The first is typically
4200      # found for profiles generated locally, and the others for
4201      # remote profiles.
4202      if (($type eq "heapprofile") || ($type !~ /heap/) ) {
4203        # No need to adjust for the sampling rate with heap-profiler-derived data
4204        $sampling_algorithm = 0;
4205      } elsif ($type =~ /_v2/) {
4206        $sampling_algorithm = 2;     # version 2 sampling
4207        if (defined($sample_period) && ($sample_period ne '')) {
4208          $sample_adjustment = int($sample_period);
4209        }
4210      } else {
4211        $sampling_algorithm = 1;     # version 1 sampling
4212        if (defined($sample_period) && ($sample_period ne '')) {
4213          $sample_adjustment = int($sample_period)/2;
4214        }
4215      }
4216    } else {
4217      # We detect whether or not this is a remote-heap profile by checking
4218      # that the total-allocated stats ($n2,$s2) are exactly the
4219      # same as the in-use stats ($n1,$s1).  It is remotely conceivable
4220      # that a non-remote-heap profile may pass this check, but it is hard
4221      # to imagine how that could happen.
4222      # In this case it's so old it's guaranteed to be remote-heap version 1.
4223      my ($n1, $s1, $n2, $s2) = ($1, $2, $3, $4);
4224      if (($n1 == $n2) && ($s1 == $s2)) {
4225        # This is likely to be a remote-heap based sample profile
4226        $sampling_algorithm = 1;
4227      }
4228    }
4229  }
4230
4231  if ($sampling_algorithm > 0) {
4232    # For remote-heap generated profiles, adjust the counts and sizes to
4233    # account for the sample rate (we sample once every 128KB by default).
4234    if ($sample_adjustment == 0) {
4235      # Turn on profile adjustment.
4236      $sample_adjustment = 128*1024;
4237      print STDERR "Adjusting heap profiles for 1-in-128KB sampling rate\n";
4238    } else {
4239      printf STDERR ("Adjusting heap profiles for 1-in-%d sampling rate\n",
4240                     $sample_adjustment);
4241    }
4242    if ($sampling_algorithm > 1) {
4243      # We don't bother printing anything for the original version (version 1)
4244      printf STDERR "Heap version $sampling_algorithm\n";
4245    }
4246  }
4247
4248  my $profile = {};
4249  my $pcs = {};
4250  my $map = "";
4251
4252  while (<PROFILE>) {
4253    s/\r//g;         # turn windows-looking lines into unix-looking lines
4254    if (/^MAPPED_LIBRARIES:/) {
4255      $map .= ReadMappedLibraries(*PROFILE);
4256      last;
4257    }
4258
4259    if (/^--- Memory map:/) {
4260      $map .= ReadMemoryMap(*PROFILE);
4261      last;
4262    }
4263
4264    # Read entry of the form:
4265    #  <count1>: <bytes1> [<count2>: <bytes2>] @ a1 a2 a3 ... an
4266    s/^\s*//;
4267    s/\s*$//;
4268    if (m/^\s*(\d+):\s+(\d+)\s+\[\s*(\d+):\s+(\d+)\]\s+@\s+(.*)$/) {
4269      my $stack = $5;
4270      my ($n1, $s1, $n2, $s2) = ($1, $2, $3, $4);
4271      my @counts = AdjustSamples($sample_adjustment, $sampling_algorithm,
4272                                 $n1, $s1, $n2, $s2);
4273      AddEntries($profile, $pcs, FixCallerAddresses($stack), $counts[$index]);
4274    }
4275  }
4276
4277  my $r = {};
4278  $r->{version} = "heap";
4279  $r->{period} = 1;
4280  $r->{profile} = $profile;
4281  $r->{libs} = ParseLibraries($prog, $map, $pcs);
4282  $r->{pcs} = $pcs;
4283  return $r;
4284}
4285
4286sub ReadThreadedHeapProfile {
4287  my ($prog, $fname, $header) = @_;
4288
4289  my $index = HeapProfileIndex();
4290  my $sampling_algorithm = 0;
4291  my $sample_adjustment = 0;
4292  chomp($header);
4293  my $type = "unknown";
4294  # Assuming a very specific type of header for now.
4295  if ($header =~ m"^heap_v2/(\d+)") {
4296    $type = "_v2";
4297    $sampling_algorithm = 2;
4298    $sample_adjustment = int($1);
4299  }
4300  if ($type ne "_v2" || !defined($sample_adjustment)) {
4301    die "Threaded heap profiles require v2 sampling with a sample rate\n";
4302  }
4303
4304  my $profile = {};
4305  my $thread_profiles = {};
4306  my $pcs = {};
4307  my $map = "";
4308  my $stack = "";
4309
4310  while (<PROFILE>) {
4311    s/\r//g;
4312    if (/^MAPPED_LIBRARIES:/) {
4313      $map .= ReadMappedLibraries(*PROFILE);
4314      last;
4315    }
4316
4317    if (/^--- Memory map:/) {
4318      $map .= ReadMemoryMap(*PROFILE);
4319      last;
4320    }
4321
4322    # Read entry of the form:
4323    # @ a1 a2 ... an
4324    #   t*: <count1>: <bytes1> [<count2>: <bytes2>]
4325    #   t1: <count1>: <bytes1> [<count2>: <bytes2>]
4326    #     ...
4327    #   tn: <count1>: <bytes1> [<count2>: <bytes2>]
4328    s/^\s*//;
4329    s/\s*$//;
4330    if (m/^@\s+(.*)$/) {
4331      $stack = $1;
4332    } elsif (m/^\s*(t(\*|\d+)):\s+(\d+):\s+(\d+)\s+\[\s*(\d+):\s+(\d+)\]$/) {
4333      if ($stack eq "") {
4334        # Still in the header, so this is just a per-thread summary.
4335        next;
4336      }
4337      my $thread = $2;
4338      my ($n1, $s1, $n2, $s2) = ($3, $4, $5, $6);
4339      my @counts = AdjustSamples($sample_adjustment, $sampling_algorithm,
4340                                 $n1, $s1, $n2, $s2);
4341      if ($thread eq "*") {
4342        AddEntries($profile, $pcs, FixCallerAddresses($stack), $counts[$index]);
4343      } else {
4344        if (!exists($thread_profiles->{$thread})) {
4345          $thread_profiles->{$thread} = {};
4346        }
4347        AddEntries($thread_profiles->{$thread}, $pcs,
4348                   FixCallerAddresses($stack), $counts[$index]);
4349      }
4350    }
4351  }
4352
4353  my $r = {};
4354  $r->{version} = "heap";
4355  $r->{period} = 1;
4356  $r->{profile} = $profile;
4357  $r->{threads} = $thread_profiles;
4358  $r->{libs} = ParseLibraries($prog, $map, $pcs);
4359  $r->{pcs} = $pcs;
4360  return $r;
4361}
4362
4363sub ReadSynchProfile {
4364  my $prog = shift;
4365  local *PROFILE = shift;
4366  my $header = shift;
4367
4368  my $map = '';
4369  my $profile = {};
4370  my $pcs = {};
4371  my $sampling_period = 1;
4372  my $cyclespernanosec = 2.8;   # Default assumption for old binaries
4373  my $seen_clockrate = 0;
4374  my $line;
4375
4376  my $index = 0;
4377  if ($main::opt_total_delay) {
4378    $index = 0;
4379  } elsif ($main::opt_contentions) {
4380    $index = 1;
4381  } elsif ($main::opt_mean_delay) {
4382    $index = 2;
4383  }
4384
4385  while ( $line = <PROFILE> ) {
4386    $line =~ s/\r//g;      # turn windows-looking lines into unix-looking lines
4387    if ( $line =~ /^\s*(\d+)\s+(\d+) \@\s*(.*?)\s*$/ ) {
4388      my ($cycles, $count, $stack) = ($1, $2, $3);
4389
4390      # Convert cycles to nanoseconds
4391      $cycles /= $cyclespernanosec;
4392
4393      # Adjust for sampling done by application
4394      $cycles *= $sampling_period;
4395      $count *= $sampling_period;
4396
4397      my @values = ($cycles, $count, $cycles / $count);
4398      AddEntries($profile, $pcs, FixCallerAddresses($stack), $values[$index]);
4399
4400    } elsif ( $line =~ /^(slow release).*thread \d+  \@\s*(.*?)\s*$/ ||
4401              $line =~ /^\s*(\d+) \@\s*(.*?)\s*$/ ) {
4402      my ($cycles, $stack) = ($1, $2);
4403      if ($cycles !~ /^\d+$/) {
4404        next;
4405      }
4406
4407      # Convert cycles to nanoseconds
4408      $cycles /= $cyclespernanosec;
4409
4410      # Adjust for sampling done by application
4411      $cycles *= $sampling_period;
4412
4413      AddEntries($profile, $pcs, FixCallerAddresses($stack), $cycles);
4414
4415    } elsif ( $line =~ m/^([a-z][^=]*)=(.*)$/ ) {
4416      my ($variable, $value) = ($1,$2);
4417      for ($variable, $value) {
4418        s/^\s+//;
4419        s/\s+$//;
4420      }
4421      if ($variable eq "cycles/second") {
4422        $cyclespernanosec = $value / 1e9;
4423        $seen_clockrate = 1;
4424      } elsif ($variable eq "sampling period") {
4425        $sampling_period = $value;
4426      } elsif ($variable eq "ms since reset") {
4427        # Currently nothing is done with this value in jeprof
4428        # So we just silently ignore it for now
4429      } elsif ($variable eq "discarded samples") {
4430        # Currently nothing is done with this value in jeprof
4431        # So we just silently ignore it for now
4432      } else {
4433        printf STDERR ("Ignoring unnknown variable in /contention output: " .
4434                       "'%s' = '%s'\n",$variable,$value);
4435      }
4436    } else {
4437      # Memory map entry
4438      $map .= $line;
4439    }
4440  }
4441
4442  if (!$seen_clockrate) {
4443    printf STDERR ("No cycles/second entry in profile; Guessing %.1f GHz\n",
4444                   $cyclespernanosec);
4445  }
4446
4447  my $r = {};
4448  $r->{version} = 0;
4449  $r->{period} = $sampling_period;
4450  $r->{profile} = $profile;
4451  $r->{libs} = ParseLibraries($prog, $map, $pcs);
4452  $r->{pcs} = $pcs;
4453  return $r;
4454}
4455
4456# Given a hex value in the form "0x1abcd" or "1abcd", return either
4457# "0001abcd" or "000000000001abcd", depending on the current (global)
4458# address length.
4459sub HexExtend {
4460  my $addr = shift;
4461
4462  $addr =~ s/^(0x)?0*//;
4463  my $zeros_needed = $address_length - length($addr);
4464  if ($zeros_needed < 0) {
4465    printf STDERR "Warning: address $addr is longer than address length $address_length\n";
4466    return $addr;
4467  }
4468  return ("0" x $zeros_needed) . $addr;
4469}
4470
4471##### Symbol extraction #####
4472
4473# Aggressively search the lib_prefix values for the given library
4474# If all else fails, just return the name of the library unmodified.
4475# If the lib_prefix is "/my/path,/other/path" and $file is "/lib/dir/mylib.so"
4476# it will search the following locations in this order, until it finds a file:
4477#   /my/path/lib/dir/mylib.so
4478#   /other/path/lib/dir/mylib.so
4479#   /my/path/dir/mylib.so
4480#   /other/path/dir/mylib.so
4481#   /my/path/mylib.so
4482#   /other/path/mylib.so
4483#   /lib/dir/mylib.so              (returned as last resort)
4484sub FindLibrary {
4485  my $file = shift;
4486  my $suffix = $file;
4487
4488  # Search for the library as described above
4489  do {
4490    foreach my $prefix (@prefix_list) {
4491      my $fullpath = $prefix . $suffix;
4492      if (-e $fullpath) {
4493        return $fullpath;
4494      }
4495    }
4496  } while ($suffix =~ s|^/[^/]+/|/|);
4497  return $file;
4498}
4499
4500# Return path to library with debugging symbols.
4501# For libc libraries, the copy in /usr/lib/debug contains debugging symbols
4502sub DebuggingLibrary {
4503  my $file = shift;
4504
4505  if ($file !~ m|^/|) {
4506    return undef;
4507  }
4508
4509  # Find debug symbol file if it's named after the library's name.
4510
4511  if (-f "/usr/lib/debug$file") {
4512    if($main::opt_debug) { print STDERR "found debug info for $file in /usr/lib/debug$file\n"; }
4513    return "/usr/lib/debug$file";
4514  } elsif (-f "/usr/lib/debug$file.debug") {
4515    if($main::opt_debug) { print STDERR "found debug info for $file in /usr/lib/debug$file.debug\n"; }
4516    return "/usr/lib/debug$file.debug";
4517  }
4518
4519  if(!$main::opt_debug_syms_by_id) {
4520    if($main::opt_debug) { print STDERR "no debug symbols found for $file\n" };
4521    return undef;
4522  }
4523
4524  # Find debug file if it's named after the library's build ID.
4525
4526  my $readelf = '';
4527  if (!$main::gave_up_on_elfutils) {
4528    $readelf = qx/eu-readelf -n ${file}/;
4529    if ($?) {
4530      print STDERR "Cannot run eu-readelf. To use --debug-syms-by-id you must be on Linux, with elfutils installed.\n";
4531      $main::gave_up_on_elfutils = 1;
4532      return undef;
4533    }
4534    my $buildID = $1 if $readelf =~ /Build ID: ([A-Fa-f0-9]+)/s;
4535    if (defined $buildID && length $buildID > 0) {
4536      my $symbolFile = '/usr/lib/debug/.build-id/' . substr($buildID, 0, 2) . '/' . substr($buildID, 2) . '.debug';
4537      if (-e $symbolFile) {
4538        if($main::opt_debug) { print STDERR "found debug symbol file $symbolFile for $file\n" };
4539        return $symbolFile;
4540      } else {
4541        if($main::opt_debug) { print STDERR "no debug symbol file found for $file, build ID: $buildID\n" };
4542        return undef;
4543      }
4544    }
4545  }
4546
4547  if($main::opt_debug) { print STDERR "no debug symbols found for $file, build ID unknown\n" };
4548  return undef;
4549}
4550
4551
4552# Parse text section header of a library using objdump
4553sub ParseTextSectionHeaderFromObjdump {
4554  my $lib = shift;
4555
4556  my $size = undef;
4557  my $vma;
4558  my $file_offset;
4559  # Get objdump output from the library file to figure out how to
4560  # map between mapped addresses and addresses in the library.
4561  my $cmd = ShellEscape($obj_tool_map{"objdump"}, "-h", $lib);
4562  open(OBJDUMP, "$cmd |") || error("$cmd: $!\n");
4563  while (<OBJDUMP>) {
4564    s/\r//g;         # turn windows-looking lines into unix-looking lines
4565    # Idx Name          Size      VMA       LMA       File off  Algn
4566    #  10 .text         00104b2c  420156f0  420156f0  000156f0  2**4
4567    # For 64-bit objects, VMA and LMA will be 16 hex digits, size and file
4568    # offset may still be 8.  But AddressSub below will still handle that.
4569    my @x = split;
4570    if (($#x >= 6) && ($x[1] eq '.text')) {
4571      $size = $x[2];
4572      $vma = $x[3];
4573      $file_offset = $x[5];
4574      last;
4575    }
4576  }
4577  close(OBJDUMP);
4578
4579  if (!defined($size)) {
4580    return undef;
4581  }
4582
4583  my $r = {};
4584  $r->{size} = $size;
4585  $r->{vma} = $vma;
4586  $r->{file_offset} = $file_offset;
4587
4588  return $r;
4589}
4590
4591# Parse text section header of a library using otool (on OS X)
4592sub ParseTextSectionHeaderFromOtool {
4593  my $lib = shift;
4594
4595  my $size = undef;
4596  my $vma = undef;
4597  my $file_offset = undef;
4598  # Get otool output from the library file to figure out how to
4599  # map between mapped addresses and addresses in the library.
4600  my $command = ShellEscape($obj_tool_map{"otool"}, "-l", $lib);
4601  open(OTOOL, "$command |") || error("$command: $!\n");
4602  my $cmd = "";
4603  my $sectname = "";
4604  my $segname = "";
4605  foreach my $line (<OTOOL>) {
4606    $line =~ s/\r//g;      # turn windows-looking lines into unix-looking lines
4607    # Load command <#>
4608    #       cmd LC_SEGMENT
4609    # [...]
4610    # Section
4611    #   sectname __text
4612    #    segname __TEXT
4613    #       addr 0x000009f8
4614    #       size 0x00018b9e
4615    #     offset 2552
4616    #      align 2^2 (4)
4617    # We will need to strip off the leading 0x from the hex addresses,
4618    # and convert the offset into hex.
4619    if ($line =~ /Load command/) {
4620      $cmd = "";
4621      $sectname = "";
4622      $segname = "";
4623    } elsif ($line =~ /Section/) {
4624      $sectname = "";
4625      $segname = "";
4626    } elsif ($line =~ /cmd (\w+)/) {
4627      $cmd = $1;
4628    } elsif ($line =~ /sectname (\w+)/) {
4629      $sectname = $1;
4630    } elsif ($line =~ /segname (\w+)/) {
4631      $segname = $1;
4632    } elsif (!(($cmd eq "LC_SEGMENT" || $cmd eq "LC_SEGMENT_64") &&
4633               $sectname eq "__text" &&
4634               $segname eq "__TEXT")) {
4635      next;
4636    } elsif ($line =~ /\baddr 0x([0-9a-fA-F]+)/) {
4637      $vma = $1;
4638    } elsif ($line =~ /\bsize 0x([0-9a-fA-F]+)/) {
4639      $size = $1;
4640    } elsif ($line =~ /\boffset ([0-9]+)/) {
4641      $file_offset = sprintf("%016x", $1);
4642    }
4643    if (defined($vma) && defined($size) && defined($file_offset)) {
4644      last;
4645    }
4646  }
4647  close(OTOOL);
4648
4649  if (!defined($vma) || !defined($size) || !defined($file_offset)) {
4650     return undef;
4651  }
4652
4653  my $r = {};
4654  $r->{size} = $size;
4655  $r->{vma} = $vma;
4656  $r->{file_offset} = $file_offset;
4657
4658  return $r;
4659}
4660
4661sub ParseTextSectionHeader {
4662  # obj_tool_map("otool") is only defined if we're in a Mach-O environment
4663  if (defined($obj_tool_map{"otool"})) {
4664    my $r = ParseTextSectionHeaderFromOtool(@_);
4665    if (defined($r)){
4666      return $r;
4667    }
4668  }
4669  # If otool doesn't work, or we don't have it, fall back to objdump
4670  return ParseTextSectionHeaderFromObjdump(@_);
4671}
4672
4673# Split /proc/pid/maps dump into a list of libraries
4674sub ParseLibraries {
4675  return if $main::use_symbol_page;  # We don't need libraries info.
4676  my $prog = Cwd::abs_path(shift);
4677  my $map = shift;
4678  my $pcs = shift;
4679
4680  my $result = [];
4681  my $h = "[a-f0-9]+";
4682  my $zero_offset = HexExtend("0");
4683
4684  my $buildvar = "";
4685  foreach my $l (split("\n", $map)) {
4686    if ($l =~ m/^\s*build=(.*)$/) {
4687      $buildvar = $1;
4688    }
4689
4690    my $start;
4691    my $finish;
4692    my $offset;
4693    my $lib;
4694    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) {
4695      # Full line from /proc/self/maps.  Example:
4696      #   40000000-40015000 r-xp 00000000 03:01 12845071   /lib/ld-2.3.2.so
4697      $start = HexExtend($1);
4698      $finish = HexExtend($2);
4699      $offset = HexExtend($3);
4700      $lib = $4;
4701      $lib =~ s|\\|/|g;     # turn windows-style paths into unix-style paths
4702    } elsif ($l =~ /^\s*($h)-($h):\s*(\S+\.so(\.\d+)*)/) {
4703      # Cooked line from DumpAddressMap.  Example:
4704      #   40000000-40015000: /lib/ld-2.3.2.so
4705      $start = HexExtend($1);
4706      $finish = HexExtend($2);
4707      $offset = $zero_offset;
4708      $lib = $3;
4709    } elsif (($l =~ /^($h)-($h)\s+..x.\s+($h)\s+\S+:\S+\s+\d+\s+(\S+)$/i) && ($4 eq $prog)) {
4710      # PIEs and address space randomization do not play well with our
4711      # default assumption that main executable is at lowest
4712      # addresses. So we're detecting main executable in
4713      # /proc/self/maps as well.
4714      $start = HexExtend($1);
4715      $finish = HexExtend($2);
4716      $offset = HexExtend($3);
4717      $lib = $4;
4718      $lib =~ s|\\|/|g;     # turn windows-style paths into unix-style paths
4719    }
4720    # FreeBSD 10.0 virtual memory map /proc/curproc/map as defined in
4721    # function procfs_doprocmap (sys/fs/procfs/procfs_map.c)
4722    #
4723    # Example:
4724    # 0x800600000 0x80061a000 26 0 0xfffff800035a0000 r-x 75 33 0x1004 COW NC vnode /libexec/ld-elf.s
4725    # o.1 NCH -1
4726    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+)*)/) {
4727      $start = HexExtend($1);
4728      $finish = HexExtend($2);
4729      $offset = $zero_offset;
4730      $lib = FindLibrary($5);
4731
4732    } else {
4733      next;
4734    }
4735
4736    # Expand "$build" variable if available
4737    $lib =~ s/\$build\b/$buildvar/g;
4738
4739    $lib = FindLibrary($lib);
4740
4741    # Check for pre-relocated libraries, which use pre-relocated symbol tables
4742    # and thus require adjusting the offset that we'll use to translate
4743    # VM addresses into symbol table addresses.
4744    # Only do this if we're not going to fetch the symbol table from a
4745    # debugging copy of the library.
4746    if (!DebuggingLibrary($lib)) {
4747      my $text = ParseTextSectionHeader($lib);
4748      if (defined($text)) {
4749         my $vma_offset = AddressSub($text->{vma}, $text->{file_offset});
4750         $offset = AddressAdd($offset, $vma_offset);
4751      }
4752    }
4753
4754    if($main::opt_debug) { printf STDERR "$start:$finish ($offset) $lib\n"; }
4755    push(@{$result}, [$lib, $start, $finish, $offset]);
4756  }
4757
4758  # Append special entry for additional library (not relocated)
4759  if ($main::opt_lib ne "") {
4760    my $text = ParseTextSectionHeader($main::opt_lib);
4761    if (defined($text)) {
4762       my $start = $text->{vma};
4763       my $finish = AddressAdd($start, $text->{size});
4764
4765       push(@{$result}, [$main::opt_lib, $start, $finish, $start]);
4766    }
4767  }
4768
4769  # Append special entry for the main program.  This covers
4770  # 0..max_pc_value_seen, so that we assume pc values not found in one
4771  # of the library ranges will be treated as coming from the main
4772  # program binary.
4773  my $min_pc = HexExtend("0");
4774  my $max_pc = $min_pc;          # find the maximal PC value in any sample
4775  foreach my $pc (keys(%{$pcs})) {
4776    if (HexExtend($pc) gt $max_pc) { $max_pc = HexExtend($pc); }
4777  }
4778  push(@{$result}, [$prog, $min_pc, $max_pc, $zero_offset]);
4779
4780  return $result;
4781}
4782
4783# Add two hex addresses of length $address_length.
4784# Run jeprof --test for unit test if this is changed.
4785sub AddressAdd {
4786  my $addr1 = shift;
4787  my $addr2 = shift;
4788  my $sum;
4789
4790  if ($address_length == 8) {
4791    # Perl doesn't cope with wraparound arithmetic, so do it explicitly:
4792    $sum = (hex($addr1)+hex($addr2)) % (0x10000000 * 16);
4793    return sprintf("%08x", $sum);
4794
4795  } else {
4796    # Do the addition in 7-nibble chunks to trivialize carry handling.
4797
4798    if ($main::opt_debug and $main::opt_test) {
4799      print STDERR "AddressAdd $addr1 + $addr2 = ";
4800    }
4801
4802    my $a1 = substr($addr1,-7);
4803    $addr1 = substr($addr1,0,-7);
4804    my $a2 = substr($addr2,-7);
4805    $addr2 = substr($addr2,0,-7);
4806    $sum = hex($a1) + hex($a2);
4807    my $c = 0;
4808    if ($sum > 0xfffffff) {
4809      $c = 1;
4810      $sum -= 0x10000000;
4811    }
4812    my $r = sprintf("%07x", $sum);
4813
4814    $a1 = substr($addr1,-7);
4815    $addr1 = substr($addr1,0,-7);
4816    $a2 = substr($addr2,-7);
4817    $addr2 = substr($addr2,0,-7);
4818    $sum = hex($a1) + hex($a2) + $c;
4819    $c = 0;
4820    if ($sum > 0xfffffff) {
4821      $c = 1;
4822      $sum -= 0x10000000;
4823    }
4824    $r = sprintf("%07x", $sum) . $r;
4825
4826    $sum = hex($addr1) + hex($addr2) + $c;
4827    if ($sum > 0xff) { $sum -= 0x100; }
4828    $r = sprintf("%02x", $sum) . $r;
4829
4830    if ($main::opt_debug and $main::opt_test) { print STDERR "$r\n"; }
4831
4832    return $r;
4833  }
4834}
4835
4836
4837# Subtract two hex addresses of length $address_length.
4838# Run jeprof --test for unit test if this is changed.
4839sub AddressSub {
4840  my $addr1 = shift;
4841  my $addr2 = shift;
4842  my $diff;
4843
4844  if ($address_length == 8) {
4845    # Perl doesn't cope with wraparound arithmetic, so do it explicitly:
4846    $diff = (hex($addr1)-hex($addr2)) % (0x10000000 * 16);
4847    return sprintf("%08x", $diff);
4848
4849  } else {
4850    # Do the addition in 7-nibble chunks to trivialize borrow handling.
4851    # if ($main::opt_debug) { print STDERR "AddressSub $addr1 - $addr2 = "; }
4852
4853    my $a1 = hex(substr($addr1,-7));
4854    $addr1 = substr($addr1,0,-7);
4855    my $a2 = hex(substr($addr2,-7));
4856    $addr2 = substr($addr2,0,-7);
4857    my $b = 0;
4858    if ($a2 > $a1) {
4859      $b = 1;
4860      $a1 += 0x10000000;
4861    }
4862    $diff = $a1 - $a2;
4863    my $r = sprintf("%07x", $diff);
4864
4865    $a1 = hex(substr($addr1,-7));
4866    $addr1 = substr($addr1,0,-7);
4867    $a2 = hex(substr($addr2,-7)) + $b;
4868    $addr2 = substr($addr2,0,-7);
4869    $b = 0;
4870    if ($a2 > $a1) {
4871      $b = 1;
4872      $a1 += 0x10000000;
4873    }
4874    $diff = $a1 - $a2;
4875    $r = sprintf("%07x", $diff) . $r;
4876
4877    $a1 = hex($addr1);
4878    $a2 = hex($addr2) + $b;
4879    if ($a2 > $a1) { $a1 += 0x100; }
4880    $diff = $a1 - $a2;
4881    $r = sprintf("%02x", $diff) . $r;
4882
4883    # if ($main::opt_debug) { print STDERR "$r\n"; }
4884
4885    return $r;
4886  }
4887}
4888
4889# Increment a hex addresses of length $address_length.
4890# Run jeprof --test for unit test if this is changed.
4891sub AddressInc {
4892  my $addr = shift;
4893  my $sum;
4894
4895  if ($address_length == 8) {
4896    # Perl doesn't cope with wraparound arithmetic, so do it explicitly:
4897    $sum = (hex($addr)+1) % (0x10000000 * 16);
4898    return sprintf("%08x", $sum);
4899
4900  } else {
4901    # Do the addition in 7-nibble chunks to trivialize carry handling.
4902    # We are always doing this to step through the addresses in a function,
4903    # and will almost never overflow the first chunk, so we check for this
4904    # case and exit early.
4905
4906    # if ($main::opt_debug) { print STDERR "AddressInc $addr1 = "; }
4907
4908    my $a1 = substr($addr,-7);
4909    $addr = substr($addr,0,-7);
4910    $sum = hex($a1) + 1;
4911    my $r = sprintf("%07x", $sum);
4912    if ($sum <= 0xfffffff) {
4913      $r = $addr . $r;
4914      # if ($main::opt_debug) { print STDERR "$r\n"; }
4915      return HexExtend($r);
4916    } else {
4917      $r = "0000000";
4918    }
4919
4920    $a1 = substr($addr,-7);
4921    $addr = substr($addr,0,-7);
4922    $sum = hex($a1) + 1;
4923    $r = sprintf("%07x", $sum) . $r;
4924    if ($sum <= 0xfffffff) {
4925      $r = $addr . $r;
4926      # if ($main::opt_debug) { print STDERR "$r\n"; }
4927      return HexExtend($r);
4928    } else {
4929      $r = "00000000000000";
4930    }
4931
4932    $sum = hex($addr) + 1;
4933    if ($sum > 0xff) { $sum -= 0x100; }
4934    $r = sprintf("%02x", $sum) . $r;
4935
4936    # if ($main::opt_debug) { print STDERR "$r\n"; }
4937    return $r;
4938  }
4939}
4940
4941# Extract symbols for all PC values found in profile
4942sub ExtractSymbols {
4943  my $libs = shift;
4944  my $pcset = shift;
4945
4946  my $symbols = {};
4947
4948  # Map each PC value to the containing library.  To make this faster,
4949  # we sort libraries by their starting pc value (highest first), and
4950  # advance through the libraries as we advance the pc.  Sometimes the
4951  # addresses of libraries may overlap with the addresses of the main
4952  # binary, so to make sure the libraries 'win', we iterate over the
4953  # libraries in reverse order (which assumes the binary doesn't start
4954  # in the middle of a library, which seems a fair assumption).
4955  my @pcs = (sort { $a cmp $b } keys(%{$pcset}));  # pcset is 0-extended strings
4956  foreach my $lib (sort {$b->[1] cmp $a->[1]} @{$libs}) {
4957    my $libname = $lib->[0];
4958    my $start = $lib->[1];
4959    my $finish = $lib->[2];
4960    my $offset = $lib->[3];
4961
4962    # Use debug library if it exists
4963    my $debug_libname = DebuggingLibrary($libname);
4964    if ($debug_libname) {
4965        $libname = $debug_libname;
4966    }
4967
4968    # Get list of pcs that belong in this library.
4969    my $contained = [];
4970    my ($start_pc_index, $finish_pc_index);
4971    # Find smallest finish_pc_index such that $finish < $pc[$finish_pc_index].
4972    for ($finish_pc_index = $#pcs + 1; $finish_pc_index > 0;
4973         $finish_pc_index--) {
4974      last if $pcs[$finish_pc_index - 1] le $finish;
4975    }
4976    # Find smallest start_pc_index such that $start <= $pc[$start_pc_index].
4977    for ($start_pc_index = $finish_pc_index; $start_pc_index > 0;
4978         $start_pc_index--) {
4979      last if $pcs[$start_pc_index - 1] lt $start;
4980    }
4981    # This keeps PC values higher than $pc[$finish_pc_index] in @pcs,
4982    # in case there are overlaps in libraries and the main binary.
4983    @{$contained} = splice(@pcs, $start_pc_index,
4984                           $finish_pc_index - $start_pc_index);
4985    # Map to symbols
4986    MapToSymbols($libname, AddressSub($start, $offset), $contained, $symbols);
4987  }
4988
4989  return $symbols;
4990}
4991
4992# Map list of PC values to symbols for a given image
4993sub MapToSymbols {
4994  my $image = shift;
4995  my $offset = shift;
4996  my $pclist = shift;
4997  my $symbols = shift;
4998
4999  my $debug = 0;
5000
5001  # Ignore empty binaries
5002  if ($#{$pclist} < 0) { return; }
5003
5004  # Figure out the addr2line command to use
5005  my $addr2line = $obj_tool_map{"addr2line"};
5006  my $cmd = ShellEscape($addr2line, "-f", "-C", "-e", $image);
5007  if (exists $obj_tool_map{"addr2line_pdb"}) {
5008    $addr2line = $obj_tool_map{"addr2line_pdb"};
5009    $cmd = ShellEscape($addr2line, "--demangle", "-f", "-C", "-e", $image);
5010  }
5011
5012  # If "addr2line" isn't installed on the system at all, just use
5013  # nm to get what info we can (function names, but not line numbers).
5014  if (system(ShellEscape($addr2line, "--help") . " >$dev_null 2>&1") != 0) {
5015    MapSymbolsWithNM($image, $offset, $pclist, $symbols);
5016    return;
5017  }
5018
5019  # "addr2line -i" can produce a variable number of lines per input
5020  # address, with no separator that allows us to tell when data for
5021  # the next address starts.  So we find the address for a special
5022  # symbol (_fini) and interleave this address between all real
5023  # addresses passed to addr2line.  The name of this special symbol
5024  # can then be used as a separator.
5025  $sep_address = undef;  # May be filled in by MapSymbolsWithNM()
5026  my $nm_symbols = {};
5027  MapSymbolsWithNM($image, $offset, $pclist, $nm_symbols);
5028  if (defined($sep_address)) {
5029    # Only add " -i" to addr2line if the binary supports it.
5030    # addr2line --help returns 0, but not if it sees an unknown flag first.
5031    if (system("$cmd -i --help >$dev_null 2>&1") == 0) {
5032      $cmd .= " -i";
5033    } else {
5034      $sep_address = undef;   # no need for sep_address if we don't support -i
5035    }
5036  }
5037
5038  # Make file with all PC values with intervening 'sep_address' so
5039  # that we can reliably detect the end of inlined function list
5040  open(ADDRESSES, ">$main::tmpfile_sym") || error("$main::tmpfile_sym: $!\n");
5041  if ($debug) { print("---- $image ---\n"); }
5042  for (my $i = 0; $i <= $#{$pclist}; $i++) {
5043    # addr2line always reads hex addresses, and does not need '0x' prefix.
5044    if ($debug) { printf STDERR ("%s\n", $pclist->[$i]); }
5045    printf ADDRESSES ("%s\n", AddressSub($pclist->[$i], $offset));
5046    if (defined($sep_address)) {
5047      printf ADDRESSES ("%s\n", $sep_address);
5048    }
5049  }
5050  close(ADDRESSES);
5051  if ($debug) {
5052    print("----\n");
5053    system("cat", $main::tmpfile_sym);
5054    print("----\n");
5055    system("$cmd < " . ShellEscape($main::tmpfile_sym));
5056    print("----\n");
5057  }
5058
5059  open(SYMBOLS, "$cmd <" . ShellEscape($main::tmpfile_sym) . " |")
5060      || error("$cmd: $!\n");
5061  my $count = 0;   # Index in pclist
5062  while (<SYMBOLS>) {
5063    # Read fullfunction and filelineinfo from next pair of lines
5064    s/\r?\n$//g;
5065    my $fullfunction = $_;
5066    $_ = <SYMBOLS>;
5067    s/\r?\n$//g;
5068    my $filelinenum = $_;
5069
5070    if (defined($sep_address) && $fullfunction eq $sep_symbol) {
5071      # Terminating marker for data for this address
5072      $count++;
5073      next;
5074    }
5075
5076    $filelinenum =~ s|\\|/|g; # turn windows-style paths into unix-style paths
5077
5078    my $pcstr = $pclist->[$count];
5079    my $function = ShortFunctionName($fullfunction);
5080    my $nms = $nm_symbols->{$pcstr};
5081    if (defined($nms)) {
5082      if ($fullfunction eq '??') {
5083        # nm found a symbol for us.
5084        $function = $nms->[0];
5085        $fullfunction = $nms->[2];
5086      } else {
5087	# MapSymbolsWithNM tags each routine with its starting address,
5088	# useful in case the image has multiple occurrences of this
5089	# routine.  (It uses a syntax that resembles template parameters,
5090	# that are automatically stripped out by ShortFunctionName().)
5091	# addr2line does not provide the same information.  So we check
5092	# if nm disambiguated our symbol, and if so take the annotated
5093	# (nm) version of the routine-name.  TODO(csilvers): this won't
5094	# catch overloaded, inlined symbols, which nm doesn't see.
5095	# Better would be to do a check similar to nm's, in this fn.
5096	if ($nms->[2] =~ m/^\Q$function\E/) {  # sanity check it's the right fn
5097	  $function = $nms->[0];
5098	  $fullfunction = $nms->[2];
5099	}
5100      }
5101    }
5102
5103    # Prepend to accumulated symbols for pcstr
5104    # (so that caller comes before callee)
5105    my $sym = $symbols->{$pcstr};
5106    if (!defined($sym)) {
5107      $sym = [];
5108      $symbols->{$pcstr} = $sym;
5109    }
5110    unshift(@{$sym}, $function, $filelinenum, $fullfunction);
5111    if ($debug) { printf STDERR ("%s => [%s]\n", $pcstr, join(" ", @{$sym})); }
5112    if (!defined($sep_address)) {
5113      # Inlining is off, so this entry ends immediately
5114      $count++;
5115    }
5116  }
5117  close(SYMBOLS);
5118}
5119
5120# Use nm to map the list of referenced PCs to symbols.  Return true iff we
5121# are able to read procedure information via nm.
5122sub MapSymbolsWithNM {
5123  my $image = shift;
5124  my $offset = shift;
5125  my $pclist = shift;
5126  my $symbols = shift;
5127
5128  # Get nm output sorted by increasing address
5129  my $symbol_table = GetProcedureBoundaries($image, ".");
5130  if (!%{$symbol_table}) {
5131    return 0;
5132  }
5133  # Start addresses are already the right length (8 or 16 hex digits).
5134  my @names = sort { $symbol_table->{$a}->[0] cmp $symbol_table->{$b}->[0] }
5135    keys(%{$symbol_table});
5136
5137  if ($#names < 0) {
5138    # No symbols: just use addresses
5139    foreach my $pc (@{$pclist}) {
5140      my $pcstr = "0x" . $pc;
5141      $symbols->{$pc} = [$pcstr, "?", $pcstr];
5142    }
5143    return 0;
5144  }
5145
5146  # Sort addresses so we can do a join against nm output
5147  my $index = 0;
5148  my $fullname = $names[0];
5149  my $name = ShortFunctionName($fullname);
5150  foreach my $pc (sort { $a cmp $b } @{$pclist}) {
5151    # Adjust for mapped offset
5152    my $mpc = AddressSub($pc, $offset);
5153    while (($index < $#names) && ($mpc ge $symbol_table->{$fullname}->[1])){
5154      $index++;
5155      $fullname = $names[$index];
5156      $name = ShortFunctionName($fullname);
5157    }
5158    if ($mpc lt $symbol_table->{$fullname}->[1]) {
5159      $symbols->{$pc} = [$name, "?", $fullname];
5160    } else {
5161      my $pcstr = "0x" . $pc;
5162      $symbols->{$pc} = [$pcstr, "?", $pcstr];
5163    }
5164  }
5165  return 1;
5166}
5167
5168sub ShortFunctionName {
5169  my $function = shift;
5170  while ($function =~ s/\([^()]*\)(\s*const)?//g) { }   # Argument types
5171  while ($function =~ s/<[^<>]*>//g)  { }    # Remove template arguments
5172  $function =~ s/^.*\s+(\w+::)/$1/;          # Remove leading type
5173  return $function;
5174}
5175
5176# Trim overly long symbols found in disassembler output
5177sub CleanDisassembly {
5178  my $d = shift;
5179  while ($d =~ s/\([^()%]*\)(\s*const)?//g) { } # Argument types, not (%rax)
5180  while ($d =~ s/(\w+)<[^<>]*>/$1/g)  { }       # Remove template arguments
5181  return $d;
5182}
5183
5184# Clean file name for display
5185sub CleanFileName {
5186  my ($f) = @_;
5187  $f =~ s|^/proc/self/cwd/||;
5188  $f =~ s|^\./||;
5189  return $f;
5190}
5191
5192# Make address relative to section and clean up for display
5193sub UnparseAddress {
5194  my ($offset, $address) = @_;
5195  $address = AddressSub($address, $offset);
5196  $address =~ s/^0x//;
5197  $address =~ s/^0*//;
5198  return $address;
5199}
5200
5201##### Miscellaneous #####
5202
5203# Find the right versions of the above object tools to use.  The
5204# argument is the program file being analyzed, and should be an ELF
5205# 32-bit or ELF 64-bit executable file.  The location of the tools
5206# is determined by considering the following options in this order:
5207#   1) --tools option, if set
5208#   2) JEPROF_TOOLS environment variable, if set
5209#   3) the environment
5210sub ConfigureObjTools {
5211  my $prog_file = shift;
5212
5213  # Check for the existence of $prog_file because /usr/bin/file does not
5214  # predictably return error status in prod.
5215  (-e $prog_file)  || error("$prog_file does not exist.\n");
5216
5217  my $file_type = undef;
5218  if (-e "/usr/bin/file") {
5219    # Follow symlinks (at least for systems where "file" supports that).
5220    my $escaped_prog_file = ShellEscape($prog_file);
5221    $file_type = `/usr/bin/file -L $escaped_prog_file 2>$dev_null ||
5222                  /usr/bin/file $escaped_prog_file`;
5223  } elsif ($^O == "MSWin32") {
5224    $file_type = "MS Windows";
5225  } else {
5226    print STDERR "WARNING: Can't determine the file type of $prog_file";
5227  }
5228
5229  if ($file_type =~ /64-bit/) {
5230    # Change $address_length to 16 if the program file is ELF 64-bit.
5231    # We can't detect this from many (most?) heap or lock contention
5232    # profiles, since the actual addresses referenced are generally in low
5233    # memory even for 64-bit programs.
5234    $address_length = 16;
5235  }
5236
5237  if ($file_type =~ /MS Windows/) {
5238    # For windows, we provide a version of nm and addr2line as part of
5239    # the opensource release, which is capable of parsing
5240    # Windows-style PDB executables.  It should live in the path, or
5241    # in the same directory as jeprof.
5242    $obj_tool_map{"nm_pdb"} = "nm-pdb";
5243    $obj_tool_map{"addr2line_pdb"} = "addr2line-pdb";
5244  }
5245
5246  if ($file_type =~ /Mach-O/) {
5247    # OS X uses otool to examine Mach-O files, rather than objdump.
5248    $obj_tool_map{"otool"} = "otool";
5249    $obj_tool_map{"addr2line"} = "false";  # no addr2line
5250    $obj_tool_map{"objdump"} = "false";  # no objdump
5251  }
5252
5253  # Go fill in %obj_tool_map with the pathnames to use:
5254  foreach my $tool (keys %obj_tool_map) {
5255    $obj_tool_map{$tool} = ConfigureTool($obj_tool_map{$tool});
5256  }
5257}
5258
5259# Returns the path of a caller-specified object tool.  If --tools or
5260# JEPROF_TOOLS are specified, then returns the full path to the tool
5261# with that prefix.  Otherwise, returns the path unmodified (which
5262# means we will look for it on PATH).
5263sub ConfigureTool {
5264  my $tool = shift;
5265  my $path;
5266
5267  # --tools (or $JEPROF_TOOLS) is a comma separated list, where each
5268  # item is either a) a pathname prefix, or b) a map of the form
5269  # <tool>:<path>.  First we look for an entry of type (b) for our
5270  # tool.  If one is found, we use it.  Otherwise, we consider all the
5271  # pathname prefixes in turn, until one yields an existing file.  If
5272  # none does, we use a default path.
5273  my $tools = $main::opt_tools || $ENV{"JEPROF_TOOLS"} || "";
5274  if ($tools =~ m/(,|^)\Q$tool\E:([^,]*)/) {
5275    $path = $2;
5276    # TODO(csilvers): sanity-check that $path exists?  Hard if it's relative.
5277  } elsif ($tools ne '') {
5278    foreach my $prefix (split(',', $tools)) {
5279      next if ($prefix =~ /:/);    # ignore "tool:fullpath" entries in the list
5280      if (-x $prefix . $tool) {
5281        $path = $prefix . $tool;
5282        last;
5283      }
5284    }
5285    if (!$path) {
5286      error("No '$tool' found with prefix specified by " .
5287            "--tools (or \$JEPROF_TOOLS) '$tools'\n");
5288    }
5289  } else {
5290    # ... otherwise use the version that exists in the same directory as
5291    # jeprof.  If there's nothing there, use $PATH.
5292    $0 =~ m,[^/]*$,;     # this is everything after the last slash
5293    my $dirname = $`;    # this is everything up to and including the last slash
5294    if (-x "$dirname$tool") {
5295      $path = "$dirname$tool";
5296    } else {
5297      $path = $tool;
5298    }
5299  }
5300  if ($main::opt_debug) { print STDERR "Using '$path' for '$tool'.\n"; }
5301  return $path;
5302}
5303
5304sub ShellEscape {
5305  my @escaped_words = ();
5306  foreach my $word (@_) {
5307    my $escaped_word = $word;
5308    if ($word =~ m![^a-zA-Z0-9/.,_=-]!) {  # check for anything not in whitelist
5309      $escaped_word =~ s/'/'\\''/;
5310      $escaped_word = "'$escaped_word'";
5311    }
5312    push(@escaped_words, $escaped_word);
5313  }
5314  return join(" ", @escaped_words);
5315}
5316
5317sub cleanup {
5318  unlink($main::tmpfile_sym);
5319  unlink(keys %main::tempnames);
5320
5321  # We leave any collected profiles in $HOME/jeprof in case the user wants
5322  # to look at them later.  We print a message informing them of this.
5323  if ((scalar(@main::profile_files) > 0) &&
5324      defined($main::collected_profile)) {
5325    if (scalar(@main::profile_files) == 1) {
5326      print STDERR "Dynamically gathered profile is in $main::collected_profile\n";
5327    }
5328    print STDERR "If you want to investigate this profile further, you can do:\n";
5329    print STDERR "\n";
5330    print STDERR "  jeprof \\\n";
5331    print STDERR "    $main::prog \\\n";
5332    print STDERR "    $main::collected_profile\n";
5333    print STDERR "\n";
5334  }
5335}
5336
5337sub sighandler {
5338  cleanup();
5339  exit(1);
5340}
5341
5342sub error {
5343  my $msg = shift;
5344  print STDERR $msg;
5345  cleanup();
5346  exit(1);
5347}
5348
5349
5350# Run $nm_command and get all the resulting procedure boundaries whose
5351# names match "$regexp" and returns them in a hashtable mapping from
5352# procedure name to a two-element vector of [start address, end address]
5353sub GetProcedureBoundariesViaNm {
5354  my $escaped_nm_command = shift;    # shell-escaped
5355  my $regexp = shift;
5356
5357  my $symbol_table = {};
5358  open(NM, "$escaped_nm_command |") || error("$escaped_nm_command: $!\n");
5359  my $last_start = "0";
5360  my $routine = "";
5361  while (<NM>) {
5362    s/\r//g;         # turn windows-looking lines into unix-looking lines
5363    if (m/^\s*([0-9a-f]+) (.) (..*)/) {
5364      my $start_val = $1;
5365      my $type = $2;
5366      my $this_routine = $3;
5367
5368      # It's possible for two symbols to share the same address, if
5369      # one is a zero-length variable (like __start_google_malloc) or
5370      # one symbol is a weak alias to another (like __libc_malloc).
5371      # In such cases, we want to ignore all values except for the
5372      # actual symbol, which in nm-speak has type "T".  The logic
5373      # below does this, though it's a bit tricky: what happens when
5374      # we have a series of lines with the same address, is the first
5375      # one gets queued up to be processed.  However, it won't
5376      # *actually* be processed until later, when we read a line with
5377      # a different address.  That means that as long as we're reading
5378      # lines with the same address, we have a chance to replace that
5379      # item in the queue, which we do whenever we see a 'T' entry --
5380      # that is, a line with type 'T'.  If we never see a 'T' entry,
5381      # we'll just go ahead and process the first entry (which never
5382      # got touched in the queue), and ignore the others.
5383      if ($start_val eq $last_start && $type =~ /t/i) {
5384        # We are the 'T' symbol at this address, replace previous symbol.
5385        $routine = $this_routine;
5386        next;
5387      } elsif ($start_val eq $last_start) {
5388        # We're not the 'T' symbol at this address, so ignore us.
5389        next;
5390      }
5391
5392      if ($this_routine eq $sep_symbol) {
5393        $sep_address = HexExtend($start_val);
5394      }
5395
5396      # Tag this routine with the starting address in case the image
5397      # has multiple occurrences of this routine.  We use a syntax
5398      # that resembles template parameters that are automatically
5399      # stripped out by ShortFunctionName()
5400      $this_routine .= "<$start_val>";
5401
5402      if (defined($routine) && $routine =~ m/$regexp/) {
5403        $symbol_table->{$routine} = [HexExtend($last_start),
5404                                     HexExtend($start_val)];
5405      }
5406      $last_start = $start_val;
5407      $routine = $this_routine;
5408    } elsif (m/^Loaded image name: (.+)/) {
5409      # The win32 nm workalike emits information about the binary it is using.
5410      if ($main::opt_debug) { print STDERR "Using Image $1\n"; }
5411    } elsif (m/^PDB file name: (.+)/) {
5412      # The win32 nm workalike emits information about the pdb it is using.
5413      if ($main::opt_debug) { print STDERR "Using PDB $1\n"; }
5414    }
5415  }
5416  close(NM);
5417  # Handle the last line in the nm output.  Unfortunately, we don't know
5418  # how big this last symbol is, because we don't know how big the file
5419  # is.  For now, we just give it a size of 0.
5420  # TODO(csilvers): do better here.
5421  if (defined($routine) && $routine =~ m/$regexp/) {
5422    $symbol_table->{$routine} = [HexExtend($last_start),
5423                                 HexExtend($last_start)];
5424  }
5425  return $symbol_table;
5426}
5427
5428# Gets the procedure boundaries for all routines in "$image" whose names
5429# match "$regexp" and returns them in a hashtable mapping from procedure
5430# name to a two-element vector of [start address, end address].
5431# Will return an empty map if nm is not installed or not working properly.
5432sub GetProcedureBoundaries {
5433  my $image = shift;
5434  my $regexp = shift;
5435
5436  # If $image doesn't start with /, then put ./ in front of it.  This works
5437  # around an obnoxious bug in our probing of nm -f behavior.
5438  # "nm -f $image" is supposed to fail on GNU nm, but if:
5439  #
5440  # a. $image starts with [BbSsPp] (for example, bin/foo/bar), AND
5441  # b. you have a.out in your current directory (a not uncommon occurrence)
5442  #
5443  # then "nm -f $image" succeeds because -f only looks at the first letter of
5444  # the argument, which looks valid because it's [BbSsPp], and then since
5445  # there's no image provided, it looks for a.out and finds it.
5446  #
5447  # This regex makes sure that $image starts with . or /, forcing the -f
5448  # parsing to fail since . and / are not valid formats.
5449  $image =~ s#^[^/]#./$&#;
5450
5451  # For libc libraries, the copy in /usr/lib/debug contains debugging symbols
5452  my $debugging = DebuggingLibrary($image);
5453  if ($debugging) {
5454    $image = $debugging;
5455  }
5456
5457  my $nm = $obj_tool_map{"nm"};
5458  my $cppfilt = $obj_tool_map{"c++filt"};
5459
5460  # nm can fail for two reasons: 1) $image isn't a debug library; 2) nm
5461  # binary doesn't support --demangle.  In addition, for OS X we need
5462  # to use the -f flag to get 'flat' nm output (otherwise we don't sort
5463  # properly and get incorrect results).  Unfortunately, GNU nm uses -f
5464  # in an incompatible way.  So first we test whether our nm supports
5465  # --demangle and -f.
5466  my $demangle_flag = "";
5467  my $cppfilt_flag = "";
5468  my $to_devnull = ">$dev_null 2>&1";
5469  if (system(ShellEscape($nm, "--demangle", $image) . $to_devnull) == 0) {
5470    # In this mode, we do "nm --demangle <foo>"
5471    $demangle_flag = "--demangle";
5472    $cppfilt_flag = "";
5473  } elsif (system(ShellEscape($cppfilt, $image) . $to_devnull) == 0) {
5474    # In this mode, we do "nm <foo> | c++filt"
5475    $cppfilt_flag = " | " . ShellEscape($cppfilt);
5476  };
5477  my $flatten_flag = "";
5478  if (system(ShellEscape($nm, "-f", $image) . $to_devnull) == 0) {
5479    $flatten_flag = "-f";
5480  }
5481
5482  # Finally, in the case $imagie isn't a debug library, we try again with
5483  # -D to at least get *exported* symbols.  If we can't use --demangle,
5484  # we use c++filt instead, if it exists on this system.
5485  my @nm_commands = (ShellEscape($nm, "-n", $flatten_flag, $demangle_flag,
5486                                 $image) . " 2>$dev_null $cppfilt_flag",
5487                     ShellEscape($nm, "-D", "-n", $flatten_flag, $demangle_flag,
5488                                 $image) . " 2>$dev_null $cppfilt_flag",
5489                     # 6nm is for Go binaries
5490                     ShellEscape("6nm", "$image") . " 2>$dev_null | sort",
5491                     );
5492
5493  # If the executable is an MS Windows PDB-format executable, we'll
5494  # have set up obj_tool_map("nm_pdb").  In this case, we actually
5495  # want to use both unix nm and windows-specific nm_pdb, since
5496  # PDB-format executables can apparently include dwarf .o files.
5497  if (exists $obj_tool_map{"nm_pdb"}) {
5498    push(@nm_commands,
5499         ShellEscape($obj_tool_map{"nm_pdb"}, "--demangle", $image)
5500         . " 2>$dev_null");
5501  }
5502
5503  foreach my $nm_command (@nm_commands) {
5504    my $symbol_table = GetProcedureBoundariesViaNm($nm_command, $regexp);
5505    return $symbol_table if (%{$symbol_table});
5506  }
5507  my $symbol_table = {};
5508  return $symbol_table;
5509}
5510
5511
5512# The test vectors for AddressAdd/Sub/Inc are 8-16-nibble hex strings.
5513# To make them more readable, we add underscores at interesting places.
5514# This routine removes the underscores, producing the canonical representation
5515# used by jeprof to represent addresses, particularly in the tested routines.
5516sub CanonicalHex {
5517  my $arg = shift;
5518  return join '', (split '_',$arg);
5519}
5520
5521
5522# Unit test for AddressAdd:
5523sub AddressAddUnitTest {
5524  my $test_data_8 = shift;
5525  my $test_data_16 = shift;
5526  my $error_count = 0;
5527  my $fail_count = 0;
5528  my $pass_count = 0;
5529  # print STDERR "AddressAddUnitTest: ", 1+$#{$test_data_8}, " tests\n";
5530
5531  # First a few 8-nibble addresses.  Note that this implementation uses
5532  # plain old arithmetic, so a quick sanity check along with verifying what
5533  # happens to overflow (we want it to wrap):
5534  $address_length = 8;
5535  foreach my $row (@{$test_data_8}) {
5536    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
5537    my $sum = AddressAdd ($row->[0], $row->[1]);
5538    if ($sum ne $row->[2]) {
5539      printf STDERR "ERROR: %s != %s + %s = %s\n", $sum,
5540             $row->[0], $row->[1], $row->[2];
5541      ++$fail_count;
5542    } else {
5543      ++$pass_count;
5544    }
5545  }
5546  printf STDERR "AddressAdd 32-bit tests: %d passes, %d failures\n",
5547         $pass_count, $fail_count;
5548  $error_count = $fail_count;
5549  $fail_count = 0;
5550  $pass_count = 0;
5551
5552  # Now 16-nibble addresses.
5553  $address_length = 16;
5554  foreach my $row (@{$test_data_16}) {
5555    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
5556    my $sum = AddressAdd (CanonicalHex($row->[0]), CanonicalHex($row->[1]));
5557    my $expected = join '', (split '_',$row->[2]);
5558    if ($sum ne CanonicalHex($row->[2])) {
5559      printf STDERR "ERROR: %s != %s + %s = %s\n", $sum,
5560             $row->[0], $row->[1], $row->[2];
5561      ++$fail_count;
5562    } else {
5563      ++$pass_count;
5564    }
5565  }
5566  printf STDERR "AddressAdd 64-bit tests: %d passes, %d failures\n",
5567         $pass_count, $fail_count;
5568  $error_count += $fail_count;
5569
5570  return $error_count;
5571}
5572
5573
5574# Unit test for AddressSub:
5575sub AddressSubUnitTest {
5576  my $test_data_8 = shift;
5577  my $test_data_16 = shift;
5578  my $error_count = 0;
5579  my $fail_count = 0;
5580  my $pass_count = 0;
5581  # print STDERR "AddressSubUnitTest: ", 1+$#{$test_data_8}, " tests\n";
5582
5583  # First a few 8-nibble addresses.  Note that this implementation uses
5584  # plain old arithmetic, so a quick sanity check along with verifying what
5585  # happens to overflow (we want it to wrap):
5586  $address_length = 8;
5587  foreach my $row (@{$test_data_8}) {
5588    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
5589    my $sum = AddressSub ($row->[0], $row->[1]);
5590    if ($sum ne $row->[3]) {
5591      printf STDERR "ERROR: %s != %s - %s = %s\n", $sum,
5592             $row->[0], $row->[1], $row->[3];
5593      ++$fail_count;
5594    } else {
5595      ++$pass_count;
5596    }
5597  }
5598  printf STDERR "AddressSub 32-bit tests: %d passes, %d failures\n",
5599         $pass_count, $fail_count;
5600  $error_count = $fail_count;
5601  $fail_count = 0;
5602  $pass_count = 0;
5603
5604  # Now 16-nibble addresses.
5605  $address_length = 16;
5606  foreach my $row (@{$test_data_16}) {
5607    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
5608    my $sum = AddressSub (CanonicalHex($row->[0]), CanonicalHex($row->[1]));
5609    if ($sum ne CanonicalHex($row->[3])) {
5610      printf STDERR "ERROR: %s != %s - %s = %s\n", $sum,
5611             $row->[0], $row->[1], $row->[3];
5612      ++$fail_count;
5613    } else {
5614      ++$pass_count;
5615    }
5616  }
5617  printf STDERR "AddressSub 64-bit tests: %d passes, %d failures\n",
5618         $pass_count, $fail_count;
5619  $error_count += $fail_count;
5620
5621  return $error_count;
5622}
5623
5624
5625# Unit test for AddressInc:
5626sub AddressIncUnitTest {
5627  my $test_data_8 = shift;
5628  my $test_data_16 = shift;
5629  my $error_count = 0;
5630  my $fail_count = 0;
5631  my $pass_count = 0;
5632  # print STDERR "AddressIncUnitTest: ", 1+$#{$test_data_8}, " tests\n";
5633
5634  # First a few 8-nibble addresses.  Note that this implementation uses
5635  # plain old arithmetic, so a quick sanity check along with verifying what
5636  # happens to overflow (we want it to wrap):
5637  $address_length = 8;
5638  foreach my $row (@{$test_data_8}) {
5639    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
5640    my $sum = AddressInc ($row->[0]);
5641    if ($sum ne $row->[4]) {
5642      printf STDERR "ERROR: %s != %s + 1 = %s\n", $sum,
5643             $row->[0], $row->[4];
5644      ++$fail_count;
5645    } else {
5646      ++$pass_count;
5647    }
5648  }
5649  printf STDERR "AddressInc 32-bit tests: %d passes, %d failures\n",
5650         $pass_count, $fail_count;
5651  $error_count = $fail_count;
5652  $fail_count = 0;
5653  $pass_count = 0;
5654
5655  # Now 16-nibble addresses.
5656  $address_length = 16;
5657  foreach my $row (@{$test_data_16}) {
5658    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
5659    my $sum = AddressInc (CanonicalHex($row->[0]));
5660    if ($sum ne CanonicalHex($row->[4])) {
5661      printf STDERR "ERROR: %s != %s + 1 = %s\n", $sum,
5662             $row->[0], $row->[4];
5663      ++$fail_count;
5664    } else {
5665      ++$pass_count;
5666    }
5667  }
5668  printf STDERR "AddressInc 64-bit tests: %d passes, %d failures\n",
5669         $pass_count, $fail_count;
5670  $error_count += $fail_count;
5671
5672  return $error_count;
5673}
5674
5675
5676# Driver for unit tests.
5677# Currently just the address add/subtract/increment routines for 64-bit.
5678sub RunUnitTests {
5679  my $error_count = 0;
5680
5681  # This is a list of tuples [a, b, a+b, a-b, a+1]
5682  my $unit_test_data_8 = [
5683    [qw(aaaaaaaa 50505050 fafafafa 5a5a5a5a aaaaaaab)],
5684    [qw(50505050 aaaaaaaa fafafafa a5a5a5a6 50505051)],
5685    [qw(ffffffff aaaaaaaa aaaaaaa9 55555555 00000000)],
5686    [qw(00000001 ffffffff 00000000 00000002 00000002)],
5687    [qw(00000001 fffffff0 fffffff1 00000011 00000002)],
5688  ];
5689  my $unit_test_data_16 = [
5690    # The implementation handles data in 7-nibble chunks, so those are the
5691    # interesting boundaries.
5692    [qw(aaaaaaaa 50505050
5693        00_000000f_afafafa 00_0000005_a5a5a5a 00_000000a_aaaaaab)],
5694    [qw(50505050 aaaaaaaa
5695        00_000000f_afafafa ff_ffffffa_5a5a5a6 00_0000005_0505051)],
5696    [qw(ffffffff aaaaaaaa
5697        00_000001a_aaaaaa9 00_0000005_5555555 00_0000010_0000000)],
5698    [qw(00000001 ffffffff
5699        00_0000010_0000000 ff_ffffff0_0000002 00_0000000_0000002)],
5700    [qw(00000001 fffffff0
5701        00_000000f_ffffff1 ff_ffffff0_0000011 00_0000000_0000002)],
5702
5703    [qw(00_a00000a_aaaaaaa 50505050
5704        00_a00000f_afafafa 00_a000005_a5a5a5a 00_a00000a_aaaaaab)],
5705    [qw(0f_fff0005_0505050 aaaaaaaa
5706        0f_fff000f_afafafa 0f_ffefffa_5a5a5a6 0f_fff0005_0505051)],
5707    [qw(00_000000f_fffffff 01_800000a_aaaaaaa
5708        01_800001a_aaaaaa9 fe_8000005_5555555 00_0000010_0000000)],
5709    [qw(00_0000000_0000001 ff_fffffff_fffffff
5710        00_0000000_0000000 00_0000000_0000002 00_0000000_0000002)],
5711    [qw(00_0000000_0000001 ff_fffffff_ffffff0
5712        ff_fffffff_ffffff1 00_0000000_0000011 00_0000000_0000002)],
5713  ];
5714
5715  $error_count += AddressAddUnitTest($unit_test_data_8, $unit_test_data_16);
5716  $error_count += AddressSubUnitTest($unit_test_data_8, $unit_test_data_16);
5717  $error_count += AddressIncUnitTest($unit_test_data_8, $unit_test_data_16);
5718  if ($error_count > 0) {
5719    print STDERR $error_count, " errors: FAILED\n";
5720  } else {
5721    print STDERR "PASS\n";
5722  }
5723  exit ($error_count);
5724}
5725||||||| dec341af7695
5726=======
5727#! /usr/bin/env perl
5728
5729# Copyright (c) 1998-2007, Google Inc.
5730# All rights reserved.
5731#
5732# Redistribution and use in source and binary forms, with or without
5733# modification, are permitted provided that the following conditions are
5734# met:
5735#
5736#     * Redistributions of source code must retain the above copyright
5737# notice, this list of conditions and the following disclaimer.
5738#     * Redistributions in binary form must reproduce the above
5739# copyright notice, this list of conditions and the following disclaimer
5740# in the documentation and/or other materials provided with the
5741# distribution.
5742#     * Neither the name of Google Inc. nor the names of its
5743# contributors may be used to endorse or promote products derived from
5744# this software without specific prior written permission.
5745#
5746# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
5747# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5748# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
5749# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
5750# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
5751# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
5752# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
5753# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
5754# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
5755# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
5756# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
5757
5758# ---
5759# Program for printing the profile generated by common/profiler.cc,
5760# or by the heap profiler (common/debugallocation.cc)
5761#
5762# The profile contains a sequence of entries of the form:
5763#       <count> <stack trace>
5764# This program parses the profile, and generates user-readable
5765# output.
5766#
5767# Examples:
5768#
5769# % tools/jeprof "program" "profile"
5770#   Enters "interactive" mode
5771#
5772# % tools/jeprof --text "program" "profile"
5773#   Generates one line per procedure
5774#
5775# % tools/jeprof --gv "program" "profile"
5776#   Generates annotated call-graph and displays via "gv"
5777#
5778# % tools/jeprof --gv --focus=Mutex "program" "profile"
5779#   Restrict to code paths that involve an entry that matches "Mutex"
5780#
5781# % tools/jeprof --gv --focus=Mutex --ignore=string "program" "profile"
5782#   Restrict to code paths that involve an entry that matches "Mutex"
5783#   and does not match "string"
5784#
5785# % tools/jeprof --list=IBF_CheckDocid "program" "profile"
5786#   Generates disassembly listing of all routines with at least one
5787#   sample that match the --list=<regexp> pattern.  The listing is
5788#   annotated with the flat and cumulative sample counts at each line.
5789#
5790# % tools/jeprof --disasm=IBF_CheckDocid "program" "profile"
5791#   Generates disassembly listing of all routines with at least one
5792#   sample that match the --disasm=<regexp> pattern.  The listing is
5793#   annotated with the flat and cumulative sample counts at each PC value.
5794#
5795# TODO: Use color to indicate files?
5796
5797use strict;
5798use warnings;
5799use Getopt::Long;
5800use Cwd;
5801
5802my $JEPROF_VERSION = "@jemalloc_version@";
5803my $PPROF_VERSION = "2.0";
5804
5805# These are the object tools we use which can come from a
5806# user-specified location using --tools, from the JEPROF_TOOLS
5807# environment variable, or from the environment.
5808my %obj_tool_map = (
5809  "objdump" => "objdump",
5810  "nm" => "nm",
5811  "addr2line" => "addr2line",
5812  "c++filt" => "c++filt",
5813  ## ConfigureObjTools may add architecture-specific entries:
5814  #"nm_pdb" => "nm-pdb",       # for reading windows (PDB-format) executables
5815  #"addr2line_pdb" => "addr2line-pdb",                                # ditto
5816  #"otool" => "otool",         # equivalent of objdump on OS X
5817);
5818# NOTE: these are lists, so you can put in commandline flags if you want.
5819my @DOT = ("dot");          # leave non-absolute, since it may be in /usr/local
5820my @GV = ("gv");
5821my @EVINCE = ("evince");    # could also be xpdf or perhaps acroread
5822my @KCACHEGRIND = ("kcachegrind");
5823my @PS2PDF = ("ps2pdf");
5824# These are used for dynamic profiles
5825my @URL_FETCHER = ("curl", "-s", "--fail");
5826
5827# These are the web pages that servers need to support for dynamic profiles
5828my $HEAP_PAGE = "/pprof/heap";
5829my $PROFILE_PAGE = "/pprof/profile";   # must support cgi-param "?seconds=#"
5830my $PMUPROFILE_PAGE = "/pprof/pmuprofile(?:\\?.*)?"; # must support cgi-param
5831                                                # ?seconds=#&event=x&period=n
5832my $GROWTH_PAGE = "/pprof/growth";
5833my $CONTENTION_PAGE = "/pprof/contention";
5834my $WALL_PAGE = "/pprof/wall(?:\\?.*)?";  # accepts options like namefilter
5835my $FILTEREDPROFILE_PAGE = "/pprof/filteredprofile(?:\\?.*)?";
5836my $CENSUSPROFILE_PAGE = "/pprof/censusprofile(?:\\?.*)?"; # must support cgi-param
5837                                                       # "?seconds=#",
5838                                                       # "?tags_regexp=#" and
5839                                                       # "?type=#".
5840my $SYMBOL_PAGE = "/pprof/symbol";     # must support symbol lookup via POST
5841my $PROGRAM_NAME_PAGE = "/pprof/cmdline";
5842
5843# These are the web pages that can be named on the command line.
5844# All the alternatives must begin with /.
5845my $PROFILES = "($HEAP_PAGE|$PROFILE_PAGE|$PMUPROFILE_PAGE|" .
5846               "$GROWTH_PAGE|$CONTENTION_PAGE|$WALL_PAGE|" .
5847               "$FILTEREDPROFILE_PAGE|$CENSUSPROFILE_PAGE)";
5848
5849# default binary name
5850my $UNKNOWN_BINARY = "(unknown)";
5851
5852# There is a pervasive dependency on the length (in hex characters,
5853# i.e., nibbles) of an address, distinguishing between 32-bit and
5854# 64-bit profiles.  To err on the safe size, default to 64-bit here:
5855my $address_length = 16;
5856
5857my $dev_null = "/dev/null";
5858if (! -e $dev_null && $^O =~ /MSWin/) {    # $^O is the OS perl was built for
5859  $dev_null = "nul";
5860}
5861
5862# A list of paths to search for shared object files
5863my @prefix_list = ();
5864
5865# Special routine name that should not have any symbols.
5866# Used as separator to parse "addr2line -i" output.
5867my $sep_symbol = '_fini';
5868my $sep_address = undef;
5869
5870##### Argument parsing #####
5871
5872sub usage_string {
5873  return <<EOF;
5874Usage:
5875jeprof [options] <program> <profiles>
5876   <profiles> is a space separated list of profile names.
5877jeprof [options] <symbolized-profiles>
5878   <symbolized-profiles> is a list of profile files where each file contains
5879   the necessary symbol mappings  as well as profile data (likely generated
5880   with --raw).
5881jeprof [options] <profile>
5882   <profile> is a remote form.  Symbols are obtained from host:port$SYMBOL_PAGE
5883
5884   Each name can be:
5885   /path/to/profile        - a path to a profile file
5886   host:port[/<service>]   - a location of a service to get profile from
5887
5888   The /<service> can be $HEAP_PAGE, $PROFILE_PAGE, /pprof/pmuprofile,
5889                         $GROWTH_PAGE, $CONTENTION_PAGE, /pprof/wall,
5890                         $CENSUSPROFILE_PAGE, or /pprof/filteredprofile.
5891   For instance:
5892     jeprof http://myserver.com:80$HEAP_PAGE
5893   If /<service> is omitted, the service defaults to $PROFILE_PAGE (cpu profiling).
5894jeprof --symbols <program>
5895   Maps addresses to symbol names.  In this mode, stdin should be a
5896   list of library mappings, in the same format as is found in the heap-
5897   and cpu-profile files (this loosely matches that of /proc/self/maps
5898   on linux), followed by a list of hex addresses to map, one per line.
5899
5900   For more help with querying remote servers, including how to add the
5901   necessary server-side support code, see this filename (or one like it):
5902
5903   /usr/doc/gperftools-$PPROF_VERSION/pprof_remote_servers.html
5904
5905Options:
5906   --cum               Sort by cumulative data
5907   --base=<base>       Subtract <base> from <profile> before display
5908   --interactive       Run in interactive mode (interactive "help" gives help) [default]
5909   --seconds=<n>       Length of time for dynamic profiles [default=30 secs]
5910   --add_lib=<file>    Read additional symbols and line info from the given library
5911   --lib_prefix=<dir>  Comma separated list of library path prefixes
5912
5913Reporting Granularity:
5914   --addresses         Report at address level
5915   --lines             Report at source line level
5916   --functions         Report at function level [default]
5917   --files             Report at source file level
5918
5919Output type:
5920   --text              Generate text report
5921   --callgrind         Generate callgrind format to stdout
5922   --gv                Generate Postscript and display
5923   --evince            Generate PDF and display
5924   --web               Generate SVG and display
5925   --list=<regexp>     Generate source listing of matching routines
5926   --disasm=<regexp>   Generate disassembly of matching routines
5927   --symbols           Print demangled symbol names found at given addresses
5928   --dot               Generate DOT file to stdout
5929   --ps                Generate Postcript to stdout
5930   --pdf               Generate PDF to stdout
5931   --svg               Generate SVG to stdout
5932   --gif               Generate GIF to stdout
5933   --raw               Generate symbolized jeprof data (useful with remote fetch)
5934
5935Heap-Profile Options:
5936   --inuse_space       Display in-use (mega)bytes [default]
5937   --inuse_objects     Display in-use objects
5938   --alloc_space       Display allocated (mega)bytes
5939   --alloc_objects     Display allocated objects
5940   --show_bytes        Display space in bytes
5941   --drop_negative     Ignore negative differences
5942
5943Contention-profile options:
5944   --total_delay       Display total delay at each region [default]
5945   --contentions       Display number of delays at each region
5946   --mean_delay        Display mean delay at each region
5947
5948Call-graph Options:
5949   --nodecount=<n>     Show at most so many nodes [default=80]
5950   --nodefraction=<f>  Hide nodes below <f>*total [default=.005]
5951   --edgefraction=<f>  Hide edges below <f>*total [default=.001]
5952   --maxdegree=<n>     Max incoming/outgoing edges per node [default=8]
5953   --focus=<regexp>    Focus on backtraces with nodes matching <regexp>
5954   --thread=<n>        Show profile for thread <n>
5955   --ignore=<regexp>   Ignore backtraces with nodes matching <regexp>
5956   --scale=<n>         Set GV scaling [default=0]
5957   --heapcheck         Make nodes with non-0 object counts
5958                       (i.e. direct leak generators) more visible
5959   --retain=<regexp>   Retain only nodes that match <regexp>
5960   --exclude=<regexp>  Exclude all nodes that match <regexp>
5961
5962Miscellaneous:
5963   --tools=<prefix or binary:fullpath>[,...]   \$PATH for object tool pathnames
5964   --test              Run unit tests
5965   --help              This message
5966   --version           Version information
5967
5968Environment Variables:
5969   JEPROF_TMPDIR        Profiles directory. Defaults to \$HOME/jeprof
5970   JEPROF_TOOLS         Prefix for object tools pathnames
5971
5972Examples:
5973
5974jeprof /bin/ls ls.prof
5975                       Enters "interactive" mode
5976jeprof --text /bin/ls ls.prof
5977                       Outputs one line per procedure
5978jeprof --web /bin/ls ls.prof
5979                       Displays annotated call-graph in web browser
5980jeprof --gv /bin/ls ls.prof
5981                       Displays annotated call-graph via 'gv'
5982jeprof --gv --focus=Mutex /bin/ls ls.prof
5983                       Restricts to code paths including a .*Mutex.* entry
5984jeprof --gv --focus=Mutex --ignore=string /bin/ls ls.prof
5985                       Code paths including Mutex but not string
5986jeprof --list=getdir /bin/ls ls.prof
5987                       (Per-line) annotated source listing for getdir()
5988jeprof --disasm=getdir /bin/ls ls.prof
5989                       (Per-PC) annotated disassembly for getdir()
5990
5991jeprof http://localhost:1234/
5992                       Enters "interactive" mode
5993jeprof --text localhost:1234
5994                       Outputs one line per procedure for localhost:1234
5995jeprof --raw localhost:1234 > ./local.raw
5996jeprof --text ./local.raw
5997                       Fetches a remote profile for later analysis and then
5998                       analyzes it in text mode.
5999EOF
6000}
6001
6002sub version_string {
6003  return <<EOF
6004jeprof (part of jemalloc $JEPROF_VERSION)
6005based on pprof (part of gperftools $PPROF_VERSION)
6006
6007Copyright 1998-2007 Google Inc.
6008
6009This is BSD licensed software; see the source for copying conditions
6010and license information.
6011There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
6012PARTICULAR PURPOSE.
6013EOF
6014}
6015
6016sub usage {
6017  my $msg = shift;
6018  print STDERR "$msg\n\n";
6019  print STDERR usage_string();
6020  print STDERR "\nFATAL ERROR: $msg\n";    # just as a reminder
6021  exit(1);
6022}
6023
6024sub Init() {
6025  # Setup tmp-file name and handler to clean it up.
6026  # We do this in the very beginning so that we can use
6027  # error() and cleanup() function anytime here after.
6028  $main::tmpfile_sym = "/tmp/jeprof$$.sym";
6029  $main::tmpfile_ps = "/tmp/jeprof$$";
6030  $main::next_tmpfile = 0;
6031  $SIG{'INT'} = \&sighandler;
6032
6033  # Cache from filename/linenumber to source code
6034  $main::source_cache = ();
6035
6036  $main::opt_help = 0;
6037  $main::opt_version = 0;
6038
6039  $main::opt_cum = 0;
6040  $main::opt_base = '';
6041  $main::opt_addresses = 0;
6042  $main::opt_lines = 0;
6043  $main::opt_functions = 0;
6044  $main::opt_files = 0;
6045  $main::opt_lib_prefix = "";
6046
6047  $main::opt_text = 0;
6048  $main::opt_callgrind = 0;
6049  $main::opt_list = "";
6050  $main::opt_disasm = "";
6051  $main::opt_symbols = 0;
6052  $main::opt_gv = 0;
6053  $main::opt_evince = 0;
6054  $main::opt_web = 0;
6055  $main::opt_dot = 0;
6056  $main::opt_ps = 0;
6057  $main::opt_pdf = 0;
6058  $main::opt_gif = 0;
6059  $main::opt_svg = 0;
6060  $main::opt_raw = 0;
6061
6062  $main::opt_nodecount = 80;
6063  $main::opt_nodefraction = 0.005;
6064  $main::opt_edgefraction = 0.001;
6065  $main::opt_maxdegree = 8;
6066  $main::opt_focus = '';
6067  $main::opt_thread = undef;
6068  $main::opt_ignore = '';
6069  $main::opt_scale = 0;
6070  $main::opt_heapcheck = 0;
6071  $main::opt_retain = '';
6072  $main::opt_exclude = '';
6073  $main::opt_seconds = 30;
6074  $main::opt_lib = "";
6075
6076  $main::opt_inuse_space   = 0;
6077  $main::opt_inuse_objects = 0;
6078  $main::opt_alloc_space   = 0;
6079  $main::opt_alloc_objects = 0;
6080  $main::opt_show_bytes    = 0;
6081  $main::opt_drop_negative = 0;
6082  $main::opt_interactive   = 0;
6083
6084  $main::opt_total_delay = 0;
6085  $main::opt_contentions = 0;
6086  $main::opt_mean_delay = 0;
6087
6088  $main::opt_tools   = "";
6089  $main::opt_debug   = 0;
6090  $main::opt_test    = 0;
6091
6092  # These are undocumented flags used only by unittests.
6093  $main::opt_test_stride = 0;
6094
6095  # Are we using $SYMBOL_PAGE?
6096  $main::use_symbol_page = 0;
6097
6098  # Files returned by TempName.
6099  %main::tempnames = ();
6100
6101  # Type of profile we are dealing with
6102  # Supported types:
6103  #     cpu
6104  #     heap
6105  #     growth
6106  #     contention
6107  $main::profile_type = '';     # Empty type means "unknown"
6108
6109  GetOptions("help!"          => \$main::opt_help,
6110             "version!"       => \$main::opt_version,
6111             "cum!"           => \$main::opt_cum,
6112             "base=s"         => \$main::opt_base,
6113             "seconds=i"      => \$main::opt_seconds,
6114             "add_lib=s"      => \$main::opt_lib,
6115             "lib_prefix=s"   => \$main::opt_lib_prefix,
6116             "functions!"     => \$main::opt_functions,
6117             "lines!"         => \$main::opt_lines,
6118             "addresses!"     => \$main::opt_addresses,
6119             "files!"         => \$main::opt_files,
6120             "text!"          => \$main::opt_text,
6121             "callgrind!"     => \$main::opt_callgrind,
6122             "list=s"         => \$main::opt_list,
6123             "disasm=s"       => \$main::opt_disasm,
6124             "symbols!"       => \$main::opt_symbols,
6125             "gv!"            => \$main::opt_gv,
6126             "evince!"        => \$main::opt_evince,
6127             "web!"           => \$main::opt_web,
6128             "dot!"           => \$main::opt_dot,
6129             "ps!"            => \$main::opt_ps,
6130             "pdf!"           => \$main::opt_pdf,
6131             "svg!"           => \$main::opt_svg,
6132             "gif!"           => \$main::opt_gif,
6133             "raw!"           => \$main::opt_raw,
6134             "interactive!"   => \$main::opt_interactive,
6135             "nodecount=i"    => \$main::opt_nodecount,
6136             "nodefraction=f" => \$main::opt_nodefraction,
6137             "edgefraction=f" => \$main::opt_edgefraction,
6138             "maxdegree=i"    => \$main::opt_maxdegree,
6139             "focus=s"        => \$main::opt_focus,
6140             "thread=s"       => \$main::opt_thread,
6141             "ignore=s"       => \$main::opt_ignore,
6142             "scale=i"        => \$main::opt_scale,
6143             "heapcheck"      => \$main::opt_heapcheck,
6144             "retain=s"       => \$main::opt_retain,
6145             "exclude=s"      => \$main::opt_exclude,
6146             "inuse_space!"   => \$main::opt_inuse_space,
6147             "inuse_objects!" => \$main::opt_inuse_objects,
6148             "alloc_space!"   => \$main::opt_alloc_space,
6149             "alloc_objects!" => \$main::opt_alloc_objects,
6150             "show_bytes!"    => \$main::opt_show_bytes,
6151             "drop_negative!" => \$main::opt_drop_negative,
6152             "total_delay!"   => \$main::opt_total_delay,
6153             "contentions!"   => \$main::opt_contentions,
6154             "mean_delay!"    => \$main::opt_mean_delay,
6155             "tools=s"        => \$main::opt_tools,
6156             "test!"          => \$main::opt_test,
6157             "debug!"         => \$main::opt_debug,
6158             # Undocumented flags used only by unittests:
6159             "test_stride=i"  => \$main::opt_test_stride,
6160      ) || usage("Invalid option(s)");
6161
6162  # Deal with the standard --help and --version
6163  if ($main::opt_help) {
6164    print usage_string();
6165    exit(0);
6166  }
6167
6168  if ($main::opt_version) {
6169    print version_string();
6170    exit(0);
6171  }
6172
6173  # Disassembly/listing/symbols mode requires address-level info
6174  if ($main::opt_disasm || $main::opt_list || $main::opt_symbols) {
6175    $main::opt_functions = 0;
6176    $main::opt_lines = 0;
6177    $main::opt_addresses = 1;
6178    $main::opt_files = 0;
6179  }
6180
6181  # Check heap-profiling flags
6182  if ($main::opt_inuse_space +
6183      $main::opt_inuse_objects +
6184      $main::opt_alloc_space +
6185      $main::opt_alloc_objects > 1) {
6186    usage("Specify at most on of --inuse/--alloc options");
6187  }
6188
6189  # Check output granularities
6190  my $grains =
6191      $main::opt_functions +
6192      $main::opt_lines +
6193      $main::opt_addresses +
6194      $main::opt_files +
6195      0;
6196  if ($grains > 1) {
6197    usage("Only specify one output granularity option");
6198  }
6199  if ($grains == 0) {
6200    $main::opt_functions = 1;
6201  }
6202
6203  # Check output modes
6204  my $modes =
6205      $main::opt_text +
6206      $main::opt_callgrind +
6207      ($main::opt_list eq '' ? 0 : 1) +
6208      ($main::opt_disasm eq '' ? 0 : 1) +
6209      ($main::opt_symbols == 0 ? 0 : 1) +
6210      $main::opt_gv +
6211      $main::opt_evince +
6212      $main::opt_web +
6213      $main::opt_dot +
6214      $main::opt_ps +
6215      $main::opt_pdf +
6216      $main::opt_svg +
6217      $main::opt_gif +
6218      $main::opt_raw +
6219      $main::opt_interactive +
6220      0;
6221  if ($modes > 1) {
6222    usage("Only specify one output mode");
6223  }
6224  if ($modes == 0) {
6225    if (-t STDOUT) {  # If STDOUT is a tty, activate interactive mode
6226      $main::opt_interactive = 1;
6227    } else {
6228      $main::opt_text = 1;
6229    }
6230  }
6231
6232  if ($main::opt_test) {
6233    RunUnitTests();
6234    # Should not return
6235    exit(1);
6236  }
6237
6238  # Binary name and profile arguments list
6239  $main::prog = "";
6240  @main::pfile_args = ();
6241
6242  # Remote profiling without a binary (using $SYMBOL_PAGE instead)
6243  if (@ARGV > 0) {
6244    if (IsProfileURL($ARGV[0])) {
6245      $main::use_symbol_page = 1;
6246    } elsif (IsSymbolizedProfileFile($ARGV[0])) {
6247      $main::use_symbolized_profile = 1;
6248      $main::prog = $UNKNOWN_BINARY;  # will be set later from the profile file
6249    }
6250  }
6251
6252  if ($main::use_symbol_page || $main::use_symbolized_profile) {
6253    # We don't need a binary!
6254    my %disabled = ('--lines' => $main::opt_lines,
6255                    '--disasm' => $main::opt_disasm);
6256    for my $option (keys %disabled) {
6257      usage("$option cannot be used without a binary") if $disabled{$option};
6258    }
6259    # Set $main::prog later...
6260    scalar(@ARGV) || usage("Did not specify profile file");
6261  } elsif ($main::opt_symbols) {
6262    # --symbols needs a binary-name (to run nm on, etc) but not profiles
6263    $main::prog = shift(@ARGV) || usage("Did not specify program");
6264  } else {
6265    $main::prog = shift(@ARGV) || usage("Did not specify program");
6266    scalar(@ARGV) || usage("Did not specify profile file");
6267  }
6268
6269  # Parse profile file/location arguments
6270  foreach my $farg (@ARGV) {
6271    if ($farg =~ m/(.*)\@([0-9]+)(|\/.*)$/ ) {
6272      my $machine = $1;
6273      my $num_machines = $2;
6274      my $path = $3;
6275      for (my $i = 0; $i < $num_machines; $i++) {
6276        unshift(@main::pfile_args, "$i.$machine$path");
6277      }
6278    } else {
6279      unshift(@main::pfile_args, $farg);
6280    }
6281  }
6282
6283  if ($main::use_symbol_page) {
6284    unless (IsProfileURL($main::pfile_args[0])) {
6285      error("The first profile should be a remote form to use $SYMBOL_PAGE\n");
6286    }
6287    CheckSymbolPage();
6288    $main::prog = FetchProgramName();
6289  } elsif (!$main::use_symbolized_profile) {  # may not need objtools!
6290    ConfigureObjTools($main::prog)
6291  }
6292
6293  # Break the opt_lib_prefix into the prefix_list array
6294  @prefix_list = split (',', $main::opt_lib_prefix);
6295
6296  # Remove trailing / from the prefixes, in the list to prevent
6297  # searching things like /my/path//lib/mylib.so
6298  foreach (@prefix_list) {
6299    s|/+$||;
6300  }
6301}
6302
6303sub FilterAndPrint {
6304  my ($profile, $symbols, $libs, $thread) = @_;
6305
6306  # Get total data in profile
6307  my $total = TotalProfile($profile);
6308
6309  # Remove uniniteresting stack items
6310  $profile = RemoveUninterestingFrames($symbols, $profile);
6311
6312  # Focus?
6313  if ($main::opt_focus ne '') {
6314    $profile = FocusProfile($symbols, $profile, $main::opt_focus);
6315  }
6316
6317  # Ignore?
6318  if ($main::opt_ignore ne '') {
6319    $profile = IgnoreProfile($symbols, $profile, $main::opt_ignore);
6320  }
6321
6322  my $calls = ExtractCalls($symbols, $profile);
6323
6324  # Reduce profiles to required output granularity, and also clean
6325  # each stack trace so a given entry exists at most once.
6326  my $reduced = ReduceProfile($symbols, $profile);
6327
6328  # Get derived profiles
6329  my $flat = FlatProfile($reduced);
6330  my $cumulative = CumulativeProfile($reduced);
6331
6332  # Print
6333  if (!$main::opt_interactive) {
6334    if ($main::opt_disasm) {
6335      PrintDisassembly($libs, $flat, $cumulative, $main::opt_disasm);
6336    } elsif ($main::opt_list) {
6337      PrintListing($total, $libs, $flat, $cumulative, $main::opt_list, 0);
6338    } elsif ($main::opt_text) {
6339      # Make sure the output is empty when have nothing to report
6340      # (only matters when --heapcheck is given but we must be
6341      # compatible with old branches that did not pass --heapcheck always):
6342      if ($total != 0) {
6343        printf("Total%s: %s %s\n",
6344               (defined($thread) ? " (t$thread)" : ""),
6345               Unparse($total), Units());
6346      }
6347      PrintText($symbols, $flat, $cumulative, -1);
6348    } elsif ($main::opt_raw) {
6349      PrintSymbolizedProfile($symbols, $profile, $main::prog);
6350    } elsif ($main::opt_callgrind) {
6351      PrintCallgrind($calls);
6352    } else {
6353      if (PrintDot($main::prog, $symbols, $profile, $flat, $cumulative, $total)) {
6354        if ($main::opt_gv) {
6355          RunGV(TempName($main::next_tmpfile, "ps"), "");
6356        } elsif ($main::opt_evince) {
6357          RunEvince(TempName($main::next_tmpfile, "pdf"), "");
6358        } elsif ($main::opt_web) {
6359          my $tmp = TempName($main::next_tmpfile, "svg");
6360          RunWeb($tmp);
6361          # The command we run might hand the file name off
6362          # to an already running browser instance and then exit.
6363          # Normally, we'd remove $tmp on exit (right now),
6364          # but fork a child to remove $tmp a little later, so that the
6365          # browser has time to load it first.
6366          delete $main::tempnames{$tmp};
6367          if (fork() == 0) {
6368            sleep 5;
6369            unlink($tmp);
6370            exit(0);
6371          }
6372        }
6373      } else {
6374        cleanup();
6375        exit(1);
6376      }
6377    }
6378  } else {
6379    InteractiveMode($profile, $symbols, $libs, $total);
6380  }
6381}
6382
6383sub Main() {
6384  Init();
6385  $main::collected_profile = undef;
6386  @main::profile_files = ();
6387  $main::op_time = time();
6388
6389  # Printing symbols is special and requires a lot less info that most.
6390  if ($main::opt_symbols) {
6391    PrintSymbols(*STDIN);   # Get /proc/maps and symbols output from stdin
6392    return;
6393  }
6394
6395  # Fetch all profile data
6396  FetchDynamicProfiles();
6397
6398  # this will hold symbols that we read from the profile files
6399  my $symbol_map = {};
6400
6401  # Read one profile, pick the last item on the list
6402  my $data = ReadProfile($main::prog, pop(@main::profile_files));
6403  my $profile = $data->{profile};
6404  my $pcs = $data->{pcs};
6405  my $libs = $data->{libs};   # Info about main program and shared libraries
6406  $symbol_map = MergeSymbols($symbol_map, $data->{symbols});
6407
6408  # Add additional profiles, if available.
6409  if (scalar(@main::profile_files) > 0) {
6410    foreach my $pname (@main::profile_files) {
6411      my $data2 = ReadProfile($main::prog, $pname);
6412      $profile = AddProfile($profile, $data2->{profile});
6413      $pcs = AddPcs($pcs, $data2->{pcs});
6414      $symbol_map = MergeSymbols($symbol_map, $data2->{symbols});
6415    }
6416  }
6417
6418  # Subtract base from profile, if specified
6419  if ($main::opt_base ne '') {
6420    my $base = ReadProfile($main::prog, $main::opt_base);
6421    $profile = SubtractProfile($profile, $base->{profile});
6422    $pcs = AddPcs($pcs, $base->{pcs});
6423    $symbol_map = MergeSymbols($symbol_map, $base->{symbols});
6424  }
6425
6426  # Collect symbols
6427  my $symbols;
6428  if ($main::use_symbolized_profile) {
6429    $symbols = FetchSymbols($pcs, $symbol_map);
6430  } elsif ($main::use_symbol_page) {
6431    $symbols = FetchSymbols($pcs);
6432  } else {
6433    # TODO(csilvers): $libs uses the /proc/self/maps data from profile1,
6434    # which may differ from the data from subsequent profiles, especially
6435    # if they were run on different machines.  Use appropriate libs for
6436    # each pc somehow.
6437    $symbols = ExtractSymbols($libs, $pcs);
6438  }
6439
6440  if (!defined($main::opt_thread)) {
6441    FilterAndPrint($profile, $symbols, $libs);
6442  }
6443  if (defined($data->{threads})) {
6444    foreach my $thread (sort { $a <=> $b } keys(%{$data->{threads}})) {
6445      if (defined($main::opt_thread) &&
6446          ($main::opt_thread eq '*' || $main::opt_thread == $thread)) {
6447        my $thread_profile = $data->{threads}{$thread};
6448        FilterAndPrint($thread_profile, $symbols, $libs, $thread);
6449      }
6450    }
6451  }
6452
6453  cleanup();
6454  exit(0);
6455}
6456
6457##### Entry Point #####
6458
6459Main();
6460
6461# Temporary code to detect if we're running on a Goobuntu system.
6462# These systems don't have the right stuff installed for the special
6463# Readline libraries to work, so as a temporary workaround, we default
6464# to using the normal stdio code, rather than the fancier readline-based
6465# code
6466sub ReadlineMightFail {
6467  if (-e '/lib/libtermcap.so.2') {
6468    return 0;  # libtermcap exists, so readline should be okay
6469  } else {
6470    return 1;
6471  }
6472}
6473
6474sub RunGV {
6475  my $fname = shift;
6476  my $bg = shift;       # "" or " &" if we should run in background
6477  if (!system(ShellEscape(@GV, "--version") . " >$dev_null 2>&1")) {
6478    # Options using double dash are supported by this gv version.
6479    # Also, turn on noantialias to better handle bug in gv for
6480    # postscript files with large dimensions.
6481    # TODO: Maybe we should not pass the --noantialias flag
6482    # if the gv version is known to work properly without the flag.
6483    system(ShellEscape(@GV, "--scale=$main::opt_scale", "--noantialias", $fname)
6484           . $bg);
6485  } else {
6486    # Old gv version - only supports options that use single dash.
6487    print STDERR ShellEscape(@GV, "-scale", $main::opt_scale) . "\n";
6488    system(ShellEscape(@GV, "-scale", "$main::opt_scale", $fname) . $bg);
6489  }
6490}
6491
6492sub RunEvince {
6493  my $fname = shift;
6494  my $bg = shift;       # "" or " &" if we should run in background
6495  system(ShellEscape(@EVINCE, $fname) . $bg);
6496}
6497
6498sub RunWeb {
6499  my $fname = shift;
6500  print STDERR "Loading web page file:///$fname\n";
6501
6502  if (`uname` =~ /Darwin/) {
6503    # OS X: open will use standard preference for SVG files.
6504    system("/usr/bin/open", $fname);
6505    return;
6506  }
6507
6508  # Some kind of Unix; try generic symlinks, then specific browsers.
6509  # (Stop once we find one.)
6510  # Works best if the browser is already running.
6511  my @alt = (
6512    "/etc/alternatives/gnome-www-browser",
6513    "/etc/alternatives/x-www-browser",
6514    "google-chrome",
6515    "firefox",
6516  );
6517  foreach my $b (@alt) {
6518    if (system($b, $fname) == 0) {
6519      return;
6520    }
6521  }
6522
6523  print STDERR "Could not load web browser.\n";
6524}
6525
6526sub RunKcachegrind {
6527  my $fname = shift;
6528  my $bg = shift;       # "" or " &" if we should run in background
6529  print STDERR "Starting '@KCACHEGRIND " . $fname . $bg . "'\n";
6530  system(ShellEscape(@KCACHEGRIND, $fname) . $bg);
6531}
6532
6533
6534##### Interactive helper routines #####
6535
6536sub InteractiveMode {
6537  $| = 1;  # Make output unbuffered for interactive mode
6538  my ($orig_profile, $symbols, $libs, $total) = @_;
6539
6540  print STDERR "Welcome to jeprof!  For help, type 'help'.\n";
6541
6542  # Use ReadLine if it's installed and input comes from a console.
6543  if ( -t STDIN &&
6544       !ReadlineMightFail() &&
6545       defined(eval {require Term::ReadLine}) ) {
6546    my $term = new Term::ReadLine 'jeprof';
6547    while ( defined ($_ = $term->readline('(jeprof) '))) {
6548      $term->addhistory($_) if /\S/;
6549      if (!InteractiveCommand($orig_profile, $symbols, $libs, $total, $_)) {
6550        last;    # exit when we get an interactive command to quit
6551      }
6552    }
6553  } else {       # don't have readline
6554    while (1) {
6555      print STDERR "(jeprof) ";
6556      $_ = <STDIN>;
6557      last if ! defined $_ ;
6558      s/\r//g;         # turn windows-looking lines into unix-looking lines
6559
6560      # Save some flags that might be reset by InteractiveCommand()
6561      my $save_opt_lines = $main::opt_lines;
6562
6563      if (!InteractiveCommand($orig_profile, $symbols, $libs, $total, $_)) {
6564        last;    # exit when we get an interactive command to quit
6565      }
6566
6567      # Restore flags
6568      $main::opt_lines = $save_opt_lines;
6569    }
6570  }
6571}
6572
6573# Takes two args: orig profile, and command to run.
6574# Returns 1 if we should keep going, or 0 if we were asked to quit
6575sub InteractiveCommand {
6576  my($orig_profile, $symbols, $libs, $total, $command) = @_;
6577  $_ = $command;                # just to make future m//'s easier
6578  if (!defined($_)) {
6579    print STDERR "\n";
6580    return 0;
6581  }
6582  if (m/^\s*quit/) {
6583    return 0;
6584  }
6585  if (m/^\s*help/) {
6586    InteractiveHelpMessage();
6587    return 1;
6588  }
6589  # Clear all the mode options -- mode is controlled by "$command"
6590  $main::opt_text = 0;
6591  $main::opt_callgrind = 0;
6592  $main::opt_disasm = 0;
6593  $main::opt_list = 0;
6594  $main::opt_gv = 0;
6595  $main::opt_evince = 0;
6596  $main::opt_cum = 0;
6597
6598  if (m/^\s*(text|top)(\d*)\s*(.*)/) {
6599    $main::opt_text = 1;
6600
6601    my $line_limit = ($2 ne "") ? int($2) : 10;
6602
6603    my $routine;
6604    my $ignore;
6605    ($routine, $ignore) = ParseInteractiveArgs($3);
6606
6607    my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
6608    my $reduced = ReduceProfile($symbols, $profile);
6609
6610    # Get derived profiles
6611    my $flat = FlatProfile($reduced);
6612    my $cumulative = CumulativeProfile($reduced);
6613
6614    PrintText($symbols, $flat, $cumulative, $line_limit);
6615    return 1;
6616  }
6617  if (m/^\s*callgrind\s*([^ \n]*)/) {
6618    $main::opt_callgrind = 1;
6619
6620    # Get derived profiles
6621    my $calls = ExtractCalls($symbols, $orig_profile);
6622    my $filename = $1;
6623    if ( $1 eq '' ) {
6624      $filename = TempName($main::next_tmpfile, "callgrind");
6625    }
6626    PrintCallgrind($calls, $filename);
6627    if ( $1 eq '' ) {
6628      RunKcachegrind($filename, " & ");
6629      $main::next_tmpfile++;
6630    }
6631
6632    return 1;
6633  }
6634  if (m/^\s*(web)?list\s*(.+)/) {
6635    my $html = (defined($1) && ($1 eq "web"));
6636    $main::opt_list = 1;
6637
6638    my $routine;
6639    my $ignore;
6640    ($routine, $ignore) = ParseInteractiveArgs($2);
6641
6642    my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
6643    my $reduced = ReduceProfile($symbols, $profile);
6644
6645    # Get derived profiles
6646    my $flat = FlatProfile($reduced);
6647    my $cumulative = CumulativeProfile($reduced);
6648
6649    PrintListing($total, $libs, $flat, $cumulative, $routine, $html);
6650    return 1;
6651  }
6652  if (m/^\s*disasm\s*(.+)/) {
6653    $main::opt_disasm = 1;
6654
6655    my $routine;
6656    my $ignore;
6657    ($routine, $ignore) = ParseInteractiveArgs($1);
6658
6659    # Process current profile to account for various settings
6660    my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
6661    my $reduced = ReduceProfile($symbols, $profile);
6662
6663    # Get derived profiles
6664    my $flat = FlatProfile($reduced);
6665    my $cumulative = CumulativeProfile($reduced);
6666
6667    PrintDisassembly($libs, $flat, $cumulative, $routine);
6668    return 1;
6669  }
6670  if (m/^\s*(gv|web|evince)\s*(.*)/) {
6671    $main::opt_gv = 0;
6672    $main::opt_evince = 0;
6673    $main::opt_web = 0;
6674    if ($1 eq "gv") {
6675      $main::opt_gv = 1;
6676    } elsif ($1 eq "evince") {
6677      $main::opt_evince = 1;
6678    } elsif ($1 eq "web") {
6679      $main::opt_web = 1;
6680    }
6681
6682    my $focus;
6683    my $ignore;
6684    ($focus, $ignore) = ParseInteractiveArgs($2);
6685
6686    # Process current profile to account for various settings
6687    my $profile = ProcessProfile($total, $orig_profile, $symbols,
6688                                 $focus, $ignore);
6689    my $reduced = ReduceProfile($symbols, $profile);
6690
6691    # Get derived profiles
6692    my $flat = FlatProfile($reduced);
6693    my $cumulative = CumulativeProfile($reduced);
6694
6695    if (PrintDot($main::prog, $symbols, $profile, $flat, $cumulative, $total)) {
6696      if ($main::opt_gv) {
6697        RunGV(TempName($main::next_tmpfile, "ps"), " &");
6698      } elsif ($main::opt_evince) {
6699        RunEvince(TempName($main::next_tmpfile, "pdf"), " &");
6700      } elsif ($main::opt_web) {
6701        RunWeb(TempName($main::next_tmpfile, "svg"));
6702      }
6703      $main::next_tmpfile++;
6704    }
6705    return 1;
6706  }
6707  if (m/^\s*$/) {
6708    return 1;
6709  }
6710  print STDERR "Unknown command: try 'help'.\n";
6711  return 1;
6712}
6713
6714
6715sub ProcessProfile {
6716  my $total_count = shift;
6717  my $orig_profile = shift;
6718  my $symbols = shift;
6719  my $focus = shift;
6720  my $ignore = shift;
6721
6722  # Process current profile to account for various settings
6723  my $profile = $orig_profile;
6724  printf("Total: %s %s\n", Unparse($total_count), Units());
6725  if ($focus ne '') {
6726    $profile = FocusProfile($symbols, $profile, $focus);
6727    my $focus_count = TotalProfile($profile);
6728    printf("After focusing on '%s': %s %s of %s (%0.1f%%)\n",
6729           $focus,
6730           Unparse($focus_count), Units(),
6731           Unparse($total_count), ($focus_count*100.0) / $total_count);
6732  }
6733  if ($ignore ne '') {
6734    $profile = IgnoreProfile($symbols, $profile, $ignore);
6735    my $ignore_count = TotalProfile($profile);
6736    printf("After ignoring '%s': %s %s of %s (%0.1f%%)\n",
6737           $ignore,
6738           Unparse($ignore_count), Units(),
6739           Unparse($total_count),
6740           ($ignore_count*100.0) / $total_count);
6741  }
6742
6743  return $profile;
6744}
6745
6746sub InteractiveHelpMessage {
6747  print STDERR <<ENDOFHELP;
6748Interactive jeprof mode
6749
6750Commands:
6751  gv
6752  gv [focus] [-ignore1] [-ignore2]
6753      Show graphical hierarchical display of current profile.  Without
6754      any arguments, shows all samples in the profile.  With the optional
6755      "focus" argument, restricts the samples shown to just those where
6756      the "focus" regular expression matches a routine name on the stack
6757      trace.
6758
6759  web
6760  web [focus] [-ignore1] [-ignore2]
6761      Like GV, but displays profile in your web browser instead of using
6762      Ghostview. Works best if your web browser is already running.
6763      To change the browser that gets used:
6764      On Linux, set the /etc/alternatives/gnome-www-browser symlink.
6765      On OS X, change the Finder association for SVG files.
6766
6767  list [routine_regexp] [-ignore1] [-ignore2]
6768      Show source listing of routines whose names match "routine_regexp"
6769
6770  weblist [routine_regexp] [-ignore1] [-ignore2]
6771     Displays a source listing of routines whose names match "routine_regexp"
6772     in a web browser.  You can click on source lines to view the
6773     corresponding disassembly.
6774
6775  top [--cum] [-ignore1] [-ignore2]
6776  top20 [--cum] [-ignore1] [-ignore2]
6777  top37 [--cum] [-ignore1] [-ignore2]
6778      Show top lines ordered by flat profile count, or cumulative count
6779      if --cum is specified.  If a number is present after 'top', the
6780      top K routines will be shown (defaults to showing the top 10)
6781
6782  disasm [routine_regexp] [-ignore1] [-ignore2]
6783      Show disassembly of routines whose names match "routine_regexp",
6784      annotated with sample counts.
6785
6786  callgrind
6787  callgrind [filename]
6788      Generates callgrind file. If no filename is given, kcachegrind is called.
6789
6790  help - This listing
6791  quit or ^D - End jeprof
6792
6793For commands that accept optional -ignore tags, samples where any routine in
6794the stack trace matches the regular expression in any of the -ignore
6795parameters will be ignored.
6796
6797Further pprof details are available at this location (or one similar):
6798
6799 /usr/doc/gperftools-$PPROF_VERSION/cpu_profiler.html
6800 /usr/doc/gperftools-$PPROF_VERSION/heap_profiler.html
6801
6802ENDOFHELP
6803}
6804sub ParseInteractiveArgs {
6805  my $args = shift;
6806  my $focus = "";
6807  my $ignore = "";
6808  my @x = split(/ +/, $args);
6809  foreach $a (@x) {
6810    if ($a =~ m/^(--|-)lines$/) {
6811      $main::opt_lines = 1;
6812    } elsif ($a =~ m/^(--|-)cum$/) {
6813      $main::opt_cum = 1;
6814    } elsif ($a =~ m/^-(.*)/) {
6815      $ignore .= (($ignore ne "") ? "|" : "" ) . $1;
6816    } else {
6817      $focus .= (($focus ne "") ? "|" : "" ) . $a;
6818    }
6819  }
6820  if ($ignore ne "") {
6821    print STDERR "Ignoring samples in call stacks that match '$ignore'\n";
6822  }
6823  return ($focus, $ignore);
6824}
6825
6826##### Output code #####
6827
6828sub TempName {
6829  my $fnum = shift;
6830  my $ext = shift;
6831  my $file = "$main::tmpfile_ps.$fnum.$ext";
6832  $main::tempnames{$file} = 1;
6833  return $file;
6834}
6835
6836# Print profile data in packed binary format (64-bit) to standard out
6837sub PrintProfileData {
6838  my $profile = shift;
6839
6840  # print header (64-bit style)
6841  # (zero) (header-size) (version) (sample-period) (zero)
6842  print pack('L*', 0, 0, 3, 0, 0, 0, 1, 0, 0, 0);
6843
6844  foreach my $k (keys(%{$profile})) {
6845    my $count = $profile->{$k};
6846    my @addrs = split(/\n/, $k);
6847    if ($#addrs >= 0) {
6848      my $depth = $#addrs + 1;
6849      # int(foo / 2**32) is the only reliable way to get rid of bottom
6850      # 32 bits on both 32- and 64-bit systems.
6851      print pack('L*', $count & 0xFFFFFFFF, int($count / 2**32));
6852      print pack('L*', $depth & 0xFFFFFFFF, int($depth / 2**32));
6853
6854      foreach my $full_addr (@addrs) {
6855        my $addr = $full_addr;
6856        $addr =~ s/0x0*//;  # strip off leading 0x, zeroes
6857        if (length($addr) > 16) {
6858          print STDERR "Invalid address in profile: $full_addr\n";
6859          next;
6860        }
6861        my $low_addr = substr($addr, -8);       # get last 8 hex chars
6862        my $high_addr = substr($addr, -16, 8);  # get up to 8 more hex chars
6863        print pack('L*', hex('0x' . $low_addr), hex('0x' . $high_addr));
6864      }
6865    }
6866  }
6867}
6868
6869# Print symbols and profile data
6870sub PrintSymbolizedProfile {
6871  my $symbols = shift;
6872  my $profile = shift;
6873  my $prog = shift;
6874
6875  $SYMBOL_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
6876  my $symbol_marker = $&;
6877
6878  print '--- ', $symbol_marker, "\n";
6879  if (defined($prog)) {
6880    print 'binary=', $prog, "\n";
6881  }
6882  while (my ($pc, $name) = each(%{$symbols})) {
6883    my $sep = ' ';
6884    print '0x', $pc;
6885    # We have a list of function names, which include the inlined
6886    # calls.  They are separated (and terminated) by --, which is
6887    # illegal in function names.
6888    for (my $j = 2; $j <= $#{$name}; $j += 3) {
6889      print $sep, $name->[$j];
6890      $sep = '--';
6891    }
6892    print "\n";
6893  }
6894  print '---', "\n";
6895
6896  my $profile_marker;
6897  if ($main::profile_type eq 'heap') {
6898    $HEAP_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
6899    $profile_marker = $&;
6900  } elsif ($main::profile_type eq 'growth') {
6901    $GROWTH_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
6902    $profile_marker = $&;
6903  } elsif ($main::profile_type eq 'contention') {
6904    $CONTENTION_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
6905    $profile_marker = $&;
6906  } else { # elsif ($main::profile_type eq 'cpu')
6907    $PROFILE_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
6908    $profile_marker = $&;
6909  }
6910
6911  print '--- ', $profile_marker, "\n";
6912  if (defined($main::collected_profile)) {
6913    # if used with remote fetch, simply dump the collected profile to output.
6914    open(SRC, "<$main::collected_profile");
6915    while (<SRC>) {
6916      print $_;
6917    }
6918    close(SRC);
6919  } else {
6920    # --raw/http: For everything to work correctly for non-remote profiles, we
6921    # would need to extend PrintProfileData() to handle all possible profile
6922    # types, re-enable the code that is currently disabled in ReadCPUProfile()
6923    # and FixCallerAddresses(), and remove the remote profile dumping code in
6924    # the block above.
6925    die "--raw/http: jeprof can only dump remote profiles for --raw\n";
6926    # dump a cpu-format profile to standard out
6927    PrintProfileData($profile);
6928  }
6929}
6930
6931# Print text output
6932sub PrintText {
6933  my $symbols = shift;
6934  my $flat = shift;
6935  my $cumulative = shift;
6936  my $line_limit = shift;
6937
6938  my $total = TotalProfile($flat);
6939
6940  # Which profile to sort by?
6941  my $s = $main::opt_cum ? $cumulative : $flat;
6942
6943  my $running_sum = 0;
6944  my $lines = 0;
6945  foreach my $k (sort { GetEntry($s, $b) <=> GetEntry($s, $a) || $a cmp $b }
6946                 keys(%{$cumulative})) {
6947    my $f = GetEntry($flat, $k);
6948    my $c = GetEntry($cumulative, $k);
6949    $running_sum += $f;
6950
6951    my $sym = $k;
6952    if (exists($symbols->{$k})) {
6953      $sym = $symbols->{$k}->[0] . " " . $symbols->{$k}->[1];
6954      if ($main::opt_addresses) {
6955        $sym = $k . " " . $sym;
6956      }
6957    }
6958
6959    if ($f != 0 || $c != 0) {
6960      printf("%8s %6s %6s %8s %6s %s\n",
6961             Unparse($f),
6962             Percent($f, $total),
6963             Percent($running_sum, $total),
6964             Unparse($c),
6965             Percent($c, $total),
6966             $sym);
6967    }
6968    $lines++;
6969    last if ($line_limit >= 0 && $lines >= $line_limit);
6970  }
6971}
6972
6973# Callgrind format has a compression for repeated function and file
6974# names.  You show the name the first time, and just use its number
6975# subsequently.  This can cut down the file to about a third or a
6976# quarter of its uncompressed size.  $key and $val are the key/value
6977# pair that would normally be printed by callgrind; $map is a map from
6978# value to number.
6979sub CompressedCGName {
6980  my($key, $val, $map) = @_;
6981  my $idx = $map->{$val};
6982  # For very short keys, providing an index hurts rather than helps.
6983  if (length($val) <= 3) {
6984    return "$key=$val\n";
6985  } elsif (defined($idx)) {
6986    return "$key=($idx)\n";
6987  } else {
6988    # scalar(keys $map) gives the number of items in the map.
6989    $idx = scalar(keys(%{$map})) + 1;
6990    $map->{$val} = $idx;
6991    return "$key=($idx) $val\n";
6992  }
6993}
6994
6995# Print the call graph in a way that's suiteable for callgrind.
6996sub PrintCallgrind {
6997  my $calls = shift;
6998  my $filename;
6999  my %filename_to_index_map;
7000  my %fnname_to_index_map;
7001
7002  if ($main::opt_interactive) {
7003    $filename = shift;
7004    print STDERR "Writing callgrind file to '$filename'.\n"
7005  } else {
7006    $filename = "&STDOUT";
7007  }
7008  open(CG, ">$filename");
7009  printf CG ("events: Hits\n\n");
7010  foreach my $call ( map { $_->[0] }
7011                     sort { $a->[1] cmp $b ->[1] ||
7012                            $a->[2] <=> $b->[2] }
7013                     map { /([^:]+):(\d+):([^ ]+)( -> ([^:]+):(\d+):(.+))?/;
7014                           [$_, $1, $2] }
7015                     keys %$calls ) {
7016    my $count = int($calls->{$call});
7017    $call =~ /([^:]+):(\d+):([^ ]+)( -> ([^:]+):(\d+):(.+))?/;
7018    my ( $caller_file, $caller_line, $caller_function,
7019         $callee_file, $callee_line, $callee_function ) =
7020       ( $1, $2, $3, $5, $6, $7 );
7021
7022    # TODO(csilvers): for better compression, collect all the
7023    # caller/callee_files and functions first, before printing
7024    # anything, and only compress those referenced more than once.
7025    printf CG CompressedCGName("fl", $caller_file, \%filename_to_index_map);
7026    printf CG CompressedCGName("fn", $caller_function, \%fnname_to_index_map);
7027    if (defined $6) {
7028      printf CG CompressedCGName("cfl", $callee_file, \%filename_to_index_map);
7029      printf CG CompressedCGName("cfn", $callee_function, \%fnname_to_index_map);
7030      printf CG ("calls=$count $callee_line\n");
7031    }
7032    printf CG ("$caller_line $count\n\n");
7033  }
7034}
7035
7036# Print disassembly for all all routines that match $main::opt_disasm
7037sub PrintDisassembly {
7038  my $libs = shift;
7039  my $flat = shift;
7040  my $cumulative = shift;
7041  my $disasm_opts = shift;
7042
7043  my $total = TotalProfile($flat);
7044
7045  foreach my $lib (@{$libs}) {
7046    my $symbol_table = GetProcedureBoundaries($lib->[0], $disasm_opts);
7047    my $offset = AddressSub($lib->[1], $lib->[3]);
7048    foreach my $routine (sort ByName keys(%{$symbol_table})) {
7049      my $start_addr = $symbol_table->{$routine}->[0];
7050      my $end_addr = $symbol_table->{$routine}->[1];
7051      # See if there are any samples in this routine
7052      my $length = hex(AddressSub($end_addr, $start_addr));
7053      my $addr = AddressAdd($start_addr, $offset);
7054      for (my $i = 0; $i < $length; $i++) {
7055        if (defined($cumulative->{$addr})) {
7056          PrintDisassembledFunction($lib->[0], $offset,
7057                                    $routine, $flat, $cumulative,
7058                                    $start_addr, $end_addr, $total);
7059          last;
7060        }
7061        $addr = AddressInc($addr);
7062      }
7063    }
7064  }
7065}
7066
7067# Return reference to array of tuples of the form:
7068#       [start_address, filename, linenumber, instruction, limit_address]
7069# E.g.,
7070#       ["0x806c43d", "/foo/bar.cc", 131, "ret", "0x806c440"]
7071sub Disassemble {
7072  my $prog = shift;
7073  my $offset = shift;
7074  my $start_addr = shift;
7075  my $end_addr = shift;
7076
7077  my $objdump = $obj_tool_map{"objdump"};
7078  my $cmd = ShellEscape($objdump, "-C", "-d", "-l", "--no-show-raw-insn",
7079                        "--start-address=0x$start_addr",
7080                        "--stop-address=0x$end_addr", $prog);
7081  open(OBJDUMP, "$cmd |") || error("$cmd: $!\n");
7082  my @result = ();
7083  my $filename = "";
7084  my $linenumber = -1;
7085  my $last = ["", "", "", ""];
7086  while (<OBJDUMP>) {
7087    s/\r//g;         # turn windows-looking lines into unix-looking lines
7088    chop;
7089    if (m|\s*([^:\s]+):(\d+)\s*$|) {
7090      # Location line of the form:
7091      #   <filename>:<linenumber>
7092      $filename = $1;
7093      $linenumber = $2;
7094    } elsif (m/^ +([0-9a-f]+):\s*(.*)/) {
7095      # Disassembly line -- zero-extend address to full length
7096      my $addr = HexExtend($1);
7097      my $k = AddressAdd($addr, $offset);
7098      $last->[4] = $k;   # Store ending address for previous instruction
7099      $last = [$k, $filename, $linenumber, $2, $end_addr];
7100      push(@result, $last);
7101    }
7102  }
7103  close(OBJDUMP);
7104  return @result;
7105}
7106
7107# The input file should contain lines of the form /proc/maps-like
7108# output (same format as expected from the profiles) or that looks
7109# like hex addresses (like "0xDEADBEEF").  We will parse all
7110# /proc/maps output, and for all the hex addresses, we will output
7111# "short" symbol names, one per line, in the same order as the input.
7112sub PrintSymbols {
7113  my $maps_and_symbols_file = shift;
7114
7115  # ParseLibraries expects pcs to be in a set.  Fine by us...
7116  my @pclist = ();   # pcs in sorted order
7117  my $pcs = {};
7118  my $map = "";
7119  foreach my $line (<$maps_and_symbols_file>) {
7120    $line =~ s/\r//g;    # turn windows-looking lines into unix-looking lines
7121    if ($line =~ /\b(0x[0-9a-f]+)\b/i) {
7122      push(@pclist, HexExtend($1));
7123      $pcs->{$pclist[-1]} = 1;
7124    } else {
7125      $map .= $line;
7126    }
7127  }
7128
7129  my $libs = ParseLibraries($main::prog, $map, $pcs);
7130  my $symbols = ExtractSymbols($libs, $pcs);
7131
7132  foreach my $pc (@pclist) {
7133    # ->[0] is the shortname, ->[2] is the full name
7134    print(($symbols->{$pc}->[0] || "??") . "\n");
7135  }
7136}
7137
7138
7139# For sorting functions by name
7140sub ByName {
7141  return ShortFunctionName($a) cmp ShortFunctionName($b);
7142}
7143
7144# Print source-listing for all all routines that match $list_opts
7145sub PrintListing {
7146  my $total = shift;
7147  my $libs = shift;
7148  my $flat = shift;
7149  my $cumulative = shift;
7150  my $list_opts = shift;
7151  my $html = shift;
7152
7153  my $output = \*STDOUT;
7154  my $fname = "";
7155
7156  if ($html) {
7157    # Arrange to write the output to a temporary file
7158    $fname = TempName($main::next_tmpfile, "html");
7159    $main::next_tmpfile++;
7160    if (!open(TEMP, ">$fname")) {
7161      print STDERR "$fname: $!\n";
7162      return;
7163    }
7164    $output = \*TEMP;
7165    print $output HtmlListingHeader();
7166    printf $output ("<div class=\"legend\">%s<br>Total: %s %s</div>\n",
7167                    $main::prog, Unparse($total), Units());
7168  }
7169
7170  my $listed = 0;
7171  foreach my $lib (@{$libs}) {
7172    my $symbol_table = GetProcedureBoundaries($lib->[0], $list_opts);
7173    my $offset = AddressSub($lib->[1], $lib->[3]);
7174    foreach my $routine (sort ByName keys(%{$symbol_table})) {
7175      # Print if there are any samples in this routine
7176      my $start_addr = $symbol_table->{$routine}->[0];
7177      my $end_addr = $symbol_table->{$routine}->[1];
7178      my $length = hex(AddressSub($end_addr, $start_addr));
7179      my $addr = AddressAdd($start_addr, $offset);
7180      for (my $i = 0; $i < $length; $i++) {
7181        if (defined($cumulative->{$addr})) {
7182          $listed += PrintSource(
7183            $lib->[0], $offset,
7184            $routine, $flat, $cumulative,
7185            $start_addr, $end_addr,
7186            $html,
7187            $output);
7188          last;
7189        }
7190        $addr = AddressInc($addr);
7191      }
7192    }
7193  }
7194
7195  if ($html) {
7196    if ($listed > 0) {
7197      print $output HtmlListingFooter();
7198      close($output);
7199      RunWeb($fname);
7200    } else {
7201      close($output);
7202      unlink($fname);
7203    }
7204  }
7205}
7206
7207sub HtmlListingHeader {
7208  return <<'EOF';
7209<DOCTYPE html>
7210<html>
7211<head>
7212<title>Pprof listing</title>
7213<style type="text/css">
7214body {
7215  font-family: sans-serif;
7216}
7217h1 {
7218  font-size: 1.5em;
7219  margin-bottom: 4px;
7220}
7221.legend {
7222  font-size: 1.25em;
7223}
7224.line {
7225  color: #aaaaaa;
7226}
7227.nop {
7228  color: #aaaaaa;
7229}
7230.unimportant {
7231  color: #cccccc;
7232}
7233.disasmloc {
7234  color: #000000;
7235}
7236.deadsrc {
7237  cursor: pointer;
7238}
7239.deadsrc:hover {
7240  background-color: #eeeeee;
7241}
7242.livesrc {
7243  color: #0000ff;
7244  cursor: pointer;
7245}
7246.livesrc:hover {
7247  background-color: #eeeeee;
7248}
7249.asm {
7250  color: #008800;
7251  display: none;
7252}
7253</style>
7254<script type="text/javascript">
7255function jeprof_toggle_asm(e) {
7256  var target;
7257  if (!e) e = window.event;
7258  if (e.target) target = e.target;
7259  else if (e.srcElement) target = e.srcElement;
7260
7261  if (target) {
7262    var asm = target.nextSibling;
7263    if (asm && asm.className == "asm") {
7264      asm.style.display = (asm.style.display == "block" ? "" : "block");
7265      e.preventDefault();
7266      return false;
7267    }
7268  }
7269}
7270</script>
7271</head>
7272<body>
7273EOF
7274}
7275
7276sub HtmlListingFooter {
7277  return <<'EOF';
7278</body>
7279</html>
7280EOF
7281}
7282
7283sub HtmlEscape {
7284  my $text = shift;
7285  $text =~ s/&/&amp;/g;
7286  $text =~ s/</&lt;/g;
7287  $text =~ s/>/&gt;/g;
7288  return $text;
7289}
7290
7291# Returns the indentation of the line, if it has any non-whitespace
7292# characters.  Otherwise, returns -1.
7293sub Indentation {
7294  my $line = shift;
7295  if (m/^(\s*)\S/) {
7296    return length($1);
7297  } else {
7298    return -1;
7299  }
7300}
7301
7302# If the symbol table contains inlining info, Disassemble() may tag an
7303# instruction with a location inside an inlined function.  But for
7304# source listings, we prefer to use the location in the function we
7305# are listing.  So use MapToSymbols() to fetch full location
7306# information for each instruction and then pick out the first
7307# location from a location list (location list contains callers before
7308# callees in case of inlining).
7309#
7310# After this routine has run, each entry in $instructions contains:
7311#   [0] start address
7312#   [1] filename for function we are listing
7313#   [2] line number for function we are listing
7314#   [3] disassembly
7315#   [4] limit address
7316#   [5] most specific filename (may be different from [1] due to inlining)
7317#   [6] most specific line number (may be different from [2] due to inlining)
7318sub GetTopLevelLineNumbers {
7319  my ($lib, $offset, $instructions) = @_;
7320  my $pcs = [];
7321  for (my $i = 0; $i <= $#{$instructions}; $i++) {
7322    push(@{$pcs}, $instructions->[$i]->[0]);
7323  }
7324  my $symbols = {};
7325  MapToSymbols($lib, $offset, $pcs, $symbols);
7326  for (my $i = 0; $i <= $#{$instructions}; $i++) {
7327    my $e = $instructions->[$i];
7328    push(@{$e}, $e->[1]);
7329    push(@{$e}, $e->[2]);
7330    my $addr = $e->[0];
7331    my $sym = $symbols->{$addr};
7332    if (defined($sym)) {
7333      if ($#{$sym} >= 2 && $sym->[1] =~ m/^(.*):(\d+)$/) {
7334        $e->[1] = $1;  # File name
7335        $e->[2] = $2;  # Line number
7336      }
7337    }
7338  }
7339}
7340
7341# Print source-listing for one routine
7342sub PrintSource {
7343  my $prog = shift;
7344  my $offset = shift;
7345  my $routine = shift;
7346  my $flat = shift;
7347  my $cumulative = shift;
7348  my $start_addr = shift;
7349  my $end_addr = shift;
7350  my $html = shift;
7351  my $output = shift;
7352
7353  # Disassemble all instructions (just to get line numbers)
7354  my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr);
7355  GetTopLevelLineNumbers($prog, $offset, \@instructions);
7356
7357  # Hack 1: assume that the first source file encountered in the
7358  # disassembly contains the routine
7359  my $filename = undef;
7360  for (my $i = 0; $i <= $#instructions; $i++) {
7361    if ($instructions[$i]->[2] >= 0) {
7362      $filename = $instructions[$i]->[1];
7363      last;
7364    }
7365  }
7366  if (!defined($filename)) {
7367    print STDERR "no filename found in $routine\n";
7368    return 0;
7369  }
7370
7371  # Hack 2: assume that the largest line number from $filename is the
7372  # end of the procedure.  This is typically safe since if P1 contains
7373  # an inlined call to P2, then P2 usually occurs earlier in the
7374  # source file.  If this does not work, we might have to compute a
7375  # density profile or just print all regions we find.
7376  my $lastline = 0;
7377  for (my $i = 0; $i <= $#instructions; $i++) {
7378    my $f = $instructions[$i]->[1];
7379    my $l = $instructions[$i]->[2];
7380    if (($f eq $filename) && ($l > $lastline)) {
7381      $lastline = $l;
7382    }
7383  }
7384
7385  # Hack 3: assume the first source location from "filename" is the start of
7386  # the source code.
7387  my $firstline = 1;
7388  for (my $i = 0; $i <= $#instructions; $i++) {
7389    if ($instructions[$i]->[1] eq $filename) {
7390      $firstline = $instructions[$i]->[2];
7391      last;
7392    }
7393  }
7394
7395  # Hack 4: Extend last line forward until its indentation is less than
7396  # the indentation we saw on $firstline
7397  my $oldlastline = $lastline;
7398  {
7399    if (!open(FILE, "<$filename")) {
7400      print STDERR "$filename: $!\n";
7401      return 0;
7402    }
7403    my $l = 0;
7404    my $first_indentation = -1;
7405    while (<FILE>) {
7406      s/\r//g;         # turn windows-looking lines into unix-looking lines
7407      $l++;
7408      my $indent = Indentation($_);
7409      if ($l >= $firstline) {
7410        if ($first_indentation < 0 && $indent >= 0) {
7411          $first_indentation = $indent;
7412          last if ($first_indentation == 0);
7413        }
7414      }
7415      if ($l >= $lastline && $indent >= 0) {
7416        if ($indent >= $first_indentation) {
7417          $lastline = $l+1;
7418        } else {
7419          last;
7420        }
7421      }
7422    }
7423    close(FILE);
7424  }
7425
7426  # Assign all samples to the range $firstline,$lastline,
7427  # Hack 4: If an instruction does not occur in the range, its samples
7428  # are moved to the next instruction that occurs in the range.
7429  my $samples1 = {};        # Map from line number to flat count
7430  my $samples2 = {};        # Map from line number to cumulative count
7431  my $running1 = 0;         # Unassigned flat counts
7432  my $running2 = 0;         # Unassigned cumulative counts
7433  my $total1 = 0;           # Total flat counts
7434  my $total2 = 0;           # Total cumulative counts
7435  my %disasm = ();          # Map from line number to disassembly
7436  my $running_disasm = "";  # Unassigned disassembly
7437  my $skip_marker = "---\n";
7438  if ($html) {
7439    $skip_marker = "";
7440    for (my $l = $firstline; $l <= $lastline; $l++) {
7441      $disasm{$l} = "";
7442    }
7443  }
7444  my $last_dis_filename = '';
7445  my $last_dis_linenum = -1;
7446  my $last_touched_line = -1;  # To detect gaps in disassembly for a line
7447  foreach my $e (@instructions) {
7448    # Add up counts for all address that fall inside this instruction
7449    my $c1 = 0;
7450    my $c2 = 0;
7451    for (my $a = $e->[0]; $a lt $e->[4]; $a = AddressInc($a)) {
7452      $c1 += GetEntry($flat, $a);
7453      $c2 += GetEntry($cumulative, $a);
7454    }
7455
7456    if ($html) {
7457      my $dis = sprintf("      %6s %6s \t\t%8s: %s ",
7458                        HtmlPrintNumber($c1),
7459                        HtmlPrintNumber($c2),
7460                        UnparseAddress($offset, $e->[0]),
7461                        CleanDisassembly($e->[3]));
7462
7463      # Append the most specific source line associated with this instruction
7464      if (length($dis) < 80) { $dis .= (' ' x (80 - length($dis))) };
7465      $dis = HtmlEscape($dis);
7466      my $f = $e->[5];
7467      my $l = $e->[6];
7468      if ($f ne $last_dis_filename) {
7469        $dis .= sprintf("<span class=disasmloc>%s:%d</span>",
7470                        HtmlEscape(CleanFileName($f)), $l);
7471      } elsif ($l ne $last_dis_linenum) {
7472        # De-emphasize the unchanged file name portion
7473        $dis .= sprintf("<span class=unimportant>%s</span>" .
7474                        "<span class=disasmloc>:%d</span>",
7475                        HtmlEscape(CleanFileName($f)), $l);
7476      } else {
7477        # De-emphasize the entire location
7478        $dis .= sprintf("<span class=unimportant>%s:%d</span>",
7479                        HtmlEscape(CleanFileName($f)), $l);
7480      }
7481      $last_dis_filename = $f;
7482      $last_dis_linenum = $l;
7483      $running_disasm .= $dis;
7484      $running_disasm .= "\n";
7485    }
7486
7487    $running1 += $c1;
7488    $running2 += $c2;
7489    $total1 += $c1;
7490    $total2 += $c2;
7491    my $file = $e->[1];
7492    my $line = $e->[2];
7493    if (($file eq $filename) &&
7494        ($line >= $firstline) &&
7495        ($line <= $lastline)) {
7496      # Assign all accumulated samples to this line
7497      AddEntry($samples1, $line, $running1);
7498      AddEntry($samples2, $line, $running2);
7499      $running1 = 0;
7500      $running2 = 0;
7501      if ($html) {
7502        if ($line != $last_touched_line && $disasm{$line} ne '') {
7503          $disasm{$line} .= "\n";
7504        }
7505        $disasm{$line} .= $running_disasm;
7506        $running_disasm = '';
7507        $last_touched_line = $line;
7508      }
7509    }
7510  }
7511
7512  # Assign any leftover samples to $lastline
7513  AddEntry($samples1, $lastline, $running1);
7514  AddEntry($samples2, $lastline, $running2);
7515  if ($html) {
7516    if ($lastline != $last_touched_line && $disasm{$lastline} ne '') {
7517      $disasm{$lastline} .= "\n";
7518    }
7519    $disasm{$lastline} .= $running_disasm;
7520  }
7521
7522  if ($html) {
7523    printf $output (
7524      "<h1>%s</h1>%s\n<pre onClick=\"jeprof_toggle_asm()\">\n" .
7525      "Total:%6s %6s (flat / cumulative %s)\n",
7526      HtmlEscape(ShortFunctionName($routine)),
7527      HtmlEscape(CleanFileName($filename)),
7528      Unparse($total1),
7529      Unparse($total2),
7530      Units());
7531  } else {
7532    printf $output (
7533      "ROUTINE ====================== %s in %s\n" .
7534      "%6s %6s Total %s (flat / cumulative)\n",
7535      ShortFunctionName($routine),
7536      CleanFileName($filename),
7537      Unparse($total1),
7538      Unparse($total2),
7539      Units());
7540  }
7541  if (!open(FILE, "<$filename")) {
7542    print STDERR "$filename: $!\n";
7543    return 0;
7544  }
7545  my $l = 0;
7546  while (<FILE>) {
7547    s/\r//g;         # turn windows-looking lines into unix-looking lines
7548    $l++;
7549    if ($l >= $firstline - 5 &&
7550        (($l <= $oldlastline + 5) || ($l <= $lastline))) {
7551      chop;
7552      my $text = $_;
7553      if ($l == $firstline) { print $output $skip_marker; }
7554      my $n1 = GetEntry($samples1, $l);
7555      my $n2 = GetEntry($samples2, $l);
7556      if ($html) {
7557        # Emit a span that has one of the following classes:
7558        #    livesrc -- has samples
7559        #    deadsrc -- has disassembly, but with no samples
7560        #    nop     -- has no matching disasembly
7561        # Also emit an optional span containing disassembly.
7562        my $dis = $disasm{$l};
7563        my $asm = "";
7564        if (defined($dis) && $dis ne '') {
7565          $asm = "<span class=\"asm\">" . $dis . "</span>";
7566        }
7567        my $source_class = (($n1 + $n2 > 0)
7568                            ? "livesrc"
7569                            : (($asm ne "") ? "deadsrc" : "nop"));
7570        printf $output (
7571          "<span class=\"line\">%5d</span> " .
7572          "<span class=\"%s\">%6s %6s %s</span>%s\n",
7573          $l, $source_class,
7574          HtmlPrintNumber($n1),
7575          HtmlPrintNumber($n2),
7576          HtmlEscape($text),
7577          $asm);
7578      } else {
7579        printf $output(
7580          "%6s %6s %4d: %s\n",
7581          UnparseAlt($n1),
7582          UnparseAlt($n2),
7583          $l,
7584          $text);
7585      }
7586      if ($l == $lastline)  { print $output $skip_marker; }
7587    };
7588  }
7589  close(FILE);
7590  if ($html) {
7591    print $output "</pre>\n";
7592  }
7593  return 1;
7594}
7595
7596# Return the source line for the specified file/linenumber.
7597# Returns undef if not found.
7598sub SourceLine {
7599  my $file = shift;
7600  my $line = shift;
7601
7602  # Look in cache
7603  if (!defined($main::source_cache{$file})) {
7604    if (100 < scalar keys(%main::source_cache)) {
7605      # Clear the cache when it gets too big
7606      $main::source_cache = ();
7607    }
7608
7609    # Read all lines from the file
7610    if (!open(FILE, "<$file")) {
7611      print STDERR "$file: $!\n";
7612      $main::source_cache{$file} = [];  # Cache the negative result
7613      return undef;
7614    }
7615    my $lines = [];
7616    push(@{$lines}, "");        # So we can use 1-based line numbers as indices
7617    while (<FILE>) {
7618      push(@{$lines}, $_);
7619    }
7620    close(FILE);
7621
7622    # Save the lines in the cache
7623    $main::source_cache{$file} = $lines;
7624  }
7625
7626  my $lines = $main::source_cache{$file};
7627  if (($line < 0) || ($line > $#{$lines})) {
7628    return undef;
7629  } else {
7630    return $lines->[$line];
7631  }
7632}
7633
7634# Print disassembly for one routine with interspersed source if available
7635sub PrintDisassembledFunction {
7636  my $prog = shift;
7637  my $offset = shift;
7638  my $routine = shift;
7639  my $flat = shift;
7640  my $cumulative = shift;
7641  my $start_addr = shift;
7642  my $end_addr = shift;
7643  my $total = shift;
7644
7645  # Disassemble all instructions
7646  my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr);
7647
7648  # Make array of counts per instruction
7649  my @flat_count = ();
7650  my @cum_count = ();
7651  my $flat_total = 0;
7652  my $cum_total = 0;
7653  foreach my $e (@instructions) {
7654    # Add up counts for all address that fall inside this instruction
7655    my $c1 = 0;
7656    my $c2 = 0;
7657    for (my $a = $e->[0]; $a lt $e->[4]; $a = AddressInc($a)) {
7658      $c1 += GetEntry($flat, $a);
7659      $c2 += GetEntry($cumulative, $a);
7660    }
7661    push(@flat_count, $c1);
7662    push(@cum_count, $c2);
7663    $flat_total += $c1;
7664    $cum_total += $c2;
7665  }
7666
7667  # Print header with total counts
7668  printf("ROUTINE ====================== %s\n" .
7669         "%6s %6s %s (flat, cumulative) %.1f%% of total\n",
7670         ShortFunctionName($routine),
7671         Unparse($flat_total),
7672         Unparse($cum_total),
7673         Units(),
7674         ($cum_total * 100.0) / $total);
7675
7676  # Process instructions in order
7677  my $current_file = "";
7678  for (my $i = 0; $i <= $#instructions; ) {
7679    my $e = $instructions[$i];
7680
7681    # Print the new file name whenever we switch files
7682    if ($e->[1] ne $current_file) {
7683      $current_file = $e->[1];
7684      my $fname = $current_file;
7685      $fname =~ s|^\./||;   # Trim leading "./"
7686
7687      # Shorten long file names
7688      if (length($fname) >= 58) {
7689        $fname = "..." . substr($fname, -55);
7690      }
7691      printf("-------------------- %s\n", $fname);
7692    }
7693
7694    # TODO: Compute range of lines to print together to deal with
7695    # small reorderings.
7696    my $first_line = $e->[2];
7697    my $last_line = $first_line;
7698    my %flat_sum = ();
7699    my %cum_sum = ();
7700    for (my $l = $first_line; $l <= $last_line; $l++) {
7701      $flat_sum{$l} = 0;
7702      $cum_sum{$l} = 0;
7703    }
7704
7705    # Find run of instructions for this range of source lines
7706    my $first_inst = $i;
7707    while (($i <= $#instructions) &&
7708           ($instructions[$i]->[2] >= $first_line) &&
7709           ($instructions[$i]->[2] <= $last_line)) {
7710      $e = $instructions[$i];
7711      $flat_sum{$e->[2]} += $flat_count[$i];
7712      $cum_sum{$e->[2]} += $cum_count[$i];
7713      $i++;
7714    }
7715    my $last_inst = $i - 1;
7716
7717    # Print source lines
7718    for (my $l = $first_line; $l <= $last_line; $l++) {
7719      my $line = SourceLine($current_file, $l);
7720      if (!defined($line)) {
7721        $line = "?\n";
7722        next;
7723      } else {
7724        $line =~ s/^\s+//;
7725      }
7726      printf("%6s %6s %5d: %s",
7727             UnparseAlt($flat_sum{$l}),
7728             UnparseAlt($cum_sum{$l}),
7729             $l,
7730             $line);
7731    }
7732
7733    # Print disassembly
7734    for (my $x = $first_inst; $x <= $last_inst; $x++) {
7735      my $e = $instructions[$x];
7736      printf("%6s %6s    %8s: %6s\n",
7737             UnparseAlt($flat_count[$x]),
7738             UnparseAlt($cum_count[$x]),
7739             UnparseAddress($offset, $e->[0]),
7740             CleanDisassembly($e->[3]));
7741    }
7742  }
7743}
7744
7745# Print DOT graph
7746sub PrintDot {
7747  my $prog = shift;
7748  my $symbols = shift;
7749  my $raw = shift;
7750  my $flat = shift;
7751  my $cumulative = shift;
7752  my $overall_total = shift;
7753
7754  # Get total
7755  my $local_total = TotalProfile($flat);
7756  my $nodelimit = int($main::opt_nodefraction * $local_total);
7757  my $edgelimit = int($main::opt_edgefraction * $local_total);
7758  my $nodecount = $main::opt_nodecount;
7759
7760  # Find nodes to include
7761  my @list = (sort { abs(GetEntry($cumulative, $b)) <=>
7762                     abs(GetEntry($cumulative, $a))
7763                     || $a cmp $b }
7764              keys(%{$cumulative}));
7765  my $last = $nodecount - 1;
7766  if ($last > $#list) {
7767    $last = $#list;
7768  }
7769  while (($last >= 0) &&
7770         (abs(GetEntry($cumulative, $list[$last])) <= $nodelimit)) {
7771    $last--;
7772  }
7773  if ($last < 0) {
7774    print STDERR "No nodes to print\n";
7775    return 0;
7776  }
7777
7778  if ($nodelimit > 0 || $edgelimit > 0) {
7779    printf STDERR ("Dropping nodes with <= %s %s; edges with <= %s abs(%s)\n",
7780                   Unparse($nodelimit), Units(),
7781                   Unparse($edgelimit), Units());
7782  }
7783
7784  # Open DOT output file
7785  my $output;
7786  my $escaped_dot = ShellEscape(@DOT);
7787  my $escaped_ps2pdf = ShellEscape(@PS2PDF);
7788  if ($main::opt_gv) {
7789    my $escaped_outfile = ShellEscape(TempName($main::next_tmpfile, "ps"));
7790    $output = "| $escaped_dot -Tps2 >$escaped_outfile";
7791  } elsif ($main::opt_evince) {
7792    my $escaped_outfile = ShellEscape(TempName($main::next_tmpfile, "pdf"));
7793    $output = "| $escaped_dot -Tps2 | $escaped_ps2pdf - $escaped_outfile";
7794  } elsif ($main::opt_ps) {
7795    $output = "| $escaped_dot -Tps2";
7796  } elsif ($main::opt_pdf) {
7797    $output = "| $escaped_dot -Tps2 | $escaped_ps2pdf - -";
7798  } elsif ($main::opt_web || $main::opt_svg) {
7799    # We need to post-process the SVG, so write to a temporary file always.
7800    my $escaped_outfile = ShellEscape(TempName($main::next_tmpfile, "svg"));
7801    $output = "| $escaped_dot -Tsvg >$escaped_outfile";
7802  } elsif ($main::opt_gif) {
7803    $output = "| $escaped_dot -Tgif";
7804  } else {
7805    $output = ">&STDOUT";
7806  }
7807  open(DOT, $output) || error("$output: $!\n");
7808
7809  # Title
7810  printf DOT ("digraph \"%s; %s %s\" {\n",
7811              $prog,
7812              Unparse($overall_total),
7813              Units());
7814  if ($main::opt_pdf) {
7815    # The output is more printable if we set the page size for dot.
7816    printf DOT ("size=\"8,11\"\n");
7817  }
7818  printf DOT ("node [width=0.375,height=0.25];\n");
7819
7820  # Print legend
7821  printf DOT ("Legend [shape=box,fontsize=24,shape=plaintext," .
7822              "label=\"%s\\l%s\\l%s\\l%s\\l%s\\l\"];\n",
7823              $prog,
7824              sprintf("Total %s: %s", Units(), Unparse($overall_total)),
7825              sprintf("Focusing on: %s", Unparse($local_total)),
7826              sprintf("Dropped nodes with <= %s abs(%s)",
7827                      Unparse($nodelimit), Units()),
7828              sprintf("Dropped edges with <= %s %s",
7829                      Unparse($edgelimit), Units())
7830              );
7831
7832  # Print nodes
7833  my %node = ();
7834  my $nextnode = 1;
7835  foreach my $a (@list[0..$last]) {
7836    # Pick font size
7837    my $f = GetEntry($flat, $a);
7838    my $c = GetEntry($cumulative, $a);
7839
7840    my $fs = 8;
7841    if ($local_total > 0) {
7842      $fs = 8 + (50.0 * sqrt(abs($f * 1.0 / $local_total)));
7843    }
7844
7845    $node{$a} = $nextnode++;
7846    my $sym = $a;
7847    $sym =~ s/\s+/\\n/g;
7848    $sym =~ s/::/\\n/g;
7849
7850    # Extra cumulative info to print for non-leaves
7851    my $extra = "";
7852    if ($f != $c) {
7853      $extra = sprintf("\\rof %s (%s)",
7854                       Unparse($c),
7855                       Percent($c, $local_total));
7856    }
7857    my $style = "";
7858    if ($main::opt_heapcheck) {
7859      if ($f > 0) {
7860        # make leak-causing nodes more visible (add a background)
7861        $style = ",style=filled,fillcolor=gray"
7862      } elsif ($f < 0) {
7863        # make anti-leak-causing nodes (which almost never occur)
7864        # stand out as well (triple border)
7865        $style = ",peripheries=3"
7866      }
7867    }
7868
7869    printf DOT ("N%d [label=\"%s\\n%s (%s)%s\\r" .
7870                "\",shape=box,fontsize=%.1f%s];\n",
7871                $node{$a},
7872                $sym,
7873                Unparse($f),
7874                Percent($f, $local_total),
7875                $extra,
7876                $fs,
7877                $style,
7878               );
7879  }
7880
7881  # Get edges and counts per edge
7882  my %edge = ();
7883  my $n;
7884  my $fullname_to_shortname_map = {};
7885  FillFullnameToShortnameMap($symbols, $fullname_to_shortname_map);
7886  foreach my $k (keys(%{$raw})) {
7887    # TODO: omit low %age edges
7888    $n = $raw->{$k};
7889    my @translated = TranslateStack($symbols, $fullname_to_shortname_map, $k);
7890    for (my $i = 1; $i <= $#translated; $i++) {
7891      my $src = $translated[$i];
7892      my $dst = $translated[$i-1];
7893      #next if ($src eq $dst);  # Avoid self-edges?
7894      if (exists($node{$src}) && exists($node{$dst})) {
7895        my $edge_label = "$src\001$dst";
7896        if (!exists($edge{$edge_label})) {
7897          $edge{$edge_label} = 0;
7898        }
7899        $edge{$edge_label} += $n;
7900      }
7901    }
7902  }
7903
7904  # Print edges (process in order of decreasing counts)
7905  my %indegree = ();   # Number of incoming edges added per node so far
7906  my %outdegree = ();  # Number of outgoing edges added per node so far
7907  foreach my $e (sort { $edge{$b} <=> $edge{$a} } keys(%edge)) {
7908    my @x = split(/\001/, $e);
7909    $n = $edge{$e};
7910
7911    # Initialize degree of kept incoming and outgoing edges if necessary
7912    my $src = $x[0];
7913    my $dst = $x[1];
7914    if (!exists($outdegree{$src})) { $outdegree{$src} = 0; }
7915    if (!exists($indegree{$dst})) { $indegree{$dst} = 0; }
7916
7917    my $keep;
7918    if ($indegree{$dst} == 0) {
7919      # Keep edge if needed for reachability
7920      $keep = 1;
7921    } elsif (abs($n) <= $edgelimit) {
7922      # Drop if we are below --edgefraction
7923      $keep = 0;
7924    } elsif ($outdegree{$src} >= $main::opt_maxdegree ||
7925             $indegree{$dst} >= $main::opt_maxdegree) {
7926      # Keep limited number of in/out edges per node
7927      $keep = 0;
7928    } else {
7929      $keep = 1;
7930    }
7931
7932    if ($keep) {
7933      $outdegree{$src}++;
7934      $indegree{$dst}++;
7935
7936      # Compute line width based on edge count
7937      my $fraction = abs($local_total ? (3 * ($n / $local_total)) : 0);
7938      if ($fraction > 1) { $fraction = 1; }
7939      my $w = $fraction * 2;
7940      if ($w < 1 && ($main::opt_web || $main::opt_svg)) {
7941        # SVG output treats line widths < 1 poorly.
7942        $w = 1;
7943      }
7944
7945      # Dot sometimes segfaults if given edge weights that are too large, so
7946      # we cap the weights at a large value
7947      my $edgeweight = abs($n) ** 0.7;
7948      if ($edgeweight > 100000) { $edgeweight = 100000; }
7949      $edgeweight = int($edgeweight);
7950
7951      my $style = sprintf("setlinewidth(%f)", $w);
7952      if ($x[1] =~ m/\(inline\)/) {
7953        $style .= ",dashed";
7954      }
7955
7956      # Use a slightly squashed function of the edge count as the weight
7957      printf DOT ("N%s -> N%s [label=%s, weight=%d, style=\"%s\"];\n",
7958                  $node{$x[0]},
7959                  $node{$x[1]},
7960                  Unparse($n),
7961                  $edgeweight,
7962                  $style);
7963    }
7964  }
7965
7966  print DOT ("}\n");
7967  close(DOT);
7968
7969  if ($main::opt_web || $main::opt_svg) {
7970    # Rewrite SVG to be more usable inside web browser.
7971    RewriteSvg(TempName($main::next_tmpfile, "svg"));
7972  }
7973
7974  return 1;
7975}
7976
7977sub RewriteSvg {
7978  my $svgfile = shift;
7979
7980  open(SVG, $svgfile) || die "open temp svg: $!";
7981  my @svg = <SVG>;
7982  close(SVG);
7983  unlink $svgfile;
7984  my $svg = join('', @svg);
7985
7986  # Dot's SVG output is
7987  #
7988  #    <svg width="___" height="___"
7989  #     viewBox="___" xmlns=...>
7990  #    <g id="graph0" transform="...">
7991  #    ...
7992  #    </g>
7993  #    </svg>
7994  #
7995  # Change it to
7996  #
7997  #    <svg width="100%" height="100%"
7998  #     xmlns=...>
7999  #    $svg_javascript
8000  #    <g id="viewport" transform="translate(0,0)">
8001  #    <g id="graph0" transform="...">
8002  #    ...
8003  #    </g>
8004  #    </g>
8005  #    </svg>
8006
8007  # Fix width, height; drop viewBox.
8008  $svg =~ s/(?s)<svg width="[^"]+" height="[^"]+"(.*?)viewBox="[^"]+"/<svg width="100%" height="100%"$1/;
8009
8010  # Insert script, viewport <g> above first <g>
8011  my $svg_javascript = SvgJavascript();
8012  my $viewport = "<g id=\"viewport\" transform=\"translate(0,0)\">\n";
8013  $svg =~ s/<g id="graph\d"/$svg_javascript$viewport$&/;
8014
8015  # Insert final </g> above </svg>.
8016  $svg =~ s/(.*)(<\/svg>)/$1<\/g>$2/;
8017  $svg =~ s/<g id="graph\d"(.*?)/<g id="viewport"$1/;
8018
8019  if ($main::opt_svg) {
8020    # --svg: write to standard output.
8021    print $svg;
8022  } else {
8023    # Write back to temporary file.
8024    open(SVG, ">$svgfile") || die "open $svgfile: $!";
8025    print SVG $svg;
8026    close(SVG);
8027  }
8028}
8029
8030sub SvgJavascript {
8031  return <<'EOF';
8032<script type="text/ecmascript"><![CDATA[
8033// SVGPan
8034// http://www.cyberz.org/blog/2009/12/08/svgpan-a-javascript-svg-panzoomdrag-library/
8035// Local modification: if(true || ...) below to force panning, never moving.
8036
8037/**
8038 *  SVGPan library 1.2
8039 * ====================
8040 *
8041 * Given an unique existing element with id "viewport", including the
8042 * the library into any SVG adds the following capabilities:
8043 *
8044 *  - Mouse panning
8045 *  - Mouse zooming (using the wheel)
8046 *  - Object dargging
8047 *
8048 * Known issues:
8049 *
8050 *  - Zooming (while panning) on Safari has still some issues
8051 *
8052 * Releases:
8053 *
8054 * 1.2, Sat Mar 20 08:42:50 GMT 2010, Zeng Xiaohui
8055 *	Fixed a bug with browser mouse handler interaction
8056 *
8057 * 1.1, Wed Feb  3 17:39:33 GMT 2010, Zeng Xiaohui
8058 *	Updated the zoom code to support the mouse wheel on Safari/Chrome
8059 *
8060 * 1.0, Andrea Leofreddi
8061 *	First release
8062 *
8063 * This code is licensed under the following BSD license:
8064 *
8065 * Copyright 2009-2010 Andrea Leofreddi <a.leofreddi@itcharm.com>. All rights reserved.
8066 *
8067 * Redistribution and use in source and binary forms, with or without modification, are
8068 * permitted provided that the following conditions are met:
8069 *
8070 *    1. Redistributions of source code must retain the above copyright notice, this list of
8071 *       conditions and the following disclaimer.
8072 *
8073 *    2. Redistributions in binary form must reproduce the above copyright notice, this list
8074 *       of conditions and the following disclaimer in the documentation and/or other materials
8075 *       provided with the distribution.
8076 *
8077 * THIS SOFTWARE IS PROVIDED BY Andrea Leofreddi ``AS IS'' AND ANY EXPRESS OR IMPLIED
8078 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
8079 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Andrea Leofreddi OR
8080 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
8081 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
8082 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
8083 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
8084 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
8085 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
8086 *
8087 * The views and conclusions contained in the software and documentation are those of the
8088 * authors and should not be interpreted as representing official policies, either expressed
8089 * or implied, of Andrea Leofreddi.
8090 */
8091
8092var root = document.documentElement;
8093
8094var state = 'none', stateTarget, stateOrigin, stateTf;
8095
8096setupHandlers(root);
8097
8098/**
8099 * Register handlers
8100 */
8101function setupHandlers(root){
8102	setAttributes(root, {
8103		"onmouseup" : "add(evt)",
8104		"onmousedown" : "handleMouseDown(evt)",
8105		"onmousemove" : "handleMouseMove(evt)",
8106		"onmouseup" : "handleMouseUp(evt)",
8107		//"onmouseout" : "handleMouseUp(evt)", // Decomment this to stop the pan functionality when dragging out of the SVG element
8108	});
8109
8110	if(navigator.userAgent.toLowerCase().indexOf('webkit') >= 0)
8111		window.addEventListener('mousewheel', handleMouseWheel, false); // Chrome/Safari
8112	else
8113		window.addEventListener('DOMMouseScroll', handleMouseWheel, false); // Others
8114
8115	var g = svgDoc.getElementById("svg");
8116	g.width = "100%";
8117	g.height = "100%";
8118}
8119
8120/**
8121 * Instance an SVGPoint object with given event coordinates.
8122 */
8123function getEventPoint(evt) {
8124	var p = root.createSVGPoint();
8125
8126	p.x = evt.clientX;
8127	p.y = evt.clientY;
8128
8129	return p;
8130}
8131
8132/**
8133 * Sets the current transform matrix of an element.
8134 */
8135function setCTM(element, matrix) {
8136	var s = "matrix(" + matrix.a + "," + matrix.b + "," + matrix.c + "," + matrix.d + "," + matrix.e + "," + matrix.f + ")";
8137
8138	element.setAttribute("transform", s);
8139}
8140
8141/**
8142 * Dumps a matrix to a string (useful for debug).
8143 */
8144function dumpMatrix(matrix) {
8145	var s = "[ " + matrix.a + ", " + matrix.c + ", " + matrix.e + "\n  " + matrix.b + ", " + matrix.d + ", " + matrix.f + "\n  0, 0, 1 ]";
8146
8147	return s;
8148}
8149
8150/**
8151 * Sets attributes of an element.
8152 */
8153function setAttributes(element, attributes){
8154	for (i in attributes)
8155		element.setAttributeNS(null, i, attributes[i]);
8156}
8157
8158/**
8159 * Handle mouse move event.
8160 */
8161function handleMouseWheel(evt) {
8162	if(evt.preventDefault)
8163		evt.preventDefault();
8164
8165	evt.returnValue = false;
8166
8167	var svgDoc = evt.target.ownerDocument;
8168
8169	var delta;
8170
8171	if(evt.wheelDelta)
8172		delta = evt.wheelDelta / 3600; // Chrome/Safari
8173	else
8174		delta = evt.detail / -90; // Mozilla
8175
8176	var z = 1 + delta; // Zoom factor: 0.9/1.1
8177
8178	var g = svgDoc.getElementById("viewport");
8179
8180	var p = getEventPoint(evt);
8181
8182	p = p.matrixTransform(g.getCTM().inverse());
8183
8184	// Compute new scale matrix in current mouse position
8185	var k = root.createSVGMatrix().translate(p.x, p.y).scale(z).translate(-p.x, -p.y);
8186
8187        setCTM(g, g.getCTM().multiply(k));
8188
8189	stateTf = stateTf.multiply(k.inverse());
8190}
8191
8192/**
8193 * Handle mouse move event.
8194 */
8195function handleMouseMove(evt) {
8196	if(evt.preventDefault)
8197		evt.preventDefault();
8198
8199	evt.returnValue = false;
8200
8201	var svgDoc = evt.target.ownerDocument;
8202
8203	var g = svgDoc.getElementById("viewport");
8204
8205	if(state == 'pan') {
8206		// Pan mode
8207		var p = getEventPoint(evt).matrixTransform(stateTf);
8208
8209		setCTM(g, stateTf.inverse().translate(p.x - stateOrigin.x, p.y - stateOrigin.y));
8210	} else if(state == 'move') {
8211		// Move mode
8212		var p = getEventPoint(evt).matrixTransform(g.getCTM().inverse());
8213
8214		setCTM(stateTarget, root.createSVGMatrix().translate(p.x - stateOrigin.x, p.y - stateOrigin.y).multiply(g.getCTM().inverse()).multiply(stateTarget.getCTM()));
8215
8216		stateOrigin = p;
8217	}
8218}
8219
8220/**
8221 * Handle click event.
8222 */
8223function handleMouseDown(evt) {
8224	if(evt.preventDefault)
8225		evt.preventDefault();
8226
8227	evt.returnValue = false;
8228
8229	var svgDoc = evt.target.ownerDocument;
8230
8231	var g = svgDoc.getElementById("viewport");
8232
8233	if(true || evt.target.tagName == "svg") {
8234		// Pan mode
8235		state = 'pan';
8236
8237		stateTf = g.getCTM().inverse();
8238
8239		stateOrigin = getEventPoint(evt).matrixTransform(stateTf);
8240	} else {
8241		// Move mode
8242		state = 'move';
8243
8244		stateTarget = evt.target;
8245
8246		stateTf = g.getCTM().inverse();
8247
8248		stateOrigin = getEventPoint(evt).matrixTransform(stateTf);
8249	}
8250}
8251
8252/**
8253 * Handle mouse button release event.
8254 */
8255function handleMouseUp(evt) {
8256	if(evt.preventDefault)
8257		evt.preventDefault();
8258
8259	evt.returnValue = false;
8260
8261	var svgDoc = evt.target.ownerDocument;
8262
8263	if(state == 'pan' || state == 'move') {
8264		// Quit pan mode
8265		state = '';
8266	}
8267}
8268
8269]]></script>
8270EOF
8271}
8272
8273# Provides a map from fullname to shortname for cases where the
8274# shortname is ambiguous.  The symlist has both the fullname and
8275# shortname for all symbols, which is usually fine, but sometimes --
8276# such as overloaded functions -- two different fullnames can map to
8277# the same shortname.  In that case, we use the address of the
8278# function to disambiguate the two.  This function fills in a map that
8279# maps fullnames to modified shortnames in such cases.  If a fullname
8280# is not present in the map, the 'normal' shortname provided by the
8281# symlist is the appropriate one to use.
8282sub FillFullnameToShortnameMap {
8283  my $symbols = shift;
8284  my $fullname_to_shortname_map = shift;
8285  my $shortnames_seen_once = {};
8286  my $shortnames_seen_more_than_once = {};
8287
8288  foreach my $symlist (values(%{$symbols})) {
8289    # TODO(csilvers): deal with inlined symbols too.
8290    my $shortname = $symlist->[0];
8291    my $fullname = $symlist->[2];
8292    if ($fullname !~ /<[0-9a-fA-F]+>$/) {  # fullname doesn't end in an address
8293      next;       # the only collisions we care about are when addresses differ
8294    }
8295    if (defined($shortnames_seen_once->{$shortname}) &&
8296        $shortnames_seen_once->{$shortname} ne $fullname) {
8297      $shortnames_seen_more_than_once->{$shortname} = 1;
8298    } else {
8299      $shortnames_seen_once->{$shortname} = $fullname;
8300    }
8301  }
8302
8303  foreach my $symlist (values(%{$symbols})) {
8304    my $shortname = $symlist->[0];
8305    my $fullname = $symlist->[2];
8306    # TODO(csilvers): take in a list of addresses we care about, and only
8307    # store in the map if $symlist->[1] is in that list.  Saves space.
8308    next if defined($fullname_to_shortname_map->{$fullname});
8309    if (defined($shortnames_seen_more_than_once->{$shortname})) {
8310      if ($fullname =~ /<0*([^>]*)>$/) {   # fullname has address at end of it
8311        $fullname_to_shortname_map->{$fullname} = "$shortname\@$1";
8312      }
8313    }
8314  }
8315}
8316
8317# Return a small number that identifies the argument.
8318# Multiple calls with the same argument will return the same number.
8319# Calls with different arguments will return different numbers.
8320sub ShortIdFor {
8321  my $key = shift;
8322  my $id = $main::uniqueid{$key};
8323  if (!defined($id)) {
8324    $id = keys(%main::uniqueid) + 1;
8325    $main::uniqueid{$key} = $id;
8326  }
8327  return $id;
8328}
8329
8330# Translate a stack of addresses into a stack of symbols
8331sub TranslateStack {
8332  my $symbols = shift;
8333  my $fullname_to_shortname_map = shift;
8334  my $k = shift;
8335
8336  my @addrs = split(/\n/, $k);
8337  my @result = ();
8338  for (my $i = 0; $i <= $#addrs; $i++) {
8339    my $a = $addrs[$i];
8340
8341    # Skip large addresses since they sometimes show up as fake entries on RH9
8342    if (length($a) > 8 && $a gt "7fffffffffffffff") {
8343      next;
8344    }
8345
8346    if ($main::opt_disasm || $main::opt_list) {
8347      # We want just the address for the key
8348      push(@result, $a);
8349      next;
8350    }
8351
8352    my $symlist = $symbols->{$a};
8353    if (!defined($symlist)) {
8354      $symlist = [$a, "", $a];
8355    }
8356
8357    # We can have a sequence of symbols for a particular entry
8358    # (more than one symbol in the case of inlining).  Callers
8359    # come before callees in symlist, so walk backwards since
8360    # the translated stack should contain callees before callers.
8361    for (my $j = $#{$symlist}; $j >= 2; $j -= 3) {
8362      my $func = $symlist->[$j-2];
8363      my $fileline = $symlist->[$j-1];
8364      my $fullfunc = $symlist->[$j];
8365      if (defined($fullname_to_shortname_map->{$fullfunc})) {
8366        $func = $fullname_to_shortname_map->{$fullfunc};
8367      }
8368      if ($j > 2) {
8369        $func = "$func (inline)";
8370      }
8371
8372      # Do not merge nodes corresponding to Callback::Run since that
8373      # causes confusing cycles in dot display.  Instead, we synthesize
8374      # a unique name for this frame per caller.
8375      if ($func =~ m/Callback.*::Run$/) {
8376        my $caller = ($i > 0) ? $addrs[$i-1] : 0;
8377        $func = "Run#" . ShortIdFor($caller);
8378      }
8379
8380      if ($main::opt_addresses) {
8381        push(@result, "$a $func $fileline");
8382      } elsif ($main::opt_lines) {
8383        if ($func eq '??' && $fileline eq '??:0') {
8384          push(@result, "$a");
8385        } else {
8386          push(@result, "$func $fileline");
8387        }
8388      } elsif ($main::opt_functions) {
8389        if ($func eq '??') {
8390          push(@result, "$a");
8391        } else {
8392          push(@result, $func);
8393        }
8394      } elsif ($main::opt_files) {
8395        if ($fileline eq '??:0' || $fileline eq '') {
8396          push(@result, "$a");
8397        } else {
8398          my $f = $fileline;
8399          $f =~ s/:\d+$//;
8400          push(@result, $f);
8401        }
8402      } else {
8403        push(@result, $a);
8404        last;  # Do not print inlined info
8405      }
8406    }
8407  }
8408
8409  # print join(",", @addrs), " => ", join(",", @result), "\n";
8410  return @result;
8411}
8412
8413# Generate percent string for a number and a total
8414sub Percent {
8415  my $num = shift;
8416  my $tot = shift;
8417  if ($tot != 0) {
8418    return sprintf("%.1f%%", $num * 100.0 / $tot);
8419  } else {
8420    return ($num == 0) ? "nan" : (($num > 0) ? "+inf" : "-inf");
8421  }
8422}
8423
8424# Generate pretty-printed form of number
8425sub Unparse {
8426  my $num = shift;
8427  if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {
8428    if ($main::opt_inuse_objects || $main::opt_alloc_objects) {
8429      return sprintf("%d", $num);
8430    } else {
8431      if ($main::opt_show_bytes) {
8432        return sprintf("%d", $num);
8433      } else {
8434        return sprintf("%.1f", $num / 1048576.0);
8435      }
8436    }
8437  } elsif ($main::profile_type eq 'contention' && !$main::opt_contentions) {
8438    return sprintf("%.3f", $num / 1e9); # Convert nanoseconds to seconds
8439  } else {
8440    return sprintf("%d", $num);
8441  }
8442}
8443
8444# Alternate pretty-printed form: 0 maps to "."
8445sub UnparseAlt {
8446  my $num = shift;
8447  if ($num == 0) {
8448    return ".";
8449  } else {
8450    return Unparse($num);
8451  }
8452}
8453
8454# Alternate pretty-printed form: 0 maps to ""
8455sub HtmlPrintNumber {
8456  my $num = shift;
8457  if ($num == 0) {
8458    return "";
8459  } else {
8460    return Unparse($num);
8461  }
8462}
8463
8464# Return output units
8465sub Units {
8466  if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {
8467    if ($main::opt_inuse_objects || $main::opt_alloc_objects) {
8468      return "objects";
8469    } else {
8470      if ($main::opt_show_bytes) {
8471        return "B";
8472      } else {
8473        return "MB";
8474      }
8475    }
8476  } elsif ($main::profile_type eq 'contention' && !$main::opt_contentions) {
8477    return "seconds";
8478  } else {
8479    return "samples";
8480  }
8481}
8482
8483##### Profile manipulation code #####
8484
8485# Generate flattened profile:
8486# If count is charged to stack [a,b,c,d], in generated profile,
8487# it will be charged to [a]
8488sub FlatProfile {
8489  my $profile = shift;
8490  my $result = {};
8491  foreach my $k (keys(%{$profile})) {
8492    my $count = $profile->{$k};
8493    my @addrs = split(/\n/, $k);
8494    if ($#addrs >= 0) {
8495      AddEntry($result, $addrs[0], $count);
8496    }
8497  }
8498  return $result;
8499}
8500
8501# Generate cumulative profile:
8502# If count is charged to stack [a,b,c,d], in generated profile,
8503# it will be charged to [a], [b], [c], [d]
8504sub CumulativeProfile {
8505  my $profile = shift;
8506  my $result = {};
8507  foreach my $k (keys(%{$profile})) {
8508    my $count = $profile->{$k};
8509    my @addrs = split(/\n/, $k);
8510    foreach my $a (@addrs) {
8511      AddEntry($result, $a, $count);
8512    }
8513  }
8514  return $result;
8515}
8516
8517# If the second-youngest PC on the stack is always the same, returns
8518# that pc.  Otherwise, returns undef.
8519sub IsSecondPcAlwaysTheSame {
8520  my $profile = shift;
8521
8522  my $second_pc = undef;
8523  foreach my $k (keys(%{$profile})) {
8524    my @addrs = split(/\n/, $k);
8525    if ($#addrs < 1) {
8526      return undef;
8527    }
8528    if (not defined $second_pc) {
8529      $second_pc = $addrs[1];
8530    } else {
8531      if ($second_pc ne $addrs[1]) {
8532        return undef;
8533      }
8534    }
8535  }
8536  return $second_pc;
8537}
8538
8539sub ExtractSymbolLocation {
8540  my $symbols = shift;
8541  my $address = shift;
8542  # 'addr2line' outputs "??:0" for unknown locations; we do the
8543  # same to be consistent.
8544  my $location = "??:0:unknown";
8545  if (exists $symbols->{$address}) {
8546    my $file = $symbols->{$address}->[1];
8547    if ($file eq "?") {
8548      $file = "??:0"
8549    }
8550    $location = $file . ":" . $symbols->{$address}->[0];
8551  }
8552  return $location;
8553}
8554
8555# Extracts a graph of calls.
8556sub ExtractCalls {
8557  my $symbols = shift;
8558  my $profile = shift;
8559
8560  my $calls = {};
8561  while( my ($stack_trace, $count) = each %$profile ) {
8562    my @address = split(/\n/, $stack_trace);
8563    my $destination = ExtractSymbolLocation($symbols, $address[0]);
8564    AddEntry($calls, $destination, $count);
8565    for (my $i = 1; $i <= $#address; $i++) {
8566      my $source = ExtractSymbolLocation($symbols, $address[$i]);
8567      my $call = "$source -> $destination";
8568      AddEntry($calls, $call, $count);
8569      $destination = $source;
8570    }
8571  }
8572
8573  return $calls;
8574}
8575
8576sub FilterFrames {
8577  my $symbols = shift;
8578  my $profile = shift;
8579
8580  if ($main::opt_retain eq '' && $main::opt_exclude eq '') {
8581    return $profile;
8582  }
8583
8584  my $result = {};
8585  foreach my $k (keys(%{$profile})) {
8586    my $count = $profile->{$k};
8587    my @addrs = split(/\n/, $k);
8588    my @path = ();
8589    foreach my $a (@addrs) {
8590      my $sym;
8591      if (exists($symbols->{$a})) {
8592        $sym = $symbols->{$a}->[0];
8593      } else {
8594        $sym = $a;
8595      }
8596      if ($main::opt_retain ne '' && $sym !~ m/$main::opt_retain/) {
8597        next;
8598      }
8599      if ($main::opt_exclude ne '' && $sym =~ m/$main::opt_exclude/) {
8600        next;
8601      }
8602      push(@path, $a);
8603    }
8604    if (scalar(@path) > 0) {
8605      my $reduced_path = join("\n", @path);
8606      AddEntry($result, $reduced_path, $count);
8607    }
8608  }
8609
8610  return $result;
8611}
8612
8613sub RemoveUninterestingFrames {
8614  my $symbols = shift;
8615  my $profile = shift;
8616
8617  # List of function names to skip
8618  my %skip = ();
8619  my $skip_regexp = 'NOMATCH';
8620  if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {
8621    foreach my $name ('@JEMALLOC_PREFIX@calloc',
8622                      'cfree',
8623                      '@JEMALLOC_PREFIX@malloc',
8624                      'newImpl',
8625                      'void* newImpl',
8626                      '@JEMALLOC_PREFIX@free',
8627                      '@JEMALLOC_PREFIX@memalign',
8628                      '@JEMALLOC_PREFIX@posix_memalign',
8629                      '@JEMALLOC_PREFIX@aligned_alloc',
8630                      'pvalloc',
8631                      '@JEMALLOC_PREFIX@valloc',
8632                      '@JEMALLOC_PREFIX@realloc',
8633                      '@JEMALLOC_PREFIX@mallocx',
8634                      '@JEMALLOC_PREFIX@rallocx',
8635                      '@JEMALLOC_PREFIX@xallocx',
8636                      '@JEMALLOC_PREFIX@dallocx',
8637                      '@JEMALLOC_PREFIX@sdallocx',
8638                      '@JEMALLOC_PREFIX@sdallocx_noflags',
8639                      'tc_calloc',
8640                      'tc_cfree',
8641                      'tc_malloc',
8642                      'tc_free',
8643                      'tc_memalign',
8644                      'tc_posix_memalign',
8645                      'tc_pvalloc',
8646                      'tc_valloc',
8647                      'tc_realloc',
8648                      'tc_new',
8649                      'tc_delete',
8650                      'tc_newarray',
8651                      'tc_deletearray',
8652                      'tc_new_nothrow',
8653                      'tc_newarray_nothrow',
8654                      'do_malloc',
8655                      '::do_malloc',   # new name -- got moved to an unnamed ns
8656                      '::do_malloc_or_cpp_alloc',
8657                      'DoSampledAllocation',
8658                      'simple_alloc::allocate',
8659                      '__malloc_alloc_template::allocate',
8660                      '__builtin_delete',
8661                      '__builtin_new',
8662                      '__builtin_vec_delete',
8663                      '__builtin_vec_new',
8664                      'operator new',
8665                      'operator new[]',
8666                      # The entry to our memory-allocation routines on OS X
8667                      'malloc_zone_malloc',
8668                      'malloc_zone_calloc',
8669                      'malloc_zone_valloc',
8670                      'malloc_zone_realloc',
8671                      'malloc_zone_memalign',
8672                      'malloc_zone_free',
8673                      # These mark the beginning/end of our custom sections
8674                      '__start_google_malloc',
8675                      '__stop_google_malloc',
8676                      '__start_malloc_hook',
8677                      '__stop_malloc_hook') {
8678      $skip{$name} = 1;
8679      $skip{"_" . $name} = 1;   # Mach (OS X) adds a _ prefix to everything
8680    }
8681    # TODO: Remove TCMalloc once everything has been
8682    # moved into the tcmalloc:: namespace and we have flushed
8683    # old code out of the system.
8684    $skip_regexp = "TCMalloc|^tcmalloc::";
8685  } elsif ($main::profile_type eq 'contention') {
8686    foreach my $vname ('base::RecordLockProfileData',
8687                       'base::SubmitMutexProfileData',
8688                       'base::SubmitSpinLockProfileData',
8689                       'Mutex::Unlock',
8690                       'Mutex::UnlockSlow',
8691                       'Mutex::ReaderUnlock',
8692                       'MutexLock::~MutexLock',
8693                       'SpinLock::Unlock',
8694                       'SpinLock::SlowUnlock',
8695                       'SpinLockHolder::~SpinLockHolder') {
8696      $skip{$vname} = 1;
8697    }
8698  } elsif ($main::profile_type eq 'cpu') {
8699    # Drop signal handlers used for CPU profile collection
8700    # TODO(dpeng): this should not be necessary; it's taken
8701    # care of by the general 2nd-pc mechanism below.
8702    foreach my $name ('ProfileData::Add',           # historical
8703                      'ProfileData::prof_handler',  # historical
8704                      'CpuProfiler::prof_handler',
8705                      '__FRAME_END__',
8706                      '__pthread_sighandler',
8707                      '__restore') {
8708      $skip{$name} = 1;
8709    }
8710  } else {
8711    # Nothing skipped for unknown types
8712  }
8713
8714  if ($main::profile_type eq 'cpu') {
8715    # If all the second-youngest program counters are the same,
8716    # this STRONGLY suggests that it is an artifact of measurement,
8717    # i.e., stack frames pushed by the CPU profiler signal handler.
8718    # Hence, we delete them.
8719    # (The topmost PC is read from the signal structure, not from
8720    # the stack, so it does not get involved.)
8721    while (my $second_pc = IsSecondPcAlwaysTheSame($profile)) {
8722      my $result = {};
8723      my $func = '';
8724      if (exists($symbols->{$second_pc})) {
8725        $second_pc = $symbols->{$second_pc}->[0];
8726      }
8727      print STDERR "Removing $second_pc from all stack traces.\n";
8728      foreach my $k (keys(%{$profile})) {
8729        my $count = $profile->{$k};
8730        my @addrs = split(/\n/, $k);
8731        splice @addrs, 1, 1;
8732        my $reduced_path = join("\n", @addrs);
8733        AddEntry($result, $reduced_path, $count);
8734      }
8735      $profile = $result;
8736    }
8737  }
8738
8739  my $result = {};
8740  foreach my $k (keys(%{$profile})) {
8741    my $count = $profile->{$k};
8742    my @addrs = split(/\n/, $k);
8743    my @path = ();
8744    foreach my $a (@addrs) {
8745      if (exists($symbols->{$a})) {
8746        my $func = $symbols->{$a}->[0];
8747        if ($skip{$func} || ($func =~ m/$skip_regexp/)) {
8748          # Throw away the portion of the backtrace seen so far, under the
8749          # assumption that previous frames were for functions internal to the
8750          # allocator.
8751          @path = ();
8752          next;
8753        }
8754      }
8755      push(@path, $a);
8756    }
8757    my $reduced_path = join("\n", @path);
8758    AddEntry($result, $reduced_path, $count);
8759  }
8760
8761  $result = FilterFrames($symbols, $result);
8762
8763  return $result;
8764}
8765
8766# Reduce profile to granularity given by user
8767sub ReduceProfile {
8768  my $symbols = shift;
8769  my $profile = shift;
8770  my $result = {};
8771  my $fullname_to_shortname_map = {};
8772  FillFullnameToShortnameMap($symbols, $fullname_to_shortname_map);
8773  foreach my $k (keys(%{$profile})) {
8774    my $count = $profile->{$k};
8775    my @translated = TranslateStack($symbols, $fullname_to_shortname_map, $k);
8776    my @path = ();
8777    my %seen = ();
8778    $seen{''} = 1;      # So that empty keys are skipped
8779    foreach my $e (@translated) {
8780      # To avoid double-counting due to recursion, skip a stack-trace
8781      # entry if it has already been seen
8782      if (!$seen{$e}) {
8783        $seen{$e} = 1;
8784        push(@path, $e);
8785      }
8786    }
8787    my $reduced_path = join("\n", @path);
8788    AddEntry($result, $reduced_path, $count);
8789  }
8790  return $result;
8791}
8792
8793# Does the specified symbol array match the regexp?
8794sub SymbolMatches {
8795  my $sym = shift;
8796  my $re = shift;
8797  if (defined($sym)) {
8798    for (my $i = 0; $i < $#{$sym}; $i += 3) {
8799      if ($sym->[$i] =~ m/$re/ || $sym->[$i+1] =~ m/$re/) {
8800        return 1;
8801      }
8802    }
8803  }
8804  return 0;
8805}
8806
8807# Focus only on paths involving specified regexps
8808sub FocusProfile {
8809  my $symbols = shift;
8810  my $profile = shift;
8811  my $focus = shift;
8812  my $result = {};
8813  foreach my $k (keys(%{$profile})) {
8814    my $count = $profile->{$k};
8815    my @addrs = split(/\n/, $k);
8816    foreach my $a (@addrs) {
8817      # Reply if it matches either the address/shortname/fileline
8818      if (($a =~ m/$focus/) || SymbolMatches($symbols->{$a}, $focus)) {
8819        AddEntry($result, $k, $count);
8820        last;
8821      }
8822    }
8823  }
8824  return $result;
8825}
8826
8827# Focus only on paths not involving specified regexps
8828sub IgnoreProfile {
8829  my $symbols = shift;
8830  my $profile = shift;
8831  my $ignore = shift;
8832  my $result = {};
8833  foreach my $k (keys(%{$profile})) {
8834    my $count = $profile->{$k};
8835    my @addrs = split(/\n/, $k);
8836    my $matched = 0;
8837    foreach my $a (@addrs) {
8838      # Reply if it matches either the address/shortname/fileline
8839      if (($a =~ m/$ignore/) || SymbolMatches($symbols->{$a}, $ignore)) {
8840        $matched = 1;
8841        last;
8842      }
8843    }
8844    if (!$matched) {
8845      AddEntry($result, $k, $count);
8846    }
8847  }
8848  return $result;
8849}
8850
8851# Get total count in profile
8852sub TotalProfile {
8853  my $profile = shift;
8854  my $result = 0;
8855  foreach my $k (keys(%{$profile})) {
8856    $result += $profile->{$k};
8857  }
8858  return $result;
8859}
8860
8861# Add A to B
8862sub AddProfile {
8863  my $A = shift;
8864  my $B = shift;
8865
8866  my $R = {};
8867  # add all keys in A
8868  foreach my $k (keys(%{$A})) {
8869    my $v = $A->{$k};
8870    AddEntry($R, $k, $v);
8871  }
8872  # add all keys in B
8873  foreach my $k (keys(%{$B})) {
8874    my $v = $B->{$k};
8875    AddEntry($R, $k, $v);
8876  }
8877  return $R;
8878}
8879
8880# Merges symbol maps
8881sub MergeSymbols {
8882  my $A = shift;
8883  my $B = shift;
8884
8885  my $R = {};
8886  foreach my $k (keys(%{$A})) {
8887    $R->{$k} = $A->{$k};
8888  }
8889  if (defined($B)) {
8890    foreach my $k (keys(%{$B})) {
8891      $R->{$k} = $B->{$k};
8892    }
8893  }
8894  return $R;
8895}
8896
8897
8898# Add A to B
8899sub AddPcs {
8900  my $A = shift;
8901  my $B = shift;
8902
8903  my $R = {};
8904  # add all keys in A
8905  foreach my $k (keys(%{$A})) {
8906    $R->{$k} = 1
8907  }
8908  # add all keys in B
8909  foreach my $k (keys(%{$B})) {
8910    $R->{$k} = 1
8911  }
8912  return $R;
8913}
8914
8915# Subtract B from A
8916sub SubtractProfile {
8917  my $A = shift;
8918  my $B = shift;
8919
8920  my $R = {};
8921  foreach my $k (keys(%{$A})) {
8922    my $v = $A->{$k} - GetEntry($B, $k);
8923    if ($v < 0 && $main::opt_drop_negative) {
8924      $v = 0;
8925    }
8926    AddEntry($R, $k, $v);
8927  }
8928  if (!$main::opt_drop_negative) {
8929    # Take care of when subtracted profile has more entries
8930    foreach my $k (keys(%{$B})) {
8931      if (!exists($A->{$k})) {
8932        AddEntry($R, $k, 0 - $B->{$k});
8933      }
8934    }
8935  }
8936  return $R;
8937}
8938
8939# Get entry from profile; zero if not present
8940sub GetEntry {
8941  my $profile = shift;
8942  my $k = shift;
8943  if (exists($profile->{$k})) {
8944    return $profile->{$k};
8945  } else {
8946    return 0;
8947  }
8948}
8949
8950# Add entry to specified profile
8951sub AddEntry {
8952  my $profile = shift;
8953  my $k = shift;
8954  my $n = shift;
8955  if (!exists($profile->{$k})) {
8956    $profile->{$k} = 0;
8957  }
8958  $profile->{$k} += $n;
8959}
8960
8961# Add a stack of entries to specified profile, and add them to the $pcs
8962# list.
8963sub AddEntries {
8964  my $profile = shift;
8965  my $pcs = shift;
8966  my $stack = shift;
8967  my $count = shift;
8968  my @k = ();
8969
8970  foreach my $e (split(/\s+/, $stack)) {
8971    my $pc = HexExtend($e);
8972    $pcs->{$pc} = 1;
8973    push @k, $pc;
8974  }
8975  AddEntry($profile, (join "\n", @k), $count);
8976}
8977
8978##### Code to profile a server dynamically #####
8979
8980sub CheckSymbolPage {
8981  my $url = SymbolPageURL();
8982  my $command = ShellEscape(@URL_FETCHER, $url);
8983  open(SYMBOL, "$command |") or error($command);
8984  my $line = <SYMBOL>;
8985  $line =~ s/\r//g;         # turn windows-looking lines into unix-looking lines
8986  close(SYMBOL);
8987  unless (defined($line)) {
8988    error("$url doesn't exist\n");
8989  }
8990
8991  if ($line =~ /^num_symbols:\s+(\d+)$/) {
8992    if ($1 == 0) {
8993      error("Stripped binary. No symbols available.\n");
8994    }
8995  } else {
8996    error("Failed to get the number of symbols from $url\n");
8997  }
8998}
8999
9000sub IsProfileURL {
9001  my $profile_name = shift;
9002  if (-f $profile_name) {
9003    printf STDERR "Using local file $profile_name.\n";
9004    return 0;
9005  }
9006  return 1;
9007}
9008
9009sub ParseProfileURL {
9010  my $profile_name = shift;
9011
9012  if (!defined($profile_name) || $profile_name eq "") {
9013    return ();
9014  }
9015
9016  # Split profile URL - matches all non-empty strings, so no test.
9017  $profile_name =~ m,^(https?://)?([^/]+)(.*?)(/|$PROFILES)?$,;
9018
9019  my $proto = $1 || "http://";
9020  my $hostport = $2;
9021  my $prefix = $3;
9022  my $profile = $4 || "/";
9023
9024  my $host = $hostport;
9025  $host =~ s/:.*//;
9026
9027  my $baseurl = "$proto$hostport$prefix";
9028  return ($host, $baseurl, $profile);
9029}
9030
9031# We fetch symbols from the first profile argument.
9032sub SymbolPageURL {
9033  my ($host, $baseURL, $path) = ParseProfileURL($main::pfile_args[0]);
9034  return "$baseURL$SYMBOL_PAGE";
9035}
9036
9037sub FetchProgramName() {
9038  my ($host, $baseURL, $path) = ParseProfileURL($main::pfile_args[0]);
9039  my $url = "$baseURL$PROGRAM_NAME_PAGE";
9040  my $command_line = ShellEscape(@URL_FETCHER, $url);
9041  open(CMDLINE, "$command_line |") or error($command_line);
9042  my $cmdline = <CMDLINE>;
9043  $cmdline =~ s/\r//g;   # turn windows-looking lines into unix-looking lines
9044  close(CMDLINE);
9045  error("Failed to get program name from $url\n") unless defined($cmdline);
9046  $cmdline =~ s/\x00.+//;  # Remove argv[1] and latters.
9047  $cmdline =~ s!\n!!g;  # Remove LFs.
9048  return $cmdline;
9049}
9050
9051# Gee, curl's -L (--location) option isn't reliable at least
9052# with its 7.12.3 version.  Curl will forget to post data if
9053# there is a redirection.  This function is a workaround for
9054# curl.  Redirection happens on borg hosts.
9055sub ResolveRedirectionForCurl {
9056  my $url = shift;
9057  my $command_line = ShellEscape(@URL_FETCHER, "--head", $url);
9058  open(CMDLINE, "$command_line |") or error($command_line);
9059  while (<CMDLINE>) {
9060    s/\r//g;         # turn windows-looking lines into unix-looking lines
9061    if (/^Location: (.*)/) {
9062      $url = $1;
9063    }
9064  }
9065  close(CMDLINE);
9066  return $url;
9067}
9068
9069# Add a timeout flat to URL_FETCHER.  Returns a new list.
9070sub AddFetchTimeout {
9071  my $timeout = shift;
9072  my @fetcher = @_;
9073  if (defined($timeout)) {
9074    if (join(" ", @fetcher) =~ m/\bcurl -s/) {
9075      push(@fetcher, "--max-time", sprintf("%d", $timeout));
9076    } elsif (join(" ", @fetcher) =~ m/\brpcget\b/) {
9077      push(@fetcher, sprintf("--deadline=%d", $timeout));
9078    }
9079  }
9080  return @fetcher;
9081}
9082
9083# Reads a symbol map from the file handle name given as $1, returning
9084# the resulting symbol map.  Also processes variables relating to symbols.
9085# Currently, the only variable processed is 'binary=<value>' which updates
9086# $main::prog to have the correct program name.
9087sub ReadSymbols {
9088  my $in = shift;
9089  my $map = {};
9090  while (<$in>) {
9091    s/\r//g;         # turn windows-looking lines into unix-looking lines
9092    # Removes all the leading zeroes from the symbols, see comment below.
9093    if (m/^0x0*([0-9a-f]+)\s+(.+)/) {
9094      $map->{$1} = $2;
9095    } elsif (m/^---/) {
9096      last;
9097    } elsif (m/^([a-z][^=]*)=(.*)$/ ) {
9098      my ($variable, $value) = ($1, $2);
9099      for ($variable, $value) {
9100        s/^\s+//;
9101        s/\s+$//;
9102      }
9103      if ($variable eq "binary") {
9104        if ($main::prog ne $UNKNOWN_BINARY && $main::prog ne $value) {
9105          printf STDERR ("Warning: Mismatched binary name '%s', using '%s'.\n",
9106                         $main::prog, $value);
9107        }
9108        $main::prog = $value;
9109      } else {
9110        printf STDERR ("Ignoring unknown variable in symbols list: " .
9111            "'%s' = '%s'\n", $variable, $value);
9112      }
9113    }
9114  }
9115  return $map;
9116}
9117
9118sub URLEncode {
9119  my $str = shift;
9120  $str =~ s/([^A-Za-z0-9\-_.!~*'()])/ sprintf "%%%02x", ord $1 /eg;
9121  return $str;
9122}
9123
9124sub AppendSymbolFilterParams {
9125  my $url = shift;
9126  my @params = ();
9127  if ($main::opt_retain ne '') {
9128    push(@params, sprintf("retain=%s", URLEncode($main::opt_retain)));
9129  }
9130  if ($main::opt_exclude ne '') {
9131    push(@params, sprintf("exclude=%s", URLEncode($main::opt_exclude)));
9132  }
9133  if (scalar @params > 0) {
9134    $url = sprintf("%s?%s", $url, join("&", @params));
9135  }
9136  return $url;
9137}
9138
9139# Fetches and processes symbols to prepare them for use in the profile output
9140# code.  If the optional 'symbol_map' arg is not given, fetches symbols from
9141# $SYMBOL_PAGE for all PC values found in profile.  Otherwise, the raw symbols
9142# are assumed to have already been fetched into 'symbol_map' and are simply
9143# extracted and processed.
9144sub FetchSymbols {
9145  my $pcset = shift;
9146  my $symbol_map = shift;
9147
9148  my %seen = ();
9149  my @pcs = grep { !$seen{$_}++ } keys(%$pcset);  # uniq
9150
9151  if (!defined($symbol_map)) {
9152    my $post_data = join("+", sort((map {"0x" . "$_"} @pcs)));
9153
9154    open(POSTFILE, ">$main::tmpfile_sym");
9155    print POSTFILE $post_data;
9156    close(POSTFILE);
9157
9158    my $url = SymbolPageURL();
9159
9160    my $command_line;
9161    if (join(" ", @URL_FETCHER) =~ m/\bcurl -s/) {
9162      $url = ResolveRedirectionForCurl($url);
9163      $url = AppendSymbolFilterParams($url);
9164      $command_line = ShellEscape(@URL_FETCHER, "-d", "\@$main::tmpfile_sym",
9165                                  $url);
9166    } else {
9167      $url = AppendSymbolFilterParams($url);
9168      $command_line = (ShellEscape(@URL_FETCHER, "--post", $url)
9169                       . " < " . ShellEscape($main::tmpfile_sym));
9170    }
9171    # We use c++filt in case $SYMBOL_PAGE gives us mangled symbols.
9172    my $escaped_cppfilt = ShellEscape($obj_tool_map{"c++filt"});
9173    open(SYMBOL, "$command_line | $escaped_cppfilt |") or error($command_line);
9174    $symbol_map = ReadSymbols(*SYMBOL{IO});
9175    close(SYMBOL);
9176  }
9177
9178  my $symbols = {};
9179  foreach my $pc (@pcs) {
9180    my $fullname;
9181    # For 64 bits binaries, symbols are extracted with 8 leading zeroes.
9182    # Then /symbol reads the long symbols in as uint64, and outputs
9183    # the result with a "0x%08llx" format which get rid of the zeroes.
9184    # By removing all the leading zeroes in both $pc and the symbols from
9185    # /symbol, the symbols match and are retrievable from the map.
9186    my $shortpc = $pc;
9187    $shortpc =~ s/^0*//;
9188    # Each line may have a list of names, which includes the function
9189    # and also other functions it has inlined.  They are separated (in
9190    # PrintSymbolizedProfile), by --, which is illegal in function names.
9191    my $fullnames;
9192    if (defined($symbol_map->{$shortpc})) {
9193      $fullnames = $symbol_map->{$shortpc};
9194    } else {
9195      $fullnames = "0x" . $pc;  # Just use addresses
9196    }
9197    my $sym = [];
9198    $symbols->{$pc} = $sym;
9199    foreach my $fullname (split("--", $fullnames)) {
9200      my $name = ShortFunctionName($fullname);
9201      push(@{$sym}, $name, "?", $fullname);
9202    }
9203  }
9204  return $symbols;
9205}
9206
9207sub BaseName {
9208  my $file_name = shift;
9209  $file_name =~ s!^.*/!!;  # Remove directory name
9210  return $file_name;
9211}
9212
9213sub MakeProfileBaseName {
9214  my ($binary_name, $profile_name) = @_;
9215  my ($host, $baseURL, $path) = ParseProfileURL($profile_name);
9216  my $binary_shortname = BaseName($binary_name);
9217  return sprintf("%s.%s.%s",
9218                 $binary_shortname, $main::op_time, $host);
9219}
9220
9221sub FetchDynamicProfile {
9222  my $binary_name = shift;
9223  my $profile_name = shift;
9224  my $fetch_name_only = shift;
9225  my $encourage_patience = shift;
9226
9227  if (!IsProfileURL($profile_name)) {
9228    return $profile_name;
9229  } else {
9230    my ($host, $baseURL, $path) = ParseProfileURL($profile_name);
9231    if ($path eq "" || $path eq "/") {
9232      # Missing type specifier defaults to cpu-profile
9233      $path = $PROFILE_PAGE;
9234    }
9235
9236    my $profile_file = MakeProfileBaseName($binary_name, $profile_name);
9237
9238    my $url = "$baseURL$path";
9239    my $fetch_timeout = undef;
9240    if ($path =~ m/$PROFILE_PAGE|$PMUPROFILE_PAGE/) {
9241      if ($path =~ m/[?]/) {
9242        $url .= "&";
9243      } else {
9244        $url .= "?";
9245      }
9246      $url .= sprintf("seconds=%d", $main::opt_seconds);
9247      $fetch_timeout = $main::opt_seconds * 1.01 + 60;
9248      # Set $profile_type for consumption by PrintSymbolizedProfile.
9249      $main::profile_type = 'cpu';
9250    } else {
9251      # For non-CPU profiles, we add a type-extension to
9252      # the target profile file name.
9253      my $suffix = $path;
9254      $suffix =~ s,/,.,g;
9255      $profile_file .= $suffix;
9256      # Set $profile_type for consumption by PrintSymbolizedProfile.
9257      if ($path =~ m/$HEAP_PAGE/) {
9258        $main::profile_type = 'heap';
9259      } elsif ($path =~ m/$GROWTH_PAGE/) {
9260        $main::profile_type = 'growth';
9261      } elsif ($path =~ m/$CONTENTION_PAGE/) {
9262        $main::profile_type = 'contention';
9263      }
9264    }
9265
9266    my $profile_dir = $ENV{"JEPROF_TMPDIR"} || ($ENV{HOME} . "/jeprof");
9267    if (! -d $profile_dir) {
9268      mkdir($profile_dir)
9269          || die("Unable to create profile directory $profile_dir: $!\n");
9270    }
9271    my $tmp_profile = "$profile_dir/.tmp.$profile_file";
9272    my $real_profile = "$profile_dir/$profile_file";
9273
9274    if ($fetch_name_only > 0) {
9275      return $real_profile;
9276    }
9277
9278    my @fetcher = AddFetchTimeout($fetch_timeout, @URL_FETCHER);
9279    my $cmd = ShellEscape(@fetcher, $url) . " > " . ShellEscape($tmp_profile);
9280    if ($path =~ m/$PROFILE_PAGE|$PMUPROFILE_PAGE|$CENSUSPROFILE_PAGE/){
9281      print STDERR "Gathering CPU profile from $url for $main::opt_seconds seconds to\n  ${real_profile}\n";
9282      if ($encourage_patience) {
9283        print STDERR "Be patient...\n";
9284      }
9285    } else {
9286      print STDERR "Fetching $path profile from $url to\n  ${real_profile}\n";
9287    }
9288
9289    (system($cmd) == 0) || error("Failed to get profile: $cmd: $!\n");
9290    (system("mv", $tmp_profile, $real_profile) == 0) || error("Unable to rename profile\n");
9291    print STDERR "Wrote profile to $real_profile\n";
9292    $main::collected_profile = $real_profile;
9293    return $main::collected_profile;
9294  }
9295}
9296
9297# Collect profiles in parallel
9298sub FetchDynamicProfiles {
9299  my $items = scalar(@main::pfile_args);
9300  my $levels = log($items) / log(2);
9301
9302  if ($items == 1) {
9303    $main::profile_files[0] = FetchDynamicProfile($main::prog, $main::pfile_args[0], 0, 1);
9304  } else {
9305    # math rounding issues
9306    if ((2 ** $levels) < $items) {
9307     $levels++;
9308    }
9309    my $count = scalar(@main::pfile_args);
9310    for (my $i = 0; $i < $count; $i++) {
9311      $main::profile_files[$i] = FetchDynamicProfile($main::prog, $main::pfile_args[$i], 1, 0);
9312    }
9313    print STDERR "Fetching $count profiles, Be patient...\n";
9314    FetchDynamicProfilesRecurse($levels, 0, 0);
9315    $main::collected_profile = join(" \\\n    ", @main::profile_files);
9316  }
9317}
9318
9319# Recursively fork a process to get enough processes
9320# collecting profiles
9321sub FetchDynamicProfilesRecurse {
9322  my $maxlevel = shift;
9323  my $level = shift;
9324  my $position = shift;
9325
9326  if (my $pid = fork()) {
9327    $position = 0 | ($position << 1);
9328    TryCollectProfile($maxlevel, $level, $position);
9329    wait;
9330  } else {
9331    $position = 1 | ($position << 1);
9332    TryCollectProfile($maxlevel, $level, $position);
9333    cleanup();
9334    exit(0);
9335  }
9336}
9337
9338# Collect a single profile
9339sub TryCollectProfile {
9340  my $maxlevel = shift;
9341  my $level = shift;
9342  my $position = shift;
9343
9344  if ($level >= ($maxlevel - 1)) {
9345    if ($position < scalar(@main::pfile_args)) {
9346      FetchDynamicProfile($main::prog, $main::pfile_args[$position], 0, 0);
9347    }
9348  } else {
9349    FetchDynamicProfilesRecurse($maxlevel, $level+1, $position);
9350  }
9351}
9352
9353##### Parsing code #####
9354
9355# Provide a small streaming-read module to handle very large
9356# cpu-profile files.  Stream in chunks along a sliding window.
9357# Provides an interface to get one 'slot', correctly handling
9358# endian-ness differences.  A slot is one 32-bit or 64-bit word
9359# (depending on the input profile).  We tell endianness and bit-size
9360# for the profile by looking at the first 8 bytes: in cpu profiles,
9361# the second slot is always 3 (we'll accept anything that's not 0).
9362BEGIN {
9363  package CpuProfileStream;
9364
9365  sub new {
9366    my ($class, $file, $fname) = @_;
9367    my $self = { file        => $file,
9368                 base        => 0,
9369                 stride      => 512 * 1024,   # must be a multiple of bitsize/8
9370                 slots       => [],
9371                 unpack_code => "",           # N for big-endian, V for little
9372                 perl_is_64bit => 1,          # matters if profile is 64-bit
9373    };
9374    bless $self, $class;
9375    # Let unittests adjust the stride
9376    if ($main::opt_test_stride > 0) {
9377      $self->{stride} = $main::opt_test_stride;
9378    }
9379    # Read the first two slots to figure out bitsize and endianness.
9380    my $slots = $self->{slots};
9381    my $str;
9382    read($self->{file}, $str, 8);
9383    # Set the global $address_length based on what we see here.
9384    # 8 is 32-bit (8 hexadecimal chars); 16 is 64-bit (16 hexadecimal chars).
9385    $address_length = ($str eq (chr(0)x8)) ? 16 : 8;
9386    if ($address_length == 8) {
9387      if (substr($str, 6, 2) eq chr(0)x2) {
9388        $self->{unpack_code} = 'V';  # Little-endian.
9389      } elsif (substr($str, 4, 2) eq chr(0)x2) {
9390        $self->{unpack_code} = 'N';  # Big-endian
9391      } else {
9392        ::error("$fname: header size >= 2**16\n");
9393      }
9394      @$slots = unpack($self->{unpack_code} . "*", $str);
9395    } else {
9396      # If we're a 64-bit profile, check if we're a 64-bit-capable
9397      # perl.  Otherwise, each slot will be represented as a float
9398      # instead of an int64, losing precision and making all the
9399      # 64-bit addresses wrong.  We won't complain yet, but will
9400      # later if we ever see a value that doesn't fit in 32 bits.
9401      my $has_q = 0;
9402      eval { $has_q = pack("Q", "1") ? 1 : 1; };
9403      if (!$has_q) {
9404        $self->{perl_is_64bit} = 0;
9405      }
9406      read($self->{file}, $str, 8);
9407      if (substr($str, 4, 4) eq chr(0)x4) {
9408        # We'd love to use 'Q', but it's a) not universal, b) not endian-proof.
9409        $self->{unpack_code} = 'V';  # Little-endian.
9410      } elsif (substr($str, 0, 4) eq chr(0)x4) {
9411        $self->{unpack_code} = 'N';  # Big-endian
9412      } else {
9413        ::error("$fname: header size >= 2**32\n");
9414      }
9415      my @pair = unpack($self->{unpack_code} . "*", $str);
9416      # Since we know one of the pair is 0, it's fine to just add them.
9417      @$slots = (0, $pair[0] + $pair[1]);
9418    }
9419    return $self;
9420  }
9421
9422  # Load more data when we access slots->get(X) which is not yet in memory.
9423  sub overflow {
9424    my ($self) = @_;
9425    my $slots = $self->{slots};
9426    $self->{base} += $#$slots + 1;   # skip over data we're replacing
9427    my $str;
9428    read($self->{file}, $str, $self->{stride});
9429    if ($address_length == 8) {      # the 32-bit case
9430      # This is the easy case: unpack provides 32-bit unpacking primitives.
9431      @$slots = unpack($self->{unpack_code} . "*", $str);
9432    } else {
9433      # We need to unpack 32 bits at a time and combine.
9434      my @b32_values = unpack($self->{unpack_code} . "*", $str);
9435      my @b64_values = ();
9436      for (my $i = 0; $i < $#b32_values; $i += 2) {
9437        # TODO(csilvers): if this is a 32-bit perl, the math below
9438        #    could end up in a too-large int, which perl will promote
9439        #    to a double, losing necessary precision.  Deal with that.
9440        #    Right now, we just die.
9441        my ($lo, $hi) = ($b32_values[$i], $b32_values[$i+1]);
9442        if ($self->{unpack_code} eq 'N') {    # big-endian
9443          ($lo, $hi) = ($hi, $lo);
9444        }
9445        my $value = $lo + $hi * (2**32);
9446        if (!$self->{perl_is_64bit} &&   # check value is exactly represented
9447            (($value % (2**32)) != $lo || int($value / (2**32)) != $hi)) {
9448          ::error("Need a 64-bit perl to process this 64-bit profile.\n");
9449        }
9450        push(@b64_values, $value);
9451      }
9452      @$slots = @b64_values;
9453    }
9454  }
9455
9456  # Access the i-th long in the file (logically), or -1 at EOF.
9457  sub get {
9458    my ($self, $idx) = @_;
9459    my $slots = $self->{slots};
9460    while ($#$slots >= 0) {
9461      if ($idx < $self->{base}) {
9462        # The only time we expect a reference to $slots[$i - something]
9463        # after referencing $slots[$i] is reading the very first header.
9464        # Since $stride > |header|, that shouldn't cause any lookback
9465        # errors.  And everything after the header is sequential.
9466        print STDERR "Unexpected look-back reading CPU profile";
9467        return -1;   # shrug, don't know what better to return
9468      } elsif ($idx > $self->{base} + $#$slots) {
9469        $self->overflow();
9470      } else {
9471        return $slots->[$idx - $self->{base}];
9472      }
9473    }
9474    # If we get here, $slots is [], which means we've reached EOF
9475    return -1;  # unique since slots is supposed to hold unsigned numbers
9476  }
9477}
9478
9479# Reads the top, 'header' section of a profile, and returns the last
9480# line of the header, commonly called a 'header line'.  The header
9481# section of a profile consists of zero or more 'command' lines that
9482# are instructions to jeprof, which jeprof executes when reading the
9483# header.  All 'command' lines start with a %.  After the command
9484# lines is the 'header line', which is a profile-specific line that
9485# indicates what type of profile it is, and perhaps other global
9486# information about the profile.  For instance, here's a header line
9487# for a heap profile:
9488#   heap profile:     53:    38236 [  5525:  1284029] @ heapprofile
9489# For historical reasons, the CPU profile does not contain a text-
9490# readable header line.  If the profile looks like a CPU profile,
9491# this function returns "".  If no header line could be found, this
9492# function returns undef.
9493#
9494# The following commands are recognized:
9495#   %warn -- emit the rest of this line to stderr, prefixed by 'WARNING:'
9496#
9497# The input file should be in binmode.
9498sub ReadProfileHeader {
9499  local *PROFILE = shift;
9500  my $firstchar = "";
9501  my $line = "";
9502  read(PROFILE, $firstchar, 1);
9503  seek(PROFILE, -1, 1);                    # unread the firstchar
9504  if ($firstchar !~ /[[:print:]]/) {       # is not a text character
9505    return "";
9506  }
9507  while (defined($line = <PROFILE>)) {
9508    $line =~ s/\r//g;   # turn windows-looking lines into unix-looking lines
9509    if ($line =~ /^%warn\s+(.*)/) {        # 'warn' command
9510      # Note this matches both '%warn blah\n' and '%warn\n'.
9511      print STDERR "WARNING: $1\n";        # print the rest of the line
9512    } elsif ($line =~ /^%/) {
9513      print STDERR "Ignoring unknown command from profile header: $line";
9514    } else {
9515      # End of commands, must be the header line.
9516      return $line;
9517    }
9518  }
9519  return undef;     # got to EOF without seeing a header line
9520}
9521
9522sub IsSymbolizedProfileFile {
9523  my $file_name = shift;
9524  if (!(-e $file_name) || !(-r $file_name)) {
9525    return 0;
9526  }
9527  # Check if the file contains a symbol-section marker.
9528  open(TFILE, "<$file_name");
9529  binmode TFILE;
9530  my $firstline = ReadProfileHeader(*TFILE);
9531  close(TFILE);
9532  if (!$firstline) {
9533    return 0;
9534  }
9535  $SYMBOL_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
9536  my $symbol_marker = $&;
9537  return $firstline =~ /^--- *$symbol_marker/;
9538}
9539
9540# Parse profile generated by common/profiler.cc and return a reference
9541# to a map:
9542#      $result->{version}     Version number of profile file
9543#      $result->{period}      Sampling period (in microseconds)
9544#      $result->{profile}     Profile object
9545#      $result->{threads}     Map of thread IDs to profile objects
9546#      $result->{map}         Memory map info from profile
9547#      $result->{pcs}         Hash of all PC values seen, key is hex address
9548sub ReadProfile {
9549  my $prog = shift;
9550  my $fname = shift;
9551  my $result;            # return value
9552
9553  $CONTENTION_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
9554  my $contention_marker = $&;
9555  $GROWTH_PAGE  =~ m,[^/]+$,;    # matches everything after the last slash
9556  my $growth_marker = $&;
9557  $SYMBOL_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
9558  my $symbol_marker = $&;
9559  $PROFILE_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
9560  my $profile_marker = $&;
9561  $HEAP_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
9562  my $heap_marker = $&;
9563
9564  # Look at first line to see if it is a heap or a CPU profile.
9565  # CPU profile may start with no header at all, and just binary data
9566  # (starting with \0\0\0\0) -- in that case, don't try to read the
9567  # whole firstline, since it may be gigabytes(!) of data.
9568  open(PROFILE, "<$fname") || error("$fname: $!\n");
9569  binmode PROFILE;      # New perls do UTF-8 processing
9570  my $header = ReadProfileHeader(*PROFILE);
9571  if (!defined($header)) {   # means "at EOF"
9572    error("Profile is empty.\n");
9573  }
9574
9575  my $symbols;
9576  if ($header =~ m/^--- *$symbol_marker/o) {
9577    # Verify that the user asked for a symbolized profile
9578    if (!$main::use_symbolized_profile) {
9579      # we have both a binary and symbolized profiles, abort
9580      error("FATAL ERROR: Symbolized profile\n   $fname\ncannot be used with " .
9581            "a binary arg. Try again without passing\n   $prog\n");
9582    }
9583    # Read the symbol section of the symbolized profile file.
9584    $symbols = ReadSymbols(*PROFILE{IO});
9585    # Read the next line to get the header for the remaining profile.
9586    $header = ReadProfileHeader(*PROFILE) || "";
9587  }
9588
9589  if ($header =~ m/^--- *($heap_marker|$growth_marker)/o) {
9590    # Skip "--- ..." line for profile types that have their own headers.
9591    $header = ReadProfileHeader(*PROFILE) || "";
9592  }
9593
9594  $main::profile_type = '';
9595
9596  if ($header =~ m/^heap profile:.*$growth_marker/o) {
9597    $main::profile_type = 'growth';
9598    $result =  ReadHeapProfile($prog, *PROFILE, $header);
9599  } elsif ($header =~ m/^heap profile:/) {
9600    $main::profile_type = 'heap';
9601    $result =  ReadHeapProfile($prog, *PROFILE, $header);
9602  } elsif ($header =~ m/^heap/) {
9603    $main::profile_type = 'heap';
9604    $result = ReadThreadedHeapProfile($prog, $fname, $header);
9605  } elsif ($header =~ m/^--- *$contention_marker/o) {
9606    $main::profile_type = 'contention';
9607    $result = ReadSynchProfile($prog, *PROFILE);
9608  } elsif ($header =~ m/^--- *Stacks:/) {
9609    print STDERR
9610      "Old format contention profile: mistakenly reports " .
9611      "condition variable signals as lock contentions.\n";
9612    $main::profile_type = 'contention';
9613    $result = ReadSynchProfile($prog, *PROFILE);
9614  } elsif ($header =~ m/^--- *$profile_marker/) {
9615    # the binary cpu profile data starts immediately after this line
9616    $main::profile_type = 'cpu';
9617    $result = ReadCPUProfile($prog, $fname, *PROFILE);
9618  } else {
9619    if (defined($symbols)) {
9620      # a symbolized profile contains a format we don't recognize, bail out
9621      error("$fname: Cannot recognize profile section after symbols.\n");
9622    }
9623    # no ascii header present -- must be a CPU profile
9624    $main::profile_type = 'cpu';
9625    $result = ReadCPUProfile($prog, $fname, *PROFILE);
9626  }
9627
9628  close(PROFILE);
9629
9630  # if we got symbols along with the profile, return those as well
9631  if (defined($symbols)) {
9632    $result->{symbols} = $symbols;
9633  }
9634
9635  return $result;
9636}
9637
9638# Subtract one from caller pc so we map back to call instr.
9639# However, don't do this if we're reading a symbolized profile
9640# file, in which case the subtract-one was done when the file
9641# was written.
9642#
9643# We apply the same logic to all readers, though ReadCPUProfile uses an
9644# independent implementation.
9645sub FixCallerAddresses {
9646  my $stack = shift;
9647  # --raw/http: Always subtract one from pc's, because PrintSymbolizedProfile()
9648  # dumps unadjusted profiles.
9649  {
9650    $stack =~ /(\s)/;
9651    my $delimiter = $1;
9652    my @addrs = split(' ', $stack);
9653    my @fixedaddrs;
9654    $#fixedaddrs = $#addrs;
9655    if ($#addrs >= 0) {
9656      $fixedaddrs[0] = $addrs[0];
9657    }
9658    for (my $i = 1; $i <= $#addrs; $i++) {
9659      $fixedaddrs[$i] = AddressSub($addrs[$i], "0x1");
9660    }
9661    return join $delimiter, @fixedaddrs;
9662  }
9663}
9664
9665# CPU profile reader
9666sub ReadCPUProfile {
9667  my $prog = shift;
9668  my $fname = shift;       # just used for logging
9669  local *PROFILE = shift;
9670  my $version;
9671  my $period;
9672  my $i;
9673  my $profile = {};
9674  my $pcs = {};
9675
9676  # Parse string into array of slots.
9677  my $slots = CpuProfileStream->new(*PROFILE, $fname);
9678
9679  # Read header.  The current header version is a 5-element structure
9680  # containing:
9681  #   0: header count (always 0)
9682  #   1: header "words" (after this one: 3)
9683  #   2: format version (0)
9684  #   3: sampling period (usec)
9685  #   4: unused padding (always 0)
9686  if ($slots->get(0) != 0 ) {
9687    error("$fname: not a profile file, or old format profile file\n");
9688  }
9689  $i = 2 + $slots->get(1);
9690  $version = $slots->get(2);
9691  $period = $slots->get(3);
9692  # Do some sanity checking on these header values.
9693  if ($version > (2**32) || $period > (2**32) || $i > (2**32) || $i < 5) {
9694    error("$fname: not a profile file, or corrupted profile file\n");
9695  }
9696
9697  # Parse profile
9698  while ($slots->get($i) != -1) {
9699    my $n = $slots->get($i++);
9700    my $d = $slots->get($i++);
9701    if ($d > (2**16)) {  # TODO(csilvers): what's a reasonable max-stack-depth?
9702      my $addr = sprintf("0%o", $i * ($address_length == 8 ? 4 : 8));
9703      print STDERR "At index $i (address $addr):\n";
9704      error("$fname: stack trace depth >= 2**32\n");
9705    }
9706    if ($slots->get($i) == 0) {
9707      # End of profile data marker
9708      $i += $d;
9709      last;
9710    }
9711
9712    # Make key out of the stack entries
9713    my @k = ();
9714    for (my $j = 0; $j < $d; $j++) {
9715      my $pc = $slots->get($i+$j);
9716      # Subtract one from caller pc so we map back to call instr.
9717      $pc--;
9718      $pc = sprintf("%0*x", $address_length, $pc);
9719      $pcs->{$pc} = 1;
9720      push @k, $pc;
9721    }
9722
9723    AddEntry($profile, (join "\n", @k), $n);
9724    $i += $d;
9725  }
9726
9727  # Parse map
9728  my $map = '';
9729  seek(PROFILE, $i * 4, 0);
9730  read(PROFILE, $map, (stat PROFILE)[7]);
9731
9732  my $r = {};
9733  $r->{version} = $version;
9734  $r->{period} = $period;
9735  $r->{profile} = $profile;
9736  $r->{libs} = ParseLibraries($prog, $map, $pcs);
9737  $r->{pcs} = $pcs;
9738
9739  return $r;
9740}
9741
9742sub HeapProfileIndex {
9743  my $index = 1;
9744  if ($main::opt_inuse_space) {
9745    $index = 1;
9746  } elsif ($main::opt_inuse_objects) {
9747    $index = 0;
9748  } elsif ($main::opt_alloc_space) {
9749    $index = 3;
9750  } elsif ($main::opt_alloc_objects) {
9751    $index = 2;
9752  }
9753  return $index;
9754}
9755
9756sub ReadMappedLibraries {
9757  my $fh = shift;
9758  my $map = "";
9759  # Read the /proc/self/maps data
9760  while (<$fh>) {
9761    s/\r//g;         # turn windows-looking lines into unix-looking lines
9762    $map .= $_;
9763  }
9764  return $map;
9765}
9766
9767sub ReadMemoryMap {
9768  my $fh = shift;
9769  my $map = "";
9770  # Read /proc/self/maps data as formatted by DumpAddressMap()
9771  my $buildvar = "";
9772  while (<PROFILE>) {
9773    s/\r//g;         # turn windows-looking lines into unix-looking lines
9774    # Parse "build=<dir>" specification if supplied
9775    if (m/^\s*build=(.*)\n/) {
9776      $buildvar = $1;
9777    }
9778
9779    # Expand "$build" variable if available
9780    $_ =~ s/\$build\b/$buildvar/g;
9781
9782    $map .= $_;
9783  }
9784  return $map;
9785}
9786
9787sub AdjustSamples {
9788  my ($sample_adjustment, $sampling_algorithm, $n1, $s1, $n2, $s2) = @_;
9789  if ($sample_adjustment) {
9790    if ($sampling_algorithm == 2) {
9791      # Remote-heap version 2
9792      # The sampling frequency is the rate of a Poisson process.
9793      # This means that the probability of sampling an allocation of
9794      # size X with sampling rate Y is 1 - exp(-X/Y)
9795      if ($n1 != 0) {
9796        my $ratio = (($s1*1.0)/$n1)/($sample_adjustment);
9797        my $scale_factor = 1/(1 - exp(-$ratio));
9798        $n1 *= $scale_factor;
9799        $s1 *= $scale_factor;
9800      }
9801      if ($n2 != 0) {
9802        my $ratio = (($s2*1.0)/$n2)/($sample_adjustment);
9803        my $scale_factor = 1/(1 - exp(-$ratio));
9804        $n2 *= $scale_factor;
9805        $s2 *= $scale_factor;
9806      }
9807    } else {
9808      # Remote-heap version 1
9809      my $ratio;
9810      $ratio = (($s1*1.0)/$n1)/($sample_adjustment);
9811      if ($ratio < 1) {
9812        $n1 /= $ratio;
9813        $s1 /= $ratio;
9814      }
9815      $ratio = (($s2*1.0)/$n2)/($sample_adjustment);
9816      if ($ratio < 1) {
9817        $n2 /= $ratio;
9818        $s2 /= $ratio;
9819      }
9820    }
9821  }
9822  return ($n1, $s1, $n2, $s2);
9823}
9824
9825sub ReadHeapProfile {
9826  my $prog = shift;
9827  local *PROFILE = shift;
9828  my $header = shift;
9829
9830  my $index = HeapProfileIndex();
9831
9832  # Find the type of this profile.  The header line looks like:
9833  #    heap profile:   1246:  8800744 [  1246:  8800744] @ <heap-url>/266053
9834  # There are two pairs <count: size>, the first inuse objects/space, and the
9835  # second allocated objects/space.  This is followed optionally by a profile
9836  # type, and if that is present, optionally by a sampling frequency.
9837  # For remote heap profiles (v1):
9838  # The interpretation of the sampling frequency is that the profiler, for
9839  # each sample, calculates a uniformly distributed random integer less than
9840  # the given value, and records the next sample after that many bytes have
9841  # been allocated.  Therefore, the expected sample interval is half of the
9842  # given frequency.  By default, if not specified, the expected sample
9843  # interval is 128KB.  Only remote-heap-page profiles are adjusted for
9844  # sample size.
9845  # For remote heap profiles (v2):
9846  # The sampling frequency is the rate of a Poisson process. This means that
9847  # the probability of sampling an allocation of size X with sampling rate Y
9848  # is 1 - exp(-X/Y)
9849  # For version 2, a typical header line might look like this:
9850  # heap profile:   1922: 127792360 [  1922: 127792360] @ <heap-url>_v2/524288
9851  # the trailing number (524288) is the sampling rate. (Version 1 showed
9852  # double the 'rate' here)
9853  my $sampling_algorithm = 0;
9854  my $sample_adjustment = 0;
9855  chomp($header);
9856  my $type = "unknown";
9857  if ($header =~ m"^heap profile:\s*(\d+):\s+(\d+)\s+\[\s*(\d+):\s+(\d+)\](\s*@\s*([^/]*)(/(\d+))?)?") {
9858    if (defined($6) && ($6 ne '')) {
9859      $type = $6;
9860      my $sample_period = $8;
9861      # $type is "heapprofile" for profiles generated by the
9862      # heap-profiler, and either "heap" or "heap_v2" for profiles
9863      # generated by sampling directly within tcmalloc.  It can also
9864      # be "growth" for heap-growth profiles.  The first is typically
9865      # found for profiles generated locally, and the others for
9866      # remote profiles.
9867      if (($type eq "heapprofile") || ($type !~ /heap/) ) {
9868        # No need to adjust for the sampling rate with heap-profiler-derived data
9869        $sampling_algorithm = 0;
9870      } elsif ($type =~ /_v2/) {
9871        $sampling_algorithm = 2;     # version 2 sampling
9872        if (defined($sample_period) && ($sample_period ne '')) {
9873          $sample_adjustment = int($sample_period);
9874        }
9875      } else {
9876        $sampling_algorithm = 1;     # version 1 sampling
9877        if (defined($sample_period) && ($sample_period ne '')) {
9878          $sample_adjustment = int($sample_period)/2;
9879        }
9880      }
9881    } else {
9882      # We detect whether or not this is a remote-heap profile by checking
9883      # that the total-allocated stats ($n2,$s2) are exactly the
9884      # same as the in-use stats ($n1,$s1).  It is remotely conceivable
9885      # that a non-remote-heap profile may pass this check, but it is hard
9886      # to imagine how that could happen.
9887      # In this case it's so old it's guaranteed to be remote-heap version 1.
9888      my ($n1, $s1, $n2, $s2) = ($1, $2, $3, $4);
9889      if (($n1 == $n2) && ($s1 == $s2)) {
9890        # This is likely to be a remote-heap based sample profile
9891        $sampling_algorithm = 1;
9892      }
9893    }
9894  }
9895
9896  if ($sampling_algorithm > 0) {
9897    # For remote-heap generated profiles, adjust the counts and sizes to
9898    # account for the sample rate (we sample once every 128KB by default).
9899    if ($sample_adjustment == 0) {
9900      # Turn on profile adjustment.
9901      $sample_adjustment = 128*1024;
9902      print STDERR "Adjusting heap profiles for 1-in-128KB sampling rate\n";
9903    } else {
9904      printf STDERR ("Adjusting heap profiles for 1-in-%d sampling rate\n",
9905                     $sample_adjustment);
9906    }
9907    if ($sampling_algorithm > 1) {
9908      # We don't bother printing anything for the original version (version 1)
9909      printf STDERR "Heap version $sampling_algorithm\n";
9910    }
9911  }
9912
9913  my $profile = {};
9914  my $pcs = {};
9915  my $map = "";
9916
9917  while (<PROFILE>) {
9918    s/\r//g;         # turn windows-looking lines into unix-looking lines
9919    if (/^MAPPED_LIBRARIES:/) {
9920      $map .= ReadMappedLibraries(*PROFILE);
9921      last;
9922    }
9923
9924    if (/^--- Memory map:/) {
9925      $map .= ReadMemoryMap(*PROFILE);
9926      last;
9927    }
9928
9929    # Read entry of the form:
9930    #  <count1>: <bytes1> [<count2>: <bytes2>] @ a1 a2 a3 ... an
9931    s/^\s*//;
9932    s/\s*$//;
9933    if (m/^\s*(\d+):\s+(\d+)\s+\[\s*(\d+):\s+(\d+)\]\s+@\s+(.*)$/) {
9934      my $stack = $5;
9935      my ($n1, $s1, $n2, $s2) = ($1, $2, $3, $4);
9936      my @counts = AdjustSamples($sample_adjustment, $sampling_algorithm,
9937                                 $n1, $s1, $n2, $s2);
9938      AddEntries($profile, $pcs, FixCallerAddresses($stack), $counts[$index]);
9939    }
9940  }
9941
9942  my $r = {};
9943  $r->{version} = "heap";
9944  $r->{period} = 1;
9945  $r->{profile} = $profile;
9946  $r->{libs} = ParseLibraries($prog, $map, $pcs);
9947  $r->{pcs} = $pcs;
9948  return $r;
9949}
9950
9951sub ReadThreadedHeapProfile {
9952  my ($prog, $fname, $header) = @_;
9953
9954  my $index = HeapProfileIndex();
9955  my $sampling_algorithm = 0;
9956  my $sample_adjustment = 0;
9957  chomp($header);
9958  my $type = "unknown";
9959  # Assuming a very specific type of header for now.
9960  if ($header =~ m"^heap_v2/(\d+)") {
9961    $type = "_v2";
9962    $sampling_algorithm = 2;
9963    $sample_adjustment = int($1);
9964  }
9965  if ($type ne "_v2" || !defined($sample_adjustment)) {
9966    die "Threaded heap profiles require v2 sampling with a sample rate\n";
9967  }
9968
9969  my $profile = {};
9970  my $thread_profiles = {};
9971  my $pcs = {};
9972  my $map = "";
9973  my $stack = "";
9974
9975  while (<PROFILE>) {
9976    s/\r//g;
9977    if (/^MAPPED_LIBRARIES:/) {
9978      $map .= ReadMappedLibraries(*PROFILE);
9979      last;
9980    }
9981
9982    if (/^--- Memory map:/) {
9983      $map .= ReadMemoryMap(*PROFILE);
9984      last;
9985    }
9986
9987    # Read entry of the form:
9988    # @ a1 a2 ... an
9989    #   t*: <count1>: <bytes1> [<count2>: <bytes2>]
9990    #   t1: <count1>: <bytes1> [<count2>: <bytes2>]
9991    #     ...
9992    #   tn: <count1>: <bytes1> [<count2>: <bytes2>]
9993    s/^\s*//;
9994    s/\s*$//;
9995    if (m/^@\s+(.*)$/) {
9996      $stack = $1;
9997    } elsif (m/^\s*(t(\*|\d+)):\s+(\d+):\s+(\d+)\s+\[\s*(\d+):\s+(\d+)\]$/) {
9998      if ($stack eq "") {
9999        # Still in the header, so this is just a per-thread summary.
10000        next;
10001      }
10002      my $thread = $2;
10003      my ($n1, $s1, $n2, $s2) = ($3, $4, $5, $6);
10004      my @counts = AdjustSamples($sample_adjustment, $sampling_algorithm,
10005                                 $n1, $s1, $n2, $s2);
10006      if ($thread eq "*") {
10007        AddEntries($profile, $pcs, FixCallerAddresses($stack), $counts[$index]);
10008      } else {
10009        if (!exists($thread_profiles->{$thread})) {
10010          $thread_profiles->{$thread} = {};
10011        }
10012        AddEntries($thread_profiles->{$thread}, $pcs,
10013                   FixCallerAddresses($stack), $counts[$index]);
10014      }
10015    }
10016  }
10017
10018  my $r = {};
10019  $r->{version} = "heap";
10020  $r->{period} = 1;
10021  $r->{profile} = $profile;
10022  $r->{threads} = $thread_profiles;
10023  $r->{libs} = ParseLibraries($prog, $map, $pcs);
10024  $r->{pcs} = $pcs;
10025  return $r;
10026}
10027
10028sub ReadSynchProfile {
10029  my $prog = shift;
10030  local *PROFILE = shift;
10031  my $header = shift;
10032
10033  my $map = '';
10034  my $profile = {};
10035  my $pcs = {};
10036  my $sampling_period = 1;
10037  my $cyclespernanosec = 2.8;   # Default assumption for old binaries
10038  my $seen_clockrate = 0;
10039  my $line;
10040
10041  my $index = 0;
10042  if ($main::opt_total_delay) {
10043    $index = 0;
10044  } elsif ($main::opt_contentions) {
10045    $index = 1;
10046  } elsif ($main::opt_mean_delay) {
10047    $index = 2;
10048  }
10049
10050  while ( $line = <PROFILE> ) {
10051    $line =~ s/\r//g;      # turn windows-looking lines into unix-looking lines
10052    if ( $line =~ /^\s*(\d+)\s+(\d+) \@\s*(.*?)\s*$/ ) {
10053      my ($cycles, $count, $stack) = ($1, $2, $3);
10054
10055      # Convert cycles to nanoseconds
10056      $cycles /= $cyclespernanosec;
10057
10058      # Adjust for sampling done by application
10059      $cycles *= $sampling_period;
10060      $count *= $sampling_period;
10061
10062      my @values = ($cycles, $count, $cycles / $count);
10063      AddEntries($profile, $pcs, FixCallerAddresses($stack), $values[$index]);
10064
10065    } elsif ( $line =~ /^(slow release).*thread \d+  \@\s*(.*?)\s*$/ ||
10066              $line =~ /^\s*(\d+) \@\s*(.*?)\s*$/ ) {
10067      my ($cycles, $stack) = ($1, $2);
10068      if ($cycles !~ /^\d+$/) {
10069        next;
10070      }
10071
10072      # Convert cycles to nanoseconds
10073      $cycles /= $cyclespernanosec;
10074
10075      # Adjust for sampling done by application
10076      $cycles *= $sampling_period;
10077
10078      AddEntries($profile, $pcs, FixCallerAddresses($stack), $cycles);
10079
10080    } elsif ( $line =~ m/^([a-z][^=]*)=(.*)$/ ) {
10081      my ($variable, $value) = ($1,$2);
10082      for ($variable, $value) {
10083        s/^\s+//;
10084        s/\s+$//;
10085      }
10086      if ($variable eq "cycles/second") {
10087        $cyclespernanosec = $value / 1e9;
10088        $seen_clockrate = 1;
10089      } elsif ($variable eq "sampling period") {
10090        $sampling_period = $value;
10091      } elsif ($variable eq "ms since reset") {
10092        # Currently nothing is done with this value in jeprof
10093        # So we just silently ignore it for now
10094      } elsif ($variable eq "discarded samples") {
10095        # Currently nothing is done with this value in jeprof
10096        # So we just silently ignore it for now
10097      } else {
10098        printf STDERR ("Ignoring unnknown variable in /contention output: " .
10099                       "'%s' = '%s'\n",$variable,$value);
10100      }
10101    } else {
10102      # Memory map entry
10103      $map .= $line;
10104    }
10105  }
10106
10107  if (!$seen_clockrate) {
10108    printf STDERR ("No cycles/second entry in profile; Guessing %.1f GHz\n",
10109                   $cyclespernanosec);
10110  }
10111
10112  my $r = {};
10113  $r->{version} = 0;
10114  $r->{period} = $sampling_period;
10115  $r->{profile} = $profile;
10116  $r->{libs} = ParseLibraries($prog, $map, $pcs);
10117  $r->{pcs} = $pcs;
10118  return $r;
10119}
10120
10121# Given a hex value in the form "0x1abcd" or "1abcd", return either
10122# "0001abcd" or "000000000001abcd", depending on the current (global)
10123# address length.
10124sub HexExtend {
10125  my $addr = shift;
10126
10127  $addr =~ s/^(0x)?0*//;
10128  my $zeros_needed = $address_length - length($addr);
10129  if ($zeros_needed < 0) {
10130    printf STDERR "Warning: address $addr is longer than address length $address_length\n";
10131    return $addr;
10132  }
10133  return ("0" x $zeros_needed) . $addr;
10134}
10135
10136##### Symbol extraction #####
10137
10138# Aggressively search the lib_prefix values for the given library
10139# If all else fails, just return the name of the library unmodified.
10140# If the lib_prefix is "/my/path,/other/path" and $file is "/lib/dir/mylib.so"
10141# it will search the following locations in this order, until it finds a file:
10142#   /my/path/lib/dir/mylib.so
10143#   /other/path/lib/dir/mylib.so
10144#   /my/path/dir/mylib.so
10145#   /other/path/dir/mylib.so
10146#   /my/path/mylib.so
10147#   /other/path/mylib.so
10148#   /lib/dir/mylib.so              (returned as last resort)
10149sub FindLibrary {
10150  my $file = shift;
10151  my $suffix = $file;
10152
10153  # Search for the library as described above
10154  do {
10155    foreach my $prefix (@prefix_list) {
10156      my $fullpath = $prefix . $suffix;
10157      if (-e $fullpath) {
10158        return $fullpath;
10159      }
10160    }
10161  } while ($suffix =~ s|^/[^/]+/|/|);
10162  return $file;
10163}
10164
10165# Return path to library with debugging symbols.
10166# For libc libraries, the copy in /usr/lib/debug contains debugging symbols
10167sub DebuggingLibrary {
10168  my $file = shift;
10169  if ($file =~ m|^/|) {
10170      if (-f "/usr/lib/debug$file") {
10171        return "/usr/lib/debug$file";
10172      } elsif (-f "/usr/lib/debug$file.debug") {
10173        return "/usr/lib/debug$file.debug";
10174      }
10175  }
10176  return undef;
10177}
10178
10179# Parse text section header of a library using objdump
10180sub ParseTextSectionHeaderFromObjdump {
10181  my $lib = shift;
10182
10183  my $size = undef;
10184  my $vma;
10185  my $file_offset;
10186  # Get objdump output from the library file to figure out how to
10187  # map between mapped addresses and addresses in the library.
10188  my $cmd = ShellEscape($obj_tool_map{"objdump"}, "-h", $lib);
10189  open(OBJDUMP, "$cmd |") || error("$cmd: $!\n");
10190  while (<OBJDUMP>) {
10191    s/\r//g;         # turn windows-looking lines into unix-looking lines
10192    # Idx Name          Size      VMA       LMA       File off  Algn
10193    #  10 .text         00104b2c  420156f0  420156f0  000156f0  2**4
10194    # For 64-bit objects, VMA and LMA will be 16 hex digits, size and file
10195    # offset may still be 8.  But AddressSub below will still handle that.
10196    my @x = split;
10197    if (($#x >= 6) && ($x[1] eq '.text')) {
10198      $size = $x[2];
10199      $vma = $x[3];
10200      $file_offset = $x[5];
10201      last;
10202    }
10203  }
10204  close(OBJDUMP);
10205
10206  if (!defined($size)) {
10207    return undef;
10208  }
10209
10210  my $r = {};
10211  $r->{size} = $size;
10212  $r->{vma} = $vma;
10213  $r->{file_offset} = $file_offset;
10214
10215  return $r;
10216}
10217
10218# Parse text section header of a library using otool (on OS X)
10219sub ParseTextSectionHeaderFromOtool {
10220  my $lib = shift;
10221
10222  my $size = undef;
10223  my $vma = undef;
10224  my $file_offset = undef;
10225  # Get otool output from the library file to figure out how to
10226  # map between mapped addresses and addresses in the library.
10227  my $command = ShellEscape($obj_tool_map{"otool"}, "-l", $lib);
10228  open(OTOOL, "$command |") || error("$command: $!\n");
10229  my $cmd = "";
10230  my $sectname = "";
10231  my $segname = "";
10232  foreach my $line (<OTOOL>) {
10233    $line =~ s/\r//g;      # turn windows-looking lines into unix-looking lines
10234    # Load command <#>
10235    #       cmd LC_SEGMENT
10236    # [...]
10237    # Section
10238    #   sectname __text
10239    #    segname __TEXT
10240    #       addr 0x000009f8
10241    #       size 0x00018b9e
10242    #     offset 2552
10243    #      align 2^2 (4)
10244    # We will need to strip off the leading 0x from the hex addresses,
10245    # and convert the offset into hex.
10246    if ($line =~ /Load command/) {
10247      $cmd = "";
10248      $sectname = "";
10249      $segname = "";
10250    } elsif ($line =~ /Section/) {
10251      $sectname = "";
10252      $segname = "";
10253    } elsif ($line =~ /cmd (\w+)/) {
10254      $cmd = $1;
10255    } elsif ($line =~ /sectname (\w+)/) {
10256      $sectname = $1;
10257    } elsif ($line =~ /segname (\w+)/) {
10258      $segname = $1;
10259    } elsif (!(($cmd eq "LC_SEGMENT" || $cmd eq "LC_SEGMENT_64") &&
10260               $sectname eq "__text" &&
10261               $segname eq "__TEXT")) {
10262      next;
10263    } elsif ($line =~ /\baddr 0x([0-9a-fA-F]+)/) {
10264      $vma = $1;
10265    } elsif ($line =~ /\bsize 0x([0-9a-fA-F]+)/) {
10266      $size = $1;
10267    } elsif ($line =~ /\boffset ([0-9]+)/) {
10268      $file_offset = sprintf("%016x", $1);
10269    }
10270    if (defined($vma) && defined($size) && defined($file_offset)) {
10271      last;
10272    }
10273  }
10274  close(OTOOL);
10275
10276  if (!defined($vma) || !defined($size) || !defined($file_offset)) {
10277     return undef;
10278  }
10279
10280  my $r = {};
10281  $r->{size} = $size;
10282  $r->{vma} = $vma;
10283  $r->{file_offset} = $file_offset;
10284
10285  return $r;
10286}
10287
10288sub ParseTextSectionHeader {
10289  # obj_tool_map("otool") is only defined if we're in a Mach-O environment
10290  if (defined($obj_tool_map{"otool"})) {
10291    my $r = ParseTextSectionHeaderFromOtool(@_);
10292    if (defined($r)){
10293      return $r;
10294    }
10295  }
10296  # If otool doesn't work, or we don't have it, fall back to objdump
10297  return ParseTextSectionHeaderFromObjdump(@_);
10298}
10299
10300# Split /proc/pid/maps dump into a list of libraries
10301sub ParseLibraries {
10302  return if $main::use_symbol_page;  # We don't need libraries info.
10303  my $prog = Cwd::abs_path(shift);
10304  my $map = shift;
10305  my $pcs = shift;
10306
10307  my $result = [];
10308  my $h = "[a-f0-9]+";
10309  my $zero_offset = HexExtend("0");
10310
10311  my $buildvar = "";
10312  foreach my $l (split("\n", $map)) {
10313    if ($l =~ m/^\s*build=(.*)$/) {
10314      $buildvar = $1;
10315    }
10316
10317    my $start;
10318    my $finish;
10319    my $offset;
10320    my $lib;
10321    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) {
10322      # Full line from /proc/self/maps.  Example:
10323      #   40000000-40015000 r-xp 00000000 03:01 12845071   /lib/ld-2.3.2.so
10324      $start = HexExtend($1);
10325      $finish = HexExtend($2);
10326      $offset = HexExtend($3);
10327      $lib = $4;
10328      $lib =~ s|\\|/|g;     # turn windows-style paths into unix-style paths
10329    } elsif ($l =~ /^\s*($h)-($h):\s*(\S+\.so(\.\d+)*)/) {
10330      # Cooked line from DumpAddressMap.  Example:
10331      #   40000000-40015000: /lib/ld-2.3.2.so
10332      $start = HexExtend($1);
10333      $finish = HexExtend($2);
10334      $offset = $zero_offset;
10335      $lib = $3;
10336    } elsif (($l =~ /^($h)-($h)\s+..x.\s+($h)\s+\S+:\S+\s+\d+\s+(\S+)$/i) && ($4 eq $prog)) {
10337      # PIEs and address space randomization do not play well with our
10338      # default assumption that main executable is at lowest
10339      # addresses. So we're detecting main executable in
10340      # /proc/self/maps as well.
10341      $start = HexExtend($1);
10342      $finish = HexExtend($2);
10343      $offset = HexExtend($3);
10344      $lib = $4;
10345      $lib =~ s|\\|/|g;     # turn windows-style paths into unix-style paths
10346    }
10347    # FreeBSD 10.0 virtual memory map /proc/curproc/map as defined in
10348    # function procfs_doprocmap (sys/fs/procfs/procfs_map.c)
10349    #
10350    # Example:
10351    # 0x800600000 0x80061a000 26 0 0xfffff800035a0000 r-x 75 33 0x1004 COW NC vnode /libexec/ld-elf.s
10352    # o.1 NCH -1
10353    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+)*)/) {
10354      $start = HexExtend($1);
10355      $finish = HexExtend($2);
10356      $offset = $zero_offset;
10357      $lib = FindLibrary($5);
10358
10359    } else {
10360      next;
10361    }
10362
10363    # Expand "$build" variable if available
10364    $lib =~ s/\$build\b/$buildvar/g;
10365
10366    $lib = FindLibrary($lib);
10367
10368    # Check for pre-relocated libraries, which use pre-relocated symbol tables
10369    # and thus require adjusting the offset that we'll use to translate
10370    # VM addresses into symbol table addresses.
10371    # Only do this if we're not going to fetch the symbol table from a
10372    # debugging copy of the library.
10373    if (!DebuggingLibrary($lib)) {
10374      my $text = ParseTextSectionHeader($lib);
10375      if (defined($text)) {
10376         my $vma_offset = AddressSub($text->{vma}, $text->{file_offset});
10377         $offset = AddressAdd($offset, $vma_offset);
10378      }
10379    }
10380
10381    if($main::opt_debug) { printf STDERR "$start:$finish ($offset) $lib\n"; }
10382    push(@{$result}, [$lib, $start, $finish, $offset]);
10383  }
10384
10385  # Append special entry for additional library (not relocated)
10386  if ($main::opt_lib ne "") {
10387    my $text = ParseTextSectionHeader($main::opt_lib);
10388    if (defined($text)) {
10389       my $start = $text->{vma};
10390       my $finish = AddressAdd($start, $text->{size});
10391
10392       push(@{$result}, [$main::opt_lib, $start, $finish, $start]);
10393    }
10394  }
10395
10396  # Append special entry for the main program.  This covers
10397  # 0..max_pc_value_seen, so that we assume pc values not found in one
10398  # of the library ranges will be treated as coming from the main
10399  # program binary.
10400  my $min_pc = HexExtend("0");
10401  my $max_pc = $min_pc;          # find the maximal PC value in any sample
10402  foreach my $pc (keys(%{$pcs})) {
10403    if (HexExtend($pc) gt $max_pc) { $max_pc = HexExtend($pc); }
10404  }
10405  push(@{$result}, [$prog, $min_pc, $max_pc, $zero_offset]);
10406
10407  return $result;
10408}
10409
10410# Add two hex addresses of length $address_length.
10411# Run jeprof --test for unit test if this is changed.
10412sub AddressAdd {
10413  my $addr1 = shift;
10414  my $addr2 = shift;
10415  my $sum;
10416
10417  if ($address_length == 8) {
10418    # Perl doesn't cope with wraparound arithmetic, so do it explicitly:
10419    $sum = (hex($addr1)+hex($addr2)) % (0x10000000 * 16);
10420    return sprintf("%08x", $sum);
10421
10422  } else {
10423    # Do the addition in 7-nibble chunks to trivialize carry handling.
10424
10425    if ($main::opt_debug and $main::opt_test) {
10426      print STDERR "AddressAdd $addr1 + $addr2 = ";
10427    }
10428
10429    my $a1 = substr($addr1,-7);
10430    $addr1 = substr($addr1,0,-7);
10431    my $a2 = substr($addr2,-7);
10432    $addr2 = substr($addr2,0,-7);
10433    $sum = hex($a1) + hex($a2);
10434    my $c = 0;
10435    if ($sum > 0xfffffff) {
10436      $c = 1;
10437      $sum -= 0x10000000;
10438    }
10439    my $r = sprintf("%07x", $sum);
10440
10441    $a1 = substr($addr1,-7);
10442    $addr1 = substr($addr1,0,-7);
10443    $a2 = substr($addr2,-7);
10444    $addr2 = substr($addr2,0,-7);
10445    $sum = hex($a1) + hex($a2) + $c;
10446    $c = 0;
10447    if ($sum > 0xfffffff) {
10448      $c = 1;
10449      $sum -= 0x10000000;
10450    }
10451    $r = sprintf("%07x", $sum) . $r;
10452
10453    $sum = hex($addr1) + hex($addr2) + $c;
10454    if ($sum > 0xff) { $sum -= 0x100; }
10455    $r = sprintf("%02x", $sum) . $r;
10456
10457    if ($main::opt_debug and $main::opt_test) { print STDERR "$r\n"; }
10458
10459    return $r;
10460  }
10461}
10462
10463
10464# Subtract two hex addresses of length $address_length.
10465# Run jeprof --test for unit test if this is changed.
10466sub AddressSub {
10467  my $addr1 = shift;
10468  my $addr2 = shift;
10469  my $diff;
10470
10471  if ($address_length == 8) {
10472    # Perl doesn't cope with wraparound arithmetic, so do it explicitly:
10473    $diff = (hex($addr1)-hex($addr2)) % (0x10000000 * 16);
10474    return sprintf("%08x", $diff);
10475
10476  } else {
10477    # Do the addition in 7-nibble chunks to trivialize borrow handling.
10478    # if ($main::opt_debug) { print STDERR "AddressSub $addr1 - $addr2 = "; }
10479
10480    my $a1 = hex(substr($addr1,-7));
10481    $addr1 = substr($addr1,0,-7);
10482    my $a2 = hex(substr($addr2,-7));
10483    $addr2 = substr($addr2,0,-7);
10484    my $b = 0;
10485    if ($a2 > $a1) {
10486      $b = 1;
10487      $a1 += 0x10000000;
10488    }
10489    $diff = $a1 - $a2;
10490    my $r = sprintf("%07x", $diff);
10491
10492    $a1 = hex(substr($addr1,-7));
10493    $addr1 = substr($addr1,0,-7);
10494    $a2 = hex(substr($addr2,-7)) + $b;
10495    $addr2 = substr($addr2,0,-7);
10496    $b = 0;
10497    if ($a2 > $a1) {
10498      $b = 1;
10499      $a1 += 0x10000000;
10500    }
10501    $diff = $a1 - $a2;
10502    $r = sprintf("%07x", $diff) . $r;
10503
10504    $a1 = hex($addr1);
10505    $a2 = hex($addr2) + $b;
10506    if ($a2 > $a1) { $a1 += 0x100; }
10507    $diff = $a1 - $a2;
10508    $r = sprintf("%02x", $diff) . $r;
10509
10510    # if ($main::opt_debug) { print STDERR "$r\n"; }
10511
10512    return $r;
10513  }
10514}
10515
10516# Increment a hex addresses of length $address_length.
10517# Run jeprof --test for unit test if this is changed.
10518sub AddressInc {
10519  my $addr = shift;
10520  my $sum;
10521
10522  if ($address_length == 8) {
10523    # Perl doesn't cope with wraparound arithmetic, so do it explicitly:
10524    $sum = (hex($addr)+1) % (0x10000000 * 16);
10525    return sprintf("%08x", $sum);
10526
10527  } else {
10528    # Do the addition in 7-nibble chunks to trivialize carry handling.
10529    # We are always doing this to step through the addresses in a function,
10530    # and will almost never overflow the first chunk, so we check for this
10531    # case and exit early.
10532
10533    # if ($main::opt_debug) { print STDERR "AddressInc $addr1 = "; }
10534
10535    my $a1 = substr($addr,-7);
10536    $addr = substr($addr,0,-7);
10537    $sum = hex($a1) + 1;
10538    my $r = sprintf("%07x", $sum);
10539    if ($sum <= 0xfffffff) {
10540      $r = $addr . $r;
10541      # if ($main::opt_debug) { print STDERR "$r\n"; }
10542      return HexExtend($r);
10543    } else {
10544      $r = "0000000";
10545    }
10546
10547    $a1 = substr($addr,-7);
10548    $addr = substr($addr,0,-7);
10549    $sum = hex($a1) + 1;
10550    $r = sprintf("%07x", $sum) . $r;
10551    if ($sum <= 0xfffffff) {
10552      $r = $addr . $r;
10553      # if ($main::opt_debug) { print STDERR "$r\n"; }
10554      return HexExtend($r);
10555    } else {
10556      $r = "00000000000000";
10557    }
10558
10559    $sum = hex($addr) + 1;
10560    if ($sum > 0xff) { $sum -= 0x100; }
10561    $r = sprintf("%02x", $sum) . $r;
10562
10563    # if ($main::opt_debug) { print STDERR "$r\n"; }
10564    return $r;
10565  }
10566}
10567
10568# Extract symbols for all PC values found in profile
10569sub ExtractSymbols {
10570  my $libs = shift;
10571  my $pcset = shift;
10572
10573  my $symbols = {};
10574
10575  # Map each PC value to the containing library.  To make this faster,
10576  # we sort libraries by their starting pc value (highest first), and
10577  # advance through the libraries as we advance the pc.  Sometimes the
10578  # addresses of libraries may overlap with the addresses of the main
10579  # binary, so to make sure the libraries 'win', we iterate over the
10580  # libraries in reverse order (which assumes the binary doesn't start
10581  # in the middle of a library, which seems a fair assumption).
10582  my @pcs = (sort { $a cmp $b } keys(%{$pcset}));  # pcset is 0-extended strings
10583  foreach my $lib (sort {$b->[1] cmp $a->[1]} @{$libs}) {
10584    my $libname = $lib->[0];
10585    my $start = $lib->[1];
10586    my $finish = $lib->[2];
10587    my $offset = $lib->[3];
10588
10589    # Use debug library if it exists
10590    my $debug_libname = DebuggingLibrary($libname);
10591    if ($debug_libname) {
10592        $libname = $debug_libname;
10593    }
10594
10595    # Get list of pcs that belong in this library.
10596    my $contained = [];
10597    my ($start_pc_index, $finish_pc_index);
10598    # Find smallest finish_pc_index such that $finish < $pc[$finish_pc_index].
10599    for ($finish_pc_index = $#pcs + 1; $finish_pc_index > 0;
10600         $finish_pc_index--) {
10601      last if $pcs[$finish_pc_index - 1] le $finish;
10602    }
10603    # Find smallest start_pc_index such that $start <= $pc[$start_pc_index].
10604    for ($start_pc_index = $finish_pc_index; $start_pc_index > 0;
10605         $start_pc_index--) {
10606      last if $pcs[$start_pc_index - 1] lt $start;
10607    }
10608    # This keeps PC values higher than $pc[$finish_pc_index] in @pcs,
10609    # in case there are overlaps in libraries and the main binary.
10610    @{$contained} = splice(@pcs, $start_pc_index,
10611                           $finish_pc_index - $start_pc_index);
10612    # Map to symbols
10613    MapToSymbols($libname, AddressSub($start, $offset), $contained, $symbols);
10614  }
10615
10616  return $symbols;
10617}
10618
10619# Map list of PC values to symbols for a given image
10620sub MapToSymbols {
10621  my $image = shift;
10622  my $offset = shift;
10623  my $pclist = shift;
10624  my $symbols = shift;
10625
10626  my $debug = 0;
10627
10628  # Ignore empty binaries
10629  if ($#{$pclist} < 0) { return; }
10630
10631  # Figure out the addr2line command to use
10632  my $addr2line = $obj_tool_map{"addr2line"};
10633  my $cmd = ShellEscape($addr2line, "-f", "-C", "-e", $image);
10634  if (exists $obj_tool_map{"addr2line_pdb"}) {
10635    $addr2line = $obj_tool_map{"addr2line_pdb"};
10636    $cmd = ShellEscape($addr2line, "--demangle", "-f", "-C", "-e", $image);
10637  }
10638
10639  # If "addr2line" isn't installed on the system at all, just use
10640  # nm to get what info we can (function names, but not line numbers).
10641  if (system(ShellEscape($addr2line, "--help") . " >$dev_null 2>&1") != 0) {
10642    MapSymbolsWithNM($image, $offset, $pclist, $symbols);
10643    return;
10644  }
10645
10646  # "addr2line -i" can produce a variable number of lines per input
10647  # address, with no separator that allows us to tell when data for
10648  # the next address starts.  So we find the address for a special
10649  # symbol (_fini) and interleave this address between all real
10650  # addresses passed to addr2line.  The name of this special symbol
10651  # can then be used as a separator.
10652  $sep_address = undef;  # May be filled in by MapSymbolsWithNM()
10653  my $nm_symbols = {};
10654  MapSymbolsWithNM($image, $offset, $pclist, $nm_symbols);
10655  if (defined($sep_address)) {
10656    # Only add " -i" to addr2line if the binary supports it.
10657    # addr2line --help returns 0, but not if it sees an unknown flag first.
10658    if (system("$cmd -i --help >$dev_null 2>&1") == 0) {
10659      $cmd .= " -i";
10660    } else {
10661      $sep_address = undef;   # no need for sep_address if we don't support -i
10662    }
10663  }
10664
10665  # Make file with all PC values with intervening 'sep_address' so
10666  # that we can reliably detect the end of inlined function list
10667  open(ADDRESSES, ">$main::tmpfile_sym") || error("$main::tmpfile_sym: $!\n");
10668  if ($debug) { print("---- $image ---\n"); }
10669  for (my $i = 0; $i <= $#{$pclist}; $i++) {
10670    # addr2line always reads hex addresses, and does not need '0x' prefix.
10671    if ($debug) { printf STDERR ("%s\n", $pclist->[$i]); }
10672    printf ADDRESSES ("%s\n", AddressSub($pclist->[$i], $offset));
10673    if (defined($sep_address)) {
10674      printf ADDRESSES ("%s\n", $sep_address);
10675    }
10676  }
10677  close(ADDRESSES);
10678  if ($debug) {
10679    print("----\n");
10680    system("cat", $main::tmpfile_sym);
10681    print("----\n");
10682    system("$cmd < " . ShellEscape($main::tmpfile_sym));
10683    print("----\n");
10684  }
10685
10686  open(SYMBOLS, "$cmd <" . ShellEscape($main::tmpfile_sym) . " |")
10687      || error("$cmd: $!\n");
10688  my $count = 0;   # Index in pclist
10689  while (<SYMBOLS>) {
10690    # Read fullfunction and filelineinfo from next pair of lines
10691    s/\r?\n$//g;
10692    my $fullfunction = $_;
10693    $_ = <SYMBOLS>;
10694    s/\r?\n$//g;
10695    my $filelinenum = $_;
10696
10697    if (defined($sep_address) && $fullfunction eq $sep_symbol) {
10698      # Terminating marker for data for this address
10699      $count++;
10700      next;
10701    }
10702
10703    $filelinenum =~ s|\\|/|g; # turn windows-style paths into unix-style paths
10704
10705    my $pcstr = $pclist->[$count];
10706    my $function = ShortFunctionName($fullfunction);
10707    my $nms = $nm_symbols->{$pcstr};
10708    if (defined($nms)) {
10709      if ($fullfunction eq '??') {
10710        # nm found a symbol for us.
10711        $function = $nms->[0];
10712        $fullfunction = $nms->[2];
10713      } else {
10714	# MapSymbolsWithNM tags each routine with its starting address,
10715	# useful in case the image has multiple occurrences of this
10716	# routine.  (It uses a syntax that resembles template paramters,
10717	# that are automatically stripped out by ShortFunctionName().)
10718	# addr2line does not provide the same information.  So we check
10719	# if nm disambiguated our symbol, and if so take the annotated
10720	# (nm) version of the routine-name.  TODO(csilvers): this won't
10721	# catch overloaded, inlined symbols, which nm doesn't see.
10722	# Better would be to do a check similar to nm's, in this fn.
10723	if ($nms->[2] =~ m/^\Q$function\E/) {  # sanity check it's the right fn
10724	  $function = $nms->[0];
10725	  $fullfunction = $nms->[2];
10726	}
10727      }
10728    }
10729
10730    # Prepend to accumulated symbols for pcstr
10731    # (so that caller comes before callee)
10732    my $sym = $symbols->{$pcstr};
10733    if (!defined($sym)) {
10734      $sym = [];
10735      $symbols->{$pcstr} = $sym;
10736    }
10737    unshift(@{$sym}, $function, $filelinenum, $fullfunction);
10738    if ($debug) { printf STDERR ("%s => [%s]\n", $pcstr, join(" ", @{$sym})); }
10739    if (!defined($sep_address)) {
10740      # Inlining is off, so this entry ends immediately
10741      $count++;
10742    }
10743  }
10744  close(SYMBOLS);
10745}
10746
10747# Use nm to map the list of referenced PCs to symbols.  Return true iff we
10748# are able to read procedure information via nm.
10749sub MapSymbolsWithNM {
10750  my $image = shift;
10751  my $offset = shift;
10752  my $pclist = shift;
10753  my $symbols = shift;
10754
10755  # Get nm output sorted by increasing address
10756  my $symbol_table = GetProcedureBoundaries($image, ".");
10757  if (!%{$symbol_table}) {
10758    return 0;
10759  }
10760  # Start addresses are already the right length (8 or 16 hex digits).
10761  my @names = sort { $symbol_table->{$a}->[0] cmp $symbol_table->{$b}->[0] }
10762    keys(%{$symbol_table});
10763
10764  if ($#names < 0) {
10765    # No symbols: just use addresses
10766    foreach my $pc (@{$pclist}) {
10767      my $pcstr = "0x" . $pc;
10768      $symbols->{$pc} = [$pcstr, "?", $pcstr];
10769    }
10770    return 0;
10771  }
10772
10773  # Sort addresses so we can do a join against nm output
10774  my $index = 0;
10775  my $fullname = $names[0];
10776  my $name = ShortFunctionName($fullname);
10777  foreach my $pc (sort { $a cmp $b } @{$pclist}) {
10778    # Adjust for mapped offset
10779    my $mpc = AddressSub($pc, $offset);
10780    while (($index < $#names) && ($mpc ge $symbol_table->{$fullname}->[1])){
10781      $index++;
10782      $fullname = $names[$index];
10783      $name = ShortFunctionName($fullname);
10784    }
10785    if ($mpc lt $symbol_table->{$fullname}->[1]) {
10786      $symbols->{$pc} = [$name, "?", $fullname];
10787    } else {
10788      my $pcstr = "0x" . $pc;
10789      $symbols->{$pc} = [$pcstr, "?", $pcstr];
10790    }
10791  }
10792  return 1;
10793}
10794
10795sub ShortFunctionName {
10796  my $function = shift;
10797  while ($function =~ s/\([^()]*\)(\s*const)?//g) { }   # Argument types
10798  while ($function =~ s/<[^<>]*>//g)  { }    # Remove template arguments
10799  $function =~ s/^.*\s+(\w+::)/$1/;          # Remove leading type
10800  return $function;
10801}
10802
10803# Trim overly long symbols found in disassembler output
10804sub CleanDisassembly {
10805  my $d = shift;
10806  while ($d =~ s/\([^()%]*\)(\s*const)?//g) { } # Argument types, not (%rax)
10807  while ($d =~ s/(\w+)<[^<>]*>/$1/g)  { }       # Remove template arguments
10808  return $d;
10809}
10810
10811# Clean file name for display
10812sub CleanFileName {
10813  my ($f) = @_;
10814  $f =~ s|^/proc/self/cwd/||;
10815  $f =~ s|^\./||;
10816  return $f;
10817}
10818
10819# Make address relative to section and clean up for display
10820sub UnparseAddress {
10821  my ($offset, $address) = @_;
10822  $address = AddressSub($address, $offset);
10823  $address =~ s/^0x//;
10824  $address =~ s/^0*//;
10825  return $address;
10826}
10827
10828##### Miscellaneous #####
10829
10830# Find the right versions of the above object tools to use.  The
10831# argument is the program file being analyzed, and should be an ELF
10832# 32-bit or ELF 64-bit executable file.  The location of the tools
10833# is determined by considering the following options in this order:
10834#   1) --tools option, if set
10835#   2) JEPROF_TOOLS environment variable, if set
10836#   3) the environment
10837sub ConfigureObjTools {
10838  my $prog_file = shift;
10839
10840  # Check for the existence of $prog_file because /usr/bin/file does not
10841  # predictably return error status in prod.
10842  (-e $prog_file)  || error("$prog_file does not exist.\n");
10843
10844  my $file_type = undef;
10845  if (-e "/usr/bin/file") {
10846    # Follow symlinks (at least for systems where "file" supports that).
10847    my $escaped_prog_file = ShellEscape($prog_file);
10848    $file_type = `/usr/bin/file -L $escaped_prog_file 2>$dev_null ||
10849                  /usr/bin/file $escaped_prog_file`;
10850  } elsif ($^O == "MSWin32") {
10851    $file_type = "MS Windows";
10852  } else {
10853    print STDERR "WARNING: Can't determine the file type of $prog_file";
10854  }
10855
10856  if ($file_type =~ /64-bit/) {
10857    # Change $address_length to 16 if the program file is ELF 64-bit.
10858    # We can't detect this from many (most?) heap or lock contention
10859    # profiles, since the actual addresses referenced are generally in low
10860    # memory even for 64-bit programs.
10861    $address_length = 16;
10862  }
10863
10864  if ($file_type =~ /MS Windows/) {
10865    # For windows, we provide a version of nm and addr2line as part of
10866    # the opensource release, which is capable of parsing
10867    # Windows-style PDB executables.  It should live in the path, or
10868    # in the same directory as jeprof.
10869    $obj_tool_map{"nm_pdb"} = "nm-pdb";
10870    $obj_tool_map{"addr2line_pdb"} = "addr2line-pdb";
10871  }
10872
10873  if ($file_type =~ /Mach-O/) {
10874    # OS X uses otool to examine Mach-O files, rather than objdump.
10875    $obj_tool_map{"otool"} = "otool";
10876    $obj_tool_map{"addr2line"} = "false";  # no addr2line
10877    $obj_tool_map{"objdump"} = "false";  # no objdump
10878  }
10879
10880  # Go fill in %obj_tool_map with the pathnames to use:
10881  foreach my $tool (keys %obj_tool_map) {
10882    $obj_tool_map{$tool} = ConfigureTool($obj_tool_map{$tool});
10883  }
10884}
10885
10886# Returns the path of a caller-specified object tool.  If --tools or
10887# JEPROF_TOOLS are specified, then returns the full path to the tool
10888# with that prefix.  Otherwise, returns the path unmodified (which
10889# means we will look for it on PATH).
10890sub ConfigureTool {
10891  my $tool = shift;
10892  my $path;
10893
10894  # --tools (or $JEPROF_TOOLS) is a comma separated list, where each
10895  # item is either a) a pathname prefix, or b) a map of the form
10896  # <tool>:<path>.  First we look for an entry of type (b) for our
10897  # tool.  If one is found, we use it.  Otherwise, we consider all the
10898  # pathname prefixes in turn, until one yields an existing file.  If
10899  # none does, we use a default path.
10900  my $tools = $main::opt_tools || $ENV{"JEPROF_TOOLS"} || "";
10901  if ($tools =~ m/(,|^)\Q$tool\E:([^,]*)/) {
10902    $path = $2;
10903    # TODO(csilvers): sanity-check that $path exists?  Hard if it's relative.
10904  } elsif ($tools ne '') {
10905    foreach my $prefix (split(',', $tools)) {
10906      next if ($prefix =~ /:/);    # ignore "tool:fullpath" entries in the list
10907      if (-x $prefix . $tool) {
10908        $path = $prefix . $tool;
10909        last;
10910      }
10911    }
10912    if (!$path) {
10913      error("No '$tool' found with prefix specified by " .
10914            "--tools (or \$JEPROF_TOOLS) '$tools'\n");
10915    }
10916  } else {
10917    # ... otherwise use the version that exists in the same directory as
10918    # jeprof.  If there's nothing there, use $PATH.
10919    $0 =~ m,[^/]*$,;     # this is everything after the last slash
10920    my $dirname = $`;    # this is everything up to and including the last slash
10921    if (-x "$dirname$tool") {
10922      $path = "$dirname$tool";
10923    } else {
10924      $path = $tool;
10925    }
10926  }
10927  if ($main::opt_debug) { print STDERR "Using '$path' for '$tool'.\n"; }
10928  return $path;
10929}
10930
10931sub ShellEscape {
10932  my @escaped_words = ();
10933  foreach my $word (@_) {
10934    my $escaped_word = $word;
10935    if ($word =~ m![^a-zA-Z0-9/.,_=-]!) {  # check for anything not in whitelist
10936      $escaped_word =~ s/'/'\\''/;
10937      $escaped_word = "'$escaped_word'";
10938    }
10939    push(@escaped_words, $escaped_word);
10940  }
10941  return join(" ", @escaped_words);
10942}
10943
10944sub cleanup {
10945  unlink($main::tmpfile_sym);
10946  unlink(keys %main::tempnames);
10947
10948  # We leave any collected profiles in $HOME/jeprof in case the user wants
10949  # to look at them later.  We print a message informing them of this.
10950  if ((scalar(@main::profile_files) > 0) &&
10951      defined($main::collected_profile)) {
10952    if (scalar(@main::profile_files) == 1) {
10953      print STDERR "Dynamically gathered profile is in $main::collected_profile\n";
10954    }
10955    print STDERR "If you want to investigate this profile further, you can do:\n";
10956    print STDERR "\n";
10957    print STDERR "  jeprof \\\n";
10958    print STDERR "    $main::prog \\\n";
10959    print STDERR "    $main::collected_profile\n";
10960    print STDERR "\n";
10961  }
10962}
10963
10964sub sighandler {
10965  cleanup();
10966  exit(1);
10967}
10968
10969sub error {
10970  my $msg = shift;
10971  print STDERR $msg;
10972  cleanup();
10973  exit(1);
10974}
10975
10976
10977# Run $nm_command and get all the resulting procedure boundaries whose
10978# names match "$regexp" and returns them in a hashtable mapping from
10979# procedure name to a two-element vector of [start address, end address]
10980sub GetProcedureBoundariesViaNm {
10981  my $escaped_nm_command = shift;    # shell-escaped
10982  my $regexp = shift;
10983
10984  my $symbol_table = {};
10985  open(NM, "$escaped_nm_command |") || error("$escaped_nm_command: $!\n");
10986  my $last_start = "0";
10987  my $routine = "";
10988  while (<NM>) {
10989    s/\r//g;         # turn windows-looking lines into unix-looking lines
10990    if (m/^\s*([0-9a-f]+) (.) (..*)/) {
10991      my $start_val = $1;
10992      my $type = $2;
10993      my $this_routine = $3;
10994
10995      # It's possible for two symbols to share the same address, if
10996      # one is a zero-length variable (like __start_google_malloc) or
10997      # one symbol is a weak alias to another (like __libc_malloc).
10998      # In such cases, we want to ignore all values except for the
10999      # actual symbol, which in nm-speak has type "T".  The logic
11000      # below does this, though it's a bit tricky: what happens when
11001      # we have a series of lines with the same address, is the first
11002      # one gets queued up to be processed.  However, it won't
11003      # *actually* be processed until later, when we read a line with
11004      # a different address.  That means that as long as we're reading
11005      # lines with the same address, we have a chance to replace that
11006      # item in the queue, which we do whenever we see a 'T' entry --
11007      # that is, a line with type 'T'.  If we never see a 'T' entry,
11008      # we'll just go ahead and process the first entry (which never
11009      # got touched in the queue), and ignore the others.
11010      if ($start_val eq $last_start && $type =~ /t/i) {
11011        # We are the 'T' symbol at this address, replace previous symbol.
11012        $routine = $this_routine;
11013        next;
11014      } elsif ($start_val eq $last_start) {
11015        # We're not the 'T' symbol at this address, so ignore us.
11016        next;
11017      }
11018
11019      if ($this_routine eq $sep_symbol) {
11020        $sep_address = HexExtend($start_val);
11021      }
11022
11023      # Tag this routine with the starting address in case the image
11024      # has multiple occurrences of this routine.  We use a syntax
11025      # that resembles template parameters that are automatically
11026      # stripped out by ShortFunctionName()
11027      $this_routine .= "<$start_val>";
11028
11029      if (defined($routine) && $routine =~ m/$regexp/) {
11030        $symbol_table->{$routine} = [HexExtend($last_start),
11031                                     HexExtend($start_val)];
11032      }
11033      $last_start = $start_val;
11034      $routine = $this_routine;
11035    } elsif (m/^Loaded image name: (.+)/) {
11036      # The win32 nm workalike emits information about the binary it is using.
11037      if ($main::opt_debug) { print STDERR "Using Image $1\n"; }
11038    } elsif (m/^PDB file name: (.+)/) {
11039      # The win32 nm workalike emits information about the pdb it is using.
11040      if ($main::opt_debug) { print STDERR "Using PDB $1\n"; }
11041    }
11042  }
11043  close(NM);
11044  # Handle the last line in the nm output.  Unfortunately, we don't know
11045  # how big this last symbol is, because we don't know how big the file
11046  # is.  For now, we just give it a size of 0.
11047  # TODO(csilvers): do better here.
11048  if (defined($routine) && $routine =~ m/$regexp/) {
11049    $symbol_table->{$routine} = [HexExtend($last_start),
11050                                 HexExtend($last_start)];
11051  }
11052  return $symbol_table;
11053}
11054
11055# Gets the procedure boundaries for all routines in "$image" whose names
11056# match "$regexp" and returns them in a hashtable mapping from procedure
11057# name to a two-element vector of [start address, end address].
11058# Will return an empty map if nm is not installed or not working properly.
11059sub GetProcedureBoundaries {
11060  my $image = shift;
11061  my $regexp = shift;
11062
11063  # If $image doesn't start with /, then put ./ in front of it.  This works
11064  # around an obnoxious bug in our probing of nm -f behavior.
11065  # "nm -f $image" is supposed to fail on GNU nm, but if:
11066  #
11067  # a. $image starts with [BbSsPp] (for example, bin/foo/bar), AND
11068  # b. you have a.out in your current directory (a not uncommon occurence)
11069  #
11070  # then "nm -f $image" succeeds because -f only looks at the first letter of
11071  # the argument, which looks valid because it's [BbSsPp], and then since
11072  # there's no image provided, it looks for a.out and finds it.
11073  #
11074  # This regex makes sure that $image starts with . or /, forcing the -f
11075  # parsing to fail since . and / are not valid formats.
11076  $image =~ s#^[^/]#./$&#;
11077
11078  # For libc libraries, the copy in /usr/lib/debug contains debugging symbols
11079  my $debugging = DebuggingLibrary($image);
11080  if ($debugging) {
11081    $image = $debugging;
11082  }
11083
11084  my $nm = $obj_tool_map{"nm"};
11085  my $cppfilt = $obj_tool_map{"c++filt"};
11086
11087  # nm can fail for two reasons: 1) $image isn't a debug library; 2) nm
11088  # binary doesn't support --demangle.  In addition, for OS X we need
11089  # to use the -f flag to get 'flat' nm output (otherwise we don't sort
11090  # properly and get incorrect results).  Unfortunately, GNU nm uses -f
11091  # in an incompatible way.  So first we test whether our nm supports
11092  # --demangle and -f.
11093  my $demangle_flag = "";
11094  my $cppfilt_flag = "";
11095  my $to_devnull = ">$dev_null 2>&1";
11096  if (system(ShellEscape($nm, "--demangle", $image) . $to_devnull) == 0) {
11097    # In this mode, we do "nm --demangle <foo>"
11098    $demangle_flag = "--demangle";
11099    $cppfilt_flag = "";
11100  } elsif (system(ShellEscape($cppfilt, $image) . $to_devnull) == 0) {
11101    # In this mode, we do "nm <foo> | c++filt"
11102    $cppfilt_flag = " | " . ShellEscape($cppfilt);
11103  };
11104  my $flatten_flag = "";
11105  if (system(ShellEscape($nm, "-f", $image) . $to_devnull) == 0) {
11106    $flatten_flag = "-f";
11107  }
11108
11109  # Finally, in the case $imagie isn't a debug library, we try again with
11110  # -D to at least get *exported* symbols.  If we can't use --demangle,
11111  # we use c++filt instead, if it exists on this system.
11112  my @nm_commands = (ShellEscape($nm, "-n", $flatten_flag, $demangle_flag,
11113                                 $image) . " 2>$dev_null $cppfilt_flag",
11114                     ShellEscape($nm, "-D", "-n", $flatten_flag, $demangle_flag,
11115                                 $image) . " 2>$dev_null $cppfilt_flag",
11116                     # 6nm is for Go binaries
11117                     ShellEscape("6nm", "$image") . " 2>$dev_null | sort",
11118                     );
11119
11120  # If the executable is an MS Windows PDB-format executable, we'll
11121  # have set up obj_tool_map("nm_pdb").  In this case, we actually
11122  # want to use both unix nm and windows-specific nm_pdb, since
11123  # PDB-format executables can apparently include dwarf .o files.
11124  if (exists $obj_tool_map{"nm_pdb"}) {
11125    push(@nm_commands,
11126         ShellEscape($obj_tool_map{"nm_pdb"}, "--demangle", $image)
11127         . " 2>$dev_null");
11128  }
11129
11130  foreach my $nm_command (@nm_commands) {
11131    my $symbol_table = GetProcedureBoundariesViaNm($nm_command, $regexp);
11132    return $symbol_table if (%{$symbol_table});
11133  }
11134  my $symbol_table = {};
11135  return $symbol_table;
11136}
11137
11138
11139# The test vectors for AddressAdd/Sub/Inc are 8-16-nibble hex strings.
11140# To make them more readable, we add underscores at interesting places.
11141# This routine removes the underscores, producing the canonical representation
11142# used by jeprof to represent addresses, particularly in the tested routines.
11143sub CanonicalHex {
11144  my $arg = shift;
11145  return join '', (split '_',$arg);
11146}
11147
11148
11149# Unit test for AddressAdd:
11150sub AddressAddUnitTest {
11151  my $test_data_8 = shift;
11152  my $test_data_16 = shift;
11153  my $error_count = 0;
11154  my $fail_count = 0;
11155  my $pass_count = 0;
11156  # print STDERR "AddressAddUnitTest: ", 1+$#{$test_data_8}, " tests\n";
11157
11158  # First a few 8-nibble addresses.  Note that this implementation uses
11159  # plain old arithmetic, so a quick sanity check along with verifying what
11160  # happens to overflow (we want it to wrap):
11161  $address_length = 8;
11162  foreach my $row (@{$test_data_8}) {
11163    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
11164    my $sum = AddressAdd ($row->[0], $row->[1]);
11165    if ($sum ne $row->[2]) {
11166      printf STDERR "ERROR: %s != %s + %s = %s\n", $sum,
11167             $row->[0], $row->[1], $row->[2];
11168      ++$fail_count;
11169    } else {
11170      ++$pass_count;
11171    }
11172  }
11173  printf STDERR "AddressAdd 32-bit tests: %d passes, %d failures\n",
11174         $pass_count, $fail_count;
11175  $error_count = $fail_count;
11176  $fail_count = 0;
11177  $pass_count = 0;
11178
11179  # Now 16-nibble addresses.
11180  $address_length = 16;
11181  foreach my $row (@{$test_data_16}) {
11182    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
11183    my $sum = AddressAdd (CanonicalHex($row->[0]), CanonicalHex($row->[1]));
11184    my $expected = join '', (split '_',$row->[2]);
11185    if ($sum ne CanonicalHex($row->[2])) {
11186      printf STDERR "ERROR: %s != %s + %s = %s\n", $sum,
11187             $row->[0], $row->[1], $row->[2];
11188      ++$fail_count;
11189    } else {
11190      ++$pass_count;
11191    }
11192  }
11193  printf STDERR "AddressAdd 64-bit tests: %d passes, %d failures\n",
11194         $pass_count, $fail_count;
11195  $error_count += $fail_count;
11196
11197  return $error_count;
11198}
11199
11200
11201# Unit test for AddressSub:
11202sub AddressSubUnitTest {
11203  my $test_data_8 = shift;
11204  my $test_data_16 = shift;
11205  my $error_count = 0;
11206  my $fail_count = 0;
11207  my $pass_count = 0;
11208  # print STDERR "AddressSubUnitTest: ", 1+$#{$test_data_8}, " tests\n";
11209
11210  # First a few 8-nibble addresses.  Note that this implementation uses
11211  # plain old arithmetic, so a quick sanity check along with verifying what
11212  # happens to overflow (we want it to wrap):
11213  $address_length = 8;
11214  foreach my $row (@{$test_data_8}) {
11215    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
11216    my $sum = AddressSub ($row->[0], $row->[1]);
11217    if ($sum ne $row->[3]) {
11218      printf STDERR "ERROR: %s != %s - %s = %s\n", $sum,
11219             $row->[0], $row->[1], $row->[3];
11220      ++$fail_count;
11221    } else {
11222      ++$pass_count;
11223    }
11224  }
11225  printf STDERR "AddressSub 32-bit tests: %d passes, %d failures\n",
11226         $pass_count, $fail_count;
11227  $error_count = $fail_count;
11228  $fail_count = 0;
11229  $pass_count = 0;
11230
11231  # Now 16-nibble addresses.
11232  $address_length = 16;
11233  foreach my $row (@{$test_data_16}) {
11234    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
11235    my $sum = AddressSub (CanonicalHex($row->[0]), CanonicalHex($row->[1]));
11236    if ($sum ne CanonicalHex($row->[3])) {
11237      printf STDERR "ERROR: %s != %s - %s = %s\n", $sum,
11238             $row->[0], $row->[1], $row->[3];
11239      ++$fail_count;
11240    } else {
11241      ++$pass_count;
11242    }
11243  }
11244  printf STDERR "AddressSub 64-bit tests: %d passes, %d failures\n",
11245         $pass_count, $fail_count;
11246  $error_count += $fail_count;
11247
11248  return $error_count;
11249}
11250
11251
11252# Unit test for AddressInc:
11253sub AddressIncUnitTest {
11254  my $test_data_8 = shift;
11255  my $test_data_16 = shift;
11256  my $error_count = 0;
11257  my $fail_count = 0;
11258  my $pass_count = 0;
11259  # print STDERR "AddressIncUnitTest: ", 1+$#{$test_data_8}, " tests\n";
11260
11261  # First a few 8-nibble addresses.  Note that this implementation uses
11262  # plain old arithmetic, so a quick sanity check along with verifying what
11263  # happens to overflow (we want it to wrap):
11264  $address_length = 8;
11265  foreach my $row (@{$test_data_8}) {
11266    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
11267    my $sum = AddressInc ($row->[0]);
11268    if ($sum ne $row->[4]) {
11269      printf STDERR "ERROR: %s != %s + 1 = %s\n", $sum,
11270             $row->[0], $row->[4];
11271      ++$fail_count;
11272    } else {
11273      ++$pass_count;
11274    }
11275  }
11276  printf STDERR "AddressInc 32-bit tests: %d passes, %d failures\n",
11277         $pass_count, $fail_count;
11278  $error_count = $fail_count;
11279  $fail_count = 0;
11280  $pass_count = 0;
11281
11282  # Now 16-nibble addresses.
11283  $address_length = 16;
11284  foreach my $row (@{$test_data_16}) {
11285    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
11286    my $sum = AddressInc (CanonicalHex($row->[0]));
11287    if ($sum ne CanonicalHex($row->[4])) {
11288      printf STDERR "ERROR: %s != %s + 1 = %s\n", $sum,
11289             $row->[0], $row->[4];
11290      ++$fail_count;
11291    } else {
11292      ++$pass_count;
11293    }
11294  }
11295  printf STDERR "AddressInc 64-bit tests: %d passes, %d failures\n",
11296         $pass_count, $fail_count;
11297  $error_count += $fail_count;
11298
11299  return $error_count;
11300}
11301
11302
11303# Driver for unit tests.
11304# Currently just the address add/subtract/increment routines for 64-bit.
11305sub RunUnitTests {
11306  my $error_count = 0;
11307
11308  # This is a list of tuples [a, b, a+b, a-b, a+1]
11309  my $unit_test_data_8 = [
11310    [qw(aaaaaaaa 50505050 fafafafa 5a5a5a5a aaaaaaab)],
11311    [qw(50505050 aaaaaaaa fafafafa a5a5a5a6 50505051)],
11312    [qw(ffffffff aaaaaaaa aaaaaaa9 55555555 00000000)],
11313    [qw(00000001 ffffffff 00000000 00000002 00000002)],
11314    [qw(00000001 fffffff0 fffffff1 00000011 00000002)],
11315  ];
11316  my $unit_test_data_16 = [
11317    # The implementation handles data in 7-nibble chunks, so those are the
11318    # interesting boundaries.
11319    [qw(aaaaaaaa 50505050
11320        00_000000f_afafafa 00_0000005_a5a5a5a 00_000000a_aaaaaab)],
11321    [qw(50505050 aaaaaaaa
11322        00_000000f_afafafa ff_ffffffa_5a5a5a6 00_0000005_0505051)],
11323    [qw(ffffffff aaaaaaaa
11324        00_000001a_aaaaaa9 00_0000005_5555555 00_0000010_0000000)],
11325    [qw(00000001 ffffffff
11326        00_0000010_0000000 ff_ffffff0_0000002 00_0000000_0000002)],
11327    [qw(00000001 fffffff0
11328        00_000000f_ffffff1 ff_ffffff0_0000011 00_0000000_0000002)],
11329
11330    [qw(00_a00000a_aaaaaaa 50505050
11331        00_a00000f_afafafa 00_a000005_a5a5a5a 00_a00000a_aaaaaab)],
11332    [qw(0f_fff0005_0505050 aaaaaaaa
11333        0f_fff000f_afafafa 0f_ffefffa_5a5a5a6 0f_fff0005_0505051)],
11334    [qw(00_000000f_fffffff 01_800000a_aaaaaaa
11335        01_800001a_aaaaaa9 fe_8000005_5555555 00_0000010_0000000)],
11336    [qw(00_0000000_0000001 ff_fffffff_fffffff
11337        00_0000000_0000000 00_0000000_0000002 00_0000000_0000002)],
11338    [qw(00_0000000_0000001 ff_fffffff_ffffff0
11339        ff_fffffff_ffffff1 00_0000000_0000011 00_0000000_0000002)],
11340  ];
11341
11342  $error_count += AddressAddUnitTest($unit_test_data_8, $unit_test_data_16);
11343  $error_count += AddressSubUnitTest($unit_test_data_8, $unit_test_data_16);
11344  $error_count += AddressIncUnitTest($unit_test_data_8, $unit_test_data_16);
11345  if ($error_count > 0) {
11346    print STDERR $error_count, " errors: FAILED\n";
11347  } else {
11348    print STDERR "PASS\n";
11349  }
11350  exit ($error_count);
11351}
11352>>>>>>> main
11353