15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#! /usr/bin/env perl
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 1998-2007, Google Inc.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# All rights reserved.
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Redistribution and use in source and binary forms, with or without
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# modification, are permitted provided that the following conditions are
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# met:
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#     * Redistributions of source code must retain the above copyright
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# notice, this list of conditions and the following disclaimer.
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#     * Redistributions in binary form must reproduce the above
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# copyright notice, this list of conditions and the following disclaimer
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# in the documentation and/or other materials provided with the
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# distribution.
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#     * Neither the name of Google Inc. nor the names of its
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# contributors may be used to endorse or promote products derived from
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# this software without specific prior written permission.
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# ---
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Program for printing the profile generated by common/profiler.cc,
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# or by the heap profiler (common/debugallocation.cc)
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# The profile contains a sequence of entries of the form:
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#       <count> <stack trace>
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# This program parses the profile, and generates user-readable
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# output.
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Examples:
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# % tools/pprof "program" "profile"
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   Enters "interactive" mode
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# % tools/pprof --text "program" "profile"
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   Generates one line per procedure
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# % tools/pprof --gv "program" "profile"
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   Generates annotated call-graph and displays via "gv"
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# % tools/pprof --gv --focus=Mutex "program" "profile"
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   Restrict to code paths that involve an entry that matches "Mutex"
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# % tools/pprof --gv --focus=Mutex --ignore=string "program" "profile"
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   Restrict to code paths that involve an entry that matches "Mutex"
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   and does not match "string"
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# % tools/pprof --list=IBF_CheckDocid "program" "profile"
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   Generates disassembly listing of all routines with at least one
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   sample that match the --list=<regexp> pattern.  The listing is
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   annotated with the flat and cumulative sample counts at each line.
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# % tools/pprof --disasm=IBF_CheckDocid "program" "profile"
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   Generates disassembly listing of all routines with at least one
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   sample that match the --disasm=<regexp> pattern.  The listing is
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   annotated with the flat and cumulative sample counts at each PC value.
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# TODO: Use color to indicate files?
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)use strict;
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)use warnings;
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)use Getopt::Long;
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)my $PPROF_VERSION = "2.0";
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# These are the object tools we use which can come from a
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# user-specified location using --tools, from the PPROF_TOOLS
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# environment variable, or from the environment.
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)my %obj_tool_map = (
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  "objdump" => "objdump",
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  "nm" => "nm",
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  "addr2line" => "addr2line",
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  "c++filt" => "c++filt",
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ## ConfigureObjTools may add architecture-specific entries:
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #"nm_pdb" => "nm-pdb",       # for reading windows (PDB-format) executables
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #"addr2line_pdb" => "addr2line-pdb",                                # ditto
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #"otool" => "otool",         # equivalent of objdump on OS X
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles));
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# NOTE: these are lists, so you can put in commandline flags if you want.
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)my @DOT = ("dot");          # leave non-absolute, since it may be in /usr/local
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)my @GV = ("gv");
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)my @EVINCE = ("evince");    # could also be xpdf or perhaps acroread
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)my @KCACHEGRIND = ("kcachegrind");
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)my @PS2PDF = ("ps2pdf");
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# These are used for dynamic profiles
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)my @URL_FETCHER = ("curl", "-s");
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# These are the web pages that servers need to support for dynamic profiles
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)my $HEAP_PAGE = "/pprof/heap";
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)my $PROFILE_PAGE = "/pprof/profile";   # must support cgi-param "?seconds=#"
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)my $PMUPROFILE_PAGE = "/pprof/pmuprofile(?:\\?.*)?"; # must support cgi-param
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                # ?seconds=#&event=x&period=n
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)my $GROWTH_PAGE = "/pprof/growth";
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)my $CONTENTION_PAGE = "/pprof/contention";
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)my $WALL_PAGE = "/pprof/wall(?:\\?.*)?";  # accepts options like namefilter
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)my $FILTEREDPROFILE_PAGE = "/pprof/filteredprofile(?:\\?.*)?";
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)my $CENSUSPROFILE_PAGE = "/pprof/censusprofile(?:\\?.*)?"; # must support cgi-param
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                       # "?seconds=#",
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                       # "?tags_regexp=#" and
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                       # "?type=#".
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)my $SYMBOL_PAGE = "/pprof/symbol";     # must support symbol lookup via POST
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)my $PROGRAM_NAME_PAGE = "/pprof/cmdline";
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# These are the web pages that can be named on the command line.
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# All the alternatives must begin with /.
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)my $PROFILES = "($HEAP_PAGE|$PROFILE_PAGE|$PMUPROFILE_PAGE|" .
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               "$GROWTH_PAGE|$CONTENTION_PAGE|$WALL_PAGE|" .
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               "$FILTEREDPROFILE_PAGE|$CENSUSPROFILE_PAGE)";
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# default binary name
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)my $UNKNOWN_BINARY = "(unknown)";
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# There is a pervasive dependency on the length (in hex characters,
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# i.e., nibbles) of an address, distinguishing between 32-bit and
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 64-bit profiles.  To err on the safe size, default to 64-bit here:
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)my $address_length = 16;
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)my $dev_null = "/dev/null";
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if (! -e $dev_null && $^O =~ /MSWin/) {    # $^O is the OS perl was built for
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $dev_null = "nul";
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# A list of paths to search for shared object files
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)my @prefix_list = ();
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Special routine name that should not have any symbols.
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Used as separator to parse "addr2line -i" output.
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)my $sep_symbol = '_fini';
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)my $sep_address = undef;
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)##### Argument parsing #####
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub usage_string {
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return <<EOF;
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Usage:
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)pprof [options] <program> <profiles>
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   <profiles> is a space separated list of profile names.
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)pprof [options] <symbolized-profiles>
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   <symbolized-profiles> is a list of profile files where each file contains
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   the necessary symbol mappings  as well as profile data (likely generated
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   with --raw).
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)pprof [options] <profile>
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   <profile> is a remote form.  Symbols are obtained from host:port$SYMBOL_PAGE
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   Each name can be:
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   /path/to/profile        - a path to a profile file
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   host:port[/<service>]   - a location of a service to get profile from
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   The /<service> can be $HEAP_PAGE, $PROFILE_PAGE, /pprof/pmuprofile,
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         $GROWTH_PAGE, $CONTENTION_PAGE, /pprof/wall,
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         $CENSUSPROFILE_PAGE, or /pprof/filteredprofile.
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   For instance:
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     pprof http://myserver.com:80$HEAP_PAGE
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   If /<service> is omitted, the service defaults to $PROFILE_PAGE (cpu profiling).
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)pprof --symbols <program>
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   Maps addresses to symbol names.  In this mode, stdin should be a
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   list of library mappings, in the same format as is found in the heap-
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   and cpu-profile files (this loosely matches that of /proc/self/maps
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   on linux), followed by a list of hex addresses to map, one per line.
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   For more help with querying remote servers, including how to add the
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   necessary server-side support code, see this filename (or one like it):
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   /usr/doc/gperftools-$PPROF_VERSION/pprof_remote_servers.html
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Options:
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --cum               Sort by cumulative data
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --base=<base>       Subtract <base> from <profile> before display
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --interactive       Run in interactive mode (interactive "help" gives help) [default]
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --seconds=<n>       Length of time for dynamic profiles [default=30 secs]
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --add_lib=<file>    Read additional symbols and line info from the given library
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --lib_prefix=<dir>  Comma separated list of library path prefixes
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Reporting Granularity:
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --addresses         Report at address level
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --lines             Report at source line level
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --functions         Report at function level [default]
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --files             Report at source file level
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Output type:
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --text              Generate text report
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --callgrind         Generate callgrind format to stdout
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --gv                Generate Postscript and display
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --evince            Generate PDF and display
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --web               Generate SVG and display
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --list=<regexp>     Generate source listing of matching routines
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --disasm=<regexp>   Generate disassembly of matching routines
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --symbols           Print demangled symbol names found at given addresses
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --dot               Generate DOT file to stdout
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --ps                Generate Postcript to stdout
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --pdf               Generate PDF to stdout
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --svg               Generate SVG to stdout
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --gif               Generate GIF to stdout
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --raw               Generate symbolized pprof data (useful with remote fetch)
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Heap-Profile Options:
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --inuse_space       Display in-use (mega)bytes [default]
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --inuse_objects     Display in-use objects
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --alloc_space       Display allocated (mega)bytes
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --alloc_objects     Display allocated objects
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --show_bytes        Display space in bytes
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --drop_negative     Ignore negative differences
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Contention-profile options:
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --total_delay       Display total delay at each region [default]
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --contentions       Display number of delays at each region
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --mean_delay        Display mean delay at each region
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Call-graph Options:
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --nodecount=<n>     Show at most so many nodes [default=80]
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --nodefraction=<f>  Hide nodes below <f>*total [default=.005]
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --edgefraction=<f>  Hide edges below <f>*total [default=.001]
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --maxdegree=<n>     Max incoming/outgoing edges per node [default=8]
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --focus=<regexp>    Focus on nodes matching <regexp>
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --ignore=<regexp>   Ignore nodes matching <regexp>
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --scale=<n>         Set GV scaling [default=0]
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --heapcheck         Make nodes with non-0 object counts
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       (i.e. direct leak generators) more visible
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Miscellaneous:
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --tools=<prefix or binary:fullpath>[,...]   \$PATH for object tool pathnames
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --test              Run unit tests
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --help              This message
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   --version           Version information
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Environment Variables:
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   PPROF_TMPDIR        Profiles directory. Defaults to \$HOME/pprof
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   PPROF_TOOLS         Prefix for object tools pathnames
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Examples:
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)pprof /bin/ls ls.prof
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       Enters "interactive" mode
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)pprof --text /bin/ls ls.prof
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       Outputs one line per procedure
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)pprof --web /bin/ls ls.prof
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       Displays annotated call-graph in web browser
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)pprof --gv /bin/ls ls.prof
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       Displays annotated call-graph via 'gv'
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)pprof --gv --focus=Mutex /bin/ls ls.prof
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       Restricts to code paths including a .*Mutex.* entry
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)pprof --gv --focus=Mutex --ignore=string /bin/ls ls.prof
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       Code paths including Mutex but not string
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)pprof --list=getdir /bin/ls ls.prof
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       (Per-line) annotated source listing for getdir()
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)pprof --disasm=getdir /bin/ls ls.prof
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       (Per-PC) annotated disassembly for getdir()
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)pprof http://localhost:1234/
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       Enters "interactive" mode
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)pprof --text localhost:1234
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       Outputs one line per procedure for localhost:1234
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)pprof --raw localhost:1234 > ./local.raw
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)pprof --text ./local.raw
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       Fetches a remote profile for later analysis and then
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       analyzes it in text mode.
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)EOF
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub version_string {
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return <<EOF
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)pprof (part of gperftools $PPROF_VERSION)
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Copyright 1998-2007 Google Inc.
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)This is BSD licensed software; see the source for copying conditions
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)and license information.
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)PARTICULAR PURPOSE.
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)EOF
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub usage {
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $msg = shift;
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  print STDERR "$msg\n\n";
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  print STDERR usage_string();
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  print STDERR "\nFATAL ERROR: $msg\n";    # just as a reminder
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  exit(1);
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub Init() {
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Setup tmp-file name and handler to clean it up.
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # We do this in the very beginning so that we can use
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # error() and cleanup() function anytime here after.
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::tmpfile_sym = "/tmp/pprof$$.sym";
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::tmpfile_ps = "/tmp/pprof$$";
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::next_tmpfile = 0;
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $SIG{'INT'} = \&sighandler;
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Cache from filename/linenumber to source code
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::source_cache = ();
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_help = 0;
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_version = 0;
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_cum = 0;
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_base = '';
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_addresses = 0;
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_lines = 0;
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_functions = 0;
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_files = 0;
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_lib_prefix = "";
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_text = 0;
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_callgrind = 0;
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_list = "";
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_disasm = "";
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_symbols = 0;
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_gv = 0;
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_evince = 0;
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_web = 0;
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_dot = 0;
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_ps = 0;
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_pdf = 0;
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_gif = 0;
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_svg = 0;
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_raw = 0;
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_nodecount = 80;
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_nodefraction = 0.005;
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_edgefraction = 0.001;
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_maxdegree = 8;
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_focus = '';
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_ignore = '';
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_scale = 0;
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_heapcheck = 0;
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_seconds = 30;
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_lib = "";
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_inuse_space   = 0;
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_inuse_objects = 0;
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_alloc_space   = 0;
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_alloc_objects = 0;
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_show_bytes    = 0;
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_drop_negative = 0;
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_interactive   = 0;
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_total_delay = 0;
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_contentions = 0;
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_mean_delay = 0;
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_tools   = "";
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_debug   = 0;
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_test    = 0;
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # These are undocumented flags used only by unittests.
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_test_stride = 0;
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Are we using $SYMBOL_PAGE?
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::use_symbol_page = 0;
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Files returned by TempName.
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  %main::tempnames = ();
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Type of profile we are dealing with
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Supported types:
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #     cpu
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #     heap
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #     growth
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #     contention
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::profile_type = '';     # Empty type means "unknown"
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GetOptions("help!"          => \$main::opt_help,
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "version!"       => \$main::opt_version,
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "cum!"           => \$main::opt_cum,
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "base=s"         => \$main::opt_base,
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "seconds=i"      => \$main::opt_seconds,
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "add_lib=s"      => \$main::opt_lib,
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "lib_prefix=s"   => \$main::opt_lib_prefix,
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "functions!"     => \$main::opt_functions,
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "lines!"         => \$main::opt_lines,
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "addresses!"     => \$main::opt_addresses,
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "files!"         => \$main::opt_files,
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "text!"          => \$main::opt_text,
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "callgrind!"     => \$main::opt_callgrind,
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "list=s"         => \$main::opt_list,
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "disasm=s"       => \$main::opt_disasm,
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "symbols!"       => \$main::opt_symbols,
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "gv!"            => \$main::opt_gv,
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "evince!"        => \$main::opt_evince,
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "web!"           => \$main::opt_web,
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "dot!"           => \$main::opt_dot,
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "ps!"            => \$main::opt_ps,
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "pdf!"           => \$main::opt_pdf,
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "svg!"           => \$main::opt_svg,
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "gif!"           => \$main::opt_gif,
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "raw!"           => \$main::opt_raw,
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "interactive!"   => \$main::opt_interactive,
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "nodecount=i"    => \$main::opt_nodecount,
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "nodefraction=f" => \$main::opt_nodefraction,
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "edgefraction=f" => \$main::opt_edgefraction,
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "maxdegree=i"    => \$main::opt_maxdegree,
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "focus=s"        => \$main::opt_focus,
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "ignore=s"       => \$main::opt_ignore,
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "scale=i"        => \$main::opt_scale,
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "heapcheck"      => \$main::opt_heapcheck,
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "inuse_space!"   => \$main::opt_inuse_space,
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "inuse_objects!" => \$main::opt_inuse_objects,
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "alloc_space!"   => \$main::opt_alloc_space,
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "alloc_objects!" => \$main::opt_alloc_objects,
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "show_bytes!"    => \$main::opt_show_bytes,
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "drop_negative!" => \$main::opt_drop_negative,
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "total_delay!"   => \$main::opt_total_delay,
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "contentions!"   => \$main::opt_contentions,
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "mean_delay!"    => \$main::opt_mean_delay,
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "tools=s"        => \$main::opt_tools,
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "test!"          => \$main::opt_test,
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "debug!"         => \$main::opt_debug,
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             # Undocumented flags used only by unittests:
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             "test_stride=i"  => \$main::opt_test_stride,
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ) || usage("Invalid option(s)");
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Deal with the standard --help and --version
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($main::opt_help) {
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print usage_string();
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit(0);
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($main::opt_version) {
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print version_string();
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit(0);
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Disassembly/listing/symbols mode requires address-level info
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($main::opt_disasm || $main::opt_list || $main::opt_symbols) {
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $main::opt_functions = 0;
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $main::opt_lines = 0;
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $main::opt_addresses = 1;
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $main::opt_files = 0;
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Check heap-profiling flags
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($main::opt_inuse_space +
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::opt_inuse_objects +
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::opt_alloc_space +
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::opt_alloc_objects > 1) {
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    usage("Specify at most on of --inuse/--alloc options");
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Check output granularities
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $grains =
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::opt_functions +
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::opt_lines +
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::opt_addresses +
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::opt_files +
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      0;
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($grains > 1) {
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    usage("Only specify one output granularity option");
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($grains == 0) {
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $main::opt_functions = 1;
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Check output modes
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $modes =
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::opt_text +
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::opt_callgrind +
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ($main::opt_list eq '' ? 0 : 1) +
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ($main::opt_disasm eq '' ? 0 : 1) +
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ($main::opt_symbols == 0 ? 0 : 1) +
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::opt_gv +
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::opt_evince +
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::opt_web +
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::opt_dot +
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::opt_ps +
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::opt_pdf +
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::opt_svg +
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::opt_gif +
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::opt_raw +
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::opt_interactive +
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      0;
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($modes > 1) {
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    usage("Only specify one output mode");
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($modes == 0) {
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (-t STDOUT) {  # If STDOUT is a tty, activate interactive mode
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::opt_interactive = 1;
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::opt_text = 1;
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($main::opt_test) {
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    RunUnitTests();
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Should not return
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit(1);
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Binary name and profile arguments list
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::prog = "";
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  @main::pfile_args = ();
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Remote profiling without a binary (using $SYMBOL_PAGE instead)
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (@ARGV > 0) {
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (IsProfileURL($ARGV[0])) {
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::use_symbol_page = 1;
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } elsif (IsSymbolizedProfileFile($ARGV[0])) {
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::use_symbolized_profile = 1;
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::prog = $UNKNOWN_BINARY;  # will be set later from the profile file
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($main::use_symbol_page || $main::use_symbolized_profile) {
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # We don't need a binary!
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my %disabled = ('--lines' => $main::opt_lines,
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    '--disasm' => $main::opt_disasm);
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for my $option (keys %disabled) {
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      usage("$option cannot be used without a binary") if $disabled{$option};
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Set $main::prog later...
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    scalar(@ARGV) || usage("Did not specify profile file");
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } elsif ($main::opt_symbols) {
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # --symbols needs a binary-name (to run nm on, etc) but not profiles
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $main::prog = shift(@ARGV) || usage("Did not specify program");
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $main::prog = shift(@ARGV) || usage("Did not specify program");
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    scalar(@ARGV) || usage("Did not specify profile file");
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Parse profile file/location arguments
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $farg (@ARGV) {
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($farg =~ m/(.*)\@([0-9]+)(|\/.*)$/ ) {
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $machine = $1;
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $num_machines = $2;
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $path = $3;
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (my $i = 0; $i < $num_machines; $i++) {
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        unshift(@main::pfile_args, "$i.$machine$path");
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      unshift(@main::pfile_args, $farg);
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($main::use_symbol_page) {
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    unless (IsProfileURL($main::pfile_args[0])) {
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      error("The first profile should be a remote form to use $SYMBOL_PAGE\n");
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CheckSymbolPage();
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $main::prog = FetchProgramName();
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } elsif (!$main::use_symbolized_profile) {  # may not need objtools!
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ConfigureObjTools($main::prog)
5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Break the opt_lib_prefix into the prefix_list array
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  @prefix_list = split (',', $main::opt_lib_prefix);
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Remove trailing / from the prefixes, in the list to prevent
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # searching things like /my/path//lib/mylib.so
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach (@prefix_list) {
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    s|/+$||;
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub Main() {
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Init();
5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::collected_profile = undef;
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  @main::profile_files = ();
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::op_time = time();
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Printing symbols is special and requires a lot less info that most.
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($main::opt_symbols) {
5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PrintSymbols(*STDIN);   # Get /proc/maps and symbols output from stdin
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Fetch all profile data
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FetchDynamicProfiles();
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # this will hold symbols that we read from the profile files
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $symbol_map = {};
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Read one profile, pick the last item on the list
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $data = ReadProfile($main::prog, pop(@main::profile_files));
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $profile = $data->{profile};
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $pcs = $data->{pcs};
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $libs = $data->{libs};   # Info about main program and shared libraries
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $symbol_map = MergeSymbols($symbol_map, $data->{symbols});
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Add additional profiles, if available.
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (scalar(@main::profile_files) > 0) {
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    foreach my $pname (@main::profile_files) {
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $data2 = ReadProfile($main::prog, $pname);
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $profile = AddProfile($profile, $data2->{profile});
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $pcs = AddPcs($pcs, $data2->{pcs});
5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $symbol_map = MergeSymbols($symbol_map, $data2->{symbols});
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Subtract base from profile, if specified
6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($main::opt_base ne '') {
6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $base = ReadProfile($main::prog, $main::opt_base);
6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $profile = SubtractProfile($profile, $base->{profile});
6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $pcs = AddPcs($pcs, $base->{pcs});
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $symbol_map = MergeSymbols($symbol_map, $base->{symbols});
6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Get total data in profile
6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $total = TotalProfile($profile);
6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Collect symbols
6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $symbols;
6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($main::use_symbolized_profile) {
6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $symbols = FetchSymbols($pcs, $symbol_map);
6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } elsif ($main::use_symbol_page) {
6165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $symbols = FetchSymbols($pcs);
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # TODO(csilvers): $libs uses the /proc/self/maps data from profile1,
6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # which may differ from the data from subsequent profiles, especially
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # if they were run on different machines.  Use appropriate libs for
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # each pc somehow.
6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $symbols = ExtractSymbols($libs, $pcs);
6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Remove uniniteresting stack items
6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $profile = RemoveUninterestingFrames($symbols, $profile);
6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Focus?
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($main::opt_focus ne '') {
6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $profile = FocusProfile($symbols, $profile, $main::opt_focus);
6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Ignore?
6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($main::opt_ignore ne '') {
6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $profile = IgnoreProfile($symbols, $profile, $main::opt_ignore);
6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $calls = ExtractCalls($symbols, $profile);
6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Reduce profiles to required output granularity, and also clean
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # each stack trace so a given entry exists at most once.
6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $reduced = ReduceProfile($symbols, $profile);
6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Get derived profiles
6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $flat = FlatProfile($reduced);
6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $cumulative = CumulativeProfile($reduced);
6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Print
6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!$main::opt_interactive) {
6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($main::opt_disasm) {
6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      PrintDisassembly($libs, $flat, $cumulative, $main::opt_disasm);
6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } elsif ($main::opt_list) {
6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      PrintListing($total, $libs, $flat, $cumulative, $main::opt_list, 0);
6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } elsif ($main::opt_text) {
6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Make sure the output is empty when have nothing to report
6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # (only matters when --heapcheck is given but we must be
6575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # compatible with old branches that did not pass --heapcheck always):
6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($total != 0) {
6595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        printf("Total: %s %s\n", Unparse($total), Units());
6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      PrintText($symbols, $flat, $cumulative, -1);
6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } elsif ($main::opt_raw) {
6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      PrintSymbolizedProfile($symbols, $profile, $main::prog);
6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } elsif ($main::opt_callgrind) {
6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      PrintCallgrind($calls);
6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (PrintDot($main::prog, $symbols, $profile, $flat, $cumulative, $total)) {
6685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if ($main::opt_gv) {
6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          RunGV(TempName($main::next_tmpfile, "ps"), "");
6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        } elsif ($main::opt_evince) {
6715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          RunEvince(TempName($main::next_tmpfile, "pdf"), "");
6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        } elsif ($main::opt_web) {
6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          my $tmp = TempName($main::next_tmpfile, "svg");
6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          RunWeb($tmp);
6755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          # The command we run might hand the file name off
6765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          # to an already running browser instance and then exit.
6775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          # Normally, we'd remove $tmp on exit (right now),
6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          # but fork a child to remove $tmp a little later, so that the
6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          # browser has time to load it first.
6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          delete $main::tempnames{$tmp};
6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if (fork() == 0) {
6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            sleep 5;
6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            unlink($tmp);
6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            exit(0);
6855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          }
6865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
6875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
6885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        cleanup();
6895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        exit(1);
6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
6915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
6925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    InteractiveMode($profile, $symbols, $libs, $total);
6945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  cleanup();
6975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  exit(0);
6985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)##### Entry Point #####
7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Main();
7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Temporary code to detect if we're running on a Goobuntu system.
7055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# These systems don't have the right stuff installed for the special
7065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Readline libraries to work, so as a temporary workaround, we default
7075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# to using the normal stdio code, rather than the fancier readline-based
7085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# code
7095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub ReadlineMightFail {
7105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (-e '/lib/libtermcap.so.2') {
7115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0;  # libtermcap exists, so readline should be okay
7125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
7135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 1;
7145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub RunGV {
7185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $fname = shift;
7195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $bg = shift;       # "" or " &" if we should run in background
7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!system(ShellEscape(@GV, "--version") . " >$dev_null 2>&1")) {
7215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Options using double dash are supported by this gv version.
7225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Also, turn on noantialias to better handle bug in gv for
7235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # postscript files with large dimensions.
7245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # TODO: Maybe we should not pass the --noantialias flag
7255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # if the gv version is known to work properly without the flag.
7265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    system(ShellEscape(@GV, "--scale=$main::opt_scale", "--noantialias", $fname)
7275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           . $bg);
7285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
7295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Old gv version - only supports options that use single dash.
7305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print STDERR ShellEscape(@GV, "-scale", $main::opt_scale) . "\n";
7315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    system(ShellEscape(@GV, "-scale", "$main::opt_scale", $fname) . $bg);
7325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub RunEvince {
7365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $fname = shift;
7375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $bg = shift;       # "" or " &" if we should run in background
7385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  system(ShellEscape(@EVINCE, $fname) . $bg);
7395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub RunWeb {
7425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $fname = shift;
7435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  print STDERR "Loading web page file:///$fname\n";
7445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (`uname` =~ /Darwin/) {
7465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # OS X: open will use standard preference for SVG files.
7475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    system("/usr/bin/open", $fname);
7485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
7495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Some kind of Unix; try generic symlinks, then specific browsers.
7525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # (Stop once we find one.)
7535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Works best if the browser is already running.
7545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my @alt = (
7555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "/etc/alternatives/gnome-www-browser",
7565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "/etc/alternatives/x-www-browser",
7575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "google-chrome",
7585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "firefox",
7595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  );
7605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $b (@alt) {
7615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (system($b, $fname) == 0) {
7625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
7635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  print STDERR "Could not load web browser.\n";
7675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub RunKcachegrind {
7705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $fname = shift;
7715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $bg = shift;       # "" or " &" if we should run in background
7725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  print STDERR "Starting '@KCACHEGRIND " . $fname . $bg . "'\n";
7735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  system(ShellEscape(@KCACHEGRIND, $fname) . $bg);
7745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)##### Interactive helper routines #####
7785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub InteractiveMode {
7805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $| = 1;  # Make output unbuffered for interactive mode
7815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my ($orig_profile, $symbols, $libs, $total) = @_;
7825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  print STDERR "Welcome to pprof!  For help, type 'help'.\n";
7845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Use ReadLine if it's installed and input comes from a console.
7865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ( -t STDIN &&
7875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       !ReadlineMightFail() &&
7885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       defined(eval {require Term::ReadLine}) ) {
7895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $term = new Term::ReadLine 'pprof';
7905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while ( defined ($_ = $term->readline('(pprof) '))) {
7915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $term->addhistory($_) if /\S/;
7925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!InteractiveCommand($orig_profile, $symbols, $libs, $total, $_)) {
7935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        last;    # exit when we get an interactive command to quit
7945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
7955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {       # don't have readline
7975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while (1) {
7985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print STDERR "(pprof) ";
7995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $_ = <STDIN>;
8005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      last if ! defined $_ ;
8015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      s/\r//g;         # turn windows-looking lines into unix-looking lines
8025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Save some flags that might be reset by InteractiveCommand()
8045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $save_opt_lines = $main::opt_lines;
8055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!InteractiveCommand($orig_profile, $symbols, $libs, $total, $_)) {
8075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        last;    # exit when we get an interactive command to quit
8085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
8095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Restore flags
8115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::opt_lines = $save_opt_lines;
8125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
8135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Takes two args: orig profile, and command to run.
8175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Returns 1 if we should keep going, or 0 if we were asked to quit
8185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub InteractiveCommand {
8195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my($orig_profile, $symbols, $libs, $total, $command) = @_;
8205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $_ = $command;                # just to make future m//'s easier
8215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!defined($_)) {
8225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print STDERR "\n";
8235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0;
8245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (m/^\s*quit/) {
8265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0;
8275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (m/^\s*help/) {
8295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    InteractiveHelpMessage();
8305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 1;
8315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Clear all the mode options -- mode is controlled by "$command"
8335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_text = 0;
8345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_callgrind = 0;
8355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_disasm = 0;
8365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_list = 0;
8375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_gv = 0;
8385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_evince = 0;
8395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::opt_cum = 0;
8405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (m/^\s*(text|top)(\d*)\s*(.*)/) {
8425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $main::opt_text = 1;
8435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $line_limit = ($2 ne "") ? int($2) : 10;
8455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $routine;
8475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $ignore;
8485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ($routine, $ignore) = ParseInteractiveArgs($3);
8495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
8515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $reduced = ReduceProfile($symbols, $profile);
8525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Get derived profiles
8545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $flat = FlatProfile($reduced);
8555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $cumulative = CumulativeProfile($reduced);
8565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PrintText($symbols, $flat, $cumulative, $line_limit);
8585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 1;
8595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (m/^\s*callgrind\s*([^ \n]*)/) {
8615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $main::opt_callgrind = 1;
8625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Get derived profiles
8645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $calls = ExtractCalls($symbols, $orig_profile);
8655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $filename = $1;
8665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ( $1 eq '' ) {
8675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $filename = TempName($main::next_tmpfile, "callgrind");
8685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
8695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PrintCallgrind($calls, $filename);
8705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ( $1 eq '' ) {
8715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      RunKcachegrind($filename, " & ");
8725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::next_tmpfile++;
8735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
8745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 1;
8765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (m/^\s*(web)?list\s*(.+)/) {
8785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $html = (defined($1) && ($1 eq "web"));
8795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $main::opt_list = 1;
8805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $routine;
8825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $ignore;
8835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ($routine, $ignore) = ParseInteractiveArgs($2);
8845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
8865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $reduced = ReduceProfile($symbols, $profile);
8875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Get derived profiles
8895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $flat = FlatProfile($reduced);
8905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $cumulative = CumulativeProfile($reduced);
8915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PrintListing($total, $libs, $flat, $cumulative, $routine, $html);
8935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 1;
8945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (m/^\s*disasm\s*(.+)/) {
8965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $main::opt_disasm = 1;
8975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $routine;
8995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $ignore;
9005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ($routine, $ignore) = ParseInteractiveArgs($1);
9015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Process current profile to account for various settings
9035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
9045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $reduced = ReduceProfile($symbols, $profile);
9055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Get derived profiles
9075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $flat = FlatProfile($reduced);
9085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $cumulative = CumulativeProfile($reduced);
9095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PrintDisassembly($libs, $flat, $cumulative, $routine);
9115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 1;
9125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (m/^\s*(gv|web|evince)\s*(.*)/) {
9145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $main::opt_gv = 0;
9155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $main::opt_evince = 0;
9165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $main::opt_web = 0;
9175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($1 eq "gv") {
9185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::opt_gv = 1;
9195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } elsif ($1 eq "evince") {
9205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::opt_evince = 1;
9215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } elsif ($1 eq "web") {
9225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::opt_web = 1;
9235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
9245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $focus;
9265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $ignore;
9275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ($focus, $ignore) = ParseInteractiveArgs($2);
9285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Process current profile to account for various settings
9305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $profile = ProcessProfile($total, $orig_profile, $symbols,
9315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 $focus, $ignore);
9325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $reduced = ReduceProfile($symbols, $profile);
9335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Get derived profiles
9355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $flat = FlatProfile($reduced);
9365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $cumulative = CumulativeProfile($reduced);
9375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (PrintDot($main::prog, $symbols, $profile, $flat, $cumulative, $total)) {
9395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($main::opt_gv) {
9405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        RunGV(TempName($main::next_tmpfile, "ps"), " &");
9415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } elsif ($main::opt_evince) {
9425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        RunEvince(TempName($main::next_tmpfile, "pdf"), " &");
9435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } elsif ($main::opt_web) {
9445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        RunWeb(TempName($main::next_tmpfile, "svg"));
9455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
9465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::next_tmpfile++;
9475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
9485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 1;
9495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (m/^\s*$/) {
9515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 1;
9525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  print STDERR "Unknown command: try 'help'.\n";
9545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 1;
9555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub ProcessProfile {
9595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $total_count = shift;
9605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $orig_profile = shift;
9615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $symbols = shift;
9625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $focus = shift;
9635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $ignore = shift;
9645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Process current profile to account for various settings
9665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $profile = $orig_profile;
9675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  printf("Total: %s %s\n", Unparse($total_count), Units());
9685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($focus ne '') {
9695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $profile = FocusProfile($symbols, $profile, $focus);
9705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $focus_count = TotalProfile($profile);
9715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    printf("After focusing on '%s': %s %s of %s (%0.1f%%)\n",
9725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           $focus,
9735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           Unparse($focus_count), Units(),
9745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           Unparse($total_count), ($focus_count*100.0) / $total_count);
9755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($ignore ne '') {
9775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $profile = IgnoreProfile($symbols, $profile, $ignore);
9785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $ignore_count = TotalProfile($profile);
9795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    printf("After ignoring '%s': %s %s of %s (%0.1f%%)\n",
9805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           $ignore,
9815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           Unparse($ignore_count), Units(),
9825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           Unparse($total_count),
9835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           ($ignore_count*100.0) / $total_count);
9845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $profile;
9875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub InteractiveHelpMessage {
9905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  print STDERR <<ENDOFHELP;
9915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Interactive pprof mode
9925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Commands:
9945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gv
9955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gv [focus] [-ignore1] [-ignore2]
9965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Show graphical hierarchical display of current profile.  Without
9975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      any arguments, shows all samples in the profile.  With the optional
9985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "focus" argument, restricts the samples shown to just those where
9995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      the "focus" regular expression matches a routine name on the stack
10005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      trace.
10015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  web
10035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  web [focus] [-ignore1] [-ignore2]
10045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Like GV, but displays profile in your web browser instead of using
10055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Ghostview. Works best if your web browser is already running.
10065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      To change the browser that gets used:
10075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      On Linux, set the /etc/alternatives/gnome-www-browser symlink.
10085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      On OS X, change the Finder association for SVG files.
10095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  list [routine_regexp] [-ignore1] [-ignore2]
10115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Show source listing of routines whose names match "routine_regexp"
10125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  weblist [routine_regexp] [-ignore1] [-ignore2]
10145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     Displays a source listing of routines whose names match "routine_regexp"
10155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     in a web browser.  You can click on source lines to view the
10165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     corresponding disassembly.
10175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  top [--cum] [-ignore1] [-ignore2]
10195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  top20 [--cum] [-ignore1] [-ignore2]
10205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  top37 [--cum] [-ignore1] [-ignore2]
10215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Show top lines ordered by flat profile count, or cumulative count
10225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if --cum is specified.  If a number is present after 'top', the
10235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      top K routines will be shown (defaults to showing the top 10)
10245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  disasm [routine_regexp] [-ignore1] [-ignore2]
10265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Show disassembly of routines whose names match "routine_regexp",
10275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      annotated with sample counts.
10285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  callgrind
10305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  callgrind [filename]
10315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Generates callgrind file. If no filename is given, kcachegrind is called.
10325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  help - This listing
10345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  quit or ^D - End pprof
10355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)For commands that accept optional -ignore tags, samples where any routine in
10375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)the stack trace matches the regular expression in any of the -ignore
10385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)parameters will be ignored.
10395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Further pprof details are available at this location (or one similar):
10415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) /usr/doc/gperftools-$PPROF_VERSION/cpu_profiler.html
10435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) /usr/doc/gperftools-$PPROF_VERSION/heap_profiler.html
10445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ENDOFHELP
10465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
10475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub ParseInteractiveArgs {
10485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $args = shift;
10495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $focus = "";
10505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $ignore = "";
10515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my @x = split(/ +/, $args);
10525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach $a (@x) {
10535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($a =~ m/^(--|-)lines$/) {
10545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::opt_lines = 1;
10555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } elsif ($a =~ m/^(--|-)cum$/) {
10565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::opt_cum = 1;
10575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } elsif ($a =~ m/^-(.*)/) {
10585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $ignore .= (($ignore ne "") ? "|" : "" ) . $1;
10595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
10605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $focus .= (($focus ne "") ? "|" : "" ) . $a;
10615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
10625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
10635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($ignore ne "") {
10645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print STDERR "Ignoring samples in call stacks that match '$ignore'\n";
10655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
10665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ($focus, $ignore);
10675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
10685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)##### Output code #####
10705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub TempName {
10725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $fnum = shift;
10735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $ext = shift;
10745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $file = "$main::tmpfile_ps.$fnum.$ext";
10755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::tempnames{$file} = 1;
10765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $file;
10775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
10785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Print profile data in packed binary format (64-bit) to standard out
10805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub PrintProfileData {
10815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $profile = shift;
10825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # print header (64-bit style)
10845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # (zero) (header-size) (version) (sample-period) (zero)
10855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  print pack('L*', 0, 0, 3, 0, 0, 0, 1, 0, 0, 0);
10865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $k (keys(%{$profile})) {
10885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $count = $profile->{$k};
10895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my @addrs = split(/\n/, $k);
10905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($#addrs >= 0) {
10915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $depth = $#addrs + 1;
10925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # int(foo / 2**32) is the only reliable way to get rid of bottom
10935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # 32 bits on both 32- and 64-bit systems.
10945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print pack('L*', $count & 0xFFFFFFFF, int($count / 2**32));
10955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print pack('L*', $depth & 0xFFFFFFFF, int($depth / 2**32));
10965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      foreach my $full_addr (@addrs) {
10985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        my $addr = $full_addr;
10995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $addr =~ s/0x0*//;  # strip off leading 0x, zeroes
11005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (length($addr) > 16) {
11015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          print STDERR "Invalid address in profile: $full_addr\n";
11025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          next;
11035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
11045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        my $low_addr = substr($addr, -8);       # get last 8 hex chars
11055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        my $high_addr = substr($addr, -16, 8);  # get up to 8 more hex chars
11065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        print pack('L*', hex('0x' . $low_addr), hex('0x' . $high_addr));
11075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
11085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
11095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
11105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
11115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Print symbols and profile data
11135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub PrintSymbolizedProfile {
11145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $symbols = shift;
11155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $profile = shift;
11165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $prog = shift;
11175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $SYMBOL_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
11195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $symbol_marker = $&;
11205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  print '--- ', $symbol_marker, "\n";
11225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (defined($prog)) {
11235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print 'binary=', $prog, "\n";
11245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
11255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (my ($pc, $name) = each(%{$symbols})) {
11265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $sep = ' ';
11275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print '0x', $pc;
11285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # We have a list of function names, which include the inlined
11295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # calls.  They are separated (and terminated) by --, which is
11305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # illegal in function names.
11315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (my $j = 2; $j <= $#{$name}; $j += 3) {
11325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print $sep, $name->[$j];
11335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $sep = '--';
11345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
11355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print "\n";
11365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
11375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  print '---', "\n";
11385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $PROFILE_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
11405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $profile_marker = $&;
11415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  print '--- ', $profile_marker, "\n";
11425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (defined($main::collected_profile)) {
11435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # if used with remote fetch, simply dump the collected profile to output.
11445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    open(SRC, "<$main::collected_profile");
11455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while (<SRC>) {
11465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print $_;
11475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
11485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    close(SRC);
11495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
11505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # dump a cpu-format profile to standard out
11515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PrintProfileData($profile);
11525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
11535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
11545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Print text output
11565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub PrintText {
11575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $symbols = shift;
11585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $flat = shift;
11595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $cumulative = shift;
11605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $line_limit = shift;
11615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $total = TotalProfile($flat);
11635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Which profile to sort by?
11655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $s = $main::opt_cum ? $cumulative : $flat;
11665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $running_sum = 0;
11685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $lines = 0;
11695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $k (sort { GetEntry($s, $b) <=> GetEntry($s, $a) || $a cmp $b }
11705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 keys(%{$cumulative})) {
11715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $f = GetEntry($flat, $k);
11725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $c = GetEntry($cumulative, $k);
11735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $running_sum += $f;
11745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $sym = $k;
11765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (exists($symbols->{$k})) {
11775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $sym = $symbols->{$k}->[0] . " " . $symbols->{$k}->[1];
11785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($main::opt_addresses) {
11795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $sym = $k . " " . $sym;
11805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
11815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
11825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($f != 0 || $c != 0) {
11845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      printf("%8s %6s %6s %8s %6s %s\n",
11855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             Unparse($f),
11865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             Percent($f, $total),
11875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             Percent($running_sum, $total),
11885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             Unparse($c),
11895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             Percent($c, $total),
11905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             $sym);
11915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
11925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $lines++;
11935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    last if ($line_limit >= 0 && $lines >= $line_limit);
11945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
11955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
11965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Callgrind format has a compression for repeated function and file
11985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# names.  You show the name the first time, and just use its number
11995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# subsequently.  This can cut down the file to about a third or a
12005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# quarter of its uncompressed size.  $key and $val are the key/value
12015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# pair that would normally be printed by callgrind; $map is a map from
12025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# value to number.
12035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub CompressedCGName {
12045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my($key, $val, $map) = @_;
12055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $idx = $map->{$val};
12065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # For very short keys, providing an index hurts rather than helps.
12075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (length($val) <= 3) {
12085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return "$key=$val\n";
12095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } elsif (defined($idx)) {
12105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return "$key=($idx)\n";
12115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
12125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # scalar(keys $map) gives the number of items in the map.
12135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $idx = scalar(keys(%{$map})) + 1;
12145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $map->{$val} = $idx;
12155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return "$key=($idx) $val\n";
12165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
12175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
12185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Print the call graph in a way that's suiteable for callgrind.
12205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub PrintCallgrind {
12215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $calls = shift;
12225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $filename;
12235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my %filename_to_index_map;
12245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my %fnname_to_index_map;
12255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($main::opt_interactive) {
12275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $filename = shift;
12285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print STDERR "Writing callgrind file to '$filename'.\n"
12295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
12305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $filename = "&STDOUT";
12315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
12325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  open(CG, ">$filename");
12335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  printf CG ("events: Hits\n\n");
12345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $call ( map { $_->[0] }
12355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     sort { $a->[1] cmp $b ->[1] ||
12365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            $a->[2] <=> $b->[2] }
12375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     map { /([^:]+):(\d+):([^ ]+)( -> ([^:]+):(\d+):(.+))?/;
12385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           [$_, $1, $2] }
12395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     keys %$calls ) {
12405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $count = int($calls->{$call});
12415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $call =~ /([^:]+):(\d+):([^ ]+)( -> ([^:]+):(\d+):(.+))?/;
12425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my ( $caller_file, $caller_line, $caller_function,
12435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         $callee_file, $callee_line, $callee_function ) =
12445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       ( $1, $2, $3, $5, $6, $7 );
12455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # TODO(csilvers): for better compression, collect all the
12475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # caller/callee_files and functions first, before printing
12485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # anything, and only compress those referenced more than once.
12495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    printf CG CompressedCGName("fl", $caller_file, \%filename_to_index_map);
12505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    printf CG CompressedCGName("fn", $caller_function, \%fnname_to_index_map);
12515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (defined $6) {
12525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      printf CG CompressedCGName("cfl", $callee_file, \%filename_to_index_map);
12535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      printf CG CompressedCGName("cfn", $callee_function, \%fnname_to_index_map);
12545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      printf CG ("calls=$count $callee_line\n");
12555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
12565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    printf CG ("$caller_line $count\n\n");
12575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
12585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
12595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Print disassembly for all all routines that match $main::opt_disasm
12615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub PrintDisassembly {
12625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $libs = shift;
12635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $flat = shift;
12645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $cumulative = shift;
12655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $disasm_opts = shift;
12665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $total = TotalProfile($flat);
12685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $lib (@{$libs}) {
12705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $symbol_table = GetProcedureBoundaries($lib->[0], $disasm_opts);
12715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $offset = AddressSub($lib->[1], $lib->[3]);
12725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    foreach my $routine (sort ByName keys(%{$symbol_table})) {
12735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $start_addr = $symbol_table->{$routine}->[0];
12745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $end_addr = $symbol_table->{$routine}->[1];
12755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # See if there are any samples in this routine
12765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $length = hex(AddressSub($end_addr, $start_addr));
12775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $addr = AddressAdd($start_addr, $offset);
12785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (my $i = 0; $i < $length; $i++) {
12795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (defined($cumulative->{$addr})) {
12805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          PrintDisassembledFunction($lib->[0], $offset,
12815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    $routine, $flat, $cumulative,
12825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    $start_addr, $end_addr, $total);
12835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          last;
12845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
12855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $addr = AddressInc($addr);
12865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
12875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
12885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
12895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
12905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Return reference to array of tuples of the form:
12925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#       [start_address, filename, linenumber, instruction, limit_address]
12935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# E.g.,
12945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#       ["0x806c43d", "/foo/bar.cc", 131, "ret", "0x806c440"]
12955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub Disassemble {
12965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $prog = shift;
12975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $offset = shift;
12985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $start_addr = shift;
12995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $end_addr = shift;
13005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $objdump = $obj_tool_map{"objdump"};
13025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $cmd = ShellEscape($objdump, "-C", "-d", "-l", "--no-show-raw-insn",
13035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        "--start-address=0x$start_addr",
13045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        "--stop-address=0x$end_addr", $prog);
13055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  open(OBJDUMP, "$cmd |") || error("$cmd: $!\n");
13065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my @result = ();
13075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $filename = "";
13085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $linenumber = -1;
13095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $last = ["", "", "", ""];
13105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (<OBJDUMP>) {
13115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    s/\r//g;         # turn windows-looking lines into unix-looking lines
13125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    chop;
13135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (m|\s*([^:\s]+):(\d+)\s*$|) {
13145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Location line of the form:
13155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      #   <filename>:<linenumber>
13165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $filename = $1;
13175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $linenumber = $2;
13185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } elsif (m/^ +([0-9a-f]+):\s*(.*)/) {
13195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Disassembly line -- zero-extend address to full length
13205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $addr = HexExtend($1);
13215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $k = AddressAdd($addr, $offset);
13225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $last->[4] = $k;   # Store ending address for previous instruction
13235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $last = [$k, $filename, $linenumber, $2, $end_addr];
13245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      push(@result, $last);
13255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
13265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
13275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  close(OBJDUMP);
13285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return @result;
13295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
13305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# The input file should contain lines of the form /proc/maps-like
13325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# output (same format as expected from the profiles) or that looks
13335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# like hex addresses (like "0xDEADBEEF").  We will parse all
13345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# /proc/maps output, and for all the hex addresses, we will output
13355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# "short" symbol names, one per line, in the same order as the input.
13365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub PrintSymbols {
13375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $maps_and_symbols_file = shift;
13385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # ParseLibraries expects pcs to be in a set.  Fine by us...
13405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my @pclist = ();   # pcs in sorted order
13415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $pcs = {};
13425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $map = "";
13435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $line (<$maps_and_symbols_file>) {
13445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $line =~ s/\r//g;    # turn windows-looking lines into unix-looking lines
13455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($line =~ /\b(0x[0-9a-f]+)\b/i) {
13465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      push(@pclist, HexExtend($1));
13475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $pcs->{$pclist[-1]} = 1;
13485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
13495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $map .= $line;
13505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
13515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
13525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $libs = ParseLibraries($main::prog, $map, $pcs);
13545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $symbols = ExtractSymbols($libs, $pcs);
13555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $pc (@pclist) {
13575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # ->[0] is the shortname, ->[2] is the full name
13585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print(($symbols->{$pc}->[0] || "??") . "\n");
13595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
13605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
13615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# For sorting functions by name
13645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub ByName {
13655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ShortFunctionName($a) cmp ShortFunctionName($b);
13665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
13675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Print source-listing for all all routines that match $list_opts
13695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub PrintListing {
13705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $total = shift;
13715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $libs = shift;
13725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $flat = shift;
13735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $cumulative = shift;
13745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $list_opts = shift;
13755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $html = shift;
13765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $output = \*STDOUT;
13785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $fname = "";
13795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($html) {
13815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Arrange to write the output to a temporary file
13825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $fname = TempName($main::next_tmpfile, "html");
13835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $main::next_tmpfile++;
13845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!open(TEMP, ">$fname")) {
13855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print STDERR "$fname: $!\n";
13865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
13875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
13885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $output = \*TEMP;
13895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print $output HtmlListingHeader();
13905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    printf $output ("<div class=\"legend\">%s<br>Total: %s %s</div>\n",
13915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    $main::prog, Unparse($total), Units());
13925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
13935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $listed = 0;
13955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $lib (@{$libs}) {
13965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $symbol_table = GetProcedureBoundaries($lib->[0], $list_opts);
13975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $offset = AddressSub($lib->[1], $lib->[3]);
13985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    foreach my $routine (sort ByName keys(%{$symbol_table})) {
13995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Print if there are any samples in this routine
14005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $start_addr = $symbol_table->{$routine}->[0];
14015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $end_addr = $symbol_table->{$routine}->[1];
14025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $length = hex(AddressSub($end_addr, $start_addr));
14035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $addr = AddressAdd($start_addr, $offset);
14045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (my $i = 0; $i < $length; $i++) {
14055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (defined($cumulative->{$addr})) {
14065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          $listed += PrintSource(
14075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            $lib->[0], $offset,
14085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            $routine, $flat, $cumulative,
14095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            $start_addr, $end_addr,
14105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            $html,
14115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            $output);
14125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          last;
14135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
14145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $addr = AddressInc($addr);
14155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
14165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
14175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
14185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($html) {
14205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($listed > 0) {
14215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print $output HtmlListingFooter();
14225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      close($output);
14235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      RunWeb($fname);
14245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
14255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      close($output);
14265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      unlink($fname);
14275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
14285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
14295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
14305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub HtmlListingHeader {
14325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return <<'EOF';
14335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)<DOCTYPE html>
14345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)<html>
14355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)<head>
14365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)<title>Pprof listing</title>
14375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)<style type="text/css">
14385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)body {
14395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  font-family: sans-serif;
14405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
14415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)h1 {
14425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  font-size: 1.5em;
14435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  margin-bottom: 4px;
14445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
14455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles).legend {
14465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  font-size: 1.25em;
14475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
14485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles).line {
14495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  color: #aaaaaa;
14505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
14515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles).nop {
14525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  color: #aaaaaa;
14535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
14545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles).unimportant {
14555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  color: #cccccc;
14565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
14575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles).disasmloc {
14585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  color: #000000;
14595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
14605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles).deadsrc {
14615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  cursor: pointer;
14625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
14635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles).deadsrc:hover {
14645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  background-color: #eeeeee;
14655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
14665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles).livesrc {
14675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  color: #0000ff;
14685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  cursor: pointer;
14695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
14705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles).livesrc:hover {
14715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  background-color: #eeeeee;
14725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
14735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles).asm {
14745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  color: #008800;
14755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  display: none;
14765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
14775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)</style>
14785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)<script type="text/javascript">
14795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function pprof_toggle_asm(e) {
14805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var target;
14815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!e) e = window.event;
14825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (e.target) target = e.target;
14835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else if (e.srcElement) target = e.srcElement;
14845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (target) {
14865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var asm = target.nextSibling;
14875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (asm && asm.className == "asm") {
14885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      asm.style.display = (asm.style.display == "block" ? "" : "block");
14895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      e.preventDefault();
14905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
14915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
14925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
14935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
14945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)</script>
14955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)</head>
14965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)<body>
14975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)EOF
14985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
14995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub HtmlListingFooter {
15015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return <<'EOF';
15025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)</body>
15035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)</html>
15045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)EOF
15055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
15065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub HtmlEscape {
15085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $text = shift;
15095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $text =~ s/&/&amp;/g;
15105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $text =~ s/</&lt;/g;
15115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $text =~ s/>/&gt;/g;
15125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $text;
15135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
15145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Returns the indentation of the line, if it has any non-whitespace
15165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# characters.  Otherwise, returns -1.
15175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub Indentation {
15185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $line = shift;
15195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (m/^(\s*)\S/) {
15205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return length($1);
15215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
15225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return -1;
15235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
15245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
15255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# If the symbol table contains inlining info, Disassemble() may tag an
15275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# instruction with a location inside an inlined function.  But for
15285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# source listings, we prefer to use the location in the function we
15295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# are listing.  So use MapToSymbols() to fetch full location
15305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# information for each instruction and then pick out the first
15315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# location from a location list (location list contains callers before
15325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# callees in case of inlining).
15335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
15345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# After this routine has run, each entry in $instructions contains:
15355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   [0] start address
15365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   [1] filename for function we are listing
15375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   [2] line number for function we are listing
15385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   [3] disassembly
15395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   [4] limit address
15405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   [5] most specific filename (may be different from [1] due to inlining)
15415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   [6] most specific line number (may be different from [2] due to inlining)
15425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub GetTopLevelLineNumbers {
15435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my ($lib, $offset, $instructions) = @_;
15445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $pcs = [];
15455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (my $i = 0; $i <= $#{$instructions}; $i++) {
15465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    push(@{$pcs}, $instructions->[$i]->[0]);
15475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
15485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $symbols = {};
15495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MapToSymbols($lib, $offset, $pcs, $symbols);
15505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (my $i = 0; $i <= $#{$instructions}; $i++) {
15515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $e = $instructions->[$i];
15525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    push(@{$e}, $e->[1]);
15535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    push(@{$e}, $e->[2]);
15545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $addr = $e->[0];
15555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $sym = $symbols->{$addr};
15565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (defined($sym)) {
15575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($#{$sym} >= 2 && $sym->[1] =~ m/^(.*):(\d+)$/) {
15585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $e->[1] = $1;  # File name
15595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $e->[2] = $2;  # Line number
15605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
15615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
15625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
15635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
15645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Print source-listing for one routine
15665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub PrintSource {
15675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $prog = shift;
15685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $offset = shift;
15695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $routine = shift;
15705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $flat = shift;
15715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $cumulative = shift;
15725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $start_addr = shift;
15735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $end_addr = shift;
15745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $html = shift;
15755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $output = shift;
15765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Disassemble all instructions (just to get line numbers)
15785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr);
15795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GetTopLevelLineNumbers($prog, $offset, \@instructions);
15805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Hack 1: assume that the first source file encountered in the
15825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # disassembly contains the routine
15835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $filename = undef;
15845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (my $i = 0; $i <= $#instructions; $i++) {
15855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($instructions[$i]->[2] >= 0) {
15865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $filename = $instructions[$i]->[1];
15875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      last;
15885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
15895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
15905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!defined($filename)) {
15915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print STDERR "no filename found in $routine\n";
15925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0;
15935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
15945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Hack 2: assume that the largest line number from $filename is the
15965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # end of the procedure.  This is typically safe since if P1 contains
15975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # an inlined call to P2, then P2 usually occurs earlier in the
15985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # source file.  If this does not work, we might have to compute a
15995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # density profile or just print all regions we find.
16005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $lastline = 0;
16015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (my $i = 0; $i <= $#instructions; $i++) {
16025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $f = $instructions[$i]->[1];
16035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $l = $instructions[$i]->[2];
16045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (($f eq $filename) && ($l > $lastline)) {
16055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $lastline = $l;
16065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
16075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
16085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Hack 3: assume the first source location from "filename" is the start of
16105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # the source code.
16115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $firstline = 1;
16125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (my $i = 0; $i <= $#instructions; $i++) {
16135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($instructions[$i]->[1] eq $filename) {
16145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $firstline = $instructions[$i]->[2];
16155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      last;
16165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
16175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
16185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Hack 4: Extend last line forward until its indentation is less than
16205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # the indentation we saw on $firstline
16215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $oldlastline = $lastline;
16225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  {
16235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!open(FILE, "<$filename")) {
16245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print STDERR "$filename: $!\n";
16255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return 0;
16265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
16275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $l = 0;
16285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $first_indentation = -1;
16295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while (<FILE>) {
16305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      s/\r//g;         # turn windows-looking lines into unix-looking lines
16315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $l++;
16325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $indent = Indentation($_);
16335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($l >= $firstline) {
16345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if ($first_indentation < 0 && $indent >= 0) {
16355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          $first_indentation = $indent;
16365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          last if ($first_indentation == 0);
16375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
16385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
16395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($l >= $lastline && $indent >= 0) {
16405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if ($indent >= $first_indentation) {
16415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          $lastline = $l+1;
16425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        } else {
16435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          last;
16445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
16455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
16465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
16475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    close(FILE);
16485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
16495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Assign all samples to the range $firstline,$lastline,
16515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Hack 4: If an instruction does not occur in the range, its samples
16525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # are moved to the next instruction that occurs in the range.
16535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $samples1 = {};        # Map from line number to flat count
16545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $samples2 = {};        # Map from line number to cumulative count
16555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $running1 = 0;         # Unassigned flat counts
16565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $running2 = 0;         # Unassigned cumulative counts
16575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $total1 = 0;           # Total flat counts
16585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $total2 = 0;           # Total cumulative counts
16595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my %disasm = ();          # Map from line number to disassembly
16605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $running_disasm = "";  # Unassigned disassembly
16615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $skip_marker = "---\n";
16625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($html) {
16635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $skip_marker = "";
16645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (my $l = $firstline; $l <= $lastline; $l++) {
16655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $disasm{$l} = "";
16665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
16675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
16685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $last_dis_filename = '';
16695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $last_dis_linenum = -1;
16705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $last_touched_line = -1;  # To detect gaps in disassembly for a line
16715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $e (@instructions) {
16725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Add up counts for all address that fall inside this instruction
16735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $c1 = 0;
16745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $c2 = 0;
16755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (my $a = $e->[0]; $a lt $e->[4]; $a = AddressInc($a)) {
16765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $c1 += GetEntry($flat, $a);
16775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $c2 += GetEntry($cumulative, $a);
16785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
16795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($html) {
16815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $dis = sprintf("      %6s %6s \t\t%8s: %s ",
16825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        HtmlPrintNumber($c1),
16835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        HtmlPrintNumber($c2),
16845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        UnparseAddress($offset, $e->[0]),
16855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        CleanDisassembly($e->[3]));
16865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      
16875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Append the most specific source line associated with this instruction
16885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (length($dis) < 80) { $dis .= (' ' x (80 - length($dis))) };
16895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $dis = HtmlEscape($dis);
16905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $f = $e->[5];
16915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $l = $e->[6];
16925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($f ne $last_dis_filename) {
16935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $dis .= sprintf("<span class=disasmloc>%s:%d</span>", 
16945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        HtmlEscape(CleanFileName($f)), $l);
16955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } elsif ($l ne $last_dis_linenum) {
16965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # De-emphasize the unchanged file name portion
16975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $dis .= sprintf("<span class=unimportant>%s</span>" .
16985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        "<span class=disasmloc>:%d</span>", 
16995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        HtmlEscape(CleanFileName($f)), $l);
17005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
17015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # De-emphasize the entire location
17025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $dis .= sprintf("<span class=unimportant>%s:%d</span>", 
17035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        HtmlEscape(CleanFileName($f)), $l);
17045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
17055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $last_dis_filename = $f;
17065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $last_dis_linenum = $l;
17075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $running_disasm .= $dis;
17085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $running_disasm .= "\n";
17095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
17105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $running1 += $c1;
17125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $running2 += $c2;
17135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $total1 += $c1;
17145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $total2 += $c2;
17155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $file = $e->[1];
17165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $line = $e->[2];
17175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (($file eq $filename) &&
17185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ($line >= $firstline) &&
17195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ($line <= $lastline)) {
17205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Assign all accumulated samples to this line
17215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      AddEntry($samples1, $line, $running1);
17225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      AddEntry($samples2, $line, $running2);
17235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $running1 = 0;
17245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $running2 = 0;
17255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($html) {
17265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if ($line != $last_touched_line && $disasm{$line} ne '') {
17275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          $disasm{$line} .= "\n";
17285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
17295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $disasm{$line} .= $running_disasm;
17305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $running_disasm = '';
17315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $last_touched_line = $line;
17325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
17335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
17345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
17355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Assign any leftover samples to $lastline
17375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AddEntry($samples1, $lastline, $running1);
17385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AddEntry($samples2, $lastline, $running2);
17395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($html) {
17405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($lastline != $last_touched_line && $disasm{$lastline} ne '') {
17415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $disasm{$lastline} .= "\n";
17425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
17435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $disasm{$lastline} .= $running_disasm;
17445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
17455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($html) {
17475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    printf $output (
17485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "<h1>%s</h1>%s\n<pre onClick=\"pprof_toggle_asm()\">\n" .
17495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "Total:%6s %6s (flat / cumulative %s)\n",
17505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      HtmlEscape(ShortFunctionName($routine)),
17515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      HtmlEscape(CleanFileName($filename)),
17525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Unparse($total1),
17535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Unparse($total2),
17545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Units());
17555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
17565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    printf $output (
17575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "ROUTINE ====================== %s in %s\n" .
17585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "%6s %6s Total %s (flat / cumulative)\n",
17595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ShortFunctionName($routine),
17605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      CleanFileName($filename),
17615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Unparse($total1),
17625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Unparse($total2),
17635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Units());
17645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
17655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!open(FILE, "<$filename")) {
17665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print STDERR "$filename: $!\n";
17675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0;
17685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
17695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $l = 0;
17705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (<FILE>) {
17715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    s/\r//g;         # turn windows-looking lines into unix-looking lines
17725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $l++;
17735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($l >= $firstline - 5 &&
17745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        (($l <= $oldlastline + 5) || ($l <= $lastline))) {
17755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      chop;
17765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $text = $_;
17775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($l == $firstline) { print $output $skip_marker; }
17785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $n1 = GetEntry($samples1, $l);
17795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $n2 = GetEntry($samples2, $l);
17805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($html) {
17815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # Emit a span that has one of the following classes:
17825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        #    livesrc -- has samples
17835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        #    deadsrc -- has disassembly, but with no samples
17845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        #    nop     -- has no matching disasembly
17855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # Also emit an optional span containing disassembly.
17865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        my $dis = $disasm{$l};
17875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        my $asm = "";
17885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (defined($dis) && $dis ne '') {
17895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          $asm = "<span class=\"asm\">" . $dis . "</span>";
17905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
17915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        my $source_class = (($n1 + $n2 > 0) 
17925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            ? "livesrc" 
17935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            : (($asm ne "") ? "deadsrc" : "nop"));
17945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        printf $output (
17955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "<span class=\"line\">%5d</span> " .
17965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "<span class=\"%s\">%6s %6s %s</span>%s\n",
17975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          $l, $source_class,
17985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          HtmlPrintNumber($n1),
17995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          HtmlPrintNumber($n2),
18005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          HtmlEscape($text),
18015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          $asm);
18025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
18035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        printf $output(
18045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "%6s %6s %4d: %s\n",
18055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          UnparseAlt($n1),
18065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          UnparseAlt($n2),
18075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          $l,
18085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          $text);
18095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
18105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($l == $lastline)  { print $output $skip_marker; }
18115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    };
18125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
18135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  close(FILE);
18145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($html) {
18155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print $output "</pre>\n";
18165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
18175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 1;
18185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
18195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Return the source line for the specified file/linenumber.
18215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Returns undef if not found.
18225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub SourceLine {
18235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $file = shift;
18245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $line = shift;
18255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Look in cache
18275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!defined($main::source_cache{$file})) {
18285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (100 < scalar keys(%main::source_cache)) {
18295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Clear the cache when it gets too big
18305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::source_cache = ();
18315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
18325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Read all lines from the file
18345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!open(FILE, "<$file")) {
18355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print STDERR "$file: $!\n";
18365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::source_cache{$file} = [];  # Cache the negative result
18375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return undef;
18385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
18395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $lines = [];
18405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    push(@{$lines}, "");        # So we can use 1-based line numbers as indices
18415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while (<FILE>) {
18425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      push(@{$lines}, $_);
18435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
18445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    close(FILE);
18455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Save the lines in the cache
18475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $main::source_cache{$file} = $lines;
18485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
18495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $lines = $main::source_cache{$file};
18515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (($line < 0) || ($line > $#{$lines})) {
18525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return undef;
18535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
18545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return $lines->[$line];
18555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
18565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
18575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Print disassembly for one routine with interspersed source if available
18595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub PrintDisassembledFunction {
18605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $prog = shift;
18615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $offset = shift;
18625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $routine = shift;
18635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $flat = shift;
18645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $cumulative = shift;
18655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $start_addr = shift;
18665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $end_addr = shift;
18675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $total = shift;
18685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Disassemble all instructions
18705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr);
18715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Make array of counts per instruction
18735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my @flat_count = ();
18745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my @cum_count = ();
18755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $flat_total = 0;
18765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $cum_total = 0;
18775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $e (@instructions) {
18785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Add up counts for all address that fall inside this instruction
18795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $c1 = 0;
18805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $c2 = 0;
18815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (my $a = $e->[0]; $a lt $e->[4]; $a = AddressInc($a)) {
18825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $c1 += GetEntry($flat, $a);
18835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $c2 += GetEntry($cumulative, $a);
18845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
18855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    push(@flat_count, $c1);
18865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    push(@cum_count, $c2);
18875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $flat_total += $c1;
18885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $cum_total += $c2;
18895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
18905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Print header with total counts
18925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  printf("ROUTINE ====================== %s\n" .
18935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         "%6s %6s %s (flat, cumulative) %.1f%% of total\n",
18945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         ShortFunctionName($routine),
18955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         Unparse($flat_total),
18965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         Unparse($cum_total),
18975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         Units(),
18985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         ($cum_total * 100.0) / $total);
18995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Process instructions in order
19015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $current_file = "";
19025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (my $i = 0; $i <= $#instructions; ) {
19035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $e = $instructions[$i];
19045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Print the new file name whenever we switch files
19065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($e->[1] ne $current_file) {
19075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $current_file = $e->[1];
19085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $fname = $current_file;
19095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $fname =~ s|^\./||;   # Trim leading "./"
19105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Shorten long file names
19125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (length($fname) >= 58) {
19135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $fname = "..." . substr($fname, -55);
19145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
19155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      printf("-------------------- %s\n", $fname);
19165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
19175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # TODO: Compute range of lines to print together to deal with
19195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # small reorderings.
19205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $first_line = $e->[2];
19215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $last_line = $first_line;
19225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my %flat_sum = ();
19235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my %cum_sum = ();
19245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (my $l = $first_line; $l <= $last_line; $l++) {
19255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $flat_sum{$l} = 0;
19265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $cum_sum{$l} = 0;
19275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
19285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Find run of instructions for this range of source lines
19305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $first_inst = $i;
19315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while (($i <= $#instructions) &&
19325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           ($instructions[$i]->[2] >= $first_line) &&
19335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           ($instructions[$i]->[2] <= $last_line)) {
19345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $e = $instructions[$i];
19355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $flat_sum{$e->[2]} += $flat_count[$i];
19365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $cum_sum{$e->[2]} += $cum_count[$i];
19375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $i++;
19385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
19395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $last_inst = $i - 1;
19405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Print source lines
19425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (my $l = $first_line; $l <= $last_line; $l++) {
19435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $line = SourceLine($current_file, $l);
19445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!defined($line)) {
19455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $line = "?\n";
19465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        next;
19475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
19485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $line =~ s/^\s+//;
19495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
19505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      printf("%6s %6s %5d: %s",
19515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             UnparseAlt($flat_sum{$l}),
19525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             UnparseAlt($cum_sum{$l}),
19535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             $l,
19545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             $line);
19555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
19565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Print disassembly
19585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (my $x = $first_inst; $x <= $last_inst; $x++) {
19595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $e = $instructions[$x];
19605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      printf("%6s %6s    %8s: %6s\n",
19615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             UnparseAlt($flat_count[$x]),
19625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             UnparseAlt($cum_count[$x]),
19635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             UnparseAddress($offset, $e->[0]),
19645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             CleanDisassembly($e->[3]));
19655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
19665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
19675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
19685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Print DOT graph
19705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub PrintDot {
19715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $prog = shift;
19725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $symbols = shift;
19735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $raw = shift;
19745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $flat = shift;
19755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $cumulative = shift;
19765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $overall_total = shift;
19775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Get total
19795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $local_total = TotalProfile($flat);
19805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $nodelimit = int($main::opt_nodefraction * $local_total);
19815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $edgelimit = int($main::opt_edgefraction * $local_total);
19825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $nodecount = $main::opt_nodecount;
19835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Find nodes to include
19855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my @list = (sort { abs(GetEntry($cumulative, $b)) <=>
19865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     abs(GetEntry($cumulative, $a))
19875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     || $a cmp $b }
19885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              keys(%{$cumulative}));
19895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $last = $nodecount - 1;
19905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($last > $#list) {
19915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $last = $#list;
19925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
19935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (($last >= 0) &&
19945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         (abs(GetEntry($cumulative, $list[$last])) <= $nodelimit)) {
19955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $last--;
19965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
19975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($last < 0) {
19985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print STDERR "No nodes to print\n";
19995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0;
20005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
20015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($nodelimit > 0 || $edgelimit > 0) {
20035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    printf STDERR ("Dropping nodes with <= %s %s; edges with <= %s abs(%s)\n",
20045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   Unparse($nodelimit), Units(),
20055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   Unparse($edgelimit), Units());
20065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
20075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Open DOT output file
20095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $output;
20105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $escaped_dot = ShellEscape(@DOT);
20115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $escaped_ps2pdf = ShellEscape(@PS2PDF);
20125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($main::opt_gv) {
20135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $escaped_outfile = ShellEscape(TempName($main::next_tmpfile, "ps"));
20145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $output = "| $escaped_dot -Tps2 >$escaped_outfile";
20155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } elsif ($main::opt_evince) {
20165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $escaped_outfile = ShellEscape(TempName($main::next_tmpfile, "pdf"));
20175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $output = "| $escaped_dot -Tps2 | $escaped_ps2pdf - $escaped_outfile";
20185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } elsif ($main::opt_ps) {
20195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $output = "| $escaped_dot -Tps2";
20205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } elsif ($main::opt_pdf) {
20215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $output = "| $escaped_dot -Tps2 | $escaped_ps2pdf - -";
20225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } elsif ($main::opt_web || $main::opt_svg) {
20235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # We need to post-process the SVG, so write to a temporary file always.
20245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $escaped_outfile = ShellEscape(TempName($main::next_tmpfile, "svg"));
20255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $output = "| $escaped_dot -Tsvg >$escaped_outfile";
20265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } elsif ($main::opt_gif) {
20275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $output = "| $escaped_dot -Tgif";
20285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
20295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $output = ">&STDOUT";
20305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
20315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  open(DOT, $output) || error("$output: $!\n");
20325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Title
20345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  printf DOT ("digraph \"%s; %s %s\" {\n",
20355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              $prog,
20365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              Unparse($overall_total),
20375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              Units());
20385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($main::opt_pdf) {
20395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # The output is more printable if we set the page size for dot.
20405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    printf DOT ("size=\"8,11\"\n");
20415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
20425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  printf DOT ("node [width=0.375,height=0.25];\n");
20435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Print legend
20455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  printf DOT ("Legend [shape=box,fontsize=24,shape=plaintext," .
20465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              "label=\"%s\\l%s\\l%s\\l%s\\l%s\\l\"];\n",
20475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              $prog,
20485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              sprintf("Total %s: %s", Units(), Unparse($overall_total)),
20495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              sprintf("Focusing on: %s", Unparse($local_total)),
20505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              sprintf("Dropped nodes with <= %s abs(%s)",
20515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      Unparse($nodelimit), Units()),
20525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              sprintf("Dropped edges with <= %s %s",
20535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      Unparse($edgelimit), Units())
20545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              );
20555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Print nodes
20575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my %node = ();
20585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $nextnode = 1;
20595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $a (@list[0..$last]) {
20605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Pick font size
20615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $f = GetEntry($flat, $a);
20625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $c = GetEntry($cumulative, $a);
20635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $fs = 8;
20655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($local_total > 0) {
20665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $fs = 8 + (50.0 * sqrt(abs($f * 1.0 / $local_total)));
20675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
20685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $node{$a} = $nextnode++;
20705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $sym = $a;
20715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $sym =~ s/\s+/\\n/g;
20725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $sym =~ s/::/\\n/g;
20735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Extra cumulative info to print for non-leaves
20755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $extra = "";
20765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($f != $c) {
20775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $extra = sprintf("\\rof %s (%s)",
20785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       Unparse($c),
20795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       Percent($c, $local_total));
20805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
20815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $style = "";
20825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($main::opt_heapcheck) {
20835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($f > 0) {
20845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # make leak-causing nodes more visible (add a background)
20855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $style = ",style=filled,fillcolor=gray"
20865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } elsif ($f < 0) {
20875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # make anti-leak-causing nodes (which almost never occur)
20885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # stand out as well (triple border)
20895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $style = ",peripheries=3"
20905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
20915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
20925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    printf DOT ("N%d [label=\"%s\\n%s (%s)%s\\r" .
20945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                "\",shape=box,fontsize=%.1f%s];\n",
20955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                $node{$a},
20965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                $sym,
20975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                Unparse($f),
20985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                Percent($f, $local_total),
20995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                $extra,
21005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                $fs,
21015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                $style,
21025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               );
21035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
21045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Get edges and counts per edge
21065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my %edge = ();
21075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $n;
21085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $fullname_to_shortname_map = {};
21095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FillFullnameToShortnameMap($symbols, $fullname_to_shortname_map);
21105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $k (keys(%{$raw})) {
21115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # TODO: omit low %age edges
21125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $n = $raw->{$k};
21135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my @translated = TranslateStack($symbols, $fullname_to_shortname_map, $k);
21145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (my $i = 1; $i <= $#translated; $i++) {
21155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $src = $translated[$i];
21165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $dst = $translated[$i-1];
21175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      #next if ($src eq $dst);  # Avoid self-edges?
21185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (exists($node{$src}) && exists($node{$dst})) {
21195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        my $edge_label = "$src\001$dst";
21205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (!exists($edge{$edge_label})) {
21215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          $edge{$edge_label} = 0;
21225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
21235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $edge{$edge_label} += $n;
21245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
21255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
21265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
21275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Print edges (process in order of decreasing counts)
21295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my %indegree = ();   # Number of incoming edges added per node so far
21305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my %outdegree = ();  # Number of outgoing edges added per node so far
21315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $e (sort { $edge{$b} <=> $edge{$a} } keys(%edge)) {
21325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my @x = split(/\001/, $e);
21335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $n = $edge{$e};
21345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Initialize degree of kept incoming and outgoing edges if necessary
21365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $src = $x[0];
21375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $dst = $x[1];
21385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!exists($outdegree{$src})) { $outdegree{$src} = 0; }
21395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!exists($indegree{$dst})) { $indegree{$dst} = 0; }
21405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $keep;
21425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($indegree{$dst} == 0) {
21435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Keep edge if needed for reachability
21445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $keep = 1;
21455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } elsif (abs($n) <= $edgelimit) {
21465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Drop if we are below --edgefraction
21475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $keep = 0;
21485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } elsif ($outdegree{$src} >= $main::opt_maxdegree ||
21495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             $indegree{$dst} >= $main::opt_maxdegree) {
21505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Keep limited number of in/out edges per node
21515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $keep = 0;
21525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
21535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $keep = 1;
21545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
21555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($keep) {
21575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $outdegree{$src}++;
21585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $indegree{$dst}++;
21595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Compute line width based on edge count
21615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $fraction = abs($local_total ? (3 * ($n / $local_total)) : 0);
21625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($fraction > 1) { $fraction = 1; }
21635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $w = $fraction * 2;
21645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($w < 1 && ($main::opt_web || $main::opt_svg)) {
21655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # SVG output treats line widths < 1 poorly.
21665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $w = 1;
21675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
21685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Dot sometimes segfaults if given edge weights that are too large, so
21705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # we cap the weights at a large value
21715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $edgeweight = abs($n) ** 0.7;
21725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($edgeweight > 100000) { $edgeweight = 100000; }
21735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $edgeweight = int($edgeweight);
21745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $style = sprintf("setlinewidth(%f)", $w);
21765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($x[1] =~ m/\(inline\)/) {
21775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $style .= ",dashed";
21785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
21795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Use a slightly squashed function of the edge count as the weight
21815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      printf DOT ("N%s -> N%s [label=%s, weight=%d, style=\"%s\"];\n",
21825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  $node{$x[0]},
21835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  $node{$x[1]},
21845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  Unparse($n),
21855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  $edgeweight,
21865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  $style);
21875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
21885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
21895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  print DOT ("}\n");
21915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  close(DOT);
21925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($main::opt_web || $main::opt_svg) {
21945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Rewrite SVG to be more usable inside web browser.
21955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    RewriteSvg(TempName($main::next_tmpfile, "svg"));
21965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
21975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 1;
21995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
22005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
22015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub RewriteSvg {
22025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $svgfile = shift;
22035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
22045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  open(SVG, $svgfile) || die "open temp svg: $!";
22055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my @svg = <SVG>;
22065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  close(SVG);
22075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  unlink $svgfile;
22085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $svg = join('', @svg);
22095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
22105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Dot's SVG output is
22115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #
22125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #    <svg width="___" height="___"
22135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #     viewBox="___" xmlns=...>
22145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #    <g id="graph0" transform="...">
22155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #    ...
22165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #    </g>
22175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #    </svg>
22185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #
22195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Change it to
22205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #
22215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #    <svg width="100%" height="100%"
22225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #     xmlns=...>
22235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #    $svg_javascript
22245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #    <g id="viewport" transform="translate(0,0)">
22255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #    <g id="graph0" transform="...">
22265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #    ...
22275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #    </g>
22285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #    </g>
22295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #    </svg>
22305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
22315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Fix width, height; drop viewBox.
22325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $svg =~ s/(?s)<svg width="[^"]+" height="[^"]+"(.*?)viewBox="[^"]+"/<svg width="100%" height="100%"$1/;
22335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
22345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Insert script, viewport <g> above first <g>
22355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $svg_javascript = SvgJavascript();
22365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $viewport = "<g id=\"viewport\" transform=\"translate(0,0)\">\n";
22375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $svg =~ s/<g id="graph\d"/$svg_javascript$viewport$&/;
22385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
22395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Insert final </g> above </svg>.
22405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $svg =~ s/(.*)(<\/svg>)/$1<\/g>$2/;
22415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $svg =~ s/<g id="graph\d"(.*?)/<g id="viewport"$1/;
22425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
22435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($main::opt_svg) {
22445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # --svg: write to standard output.
22455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print $svg;
22465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
22475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Write back to temporary file.
22485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    open(SVG, ">$svgfile") || die "open $svgfile: $!";
22495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print SVG $svg;
22505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    close(SVG);
22515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
22525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
22535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
22545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub SvgJavascript {
22555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return <<'EOF';
22565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)<script type="text/ecmascript"><![CDATA[
22575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// SVGPan
22585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// http://www.cyberz.org/blog/2009/12/08/svgpan-a-javascript-svg-panzoomdrag-library/
22595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Local modification: if(true || ...) below to force panning, never moving.
22605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
22615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
22625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *  SVGPan library 1.2
22635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * ====================
22645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
22655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Given an unique existing element with id "viewport", including the
22665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the library into any SVG adds the following capabilities:
22675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
22685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *  - Mouse panning
22695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *  - Mouse zooming (using the wheel)
22705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *  - Object dargging
22715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
22725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Known issues:
22735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
22745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *  - Zooming (while panning) on Safari has still some issues
22755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
22765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Releases:
22775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
22785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 1.2, Sat Mar 20 08:42:50 GMT 2010, Zeng Xiaohui
22795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *	Fixed a bug with browser mouse handler interaction
22805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
22815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 1.1, Wed Feb  3 17:39:33 GMT 2010, Zeng Xiaohui
22825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *	Updated the zoom code to support the mouse wheel on Safari/Chrome
22835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
22845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 1.0, Andrea Leofreddi
22855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *	First release
22865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
22875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * This code is licensed under the following BSD license:
22885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
22895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Copyright 2009-2010 Andrea Leofreddi <a.leofreddi@itcharm.com>. All rights reserved.
22905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
22915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Redistribution and use in source and binary forms, with or without modification, are
22925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * permitted provided that the following conditions are met:
22935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
22945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *    1. Redistributions of source code must retain the above copyright notice, this list of
22955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *       conditions and the following disclaimer.
22965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
22975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *    2. Redistributions in binary form must reproduce the above copyright notice, this list
22985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *       of conditions and the following disclaimer in the documentation and/or other materials
22995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *       provided with the distribution.
23005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
23015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * THIS SOFTWARE IS PROVIDED BY Andrea Leofreddi ``AS IS'' AND ANY EXPRESS OR IMPLIED
23025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
23035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Andrea Leofreddi OR
23045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
23075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
23095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
23115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * The views and conclusions contained in the software and documentation are those of the
23125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * authors and should not be interpreted as representing official policies, either expressed
23135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * or implied, of Andrea Leofreddi.
23145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
23155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var root = document.documentElement;
23175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var state = 'none', stateTarget, stateOrigin, stateTf;
23195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)setupHandlers(root);
23215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
23235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Register handlers
23245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
23255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function setupHandlers(root){
23265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	setAttributes(root, {
23275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		"onmouseup" : "add(evt)",
23285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		"onmousedown" : "handleMouseDown(evt)",
23295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		"onmousemove" : "handleMouseMove(evt)",
23305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		"onmouseup" : "handleMouseUp(evt)",
23315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		//"onmouseout" : "handleMouseUp(evt)", // Decomment this to stop the pan functionality when dragging out of the SVG element
23325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	});
23335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if(navigator.userAgent.toLowerCase().indexOf('webkit') >= 0)
23355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		window.addEventListener('mousewheel', handleMouseWheel, false); // Chrome/Safari
23365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	else
23375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		window.addEventListener('DOMMouseScroll', handleMouseWheel, false); // Others
23385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	var g = svgDoc.getElementById("svg");
23405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	g.width = "100%";
23415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	g.height = "100%";
23425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
23435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
23455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Instance an SVGPoint object with given event coordinates.
23465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
23475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function getEventPoint(evt) {
23485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	var p = root.createSVGPoint();
23495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	p.x = evt.clientX;
23515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	p.y = evt.clientY;
23525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	return p;
23545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
23555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
23575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Sets the current transform matrix of an element.
23585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
23595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function setCTM(element, matrix) {
23605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	var s = "matrix(" + matrix.a + "," + matrix.b + "," + matrix.c + "," + matrix.d + "," + matrix.e + "," + matrix.f + ")";
23615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	element.setAttribute("transform", s);
23635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
23645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
23665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Dumps a matrix to a string (useful for debug).
23675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
23685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function dumpMatrix(matrix) {
23695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	var s = "[ " + matrix.a + ", " + matrix.c + ", " + matrix.e + "\n  " + matrix.b + ", " + matrix.d + ", " + matrix.f + "\n  0, 0, 1 ]";
23705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	return s;
23725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
23735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
23755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Sets attributes of an element.
23765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
23775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function setAttributes(element, attributes){
23785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	for (i in attributes)
23795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		element.setAttributeNS(null, i, attributes[i]);
23805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
23815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
23835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Handle mouse move event.
23845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
23855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function handleMouseWheel(evt) {
23865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if(evt.preventDefault)
23875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		evt.preventDefault();
23885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	evt.returnValue = false;
23905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	var svgDoc = evt.target.ownerDocument;
23925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	var delta;
23945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if(evt.wheelDelta)
23965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		delta = evt.wheelDelta / 3600; // Chrome/Safari
23975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	else
23985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		delta = evt.detail / -90; // Mozilla
23995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	var z = 1 + delta; // Zoom factor: 0.9/1.1
24015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	var g = svgDoc.getElementById("viewport");
24035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	var p = getEventPoint(evt);
24055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	p = p.matrixTransform(g.getCTM().inverse());
24075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	// Compute new scale matrix in current mouse position
24095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	var k = root.createSVGMatrix().translate(p.x, p.y).scale(z).translate(-p.x, -p.y);
24105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        setCTM(g, g.getCTM().multiply(k));
24125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	stateTf = stateTf.multiply(k.inverse());
24145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
24155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
24175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Handle mouse move event.
24185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
24195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function handleMouseMove(evt) {
24205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if(evt.preventDefault)
24215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		evt.preventDefault();
24225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	evt.returnValue = false;
24245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	var svgDoc = evt.target.ownerDocument;
24265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	var g = svgDoc.getElementById("viewport");
24285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if(state == 'pan') {
24305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		// Pan mode
24315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		var p = getEventPoint(evt).matrixTransform(stateTf);
24325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		setCTM(g, stateTf.inverse().translate(p.x - stateOrigin.x, p.y - stateOrigin.y));
24345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	} else if(state == 'move') {
24355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		// Move mode
24365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		var p = getEventPoint(evt).matrixTransform(g.getCTM().inverse());
24375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		setCTM(stateTarget, root.createSVGMatrix().translate(p.x - stateOrigin.x, p.y - stateOrigin.y).multiply(g.getCTM().inverse()).multiply(stateTarget.getCTM()));
24395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		stateOrigin = p;
24415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
24425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
24435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
24455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Handle click event.
24465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
24475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function handleMouseDown(evt) {
24485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if(evt.preventDefault)
24495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		evt.preventDefault();
24505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	evt.returnValue = false;
24525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	var svgDoc = evt.target.ownerDocument;
24545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	var g = svgDoc.getElementById("viewport");
24565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if(true || evt.target.tagName == "svg") {
24585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		// Pan mode
24595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		state = 'pan';
24605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		stateTf = g.getCTM().inverse();
24625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		stateOrigin = getEventPoint(evt).matrixTransform(stateTf);
24645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	} else {
24655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		// Move mode
24665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		state = 'move';
24675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		stateTarget = evt.target;
24695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		stateTf = g.getCTM().inverse();
24715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		stateOrigin = getEventPoint(evt).matrixTransform(stateTf);
24735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
24745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
24755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
24775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Handle mouse button release event.
24785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
24795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function handleMouseUp(evt) {
24805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if(evt.preventDefault)
24815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		evt.preventDefault();
24825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	evt.returnValue = false;
24845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	var svgDoc = evt.target.ownerDocument;
24865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if(state == 'pan' || state == 'move') {
24885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		// Quit pan mode
24895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		state = '';
24905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
24915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
24925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)]]></script>
24945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)EOF
24955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
24965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Provides a map from fullname to shortname for cases where the
24985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# shortname is ambiguous.  The symlist has both the fullname and
24995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# shortname for all symbols, which is usually fine, but sometimes --
25005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# such as overloaded functions -- two different fullnames can map to
25015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# the same shortname.  In that case, we use the address of the
25025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# function to disambiguate the two.  This function fills in a map that
25035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# maps fullnames to modified shortnames in such cases.  If a fullname
25045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# is not present in the map, the 'normal' shortname provided by the
25055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# symlist is the appropriate one to use.
25065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub FillFullnameToShortnameMap {
25075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $symbols = shift;
25085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $fullname_to_shortname_map = shift;
25095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $shortnames_seen_once = {};
25105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $shortnames_seen_more_than_once = {};
25115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
25125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $symlist (values(%{$symbols})) {
25135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # TODO(csilvers): deal with inlined symbols too.
25145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $shortname = $symlist->[0];
25155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $fullname = $symlist->[2];
25165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($fullname !~ /<[0-9a-fA-F]+>$/) {  # fullname doesn't end in an address
25175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next;       # the only collisions we care about are when addresses differ
25185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
25195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (defined($shortnames_seen_once->{$shortname}) &&
25205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $shortnames_seen_once->{$shortname} ne $fullname) {
25215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $shortnames_seen_more_than_once->{$shortname} = 1;
25225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
25235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $shortnames_seen_once->{$shortname} = $fullname;
25245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
25255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
25265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
25275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $symlist (values(%{$symbols})) {
25285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $shortname = $symlist->[0];
25295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $fullname = $symlist->[2];
25305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # TODO(csilvers): take in a list of addresses we care about, and only
25315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # store in the map if $symlist->[1] is in that list.  Saves space.
25325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    next if defined($fullname_to_shortname_map->{$fullname});
25335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (defined($shortnames_seen_more_than_once->{$shortname})) {
25345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($fullname =~ /<0*([^>]*)>$/) {   # fullname has address at end of it
25355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $fullname_to_shortname_map->{$fullname} = "$shortname\@$1";
25365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
25375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
25385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
25395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
25405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
25415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Return a small number that identifies the argument.
25425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Multiple calls with the same argument will return the same number.
25435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Calls with different arguments will return different numbers.
25445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub ShortIdFor {
25455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $key = shift;
25465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $id = $main::uniqueid{$key};
25475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!defined($id)) {
25485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $id = keys(%main::uniqueid) + 1;
25495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $main::uniqueid{$key} = $id;
25505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
25515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $id;
25525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
25535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
25545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Translate a stack of addresses into a stack of symbols
25555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub TranslateStack {
25565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $symbols = shift;
25575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $fullname_to_shortname_map = shift;
25585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $k = shift;
25595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
25605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my @addrs = split(/\n/, $k);
25615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my @result = ();
25625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (my $i = 0; $i <= $#addrs; $i++) {
25635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $a = $addrs[$i];
25645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
25655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Skip large addresses since they sometimes show up as fake entries on RH9
25665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (length($a) > 8 && $a gt "7fffffffffffffff") {
25675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next;
25685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
25695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
25705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($main::opt_disasm || $main::opt_list) {
25715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # We want just the address for the key
25725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      push(@result, $a);
25735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next;
25745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
25755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
25765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $symlist = $symbols->{$a};
25775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!defined($symlist)) {
25785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $symlist = [$a, "", $a];
25795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
25805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
25815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # We can have a sequence of symbols for a particular entry
25825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # (more than one symbol in the case of inlining).  Callers
25835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # come before callees in symlist, so walk backwards since
25845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # the translated stack should contain callees before callers.
25855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (my $j = $#{$symlist}; $j >= 2; $j -= 3) {
25865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $func = $symlist->[$j-2];
25875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $fileline = $symlist->[$j-1];
25885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $fullfunc = $symlist->[$j];
25895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (defined($fullname_to_shortname_map->{$fullfunc})) {
25905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $func = $fullname_to_shortname_map->{$fullfunc};
25915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
25925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($j > 2) {
25935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $func = "$func (inline)";
25945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
25955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
25965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Do not merge nodes corresponding to Callback::Run since that
25975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # causes confusing cycles in dot display.  Instead, we synthesize
25985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # a unique name for this frame per caller.
25995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($func =~ m/Callback.*::Run$/) {
26005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        my $caller = ($i > 0) ? $addrs[$i-1] : 0;
26015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $func = "Run#" . ShortIdFor($caller);
26025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
26035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
26045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($main::opt_addresses) {
26055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        push(@result, "$a $func $fileline");
26065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } elsif ($main::opt_lines) {
26075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if ($func eq '??' && $fileline eq '??:0') {
26085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          push(@result, "$a");
26095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        } else {
26105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          push(@result, "$func $fileline");
26115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
26125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } elsif ($main::opt_functions) {
26135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if ($func eq '??') {
26145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          push(@result, "$a");
26155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        } else {
26165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          push(@result, $func);
26175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
26185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } elsif ($main::opt_files) {
26195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if ($fileline eq '??:0' || $fileline eq '') {
26205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          push(@result, "$a");
26215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        } else {
26225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          my $f = $fileline;
26235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          $f =~ s/:\d+$//;
26245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          push(@result, $f);
26255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
26265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
26275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        push(@result, $a);
26285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        last;  # Do not print inlined info
26295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
26305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
26315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
26325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
26335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # print join(",", @addrs), " => ", join(",", @result), "\n";
26345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return @result;
26355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
26365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
26375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Generate percent string for a number and a total
26385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub Percent {
26395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $num = shift;
26405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $tot = shift;
26415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($tot != 0) {
26425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return sprintf("%.1f%%", $num * 100.0 / $tot);
26435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
26445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ($num == 0) ? "nan" : (($num > 0) ? "+inf" : "-inf");
26455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
26465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
26475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
26485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Generate pretty-printed form of number
26495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub Unparse {
26505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $num = shift;
26515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {
26525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($main::opt_inuse_objects || $main::opt_alloc_objects) {
26535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return sprintf("%d", $num);
26545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
26555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($main::opt_show_bytes) {
26565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return sprintf("%d", $num);
26575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
26585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return sprintf("%.1f", $num / 1048576.0);
26595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
26605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
26615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } elsif ($main::profile_type eq 'contention' && !$main::opt_contentions) {
26625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return sprintf("%.3f", $num / 1e9); # Convert nanoseconds to seconds
26635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
26645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return sprintf("%d", $num);
26655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
26665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
26675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
26685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Alternate pretty-printed form: 0 maps to "."
26695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub UnparseAlt {
26705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $num = shift;
26715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($num == 0) {
26725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ".";
26735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
26745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return Unparse($num);
26755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
26765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
26775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
26785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Alternate pretty-printed form: 0 maps to ""
26795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub HtmlPrintNumber {
26805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $num = shift;
26815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($num == 0) {
26825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return "";
26835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
26845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return Unparse($num);
26855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
26865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
26875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
26885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Return output units
26895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub Units {
26905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {
26915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($main::opt_inuse_objects || $main::opt_alloc_objects) {
26925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return "objects";
26935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
26945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($main::opt_show_bytes) {
26955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return "B";
26965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
26975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return "MB";
26985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
26995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
27005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } elsif ($main::profile_type eq 'contention' && !$main::opt_contentions) {
27015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return "seconds";
27025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
27035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return "samples";
27045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
27055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
27065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
27075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)##### Profile manipulation code #####
27085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
27095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Generate flattened profile:
27105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# If count is charged to stack [a,b,c,d], in generated profile,
27115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# it will be charged to [a]
27125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub FlatProfile {
27135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $profile = shift;
27145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $result = {};
27155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $k (keys(%{$profile})) {
27165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $count = $profile->{$k};
27175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my @addrs = split(/\n/, $k);
27185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($#addrs >= 0) {
27195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      AddEntry($result, $addrs[0], $count);
27205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
27215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
27225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $result;
27235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
27245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
27255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Generate cumulative profile:
27265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# If count is charged to stack [a,b,c,d], in generated profile,
27275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# it will be charged to [a], [b], [c], [d]
27285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub CumulativeProfile {
27295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $profile = shift;
27305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $result = {};
27315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $k (keys(%{$profile})) {
27325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $count = $profile->{$k};
27335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my @addrs = split(/\n/, $k);
27345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    foreach my $a (@addrs) {
27355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      AddEntry($result, $a, $count);
27365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
27375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
27385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $result;
27395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
27405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
27415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# If the second-youngest PC on the stack is always the same, returns
27425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# that pc.  Otherwise, returns undef.
27435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub IsSecondPcAlwaysTheSame {
27445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $profile = shift;
27455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
27465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $second_pc = undef;
27475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $k (keys(%{$profile})) {
27485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my @addrs = split(/\n/, $k);
27495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($#addrs < 1) {
27505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return undef;
27515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
27525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (not defined $second_pc) {
27535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $second_pc = $addrs[1];
27545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
27555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($second_pc ne $addrs[1]) {
27565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return undef;
27575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
27585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
27595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
27605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $second_pc;
27615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
27625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
27635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub ExtractSymbolLocation {
27645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $symbols = shift;
27655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $address = shift;
27665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # 'addr2line' outputs "??:0" for unknown locations; we do the
27675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # same to be consistent.
27685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $location = "??:0:unknown";
27695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (exists $symbols->{$address}) {
27705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $file = $symbols->{$address}->[1];
27715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($file eq "?") {
27725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $file = "??:0"
27735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
27745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $location = $file . ":" . $symbols->{$address}->[0];
27755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
27765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $location;
27775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
27785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
27795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Extracts a graph of calls.
27805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub ExtractCalls {
27815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $symbols = shift;
27825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $profile = shift;
27835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
27845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $calls = {};
27855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while( my ($stack_trace, $count) = each %$profile ) {
27865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my @address = split(/\n/, $stack_trace);
27875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $destination = ExtractSymbolLocation($symbols, $address[0]);
27885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddEntry($calls, $destination, $count);
27895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (my $i = 1; $i <= $#address; $i++) {
27905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $source = ExtractSymbolLocation($symbols, $address[$i]);
27915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $call = "$source -> $destination";
27925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      AddEntry($calls, $call, $count);
27935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $destination = $source;
27945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
27955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
27965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
27975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $calls;
27985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
27995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
28005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub RemoveUninterestingFrames {
28015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $symbols = shift;
28025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $profile = shift;
28035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
28045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # List of function names to skip
28055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my %skip = ();
28065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $skip_regexp = 'NOMATCH';
28075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {
28085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    foreach my $name ('calloc',
28095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'cfree',
28105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'malloc',
28115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'free',
28125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'memalign',
28135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'posix_memalign',
28145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'pvalloc',
28155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'valloc',
28165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'realloc',
28175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'tc_calloc',
28185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'tc_cfree',
28195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'tc_malloc',
28205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'tc_free',
28215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'tc_memalign',
28225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'tc_posix_memalign',
28235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'tc_pvalloc',
28245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'tc_valloc',
28255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'tc_realloc',
28265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'tc_new',
28275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'tc_delete',
28285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'tc_newarray',
28295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'tc_deletearray',
28305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'tc_new_nothrow',
28315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'tc_newarray_nothrow',
28325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'do_malloc',
28335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      '::do_malloc',   # new name -- got moved to an unnamed ns
28345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      '::do_malloc_or_cpp_alloc',
28355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'DoSampledAllocation',
28365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'simple_alloc::allocate',
28375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      '__malloc_alloc_template::allocate',
28385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      '__builtin_delete',
28395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      '__builtin_new',
28405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      '__builtin_vec_delete',
28415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      '__builtin_vec_new',
28425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'operator new',
28435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'operator new[]',
28445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      # The entry to our memory-allocation routines on OS X
28455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'malloc_zone_malloc',
28465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'malloc_zone_calloc',
28475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'malloc_zone_valloc',
28485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'malloc_zone_realloc',
28495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'malloc_zone_memalign',
28505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'malloc_zone_free',
28515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      # These mark the beginning/end of our custom sections
28525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      '__start_google_malloc',
28535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      '__stop_google_malloc',
28545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      '__start_malloc_hook',
28555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      '__stop_malloc_hook') {
28565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $skip{$name} = 1;
28575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $skip{"_" . $name} = 1;   # Mach (OS X) adds a _ prefix to everything
28585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
28595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # TODO: Remove TCMalloc once everything has been
28605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # moved into the tcmalloc:: namespace and we have flushed
28615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # old code out of the system.
28625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $skip_regexp = "TCMalloc|^tcmalloc::";
28635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } elsif ($main::profile_type eq 'contention') {
28645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    foreach my $vname ('base::RecordLockProfileData',
28655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       'base::SubmitMutexProfileData',
28665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       'base::SubmitSpinLockProfileData',
28675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       'Mutex::Unlock',
28685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       'Mutex::UnlockSlow',
28695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       'Mutex::ReaderUnlock',
28705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       'MutexLock::~MutexLock',
28715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       'SpinLock::Unlock',
28725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       'SpinLock::SlowUnlock',
28735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       'SpinLockHolder::~SpinLockHolder') {
28745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $skip{$vname} = 1;
28755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
28765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } elsif ($main::profile_type eq 'cpu') {
28775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Drop signal handlers used for CPU profile collection
28785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # TODO(dpeng): this should not be necessary; it's taken
28795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # care of by the general 2nd-pc mechanism below.
28805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    foreach my $name ('ProfileData::Add',           # historical
28815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'ProfileData::prof_handler',  # historical
28825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'CpuProfiler::prof_handler',
28835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      '__FRAME_END__',
28845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      '__pthread_sighandler',
28855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      '__restore') {
28865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $skip{$name} = 1;
28875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
28885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
28895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Nothing skipped for unknown types
28905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
28915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
28925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($main::profile_type eq 'cpu') {
28935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # If all the second-youngest program counters are the same,
28945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # this STRONGLY suggests that it is an artifact of measurement,
28955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # i.e., stack frames pushed by the CPU profiler signal handler.
28965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Hence, we delete them.
28975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # (The topmost PC is read from the signal structure, not from
28985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # the stack, so it does not get involved.)
28995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while (my $second_pc = IsSecondPcAlwaysTheSame($profile)) {
29005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $result = {};
29015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $func = '';
29025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (exists($symbols->{$second_pc})) {
29035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $second_pc = $symbols->{$second_pc}->[0];
29045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
29055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print STDERR "Removing $second_pc from all stack traces.\n";
29065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      foreach my $k (keys(%{$profile})) {
29075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        my $count = $profile->{$k};
29085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        my @addrs = split(/\n/, $k);
29095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        splice @addrs, 1, 1;
29105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        my $reduced_path = join("\n", @addrs);
29115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        AddEntry($result, $reduced_path, $count);
29125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
29135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $profile = $result;
29145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
29155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
29165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
29175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $result = {};
29185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $k (keys(%{$profile})) {
29195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $count = $profile->{$k};
29205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my @addrs = split(/\n/, $k);
29215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my @path = ();
29225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    foreach my $a (@addrs) {
29235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (exists($symbols->{$a})) {
29245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        my $func = $symbols->{$a}->[0];
29255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if ($skip{$func} || ($func =~ m/$skip_regexp/)) {
29265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          next;
29275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
29285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
29295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      push(@path, $a);
29305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
29315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $reduced_path = join("\n", @path);
29325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddEntry($result, $reduced_path, $count);
29335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
29345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $result;
29355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
29365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
29375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Reduce profile to granularity given by user
29385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub ReduceProfile {
29395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $symbols = shift;
29405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $profile = shift;
29415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $result = {};
29425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $fullname_to_shortname_map = {};
29435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FillFullnameToShortnameMap($symbols, $fullname_to_shortname_map);
29445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $k (keys(%{$profile})) {
29455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $count = $profile->{$k};
29465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my @translated = TranslateStack($symbols, $fullname_to_shortname_map, $k);
29475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my @path = ();
29485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my %seen = ();
29495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $seen{''} = 1;      # So that empty keys are skipped
29505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    foreach my $e (@translated) {
29515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # To avoid double-counting due to recursion, skip a stack-trace
29525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # entry if it has already been seen
29535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!$seen{$e}) {
29545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $seen{$e} = 1;
29555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        push(@path, $e);
29565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
29575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
29585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $reduced_path = join("\n", @path);
29595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddEntry($result, $reduced_path, $count);
29605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
29615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $result;
29625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
29635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
29645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Does the specified symbol array match the regexp?
29655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub SymbolMatches {
29665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $sym = shift;
29675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $re = shift;
29685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (defined($sym)) {
29695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (my $i = 0; $i < $#{$sym}; $i += 3) {
29705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($sym->[$i] =~ m/$re/ || $sym->[$i+1] =~ m/$re/) {
29715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return 1;
29725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
29735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
29745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
29755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 0;
29765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
29775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
29785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Focus only on paths involving specified regexps
29795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub FocusProfile {
29805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $symbols = shift;
29815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $profile = shift;
29825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $focus = shift;
29835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $result = {};
29845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $k (keys(%{$profile})) {
29855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $count = $profile->{$k};
29865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my @addrs = split(/\n/, $k);
29875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    foreach my $a (@addrs) {
29885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Reply if it matches either the address/shortname/fileline
29895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (($a =~ m/$focus/) || SymbolMatches($symbols->{$a}, $focus)) {
29905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        AddEntry($result, $k, $count);
29915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        last;
29925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
29935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
29945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
29955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $result;
29965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
29975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
29985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Focus only on paths not involving specified regexps
29995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub IgnoreProfile {
30005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $symbols = shift;
30015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $profile = shift;
30025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $ignore = shift;
30035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $result = {};
30045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $k (keys(%{$profile})) {
30055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $count = $profile->{$k};
30065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my @addrs = split(/\n/, $k);
30075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $matched = 0;
30085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    foreach my $a (@addrs) {
30095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Reply if it matches either the address/shortname/fileline
30105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (($a =~ m/$ignore/) || SymbolMatches($symbols->{$a}, $ignore)) {
30115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $matched = 1;
30125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        last;
30135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
30145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
30155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!$matched) {
30165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      AddEntry($result, $k, $count);
30175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
30185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
30195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $result;
30205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
30215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
30225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Get total count in profile
30235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub TotalProfile {
30245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $profile = shift;
30255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $result = 0;
30265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $k (keys(%{$profile})) {
30275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $result += $profile->{$k};
30285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
30295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $result;
30305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
30315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
30325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Add A to B
30335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub AddProfile {
30345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $A = shift;
30355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $B = shift;
30365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
30375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $R = {};
30385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # add all keys in A
30395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $k (keys(%{$A})) {
30405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $v = $A->{$k};
30415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddEntry($R, $k, $v);
30425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
30435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # add all keys in B
30445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $k (keys(%{$B})) {
30455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $v = $B->{$k};
30465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddEntry($R, $k, $v);
30475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
30485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $R;
30495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
30505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
30515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Merges symbol maps
30525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub MergeSymbols {
30535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $A = shift;
30545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $B = shift;
30555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
30565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $R = {};
30575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $k (keys(%{$A})) {
30585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $R->{$k} = $A->{$k};
30595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
30605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (defined($B)) {
30615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    foreach my $k (keys(%{$B})) {
30625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $R->{$k} = $B->{$k};
30635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
30645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
30655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $R;
30665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
30675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
30685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
30695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Add A to B
30705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub AddPcs {
30715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $A = shift;
30725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $B = shift;
30735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
30745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $R = {};
30755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # add all keys in A
30765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $k (keys(%{$A})) {
30775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $R->{$k} = 1
30785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
30795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # add all keys in B
30805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $k (keys(%{$B})) {
30815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $R->{$k} = 1
30825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
30835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $R;
30845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
30855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
30865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Subtract B from A
30875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub SubtractProfile {
30885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $A = shift;
30895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $B = shift;
30905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
30915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $R = {};
30925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $k (keys(%{$A})) {
30935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $v = $A->{$k} - GetEntry($B, $k);
30945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($v < 0 && $main::opt_drop_negative) {
30955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $v = 0;
30965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
30975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddEntry($R, $k, $v);
30985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
30995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!$main::opt_drop_negative) {
31005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Take care of when subtracted profile has more entries
31015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    foreach my $k (keys(%{$B})) {
31025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!exists($A->{$k})) {
31035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        AddEntry($R, $k, 0 - $B->{$k});
31045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
31055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
31065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
31075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $R;
31085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
31095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
31105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Get entry from profile; zero if not present
31115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub GetEntry {
31125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $profile = shift;
31135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $k = shift;
31145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (exists($profile->{$k})) {
31155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return $profile->{$k};
31165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
31175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0;
31185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
31195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
31205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
31215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Add entry to specified profile
31225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub AddEntry {
31235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $profile = shift;
31245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $k = shift;
31255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $n = shift;
31265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!exists($profile->{$k})) {
31275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $profile->{$k} = 0;
31285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
31295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $profile->{$k} += $n;
31305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
31315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
31325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Add a stack of entries to specified profile, and add them to the $pcs
31335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# list.
31345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub AddEntries {
31355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $profile = shift;
31365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $pcs = shift;
31375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $stack = shift;
31385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $count = shift;
31395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my @k = ();
31405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
31415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $e (split(/\s+/, $stack)) {
31425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $pc = HexExtend($e);
31435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $pcs->{$pc} = 1;
31445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    push @k, $pc;
31455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
31465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AddEntry($profile, (join "\n", @k), $count);
31475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
31485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
31495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)##### Code to profile a server dynamically #####
31505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
31515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub CheckSymbolPage {
31525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $url = SymbolPageURL();
31535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $command = ShellEscape(@URL_FETCHER, $url);
31545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  open(SYMBOL, "$command |") or error($command);
31555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $line = <SYMBOL>;
31565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $line =~ s/\r//g;         # turn windows-looking lines into unix-looking lines
31575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  close(SYMBOL);
31585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  unless (defined($line)) {
31595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    error("$url doesn't exist\n");
31605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
31615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
31625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($line =~ /^num_symbols:\s+(\d+)$/) {
31635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($1 == 0) {
31645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      error("Stripped binary. No symbols available.\n");
31655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
31665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
31675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    error("Failed to get the number of symbols from $url\n");
31685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
31695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
31705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
31715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub IsProfileURL {
31725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $profile_name = shift;
31735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (-f $profile_name) {
31745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    printf STDERR "Using local file $profile_name.\n";
31755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0;
31765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
31775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 1;
31785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
31795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
31805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub ParseProfileURL {
31815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $profile_name = shift;
31825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
31835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!defined($profile_name) || $profile_name eq "") {
31845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ();
31855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
31865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
31875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Split profile URL - matches all non-empty strings, so no test.
31885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $profile_name =~ m,^(https?://)?([^/]+)(.*?)(/|$PROFILES)?$,;
31895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
31905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $proto = $1 || "http://";
31915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $hostport = $2;
31925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $prefix = $3;
31935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $profile = $4 || "/";
31945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
31955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $host = $hostport;
31965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $host =~ s/:.*//;
31975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
31985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $baseurl = "$proto$hostport$prefix";
31995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ($host, $baseurl, $profile);
32005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
32015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
32025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# We fetch symbols from the first profile argument.
32035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub SymbolPageURL {
32045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my ($host, $baseURL, $path) = ParseProfileURL($main::pfile_args[0]);
32055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return "$baseURL$SYMBOL_PAGE";
32065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
32075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
32085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub FetchProgramName() {
32095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my ($host, $baseURL, $path) = ParseProfileURL($main::pfile_args[0]);
32105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $url = "$baseURL$PROGRAM_NAME_PAGE";
32115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $command_line = ShellEscape(@URL_FETCHER, $url);
32125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  open(CMDLINE, "$command_line |") or error($command_line);
32135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $cmdline = <CMDLINE>;
32145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $cmdline =~ s/\r//g;   # turn windows-looking lines into unix-looking lines
32155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  close(CMDLINE);
32165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  error("Failed to get program name from $url\n") unless defined($cmdline);
32175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $cmdline =~ s/\x00.+//;  # Remove argv[1] and latters.
32185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $cmdline =~ s!\n!!g;  # Remove LFs.
32195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $cmdline;
32205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
32215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
32225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Gee, curl's -L (--location) option isn't reliable at least
32235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# with its 7.12.3 version.  Curl will forget to post data if
32245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# there is a redirection.  This function is a workaround for
32255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# curl.  Redirection happens on borg hosts.
32265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub ResolveRedirectionForCurl {
32275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $url = shift;
32285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $command_line = ShellEscape(@URL_FETCHER, "--head", $url);
32295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  open(CMDLINE, "$command_line |") or error($command_line);
32305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (<CMDLINE>) {
32315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    s/\r//g;         # turn windows-looking lines into unix-looking lines
32325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (/^Location: (.*)/) {
32335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $url = $1;
32345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
32355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
32365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  close(CMDLINE);
32375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $url;
32385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
32395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
32405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Add a timeout flat to URL_FETCHER.  Returns a new list.
32415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub AddFetchTimeout {
32425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $timeout = shift;
32435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my @fetcher = shift;
32445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (defined($timeout)) {
32455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (join(" ", @fetcher) =~ m/\bcurl -s/) {
32465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      push(@fetcher, "--max-time", sprintf("%d", $timeout));
32475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } elsif (join(" ", @fetcher) =~ m/\brpcget\b/) {
32485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      push(@fetcher, sprintf("--deadline=%d", $timeout));
32495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
32505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
32515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return @fetcher;
32525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
32535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
32545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Reads a symbol map from the file handle name given as $1, returning
32555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# the resulting symbol map.  Also processes variables relating to symbols.
32565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Currently, the only variable processed is 'binary=<value>' which updates
32575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# $main::prog to have the correct program name.
32585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub ReadSymbols {
32595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $in = shift;
32605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $map = {};
32615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (<$in>) {
32625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    s/\r//g;         # turn windows-looking lines into unix-looking lines
32635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Removes all the leading zeroes from the symbols, see comment below.
32645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (m/^0x0*([0-9a-f]+)\s+(.+)/) {
32655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $map->{$1} = $2;
32665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } elsif (m/^---/) {
32675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      last;
32685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } elsif (m/^([a-z][^=]*)=(.*)$/ ) {
32695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my ($variable, $value) = ($1, $2);
32705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for ($variable, $value) {
32715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        s/^\s+//;
32725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        s/\s+$//;
32735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
32745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($variable eq "binary") {
32755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if ($main::prog ne $UNKNOWN_BINARY && $main::prog ne $value) {
32765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          printf STDERR ("Warning: Mismatched binary name '%s', using '%s'.\n",
32775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         $main::prog, $value);
32785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
32795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $main::prog = $value;
32805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
32815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        printf STDERR ("Ignoring unknown variable in symbols list: " .
32825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            "'%s' = '%s'\n", $variable, $value);
32835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
32845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
32855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
32865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $map;
32875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
32885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
32895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Fetches and processes symbols to prepare them for use in the profile output
32905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# code.  If the optional 'symbol_map' arg is not given, fetches symbols from
32915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# $SYMBOL_PAGE for all PC values found in profile.  Otherwise, the raw symbols
32925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# are assumed to have already been fetched into 'symbol_map' and are simply
32935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# extracted and processed.
32945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub FetchSymbols {
32955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $pcset = shift;
32965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $symbol_map = shift;
32975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
32985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my %seen = ();
32995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my @pcs = grep { !$seen{$_}++ } keys(%$pcset);  # uniq
33005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
33015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!defined($symbol_map)) {
33025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $post_data = join("+", sort((map {"0x" . "$_"} @pcs)));
33035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
33045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    open(POSTFILE, ">$main::tmpfile_sym");
33055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print POSTFILE $post_data;
33065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    close(POSTFILE);
33075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
33085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $url = SymbolPageURL();
33095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
33105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $command_line;
33115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (join(" ", @URL_FETCHER) =~ m/\bcurl -s/) {
33125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $url = ResolveRedirectionForCurl($url);
33135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $command_line = ShellEscape(@URL_FETCHER, "-d", "\@$main::tmpfile_sym",
33145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  $url);
33155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
33165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $command_line = (ShellEscape(@URL_FETCHER, "--post", $url)
33175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       . " < " . ShellEscape($main::tmpfile_sym));
33185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
33195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # We use c++filt in case $SYMBOL_PAGE gives us mangled symbols.
33205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $escaped_cppfilt = ShellEscape($obj_tool_map{"c++filt"});
33215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    open(SYMBOL, "$command_line | $escaped_cppfilt |") or error($command_line);
33225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $symbol_map = ReadSymbols(*SYMBOL{IO});
33235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    close(SYMBOL);
33245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
33255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
33265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $symbols = {};
33275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $pc (@pcs) {
33285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $fullname;
33295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # For 64 bits binaries, symbols are extracted with 8 leading zeroes.
33305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Then /symbol reads the long symbols in as uint64, and outputs
33315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # the result with a "0x%08llx" format which get rid of the zeroes.
33325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # By removing all the leading zeroes in both $pc and the symbols from
33335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # /symbol, the symbols match and are retrievable from the map.
33345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $shortpc = $pc;
33355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $shortpc =~ s/^0*//;
33365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Each line may have a list of names, which includes the function
33375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # and also other functions it has inlined.  They are separated (in
33385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # PrintSymbolizedProfile), by --, which is illegal in function names.
33395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $fullnames;
33405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (defined($symbol_map->{$shortpc})) {
33415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $fullnames = $symbol_map->{$shortpc};
33425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
33435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $fullnames = "0x" . $pc;  # Just use addresses
33445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
33455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $sym = [];
33465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $symbols->{$pc} = $sym;
33475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    foreach my $fullname (split("--", $fullnames)) {
33485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $name = ShortFunctionName($fullname);
33495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      push(@{$sym}, $name, "?", $fullname);
33505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
33515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
33525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $symbols;
33535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
33545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
33555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub BaseName {
33565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $file_name = shift;
33575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $file_name =~ s!^.*/!!;  # Remove directory name
33585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $file_name;
33595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
33605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
33615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub MakeProfileBaseName {
33625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my ($binary_name, $profile_name) = @_;
33635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my ($host, $baseURL, $path) = ParseProfileURL($profile_name);
33645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $binary_shortname = BaseName($binary_name);
33655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return sprintf("%s.%s.%s",
33665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 $binary_shortname, $main::op_time, $host);
33675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
33685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
33695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub FetchDynamicProfile {
33705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $binary_name = shift;
33715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $profile_name = shift;
33725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $fetch_name_only = shift;
33735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $encourage_patience = shift;
33745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
33755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!IsProfileURL($profile_name)) {
33765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return $profile_name;
33775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
33785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my ($host, $baseURL, $path) = ParseProfileURL($profile_name);
33795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($path eq "" || $path eq "/") {
33805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Missing type specifier defaults to cpu-profile
33815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $path = $PROFILE_PAGE;
33825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
33835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
33845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $profile_file = MakeProfileBaseName($binary_name, $profile_name);
33855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
33865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $url = "$baseURL$path";
33875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $fetch_timeout = undef;
33885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($path =~ m/$PROFILE_PAGE|$PMUPROFILE_PAGE/) {
33895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($path =~ m/[?]/) {
33905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $url .= "&";
33915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
33925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $url .= "?";
33935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
33945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $url .= sprintf("seconds=%d", $main::opt_seconds);
33955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $fetch_timeout = $main::opt_seconds * 1.01 + 60;
33965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
33975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # For non-CPU profiles, we add a type-extension to
33985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # the target profile file name.
33995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $suffix = $path;
34005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $suffix =~ s,/,.,g;
34015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $profile_file .= $suffix;
34025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
34035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
34045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $profile_dir = $ENV{"PPROF_TMPDIR"} || ($ENV{HOME} . "/pprof");
34055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (! -d $profile_dir) {
34065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      mkdir($profile_dir)
34075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          || die("Unable to create profile directory $profile_dir: $!\n");
34085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
34095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $tmp_profile = "$profile_dir/.tmp.$profile_file";
34105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $real_profile = "$profile_dir/$profile_file";
34115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
34125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($fetch_name_only > 0) {
34135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return $real_profile;
34145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
34155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
34165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my @fetcher = AddFetchTimeout($fetch_timeout, @URL_FETCHER);
34175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $cmd = ShellEscape(@fetcher, $url) . " > " . ShellEscape($tmp_profile);
34185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($path =~ m/$PROFILE_PAGE|$PMUPROFILE_PAGE|$CENSUSPROFILE_PAGE/){
34195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print STDERR "Gathering CPU profile from $url for $main::opt_seconds seconds to\n  ${real_profile}\n";
34205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($encourage_patience) {
34215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        print STDERR "Be patient...\n";
34225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
34235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
34245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print STDERR "Fetching $path profile from $url to\n  ${real_profile}\n";
34255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
34265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
34275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    (system($cmd) == 0) || error("Failed to get profile: $cmd: $!\n");
34285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    (system("mv", $tmp_profile, $real_profile) == 0) || error("Unable to rename profile\n");
34295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print STDERR "Wrote profile to $real_profile\n";
34305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $main::collected_profile = $real_profile;
34315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return $main::collected_profile;
34325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
34335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
34345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
34355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Collect profiles in parallel
34365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub FetchDynamicProfiles {
34375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $items = scalar(@main::pfile_args);
34385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $levels = log($items) / log(2);
34395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
34405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($items == 1) {
34415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $main::profile_files[0] = FetchDynamicProfile($main::prog, $main::pfile_args[0], 0, 1);
34425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
34435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # math rounding issues
34445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ((2 ** $levels) < $items) {
34455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     $levels++;
34465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
34475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $count = scalar(@main::pfile_args);
34485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (my $i = 0; $i < $count; $i++) {
34495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $main::profile_files[$i] = FetchDynamicProfile($main::prog, $main::pfile_args[$i], 1, 0);
34505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
34515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print STDERR "Fetching $count profiles, Be patient...\n";
34525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    FetchDynamicProfilesRecurse($levels, 0, 0);
34535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $main::collected_profile = join(" \\\n    ", @main::profile_files);
34545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
34555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
34565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
34575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Recursively fork a process to get enough processes
34585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# collecting profiles
34595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub FetchDynamicProfilesRecurse {
34605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $maxlevel = shift;
34615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $level = shift;
34625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $position = shift;
34635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
34645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (my $pid = fork()) {
34655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $position = 0 | ($position << 1);
34665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    TryCollectProfile($maxlevel, $level, $position);
34675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    wait;
34685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
34695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $position = 1 | ($position << 1);
34705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    TryCollectProfile($maxlevel, $level, $position);
34715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    cleanup();
34725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit(0);
34735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
34745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
34755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
34765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Collect a single profile
34775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub TryCollectProfile {
34785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $maxlevel = shift;
34795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $level = shift;
34805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $position = shift;
34815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
34825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($level >= ($maxlevel - 1)) {
34835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($position < scalar(@main::pfile_args)) {
34845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      FetchDynamicProfile($main::prog, $main::pfile_args[$position], 0, 0);
34855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
34865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
34875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    FetchDynamicProfilesRecurse($maxlevel, $level+1, $position);
34885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
34895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
34905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
34915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)##### Parsing code #####
34925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
34935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Provide a small streaming-read module to handle very large
34945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# cpu-profile files.  Stream in chunks along a sliding window.
34955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Provides an interface to get one 'slot', correctly handling
34965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# endian-ness differences.  A slot is one 32-bit or 64-bit word
34975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# (depending on the input profile).  We tell endianness and bit-size
34985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# for the profile by looking at the first 8 bytes: in cpu profiles,
34995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# the second slot is always 3 (we'll accept anything that's not 0).
35005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BEGIN {
35015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  package CpuProfileStream;
35025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
35035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sub new {
35045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my ($class, $file, $fname) = @_;
35055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $self = { file        => $file,
35065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 base        => 0,
35075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 stride      => 512 * 1024,   # must be a multiple of bitsize/8
35085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 slots       => [],
35095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 unpack_code => "",           # N for big-endian, V for little
35105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 perl_is_64bit => 1,          # matters if profile is 64-bit
35115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    };
35125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bless $self, $class;
35135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Let unittests adjust the stride
35145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($main::opt_test_stride > 0) {
35155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $self->{stride} = $main::opt_test_stride;
35165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
35175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Read the first two slots to figure out bitsize and endianness.
35185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $slots = $self->{slots};
35195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $str;
35205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    read($self->{file}, $str, 8);
35215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Set the global $address_length based on what we see here.
35225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # 8 is 32-bit (8 hexadecimal chars); 16 is 64-bit (16 hexadecimal chars).
35235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $address_length = ($str eq (chr(0)x8)) ? 16 : 8;
35245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($address_length == 8) {
35255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (substr($str, 6, 2) eq chr(0)x2) {
35265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $self->{unpack_code} = 'V';  # Little-endian.
35275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } elsif (substr($str, 4, 2) eq chr(0)x2) {
35285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $self->{unpack_code} = 'N';  # Big-endian
35295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
35305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ::error("$fname: header size >= 2**16\n");
35315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
35325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      @$slots = unpack($self->{unpack_code} . "*", $str);
35335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
35345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # If we're a 64-bit profile, check if we're a 64-bit-capable
35355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # perl.  Otherwise, each slot will be represented as a float
35365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # instead of an int64, losing precision and making all the
35375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # 64-bit addresses wrong.  We won't complain yet, but will
35385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # later if we ever see a value that doesn't fit in 32 bits.
35395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $has_q = 0;
35405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      eval { $has_q = pack("Q", "1") ? 1 : 1; };
35415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!$has_q) {
35425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $self->{perl_is_64bit} = 0;
35435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
35445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      read($self->{file}, $str, 8);
35455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (substr($str, 4, 4) eq chr(0)x4) {
35465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # We'd love to use 'Q', but it's a) not universal, b) not endian-proof.
35475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $self->{unpack_code} = 'V';  # Little-endian.
35485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } elsif (substr($str, 0, 4) eq chr(0)x4) {
35495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $self->{unpack_code} = 'N';  # Big-endian
35505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
35515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ::error("$fname: header size >= 2**32\n");
35525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
35535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my @pair = unpack($self->{unpack_code} . "*", $str);
35545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Since we know one of the pair is 0, it's fine to just add them.
35555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      @$slots = (0, $pair[0] + $pair[1]);
35565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
35575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return $self;
35585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
35595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
35605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Load more data when we access slots->get(X) which is not yet in memory.
35615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sub overflow {
35625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my ($self) = @_;
35635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $slots = $self->{slots};
35645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $self->{base} += $#$slots + 1;   # skip over data we're replacing
35655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $str;
35665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    read($self->{file}, $str, $self->{stride});
35675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($address_length == 8) {      # the 32-bit case
35685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # This is the easy case: unpack provides 32-bit unpacking primitives.
35695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      @$slots = unpack($self->{unpack_code} . "*", $str);
35705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
35715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # We need to unpack 32 bits at a time and combine.
35725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my @b32_values = unpack($self->{unpack_code} . "*", $str);
35735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my @b64_values = ();
35745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (my $i = 0; $i < $#b32_values; $i += 2) {
35755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # TODO(csilvers): if this is a 32-bit perl, the math below
35765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        #    could end up in a too-large int, which perl will promote
35775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        #    to a double, losing necessary precision.  Deal with that.
35785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        #    Right now, we just die.
35795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        my ($lo, $hi) = ($b32_values[$i], $b32_values[$i+1]);
35805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if ($self->{unpack_code} eq 'N') {    # big-endian
35815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          ($lo, $hi) = ($hi, $lo);
35825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
35835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        my $value = $lo + $hi * (2**32);
35845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (!$self->{perl_is_64bit} &&   # check value is exactly represented
35855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            (($value % (2**32)) != $lo || int($value / (2**32)) != $hi)) {
35865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          ::error("Need a 64-bit perl to process this 64-bit profile.\n");
35875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
35885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        push(@b64_values, $value);
35895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
35905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      @$slots = @b64_values;
35915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
35925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
35935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
35945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Access the i-th long in the file (logically), or -1 at EOF.
35955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sub get {
35965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my ($self, $idx) = @_;
35975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $slots = $self->{slots};
35985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while ($#$slots >= 0) {
35995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($idx < $self->{base}) {
36005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # The only time we expect a reference to $slots[$i - something]
36015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # after referencing $slots[$i] is reading the very first header.
36025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # Since $stride > |header|, that shouldn't cause any lookback
36035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # errors.  And everything after the header is sequential.
36045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        print STDERR "Unexpected look-back reading CPU profile";
36055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return -1;   # shrug, don't know what better to return
36065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } elsif ($idx > $self->{base} + $#$slots) {
36075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $self->overflow();
36085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
36095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return $slots->[$idx - $self->{base}];
36105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
36115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
36125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # If we get here, $slots is [], which means we've reached EOF
36135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return -1;  # unique since slots is supposed to hold unsigned numbers
36145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
36155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
36165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
36175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Reads the top, 'header' section of a profile, and returns the last
36185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# line of the header, commonly called a 'header line'.  The header
36195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# section of a profile consists of zero or more 'command' lines that
36205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# are instructions to pprof, which pprof executes when reading the
36215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# header.  All 'command' lines start with a %.  After the command
36225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# lines is the 'header line', which is a profile-specific line that
36235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# indicates what type of profile it is, and perhaps other global
36245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# information about the profile.  For instance, here's a header line
36255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# for a heap profile:
36265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   heap profile:     53:    38236 [  5525:  1284029] @ heapprofile
36275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# For historical reasons, the CPU profile does not contain a text-
36285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# readable header line.  If the profile looks like a CPU profile,
36295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# this function returns "".  If no header line could be found, this
36305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# function returns undef.
36315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
36325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# The following commands are recognized:
36335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   %warn -- emit the rest of this line to stderr, prefixed by 'WARNING:'
36345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
36355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# The input file should be in binmode.
36365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub ReadProfileHeader {
36375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local *PROFILE = shift;
36385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $firstchar = "";
36395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $line = "";
36405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  read(PROFILE, $firstchar, 1);
36415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  seek(PROFILE, -1, 1);                    # unread the firstchar
36425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($firstchar !~ /[[:print:]]/) {       # is not a text character
36435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return "";
36445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
36455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (defined($line = <PROFILE>)) {
36465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $line =~ s/\r//g;   # turn windows-looking lines into unix-looking lines
36475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($line =~ /^%warn\s+(.*)/) {        # 'warn' command
36485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Note this matches both '%warn blah\n' and '%warn\n'.
36495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print STDERR "WARNING: $1\n";        # print the rest of the line
36505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } elsif ($line =~ /^%/) {
36515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print STDERR "Ignoring unknown command from profile header: $line";
36525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
36535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # End of commands, must be the header line.
36545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return $line;
36555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
36565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
36575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return undef;     # got to EOF without seeing a header line
36585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
36595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
36605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub IsSymbolizedProfileFile {
36615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $file_name = shift;
36625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!(-e $file_name) || !(-r $file_name)) {
36635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0;
36645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
36655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Check if the file contains a symbol-section marker.
36665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  open(TFILE, "<$file_name");
36675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  binmode TFILE;
36685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $firstline = ReadProfileHeader(*TFILE);
36695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  close(TFILE);
36705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!$firstline) {
36715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0;
36725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
36735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $SYMBOL_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
36745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $symbol_marker = $&;
36755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $firstline =~ /^--- *$symbol_marker/;
36765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
36775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
36785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Parse profile generated by common/profiler.cc and return a reference
36795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# to a map:
36805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#      $result->{version}     Version number of profile file
36815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#      $result->{period}      Sampling period (in microseconds)
36825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#      $result->{profile}     Profile object
36835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#      $result->{map}         Memory map info from profile
36845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#      $result->{pcs}         Hash of all PC values seen, key is hex address
36855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub ReadProfile {
36865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $prog = shift;
36875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $fname = shift;
36885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $result;            # return value
36895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
36905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $CONTENTION_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
36915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $contention_marker = $&;
36925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $GROWTH_PAGE  =~ m,[^/]+$,;    # matches everything after the last slash
36935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $growth_marker = $&;
36945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $SYMBOL_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
36955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $symbol_marker = $&;
36965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $PROFILE_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
36975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $profile_marker = $&;
36985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
36995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Look at first line to see if it is a heap or a CPU profile.
37005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # CPU profile may start with no header at all, and just binary data
37015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # (starting with \0\0\0\0) -- in that case, don't try to read the
37025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # whole firstline, since it may be gigabytes(!) of data.
37035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  open(PROFILE, "<$fname") || error("$fname: $!\n");
37045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  binmode PROFILE;      # New perls do UTF-8 processing
37055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $header = ReadProfileHeader(*PROFILE);
37065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!defined($header)) {   # means "at EOF"
37075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    error("Profile is empty.\n");
37085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
37095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
37105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $symbols;
37115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($header =~ m/^--- *$symbol_marker/o) {
37125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Verify that the user asked for a symbolized profile
37135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!$main::use_symbolized_profile) {
37145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # we have both a binary and symbolized profiles, abort
37155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      error("FATAL ERROR: Symbolized profile\n   $fname\ncannot be used with " .
37165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            "a binary arg. Try again without passing\n   $prog\n");
37175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
37185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Read the symbol section of the symbolized profile file.
37195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $symbols = ReadSymbols(*PROFILE{IO});
37205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Read the next line to get the header for the remaining profile.
37215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $header = ReadProfileHeader(*PROFILE) || "";
37225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
37235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
37245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $main::profile_type = '';
37255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($header =~ m/^heap profile:.*$growth_marker/o) {
37265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $main::profile_type = 'growth';
37275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $result =  ReadHeapProfile($prog, *PROFILE, $header);
37285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } elsif ($header =~ m/^heap profile:/) {
37295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $main::profile_type = 'heap';
37305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $result =  ReadHeapProfile($prog, *PROFILE, $header);
37315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } elsif ($header =~ m/^--- *$contention_marker/o) {
37325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $main::profile_type = 'contention';
37335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $result = ReadSynchProfile($prog, *PROFILE);
37345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } elsif ($header =~ m/^--- *Stacks:/) {
37355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print STDERR
37365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "Old format contention profile: mistakenly reports " .
37375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "condition variable signals as lock contentions.\n";
37385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $main::profile_type = 'contention';
37395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $result = ReadSynchProfile($prog, *PROFILE);
37405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } elsif ($header =~ m/^--- *$profile_marker/) {
37415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # the binary cpu profile data starts immediately after this line
37425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $main::profile_type = 'cpu';
37435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $result = ReadCPUProfile($prog, $fname, *PROFILE);
37445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
37455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (defined($symbols)) {
37465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # a symbolized profile contains a format we don't recognize, bail out
37475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      error("$fname: Cannot recognize profile section after symbols.\n");
37485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
37495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # no ascii header present -- must be a CPU profile
37505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $main::profile_type = 'cpu';
37515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $result = ReadCPUProfile($prog, $fname, *PROFILE);
37525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
37535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
37545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  close(PROFILE);
37555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
37565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # if we got symbols along with the profile, return those as well
37575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (defined($symbols)) {
37585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $result->{symbols} = $symbols;
37595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
37605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
37615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $result;
37625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
37635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
37645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Subtract one from caller pc so we map back to call instr.
37655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# However, don't do this if we're reading a symbolized profile
37665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# file, in which case the subtract-one was done when the file
37675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# was written.
37685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
37695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# We apply the same logic to all readers, though ReadCPUProfile uses an
37705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# independent implementation.
37715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub FixCallerAddresses {
37725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $stack = shift;
37735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($main::use_symbolized_profile) {
37745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return $stack;
37755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
37765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $stack =~ /(\s)/;
37775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $delimiter = $1;
37785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my @addrs = split(' ', $stack);
37795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my @fixedaddrs;
37805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $#fixedaddrs = $#addrs;
37815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($#addrs >= 0) {
37825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $fixedaddrs[0] = $addrs[0];
37835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
37845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (my $i = 1; $i <= $#addrs; $i++) {
37855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $fixedaddrs[$i] = AddressSub($addrs[$i], "0x1");
37865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
37875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return join $delimiter, @fixedaddrs;
37885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
37895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
37905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
37915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# CPU profile reader
37925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub ReadCPUProfile {
37935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $prog = shift;
37945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $fname = shift;       # just used for logging
37955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local *PROFILE = shift;
37965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $version;
37975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $period;
37985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $i;
37995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $profile = {};
38005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $pcs = {};
38015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
38025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Parse string into array of slots.
38035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $slots = CpuProfileStream->new(*PROFILE, $fname);
38045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
38055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Read header.  The current header version is a 5-element structure
38065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # containing:
38075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #   0: header count (always 0)
38085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #   1: header "words" (after this one: 3)
38095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #   2: format version (0)
38105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #   3: sampling period (usec)
38115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #   4: unused padding (always 0)
38125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($slots->get(0) != 0 ) {
38135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    error("$fname: not a profile file, or old format profile file\n");
38145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
38155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $i = 2 + $slots->get(1);
38165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $version = $slots->get(2);
38175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $period = $slots->get(3);
38185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Do some sanity checking on these header values.
38195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($version > (2**32) || $period > (2**32) || $i > (2**32) || $i < 5) {
38205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    error("$fname: not a profile file, or corrupted profile file\n");
38215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
38225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
38235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Parse profile
38245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while ($slots->get($i) != -1) {
38255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $n = $slots->get($i++);
38265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $d = $slots->get($i++);
38275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($d > (2**16)) {  # TODO(csilvers): what's a reasonable max-stack-depth?
38285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $addr = sprintf("0%o", $i * ($address_length == 8 ? 4 : 8));
38295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print STDERR "At index $i (address $addr):\n";
38305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      error("$fname: stack trace depth >= 2**32\n");
38315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
38325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($slots->get($i) == 0) {
38335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # End of profile data marker
38345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $i += $d;
38355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      last;
38365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
38375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
38385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Make key out of the stack entries
38395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my @k = ();
38405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (my $j = 0; $j < $d; $j++) {
38415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $pc = $slots->get($i+$j);
38425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Subtract one from caller pc so we map back to call instr.
38435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # However, don't do this if we're reading a symbolized profile
38445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # file, in which case the subtract-one was done when the file
38455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # was written.
38465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($j > 0 && !$main::use_symbolized_profile) {
38475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $pc--;
38485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
38495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $pc = sprintf("%0*x", $address_length, $pc);
38505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $pcs->{$pc} = 1;
38515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      push @k, $pc;
38525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
38535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
38545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddEntry($profile, (join "\n", @k), $n);
38555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $i += $d;
38565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
38575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
38585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Parse map
38595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $map = '';
38605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  seek(PROFILE, $i * 4, 0);
38615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  read(PROFILE, $map, (stat PROFILE)[7]);
38625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
38635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $r = {};
38645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $r->{version} = $version;
38655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $r->{period} = $period;
38665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $r->{profile} = $profile;
38675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $r->{libs} = ParseLibraries($prog, $map, $pcs);
38685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $r->{pcs} = $pcs;
38695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
38705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $r;
38715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
38725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
38735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub ReadHeapProfile {
38745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $prog = shift;
38755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local *PROFILE = shift;
38765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $header = shift;
38775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
38785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $index = 1;
38795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($main::opt_inuse_space) {
38805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $index = 1;
38815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } elsif ($main::opt_inuse_objects) {
38825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $index = 0;
38835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } elsif ($main::opt_alloc_space) {
38845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $index = 3;
38855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } elsif ($main::opt_alloc_objects) {
38865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $index = 2;
38875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
38885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
38895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Find the type of this profile.  The header line looks like:
38905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #    heap profile:   1246:  8800744 [  1246:  8800744] @ <heap-url>/266053
38915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # There are two pairs <count: size>, the first inuse objects/space, and the
38925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # second allocated objects/space.  This is followed optionally by a profile
38935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # type, and if that is present, optionally by a sampling frequency.
38945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # For remote heap profiles (v1):
38955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # The interpretation of the sampling frequency is that the profiler, for
38965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # each sample, calculates a uniformly distributed random integer less than
38975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # the given value, and records the next sample after that many bytes have
38985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # been allocated.  Therefore, the expected sample interval is half of the
38995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # given frequency.  By default, if not specified, the expected sample
39005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # interval is 128KB.  Only remote-heap-page profiles are adjusted for
39015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # sample size.
39025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # For remote heap profiles (v2):
39035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # The sampling frequency is the rate of a Poisson process. This means that
39045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # the probability of sampling an allocation of size X with sampling rate Y
39055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # is 1 - exp(-X/Y)
39065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # For version 2, a typical header line might look like this:
39075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # heap profile:   1922: 127792360 [  1922: 127792360] @ <heap-url>_v2/524288
39085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # the trailing number (524288) is the sampling rate. (Version 1 showed
39095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # double the 'rate' here)
39105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $sampling_algorithm = 0;
39115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $sample_adjustment = 0;
39125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  chomp($header);
39135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $type = "unknown";
39145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($header =~ m"^heap profile:\s*(\d+):\s+(\d+)\s+\[\s*(\d+):\s+(\d+)\](\s*@\s*([^/]*)(/(\d+))?)?") {
39155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (defined($6) && ($6 ne '')) {
39165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $type = $6;
39175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $sample_period = $8;
39185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # $type is "heapprofile" for profiles generated by the
39195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # heap-profiler, and either "heap" or "heap_v2" for profiles
39205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # generated by sampling directly within tcmalloc.  It can also
39215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # be "growth" for heap-growth profiles.  The first is typically
39225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # found for profiles generated locally, and the others for
39235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # remote profiles.
39245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (($type eq "heapprofile") || ($type !~ /heap/) ) {
39255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # No need to adjust for the sampling rate with heap-profiler-derived data
39265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $sampling_algorithm = 0;
39275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } elsif ($type =~ /_v2/) {
39285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $sampling_algorithm = 2;     # version 2 sampling
39295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (defined($sample_period) && ($sample_period ne '')) {
39305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          $sample_adjustment = int($sample_period);
39315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
39325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
39335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $sampling_algorithm = 1;     # version 1 sampling
39345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (defined($sample_period) && ($sample_period ne '')) {
39355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          $sample_adjustment = int($sample_period)/2;
39365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
39375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
39385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
39395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # We detect whether or not this is a remote-heap profile by checking
39405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # that the total-allocated stats ($n2,$s2) are exactly the
39415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # same as the in-use stats ($n1,$s1).  It is remotely conceivable
39425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # that a non-remote-heap profile may pass this check, but it is hard
39435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # to imagine how that could happen.
39445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # In this case it's so old it's guaranteed to be remote-heap version 1.
39455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my ($n1, $s1, $n2, $s2) = ($1, $2, $3, $4);
39465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (($n1 == $n2) && ($s1 == $s2)) {
39475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # This is likely to be a remote-heap based sample profile
39485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $sampling_algorithm = 1;
39495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
39505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
39515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
39525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
39535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($sampling_algorithm > 0) {
39545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # For remote-heap generated profiles, adjust the counts and sizes to
39555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # account for the sample rate (we sample once every 128KB by default).
39565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($sample_adjustment == 0) {
39575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Turn on profile adjustment.
39585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $sample_adjustment = 128*1024;
39595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print STDERR "Adjusting heap profiles for 1-in-128KB sampling rate\n";
39605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
39615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      printf STDERR ("Adjusting heap profiles for 1-in-%d sampling rate\n",
39625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     $sample_adjustment);
39635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
39645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($sampling_algorithm > 1) {
39655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # We don't bother printing anything for the original version (version 1)
39665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      printf STDERR "Heap version $sampling_algorithm\n";
39675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
39685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
39695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
39705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $profile = {};
39715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $pcs = {};
39725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $map = "";
39735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
39745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (<PROFILE>) {
39755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    s/\r//g;         # turn windows-looking lines into unix-looking lines
39765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (/^MAPPED_LIBRARIES:/) {
39775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Read the /proc/self/maps data
39785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      while (<PROFILE>) {
39795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        s/\r//g;         # turn windows-looking lines into unix-looking lines
39805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $map .= $_;
39815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
39825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      last;
39835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
39845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
39855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (/^--- Memory map:/) {
39865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Read /proc/self/maps data as formatted by DumpAddressMap()
39875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $buildvar = "";
39885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      while (<PROFILE>) {
39895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        s/\r//g;         # turn windows-looking lines into unix-looking lines
39905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # Parse "build=<dir>" specification if supplied
39915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (m/^\s*build=(.*)\n/) {
39925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          $buildvar = $1;
39935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
39945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
39955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # Expand "$build" variable if available
39965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $_ =~ s/\$build\b/$buildvar/g;
39975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
39985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $map .= $_;
39995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
40005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      last;
40015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
40025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
40035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Read entry of the form:
40045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    #  <count1>: <bytes1> [<count2>: <bytes2>] @ a1 a2 a3 ... an
40055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    s/^\s*//;
40065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    s/\s*$//;
40075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (m/^\s*(\d+):\s+(\d+)\s+\[\s*(\d+):\s+(\d+)\]\s+@\s+(.*)$/) {
40085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $stack = $5;
40095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my ($n1, $s1, $n2, $s2) = ($1, $2, $3, $4);
40105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
40115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($sample_adjustment) {
40125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if ($sampling_algorithm == 2) {
40135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          # Remote-heap version 2
40145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          # The sampling frequency is the rate of a Poisson process.
40155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          # This means that the probability of sampling an allocation of
40165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          # size X with sampling rate Y is 1 - exp(-X/Y)
40175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if ($n1 != 0) {
40185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            my $ratio = (($s1*1.0)/$n1)/($sample_adjustment);
40195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            my $scale_factor = 1/(1 - exp(-$ratio));
40205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            $n1 *= $scale_factor;
40215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            $s1 *= $scale_factor;
40225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          }
40235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if ($n2 != 0) {
40245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            my $ratio = (($s2*1.0)/$n2)/($sample_adjustment);
40255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            my $scale_factor = 1/(1 - exp(-$ratio));
40265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            $n2 *= $scale_factor;
40275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            $s2 *= $scale_factor;
40285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          }
40295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        } else {
40305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          # Remote-heap version 1
40315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          my $ratio;
40325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          $ratio = (($s1*1.0)/$n1)/($sample_adjustment);
40335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if ($ratio < 1) {
40345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            $n1 /= $ratio;
40355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            $s1 /= $ratio;
40365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          }
40375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          $ratio = (($s2*1.0)/$n2)/($sample_adjustment);
40385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if ($ratio < 1) {
40395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            $n2 /= $ratio;
40405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            $s2 /= $ratio;
40415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          }
40425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
40435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
40445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
40455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my @counts = ($n1, $s1, $n2, $s2);
40465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      AddEntries($profile, $pcs, FixCallerAddresses($stack), $counts[$index]);
40475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
40485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
40495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
40505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $r = {};
40515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $r->{version} = "heap";
40525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $r->{period} = 1;
40535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $r->{profile} = $profile;
40545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $r->{libs} = ParseLibraries($prog, $map, $pcs);
40555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $r->{pcs} = $pcs;
40565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $r;
40575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
40585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
40595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub ReadSynchProfile {
40605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $prog = shift;
40615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local *PROFILE = shift;
40625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $header = shift;
40635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
40645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $map = '';
40655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $profile = {};
40665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $pcs = {};
40675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $sampling_period = 1;
40685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $cyclespernanosec = 2.8;   # Default assumption for old binaries
40695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $seen_clockrate = 0;
40705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $line;
40715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
40725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $index = 0;
40735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($main::opt_total_delay) {
40745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $index = 0;
40755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } elsif ($main::opt_contentions) {
40765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $index = 1;
40775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } elsif ($main::opt_mean_delay) {
40785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $index = 2;
40795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
40805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
40815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while ( $line = <PROFILE> ) {
40825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $line =~ s/\r//g;      # turn windows-looking lines into unix-looking lines
40835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ( $line =~ /^\s*(\d+)\s+(\d+) \@\s*(.*?)\s*$/ ) {
40845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my ($cycles, $count, $stack) = ($1, $2, $3);
40855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
40865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Convert cycles to nanoseconds
40875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $cycles /= $cyclespernanosec;
40885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
40895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Adjust for sampling done by application
40905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $cycles *= $sampling_period;
40915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $count *= $sampling_period;
40925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
40935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my @values = ($cycles, $count, $cycles / $count);
40945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      AddEntries($profile, $pcs, FixCallerAddresses($stack), $values[$index]);
40955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
40965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } elsif ( $line =~ /^(slow release).*thread \d+  \@\s*(.*?)\s*$/ ||
40975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              $line =~ /^\s*(\d+) \@\s*(.*?)\s*$/ ) {
40985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my ($cycles, $stack) = ($1, $2);
40995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($cycles !~ /^\d+$/) {
41005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        next;
41015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
41025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
41035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Convert cycles to nanoseconds
41045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $cycles /= $cyclespernanosec;
41055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
41065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Adjust for sampling done by application
41075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $cycles *= $sampling_period;
41085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
41095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      AddEntries($profile, $pcs, FixCallerAddresses($stack), $cycles);
41105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
41115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } elsif ( $line =~ m/^([a-z][^=]*)=(.*)$/ ) {
41125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my ($variable, $value) = ($1,$2);
41135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for ($variable, $value) {
41145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        s/^\s+//;
41155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        s/\s+$//;
41165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
41175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($variable eq "cycles/second") {
41185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $cyclespernanosec = $value / 1e9;
41195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $seen_clockrate = 1;
41205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } elsif ($variable eq "sampling period") {
41215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $sampling_period = $value;
41225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } elsif ($variable eq "ms since reset") {
41235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # Currently nothing is done with this value in pprof
41245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # So we just silently ignore it for now
41255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } elsif ($variable eq "discarded samples") {
41265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # Currently nothing is done with this value in pprof
41275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # So we just silently ignore it for now
41285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
41295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        printf STDERR ("Ignoring unnknown variable in /contention output: " .
41305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       "'%s' = '%s'\n",$variable,$value);
41315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
41325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
41335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Memory map entry
41345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $map .= $line;
41355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
41365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
41375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
41385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!$seen_clockrate) {
41395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    printf STDERR ("No cycles/second entry in profile; Guessing %.1f GHz\n",
41405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   $cyclespernanosec);
41415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
41425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
41435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $r = {};
41445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $r->{version} = 0;
41455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $r->{period} = $sampling_period;
41465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $r->{profile} = $profile;
41475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $r->{libs} = ParseLibraries($prog, $map, $pcs);
41485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $r->{pcs} = $pcs;
41495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $r;
41505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
41515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
41525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Given a hex value in the form "0x1abcd" or "1abcd", return either
41535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# "0001abcd" or "000000000001abcd", depending on the current (global)
41545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# address length.
41555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub HexExtend {
41565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $addr = shift;
41575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
41585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $addr =~ s/^(0x)?0*//;
41595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $zeros_needed = $address_length - length($addr);
41605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($zeros_needed < 0) {
41615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    printf STDERR "Warning: address $addr is longer than address length $address_length\n";
41625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return $addr;
41635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
41645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ("0" x $zeros_needed) . $addr;
41655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
41665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
41675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)##### Symbol extraction #####
41685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
41695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Aggressively search the lib_prefix values for the given library
41705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# If all else fails, just return the name of the library unmodified.
41715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# If the lib_prefix is "/my/path,/other/path" and $file is "/lib/dir/mylib.so"
41725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# it will search the following locations in this order, until it finds a file:
41735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   /my/path/lib/dir/mylib.so
41745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   /other/path/lib/dir/mylib.so
41755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   /my/path/dir/mylib.so
41765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   /other/path/dir/mylib.so
41775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   /my/path/mylib.so
41785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   /other/path/mylib.so
41795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   /lib/dir/mylib.so              (returned as last resort)
41805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub FindLibrary {
41815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $file = shift;
41825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $suffix = $file;
41835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
41845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Search for the library as described above
41855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  do {
41865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    foreach my $prefix (@prefix_list) {
41875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $fullpath = $prefix . $suffix;
41885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (-e $fullpath) {
41895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return $fullpath;
41905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
41915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
41925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } while ($suffix =~ s|^/[^/]+/|/|);
41935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $file;
41945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
41955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
41965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Return path to library with debugging symbols.
41975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# For libc libraries, the copy in /usr/lib/debug contains debugging symbols
41985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub DebuggingLibrary {
41995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $file = shift;
42005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($file =~ m|^/| && -f "/usr/lib/debug$file") {
42015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return "/usr/lib/debug$file";
42025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
42035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return undef;
42045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
42055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
42065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Parse text section header of a library using objdump
42075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub ParseTextSectionHeaderFromObjdump {
42085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $lib = shift;
42095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
42105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $size = undef;
42115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $vma;
42125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $file_offset;
42135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Get objdump output from the library file to figure out how to
42145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # map between mapped addresses and addresses in the library.
42155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $cmd = ShellEscape($obj_tool_map{"objdump"}, "-h", $lib);
42165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  open(OBJDUMP, "$cmd |") || error("$cmd: $!\n");
42175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (<OBJDUMP>) {
42185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    s/\r//g;         # turn windows-looking lines into unix-looking lines
42195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Idx Name          Size      VMA       LMA       File off  Algn
42205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    #  10 .text         00104b2c  420156f0  420156f0  000156f0  2**4
42215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # For 64-bit objects, VMA and LMA will be 16 hex digits, size and file
42225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # offset may still be 8.  But AddressSub below will still handle that.
42235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my @x = split;
42245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (($#x >= 6) && ($x[1] eq '.text')) {
42255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $size = $x[2];
42265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $vma = $x[3];
42275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $file_offset = $x[5];
42285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      last;
42295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
42305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
42315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  close(OBJDUMP);
42325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
42335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!defined($size)) {
42345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return undef;
42355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
42365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
42375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $r = {};
42385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $r->{size} = $size;
42395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $r->{vma} = $vma;
42405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $r->{file_offset} = $file_offset;
42415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
42425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $r;
42435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
42445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
42455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Parse text section header of a library using otool (on OS X)
42465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub ParseTextSectionHeaderFromOtool {
42475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $lib = shift;
42485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
42495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $size = undef;
42505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $vma = undef;
42515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $file_offset = undef;
42525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Get otool output from the library file to figure out how to
42535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # map between mapped addresses and addresses in the library.
42545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $command = ShellEscape($obj_tool_map{"otool"}, "-l", $lib);
42555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  open(OTOOL, "$command |") || error("$command: $!\n");
42565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $cmd = "";
42575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $sectname = "";
42585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $segname = "";
42595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $line (<OTOOL>) {
42605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $line =~ s/\r//g;      # turn windows-looking lines into unix-looking lines
42615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Load command <#>
42625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    #       cmd LC_SEGMENT
42635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # [...]
42645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Section
42655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    #   sectname __text
42665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    #    segname __TEXT
42675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    #       addr 0x000009f8
42685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    #       size 0x00018b9e
42695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    #     offset 2552
42705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    #      align 2^2 (4)
42715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # We will need to strip off the leading 0x from the hex addresses,
42725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # and convert the offset into hex.
42735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($line =~ /Load command/) {
42745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $cmd = "";
42755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $sectname = "";
42765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $segname = "";
42775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } elsif ($line =~ /Section/) {
42785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $sectname = "";
42795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $segname = "";
42805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } elsif ($line =~ /cmd (\w+)/) {
42815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $cmd = $1;
42825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } elsif ($line =~ /sectname (\w+)/) {
42835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $sectname = $1;
42845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } elsif ($line =~ /segname (\w+)/) {
42855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $segname = $1;
42865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } elsif (!(($cmd eq "LC_SEGMENT" || $cmd eq "LC_SEGMENT_64") &&
42875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               $sectname eq "__text" &&
42885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               $segname eq "__TEXT")) {
42895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next;
42905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } elsif ($line =~ /\baddr 0x([0-9a-fA-F]+)/) {
42915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $vma = $1;
42925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } elsif ($line =~ /\bsize 0x([0-9a-fA-F]+)/) {
42935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $size = $1;
42945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } elsif ($line =~ /\boffset ([0-9]+)/) {
42955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $file_offset = sprintf("%016x", $1);
42965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
42975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (defined($vma) && defined($size) && defined($file_offset)) {
42985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      last;
42995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
43005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
43015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  close(OTOOL);
43025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
43035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!defined($vma) || !defined($size) || !defined($file_offset)) {
43045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     return undef;
43055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
43065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
43075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $r = {};
43085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $r->{size} = $size;
43095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $r->{vma} = $vma;
43105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $r->{file_offset} = $file_offset;
43115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
43125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $r;
43135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
43145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
43155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub ParseTextSectionHeader {
43165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # obj_tool_map("otool") is only defined if we're in a Mach-O environment
43175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (defined($obj_tool_map{"otool"})) {
43185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $r = ParseTextSectionHeaderFromOtool(@_);
43195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (defined($r)){
43205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return $r;
43215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
43225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
43235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # If otool doesn't work, or we don't have it, fall back to objdump
43245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ParseTextSectionHeaderFromObjdump(@_);
43255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
43265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
43275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Split /proc/pid/maps dump into a list of libraries
43285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub ParseLibraries {
43295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return if $main::use_symbol_page;  # We don't need libraries info.
43305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $prog = shift;
43315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $map = shift;
43325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $pcs = shift;
43335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
43345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $result = [];
43355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $h = "[a-f0-9]+";
43365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $zero_offset = HexExtend("0");
43375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
43385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $buildvar = "";
43395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $l (split("\n", $map)) {
43405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($l =~ m/^\s*build=(.*)$/) {
43415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $buildvar = $1;
43425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
43435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
43445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $start;
43455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $finish;
43465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $offset;
43475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $lib;
43485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    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) {
43495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Full line from /proc/self/maps.  Example:
43505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      #   40000000-40015000 r-xp 00000000 03:01 12845071   /lib/ld-2.3.2.so
43515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $start = HexExtend($1);
43525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $finish = HexExtend($2);
43535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $offset = HexExtend($3);
43545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $lib = $4;
43555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $lib =~ s|\\|/|g;     # turn windows-style paths into unix-style paths
43565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } elsif ($l =~ /^\s*($h)-($h):\s*(\S+\.so(\.\d+)*)/) {
43575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Cooked line from DumpAddressMap.  Example:
43585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      #   40000000-40015000: /lib/ld-2.3.2.so
43595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $start = HexExtend($1);
43605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $finish = HexExtend($2);
43615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $offset = $zero_offset;
43625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $lib = $3;
43635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
43645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next;
43655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
43665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
43675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Expand "$build" variable if available
43685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $lib =~ s/\$build\b/$buildvar/g;
43695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
43705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $lib = FindLibrary($lib);
43715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
43725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Check for pre-relocated libraries, which use pre-relocated symbol tables
43735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # and thus require adjusting the offset that we'll use to translate
43745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # VM addresses into symbol table addresses.
43755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Only do this if we're not going to fetch the symbol table from a
43765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # debugging copy of the library.
43775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!DebuggingLibrary($lib)) {
43785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $text = ParseTextSectionHeader($lib);
43795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (defined($text)) {
43805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         my $vma_offset = AddressSub($text->{vma}, $text->{file_offset});
43815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         $offset = AddressAdd($offset, $vma_offset);
43825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
43835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
43845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
43855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    push(@{$result}, [$lib, $start, $finish, $offset]);
43865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
43875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
43885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Append special entry for additional library (not relocated)
43895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($main::opt_lib ne "") {
43905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $text = ParseTextSectionHeader($main::opt_lib);
43915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (defined($text)) {
43925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       my $start = $text->{vma};
43935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       my $finish = AddressAdd($start, $text->{size});
43945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
43955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       push(@{$result}, [$main::opt_lib, $start, $finish, $start]);
43965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
43975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
43985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
43995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Append special entry for the main program.  This covers
44005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # 0..max_pc_value_seen, so that we assume pc values not found in one
44015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # of the library ranges will be treated as coming from the main
44025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # program binary.
44035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $min_pc = HexExtend("0");
44045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $max_pc = $min_pc;          # find the maximal PC value in any sample
44055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $pc (keys(%{$pcs})) {
44065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (HexExtend($pc) gt $max_pc) { $max_pc = HexExtend($pc); }
44075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
44085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  push(@{$result}, [$prog, $min_pc, $max_pc, $zero_offset]);
44095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
44105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $result;
44115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
44125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
44135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Add two hex addresses of length $address_length.
44145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Run pprof --test for unit test if this is changed.
44155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub AddressAdd {
44165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $addr1 = shift;
44175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $addr2 = shift;
44185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $sum;
44195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
44205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($address_length == 8) {
44215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Perl doesn't cope with wraparound arithmetic, so do it explicitly:
44225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $sum = (hex($addr1)+hex($addr2)) % (0x10000000 * 16);
44235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return sprintf("%08x", $sum);
44245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
44255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
44265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Do the addition in 7-nibble chunks to trivialize carry handling.
44275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
44285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($main::opt_debug and $main::opt_test) {
44295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print STDERR "AddressAdd $addr1 + $addr2 = ";
44305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
44315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
44325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $a1 = substr($addr1,-7);
44335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $addr1 = substr($addr1,0,-7);
44345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $a2 = substr($addr2,-7);
44355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $addr2 = substr($addr2,0,-7);
44365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $sum = hex($a1) + hex($a2);
44375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $c = 0;
44385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($sum > 0xfffffff) {
44395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $c = 1;
44405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $sum -= 0x10000000;
44415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
44425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $r = sprintf("%07x", $sum);
44435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
44445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $a1 = substr($addr1,-7);
44455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $addr1 = substr($addr1,0,-7);
44465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $a2 = substr($addr2,-7);
44475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $addr2 = substr($addr2,0,-7);
44485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $sum = hex($a1) + hex($a2) + $c;
44495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $c = 0;
44505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($sum > 0xfffffff) {
44515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $c = 1;
44525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $sum -= 0x10000000;
44535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
44545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $r = sprintf("%07x", $sum) . $r;
44555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
44565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $sum = hex($addr1) + hex($addr2) + $c;
44575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($sum > 0xff) { $sum -= 0x100; }
44585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $r = sprintf("%02x", $sum) . $r;
44595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
44605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($main::opt_debug and $main::opt_test) { print STDERR "$r\n"; }
44615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
44625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return $r;
44635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
44645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
44655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
44665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
44675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Subtract two hex addresses of length $address_length.
44685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Run pprof --test for unit test if this is changed.
44695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub AddressSub {
44705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $addr1 = shift;
44715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $addr2 = shift;
44725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $diff;
44735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
44745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($address_length == 8) {
44755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Perl doesn't cope with wraparound arithmetic, so do it explicitly:
44765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $diff = (hex($addr1)-hex($addr2)) % (0x10000000 * 16);
44775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return sprintf("%08x", $diff);
44785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
44795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
44805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Do the addition in 7-nibble chunks to trivialize borrow handling.
44815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # if ($main::opt_debug) { print STDERR "AddressSub $addr1 - $addr2 = "; }
44825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
44835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $a1 = hex(substr($addr1,-7));
44845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $addr1 = substr($addr1,0,-7);
44855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $a2 = hex(substr($addr2,-7));
44865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $addr2 = substr($addr2,0,-7);
44875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $b = 0;
44885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($a2 > $a1) {
44895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $b = 1;
44905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $a1 += 0x10000000;
44915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
44925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $diff = $a1 - $a2;
44935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $r = sprintf("%07x", $diff);
44945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
44955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $a1 = hex(substr($addr1,-7));
44965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $addr1 = substr($addr1,0,-7);
44975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $a2 = hex(substr($addr2,-7)) + $b;
44985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $addr2 = substr($addr2,0,-7);
44995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $b = 0;
45005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($a2 > $a1) {
45015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $b = 1;
45025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $a1 += 0x10000000;
45035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
45045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $diff = $a1 - $a2;
45055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $r = sprintf("%07x", $diff) . $r;
45065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
45075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $a1 = hex($addr1);
45085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $a2 = hex($addr2) + $b;
45095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($a2 > $a1) { $a1 += 0x100; }
45105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $diff = $a1 - $a2;
45115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $r = sprintf("%02x", $diff) . $r;
45125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
45135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # if ($main::opt_debug) { print STDERR "$r\n"; }
45145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
45155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return $r;
45165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
45175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
45185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
45195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Increment a hex addresses of length $address_length.
45205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Run pprof --test for unit test if this is changed.
45215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub AddressInc {
45225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $addr = shift;
45235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $sum;
45245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
45255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($address_length == 8) {
45265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Perl doesn't cope with wraparound arithmetic, so do it explicitly:
45275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $sum = (hex($addr)+1) % (0x10000000 * 16);
45285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return sprintf("%08x", $sum);
45295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
45305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
45315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Do the addition in 7-nibble chunks to trivialize carry handling.
45325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # We are always doing this to step through the addresses in a function,
45335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # and will almost never overflow the first chunk, so we check for this
45345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # case and exit early.
45355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
45365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # if ($main::opt_debug) { print STDERR "AddressInc $addr1 = "; }
45375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
45385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $a1 = substr($addr,-7);
45395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $addr = substr($addr,0,-7);
45405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $sum = hex($a1) + 1;
45415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $r = sprintf("%07x", $sum);
45425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($sum <= 0xfffffff) {
45435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $r = $addr . $r;
45445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # if ($main::opt_debug) { print STDERR "$r\n"; }
45455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return HexExtend($r);
45465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
45475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $r = "0000000";
45485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
45495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
45505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $a1 = substr($addr,-7);
45515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $addr = substr($addr,0,-7);
45525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $sum = hex($a1) + 1;
45535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $r = sprintf("%07x", $sum) . $r;
45545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($sum <= 0xfffffff) {
45555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $r = $addr . $r;
45565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # if ($main::opt_debug) { print STDERR "$r\n"; }
45575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return HexExtend($r);
45585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
45595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $r = "00000000000000";
45605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
45615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
45625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $sum = hex($addr) + 1;
45635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($sum > 0xff) { $sum -= 0x100; }
45645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $r = sprintf("%02x", $sum) . $r;
45655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
45665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # if ($main::opt_debug) { print STDERR "$r\n"; }
45675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return $r;
45685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
45695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
45705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
45715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Extract symbols for all PC values found in profile
45725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub ExtractSymbols {
45735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $libs = shift;
45745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $pcset = shift;
45755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
45765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $symbols = {};
45775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
45785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Map each PC value to the containing library.  To make this faster,
45795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # we sort libraries by their starting pc value (highest first), and
45805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # advance through the libraries as we advance the pc.  Sometimes the
45815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # addresses of libraries may overlap with the addresses of the main
45825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # binary, so to make sure the libraries 'win', we iterate over the
45835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # libraries in reverse order (which assumes the binary doesn't start
45845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # in the middle of a library, which seems a fair assumption).
45855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my @pcs = (sort { $a cmp $b } keys(%{$pcset}));  # pcset is 0-extended strings
45865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $lib (sort {$b->[1] cmp $a->[1]} @{$libs}) {
45875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $libname = $lib->[0];
45885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $start = $lib->[1];
45895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $finish = $lib->[2];
45905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $offset = $lib->[3];
45915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
45925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Get list of pcs that belong in this library.
45935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $contained = [];
45945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my ($start_pc_index, $finish_pc_index);
45955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Find smallest finish_pc_index such that $finish < $pc[$finish_pc_index].
45965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for ($finish_pc_index = $#pcs + 1; $finish_pc_index > 0;
45975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         $finish_pc_index--) {
45985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      last if $pcs[$finish_pc_index - 1] le $finish;
45995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
46005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Find smallest start_pc_index such that $start <= $pc[$start_pc_index].
46015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for ($start_pc_index = $finish_pc_index; $start_pc_index > 0;
46025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         $start_pc_index--) {
46035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      last if $pcs[$start_pc_index - 1] lt $start;
46045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
46055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # This keeps PC values higher than $pc[$finish_pc_index] in @pcs,
46065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # in case there are overlaps in libraries and the main binary.
46075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    @{$contained} = splice(@pcs, $start_pc_index,
46085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           $finish_pc_index - $start_pc_index);
46095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Map to symbols
46105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    MapToSymbols($libname, AddressSub($start, $offset), $contained, $symbols);
46115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
46125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
46135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $symbols;
46145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
46155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
46165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Map list of PC values to symbols for a given image
46175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub MapToSymbols {
46185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $image = shift;
46195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $offset = shift;
46205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $pclist = shift;
46215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $symbols = shift;
46225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
46235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $debug = 0;
46245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
46255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Ignore empty binaries
46265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($#{$pclist} < 0) { return; }
46275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
46285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Figure out the addr2line command to use
46295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $addr2line = $obj_tool_map{"addr2line"};
46305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $cmd = ShellEscape($addr2line, "-f", "-C", "-e", $image);
46315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (exists $obj_tool_map{"addr2line_pdb"}) {
46325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $addr2line = $obj_tool_map{"addr2line_pdb"};
46335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $cmd = ShellEscape($addr2line, "--demangle", "-f", "-C", "-e", $image);
46345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
46355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
46365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # If "addr2line" isn't installed on the system at all, just use
46375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # nm to get what info we can (function names, but not line numbers).
46385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (system(ShellEscape($addr2line, "--help") . " >$dev_null 2>&1") != 0) {
46395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    MapSymbolsWithNM($image, $offset, $pclist, $symbols);
46405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
46415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
46425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
46435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # "addr2line -i" can produce a variable number of lines per input
46445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # address, with no separator that allows us to tell when data for
46455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # the next address starts.  So we find the address for a special
46465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # symbol (_fini) and interleave this address between all real
46475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # addresses passed to addr2line.  The name of this special symbol
46485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # can then be used as a separator.
46495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $sep_address = undef;  # May be filled in by MapSymbolsWithNM()
46505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $nm_symbols = {};
46515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MapSymbolsWithNM($image, $offset, $pclist, $nm_symbols);
46525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (defined($sep_address)) {
46535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Only add " -i" to addr2line if the binary supports it.
46545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # addr2line --help returns 0, but not if it sees an unknown flag first.
46555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (system("$cmd -i --help >$dev_null 2>&1") == 0) {
46565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $cmd .= " -i";
46575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
46585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $sep_address = undef;   # no need for sep_address if we don't support -i
46595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
46605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
46615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
46625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Make file with all PC values with intervening 'sep_address' so
46635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # that we can reliably detect the end of inlined function list
46645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  open(ADDRESSES, ">$main::tmpfile_sym") || error("$main::tmpfile_sym: $!\n");
46655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($debug) { print("---- $image ---\n"); }
46665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (my $i = 0; $i <= $#{$pclist}; $i++) {
46675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # addr2line always reads hex addresses, and does not need '0x' prefix.
46685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($debug) { printf STDERR ("%s\n", $pclist->[$i]); }
46695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    printf ADDRESSES ("%s\n", AddressSub($pclist->[$i], $offset));
46705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (defined($sep_address)) {
46715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      printf ADDRESSES ("%s\n", $sep_address);
46725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
46735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
46745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  close(ADDRESSES);
46755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($debug) {
46765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print("----\n");
46775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    system("cat", $main::tmpfile_sym);
46785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print("----\n");
46795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    system("$cmd < " . ShellEscape($main::tmpfile_sym));
46805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print("----\n");
46815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
46825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
46835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  open(SYMBOLS, "$cmd <" . ShellEscape($main::tmpfile_sym) . " |")
46845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      || error("$cmd: $!\n");
46855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $count = 0;   # Index in pclist
46865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (<SYMBOLS>) {
46875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Read fullfunction and filelineinfo from next pair of lines
46885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    s/\r?\n$//g;
46895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $fullfunction = $_;
46905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $_ = <SYMBOLS>;
46915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    s/\r?\n$//g;
46925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $filelinenum = $_;
46935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
46945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (defined($sep_address) && $fullfunction eq $sep_symbol) {
46955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Terminating marker for data for this address
46965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $count++;
46975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next;
46985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
46995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
47005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $filelinenum =~ s|\\|/|g; # turn windows-style paths into unix-style paths
47015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
47025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $pcstr = $pclist->[$count];
47035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $function = ShortFunctionName($fullfunction);
47045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $nms = $nm_symbols->{$pcstr};
47055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (defined($nms)) {
47065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($fullfunction eq '??') {
47075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # nm found a symbol for us.
47085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $function = $nms->[0];
47095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $fullfunction = $nms->[2];
47105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
47115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	# MapSymbolsWithNM tags each routine with its starting address,
47125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	# useful in case the image has multiple occurrences of this
47135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	# routine.  (It uses a syntax that resembles template paramters,
47145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	# that are automatically stripped out by ShortFunctionName().)
47155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	# addr2line does not provide the same information.  So we check
47165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	# if nm disambiguated our symbol, and if so take the annotated
47175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	# (nm) version of the routine-name.  TODO(csilvers): this won't
47185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	# catch overloaded, inlined symbols, which nm doesn't see.
47195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	# Better would be to do a check similar to nm's, in this fn.
47205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if ($nms->[2] =~ m/^\Q$function\E/) {  # sanity check it's the right fn
47215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	  $function = $nms->[0];
47225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	  $fullfunction = $nms->[2];
47235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
47245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
47255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
47265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    
47275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Prepend to accumulated symbols for pcstr
47285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # (so that caller comes before callee)
47295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $sym = $symbols->{$pcstr};
47305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!defined($sym)) {
47315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $sym = [];
47325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $symbols->{$pcstr} = $sym;
47335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
47345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    unshift(@{$sym}, $function, $filelinenum, $fullfunction);
47355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($debug) { printf STDERR ("%s => [%s]\n", $pcstr, join(" ", @{$sym})); }
47365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!defined($sep_address)) {
47375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Inlining is off, so this entry ends immediately
47385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $count++;
47395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
47405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
47415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  close(SYMBOLS);
47425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
47435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
47445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use nm to map the list of referenced PCs to symbols.  Return true iff we
47455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# are able to read procedure information via nm.
47465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub MapSymbolsWithNM {
47475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $image = shift;
47485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $offset = shift;
47495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $pclist = shift;
47505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $symbols = shift;
47515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
47525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Get nm output sorted by increasing address
47535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $symbol_table = GetProcedureBoundaries($image, ".");
47545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!%{$symbol_table}) {
47555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0;
47565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
47575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Start addresses are already the right length (8 or 16 hex digits).
47585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my @names = sort { $symbol_table->{$a}->[0] cmp $symbol_table->{$b}->[0] }
47595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    keys(%{$symbol_table});
47605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
47615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($#names < 0) {
47625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # No symbols: just use addresses
47635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    foreach my $pc (@{$pclist}) {
47645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $pcstr = "0x" . $pc;
47655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $symbols->{$pc} = [$pcstr, "?", $pcstr];
47665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
47675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0;
47685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
47695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
47705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Sort addresses so we can do a join against nm output
47715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $index = 0;
47725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $fullname = $names[0];
47735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $name = ShortFunctionName($fullname);
47745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $pc (sort { $a cmp $b } @{$pclist}) {
47755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Adjust for mapped offset
47765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $mpc = AddressSub($pc, $offset);
47775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while (($index < $#names) && ($mpc ge $symbol_table->{$fullname}->[1])){
47785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $index++;
47795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $fullname = $names[$index];
47805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $name = ShortFunctionName($fullname);
47815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
47825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($mpc lt $symbol_table->{$fullname}->[1]) {
47835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $symbols->{$pc} = [$name, "?", $fullname];
47845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
47855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $pcstr = "0x" . $pc;
47865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $symbols->{$pc} = [$pcstr, "?", $pcstr];
47875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
47885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
47895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 1;
47905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
47915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
47925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub ShortFunctionName {
47935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $function = shift;
47945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while ($function =~ s/\([^()]*\)(\s*const)?//g) { }   # Argument types
47955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while ($function =~ s/<[^<>]*>//g)  { }    # Remove template arguments
47965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $function =~ s/^.*\s+(\w+::)/$1/;          # Remove leading type
47975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $function;
47985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
47995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
48005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Trim overly long symbols found in disassembler output
48015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub CleanDisassembly {
48025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $d = shift;
48035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while ($d =~ s/\([^()%]*\)(\s*const)?//g) { } # Argument types, not (%rax)
48045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while ($d =~ s/(\w+)<[^<>]*>/$1/g)  { }       # Remove template arguments
48055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $d;
48065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
48075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
48085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Clean file name for display
48095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub CleanFileName {
48105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my ($f) = @_;
48115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $f =~ s|^/proc/self/cwd/||;
48125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $f =~ s|^\./||;
48135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $f;
48145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
48155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
48165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Make address relative to section and clean up for display
48175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub UnparseAddress {
48185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my ($offset, $address) = @_;
48195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $address = AddressSub($address, $offset);
48205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $address =~ s/^0x//;
48215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $address =~ s/^0*//;
48225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $address;
48235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
48245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
48255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)##### Miscellaneous #####
48265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
48275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Find the right versions of the above object tools to use.  The
48285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# argument is the program file being analyzed, and should be an ELF
48295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 32-bit or ELF 64-bit executable file.  The location of the tools
48305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# is determined by considering the following options in this order:
48315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   1) --tools option, if set
48325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   2) PPROF_TOOLS environment variable, if set
48335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   3) the environment
48345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub ConfigureObjTools {
48355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $prog_file = shift;
48365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
48375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Check for the existence of $prog_file because /usr/bin/file does not
48385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # predictably return error status in prod.
48395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  (-e $prog_file)  || error("$prog_file does not exist.\n");
48405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
48415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $file_type = undef;
48425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (-e "/usr/bin/file") {
48435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Follow symlinks (at least for systems where "file" supports that).
48445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $escaped_prog_file = ShellEscape($prog_file);
48455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $file_type = `/usr/bin/file -L $escaped_prog_file 2>$dev_null ||
48465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  /usr/bin/file $escaped_prog_file`;
48475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } elsif ($^O == "MSWin32") {
48485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $file_type = "MS Windows";
48495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
48505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print STDERR "WARNING: Can't determine the file type of $prog_file";
48515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
48525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
48535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($file_type =~ /64-bit/) {
48545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Change $address_length to 16 if the program file is ELF 64-bit.
48555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # We can't detect this from many (most?) heap or lock contention
48565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # profiles, since the actual addresses referenced are generally in low
48575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # memory even for 64-bit programs.
48585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $address_length = 16;
48595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
48605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
48615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($file_type =~ /MS Windows/) {
48625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # For windows, we provide a version of nm and addr2line as part of
48635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # the opensource release, which is capable of parsing
48645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Windows-style PDB executables.  It should live in the path, or
48655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # in the same directory as pprof.
48665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $obj_tool_map{"nm_pdb"} = "nm-pdb";
48675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $obj_tool_map{"addr2line_pdb"} = "addr2line-pdb";
48685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
48695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
48705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($file_type =~ /Mach-O/) {
48715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # OS X uses otool to examine Mach-O files, rather than objdump.
48725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $obj_tool_map{"otool"} = "otool";
48735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $obj_tool_map{"addr2line"} = "false";  # no addr2line
48745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $obj_tool_map{"objdump"} = "false";  # no objdump
48755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
48765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
48775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Go fill in %obj_tool_map with the pathnames to use:
48785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $tool (keys %obj_tool_map) {
48795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $obj_tool_map{$tool} = ConfigureTool($obj_tool_map{$tool});
48805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
48815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
48825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
48835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Returns the path of a caller-specified object tool.  If --tools or
48845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# PPROF_TOOLS are specified, then returns the full path to the tool
48855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# with that prefix.  Otherwise, returns the path unmodified (which
48865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# means we will look for it on PATH).
48875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub ConfigureTool {
48885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $tool = shift;
48895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $path;
48905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
48915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # --tools (or $PPROF_TOOLS) is a comma separated list, where each
48925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # item is either a) a pathname prefix, or b) a map of the form
48935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # <tool>:<path>.  First we look for an entry of type (b) for our
48945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # tool.  If one is found, we use it.  Otherwise, we consider all the
48955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # pathname prefixes in turn, until one yields an existing file.  If
48965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # none does, we use a default path.
48975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $tools = $main::opt_tools || $ENV{"PPROF_TOOLS"} || "";
48985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($tools =~ m/(,|^)\Q$tool\E:([^,]*)/) {
48995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $path = $2;
49005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # TODO(csilvers): sanity-check that $path exists?  Hard if it's relative.
49015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } elsif ($tools ne '') {
49025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    foreach my $prefix (split(',', $tools)) {
49035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next if ($prefix =~ /:/);    # ignore "tool:fullpath" entries in the list
49045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (-x $prefix . $tool) {
49055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $path = $prefix . $tool;
49065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        last;
49075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
49085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
49095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!$path) {
49105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      error("No '$tool' found with prefix specified by " .
49115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            "--tools (or \$PPROF_TOOLS) '$tools'\n");
49125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
49135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
49145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # ... otherwise use the version that exists in the same directory as
49155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # pprof.  If there's nothing there, use $PATH.
49165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $0 =~ m,[^/]*$,;     # this is everything after the last slash
49175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $dirname = $`;    # this is everything up to and including the last slash
49185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (-x "$dirname$tool") {
49195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $path = "$dirname$tool";
49205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else { 
49215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $path = $tool;
49225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
49235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
49245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($main::opt_debug) { print STDERR "Using '$path' for '$tool'.\n"; }
49255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $path;
49265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
49275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
49285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub ShellEscape {
49295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my @escaped_words = ();
49305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $word (@_) {
49315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $escaped_word = $word;
49325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($word =~ m![^a-zA-Z0-9/.,_=-]!) {  # check for anything not in whitelist
49335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $escaped_word =~ s/'/'\\''/;
49345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $escaped_word = "'$escaped_word'";
49355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
49365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    push(@escaped_words, $escaped_word);
49375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
49385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return join(" ", @escaped_words);
49395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
49405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
49415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub cleanup {
49425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  unlink($main::tmpfile_sym);
49435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  unlink(keys %main::tempnames);
49445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
49455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # We leave any collected profiles in $HOME/pprof in case the user wants
49465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # to look at them later.  We print a message informing them of this.
49475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ((scalar(@main::profile_files) > 0) &&
49485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      defined($main::collected_profile)) {
49495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (scalar(@main::profile_files) == 1) {
49505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print STDERR "Dynamically gathered profile is in $main::collected_profile\n";
49515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
49525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print STDERR "If you want to investigate this profile further, you can do:\n";
49535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print STDERR "\n";
49545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print STDERR "  pprof \\\n";
49555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print STDERR "    $main::prog \\\n";
49565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print STDERR "    $main::collected_profile\n";
49575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print STDERR "\n";
49585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
49595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
49605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
49615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub sighandler {
49625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  cleanup();
49635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  exit(1);
49645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
49655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
49665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub error {
49675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $msg = shift;
49685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  print STDERR $msg;
49695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  cleanup();
49705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  exit(1);
49715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
49725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
49735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
49745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Run $nm_command and get all the resulting procedure boundaries whose
49755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# names match "$regexp" and returns them in a hashtable mapping from
49765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# procedure name to a two-element vector of [start address, end address]
49775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub GetProcedureBoundariesViaNm {
49785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $escaped_nm_command = shift;    # shell-escaped
49795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $regexp = shift;
49805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
49815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $symbol_table = {};
49825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  open(NM, "$escaped_nm_command |") || error("$escaped_nm_command: $!\n");
49835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $last_start = "0";
49845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $routine = "";
49855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (<NM>) {
49865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    s/\r//g;         # turn windows-looking lines into unix-looking lines
49875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (m/^\s*([0-9a-f]+) (.) (..*)/) {
49885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $start_val = $1;
49895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $type = $2;
49905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      my $this_routine = $3;
49915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
49925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # It's possible for two symbols to share the same address, if
49935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # one is a zero-length variable (like __start_google_malloc) or
49945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # one symbol is a weak alias to another (like __libc_malloc).
49955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # In such cases, we want to ignore all values except for the
49965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # actual symbol, which in nm-speak has type "T".  The logic
49975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # below does this, though it's a bit tricky: what happens when
49985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # we have a series of lines with the same address, is the first
49995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # one gets queued up to be processed.  However, it won't
50005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # *actually* be processed until later, when we read a line with
50015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # a different address.  That means that as long as we're reading
50025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # lines with the same address, we have a chance to replace that
50035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # item in the queue, which we do whenever we see a 'T' entry --
50045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # that is, a line with type 'T'.  If we never see a 'T' entry,
50055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # we'll just go ahead and process the first entry (which never
50065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # got touched in the queue), and ignore the others.
50075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($start_val eq $last_start && $type =~ /t/i) {
50085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # We are the 'T' symbol at this address, replace previous symbol.
50095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $routine = $this_routine;
50105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        next;
50115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } elsif ($start_val eq $last_start) {
50125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # We're not the 'T' symbol at this address, so ignore us.
50135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        next;
50145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
50155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
50165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($this_routine eq $sep_symbol) {
50175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $sep_address = HexExtend($start_val);
50185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
50195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
50205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Tag this routine with the starting address in case the image
50215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # has multiple occurrences of this routine.  We use a syntax
50225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # that resembles template paramters that are automatically
50235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # stripped out by ShortFunctionName()
50245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $this_routine .= "<$start_val>";
50255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
50265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (defined($routine) && $routine =~ m/$regexp/) {
50275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        $symbol_table->{$routine} = [HexExtend($last_start),
50285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     HexExtend($start_val)];
50295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
50305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $last_start = $start_val;
50315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $routine = $this_routine;
50325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } elsif (m/^Loaded image name: (.+)/) {
50335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # The win32 nm workalike emits information about the binary it is using.
50345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($main::opt_debug) { print STDERR "Using Image $1\n"; }
50355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } elsif (m/^PDB file name: (.+)/) {
50365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # The win32 nm workalike emits information about the pdb it is using.
50375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ($main::opt_debug) { print STDERR "Using PDB $1\n"; }
50385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
50395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
50405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  close(NM);
50415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Handle the last line in the nm output.  Unfortunately, we don't know
50425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # how big this last symbol is, because we don't know how big the file
50435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # is.  For now, we just give it a size of 0.
50445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # TODO(csilvers): do better here.
50455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (defined($routine) && $routine =~ m/$regexp/) {
50465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $symbol_table->{$routine} = [HexExtend($last_start),
50475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 HexExtend($last_start)];
50485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
50495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $symbol_table;
50505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
50515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
50525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Gets the procedure boundaries for all routines in "$image" whose names
50535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# match "$regexp" and returns them in a hashtable mapping from procedure
50545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# name to a two-element vector of [start address, end address].
50555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Will return an empty map if nm is not installed or not working properly.
50565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub GetProcedureBoundaries {
50575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $image = shift;
50585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $regexp = shift;
50595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
50605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # If $image doesn't start with /, then put ./ in front of it.  This works
50615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # around an obnoxious bug in our probing of nm -f behavior.
50625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # "nm -f $image" is supposed to fail on GNU nm, but if:
50635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #
50645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # a. $image starts with [BbSsPp] (for example, bin/foo/bar), AND
50655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # b. you have a.out in your current directory (a not uncommon occurence)
50665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #
50675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # then "nm -f $image" succeeds because -f only looks at the first letter of
50685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # the argument, which looks valid because it's [BbSsPp], and then since
50695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # there's no image provided, it looks for a.out and finds it.
50705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #
50715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # This regex makes sure that $image starts with . or /, forcing the -f
50725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # parsing to fail since . and / are not valid formats.
50735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $image =~ s#^[^/]#./$&#;
50745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
50755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # For libc libraries, the copy in /usr/lib/debug contains debugging symbols
50765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $debugging = DebuggingLibrary($image);
50775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($debugging) {
50785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $image = $debugging;
50795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
50805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
50815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $nm = $obj_tool_map{"nm"};
50825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $cppfilt = $obj_tool_map{"c++filt"};
50835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
50845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # nm can fail for two reasons: 1) $image isn't a debug library; 2) nm
50855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # binary doesn't support --demangle.  In addition, for OS X we need
50865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # to use the -f flag to get 'flat' nm output (otherwise we don't sort
50875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # properly and get incorrect results).  Unfortunately, GNU nm uses -f
50885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # in an incompatible way.  So first we test whether our nm supports
50895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # --demangle and -f.
50905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $demangle_flag = "";
50915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $cppfilt_flag = "";
50925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $to_devnull = ">$dev_null 2>&1";
50935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (system(ShellEscape($nm, "--demangle", "image") . $to_devnull) == 0) {
50945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # In this mode, we do "nm --demangle <foo>"
50955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $demangle_flag = "--demangle";
50965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $cppfilt_flag = "";
50975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } elsif (system(ShellEscape($cppfilt, $image) . $to_devnull) == 0) {
50985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # In this mode, we do "nm <foo> | c++filt"
50995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $cppfilt_flag = " | " . ShellEscape($cppfilt);
51005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
51015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $flatten_flag = "";
51025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (system(ShellEscape($nm, "-f", $image) . $to_devnull) == 0) {
51035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    $flatten_flag = "-f";
51045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
51055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
51065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Finally, in the case $imagie isn't a debug library, we try again with
51075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # -D to at least get *exported* symbols.  If we can't use --demangle,
51085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # we use c++filt instead, if it exists on this system.
51095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my @nm_commands = (ShellEscape($nm, "-n", $flatten_flag, $demangle_flag,
51105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 $image) . " 2>$dev_null $cppfilt_flag",
51115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     ShellEscape($nm, "-D", "-n", $flatten_flag, $demangle_flag,
51125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 $image) . " 2>$dev_null $cppfilt_flag",
51135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     # 6nm is for Go binaries
51145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     ShellEscape("6nm", "$image") . " 2>$dev_null | sort",
51155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     );
51165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
51175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # If the executable is an MS Windows PDB-format executable, we'll
51185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # have set up obj_tool_map("nm_pdb").  In this case, we actually
51195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # want to use both unix nm and windows-specific nm_pdb, since
51205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # PDB-format executables can apparently include dwarf .o files.
51215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (exists $obj_tool_map{"nm_pdb"}) {
51225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    push(@nm_commands,
51235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         ShellEscape($obj_tool_map{"nm_pdb"}, "--demangle", $image)
51245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         . " 2>$dev_null");
51255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
51265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
51275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $nm_command (@nm_commands) {
51285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $symbol_table = GetProcedureBoundariesViaNm($nm_command, $regexp);
51295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return $symbol_table if (%{$symbol_table});
51305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
51315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $symbol_table = {};
51325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $symbol_table;
51335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
51345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
51355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
51365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# The test vectors for AddressAdd/Sub/Inc are 8-16-nibble hex strings.
51375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# To make them more readable, we add underscores at interesting places.
51385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# This routine removes the underscores, producing the canonical representation
51395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# used by pprof to represent addresses, particularly in the tested routines.
51405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub CanonicalHex {
51415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $arg = shift;
51425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return join '', (split '_',$arg);
51435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
51445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
51455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
51465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Unit test for AddressAdd:
51475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub AddressAddUnitTest {
51485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $test_data_8 = shift;
51495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $test_data_16 = shift;
51505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $error_count = 0;
51515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $fail_count = 0;
51525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $pass_count = 0;
51535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # print STDERR "AddressAddUnitTest: ", 1+$#{$test_data_8}, " tests\n";
51545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
51555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # First a few 8-nibble addresses.  Note that this implementation uses
51565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # plain old arithmetic, so a quick sanity check along with verifying what
51575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # happens to overflow (we want it to wrap):
51585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $address_length = 8;
51595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $row (@{$test_data_8}) {
51605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
51615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $sum = AddressAdd ($row->[0], $row->[1]);
51625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($sum ne $row->[2]) {
51635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      printf STDERR "ERROR: %s != %s + %s = %s\n", $sum,
51645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             $row->[0], $row->[1], $row->[2];
51655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++$fail_count;
51665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
51675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++$pass_count;
51685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
51695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
51705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  printf STDERR "AddressAdd 32-bit tests: %d passes, %d failures\n",
51715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         $pass_count, $fail_count;
51725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $error_count = $fail_count;
51735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $fail_count = 0;
51745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $pass_count = 0;
51755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
51765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Now 16-nibble addresses.
51775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $address_length = 16;
51785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $row (@{$test_data_16}) {
51795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
51805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $sum = AddressAdd (CanonicalHex($row->[0]), CanonicalHex($row->[1]));
51815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $expected = join '', (split '_',$row->[2]);
51825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($sum ne CanonicalHex($row->[2])) {
51835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      printf STDERR "ERROR: %s != %s + %s = %s\n", $sum,
51845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             $row->[0], $row->[1], $row->[2];
51855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++$fail_count;
51865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
51875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++$pass_count;
51885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
51895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
51905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  printf STDERR "AddressAdd 64-bit tests: %d passes, %d failures\n",
51915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         $pass_count, $fail_count;
51925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $error_count += $fail_count;
51935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
51945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $error_count;
51955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
51965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
51975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
51985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Unit test for AddressSub:
51995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub AddressSubUnitTest {
52005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $test_data_8 = shift;
52015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $test_data_16 = shift;
52025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $error_count = 0;
52035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $fail_count = 0;
52045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $pass_count = 0;
52055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # print STDERR "AddressSubUnitTest: ", 1+$#{$test_data_8}, " tests\n";
52065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
52075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # First a few 8-nibble addresses.  Note that this implementation uses
52085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # plain old arithmetic, so a quick sanity check along with verifying what
52095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # happens to overflow (we want it to wrap):
52105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $address_length = 8;
52115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $row (@{$test_data_8}) {
52125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
52135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $sum = AddressSub ($row->[0], $row->[1]);
52145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($sum ne $row->[3]) {
52155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      printf STDERR "ERROR: %s != %s - %s = %s\n", $sum,
52165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             $row->[0], $row->[1], $row->[3];
52175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++$fail_count;
52185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
52195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++$pass_count;
52205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
52215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
52225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  printf STDERR "AddressSub 32-bit tests: %d passes, %d failures\n",
52235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         $pass_count, $fail_count;
52245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $error_count = $fail_count;
52255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $fail_count = 0;
52265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $pass_count = 0;
52275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
52285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Now 16-nibble addresses.
52295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $address_length = 16;
52305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $row (@{$test_data_16}) {
52315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
52325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $sum = AddressSub (CanonicalHex($row->[0]), CanonicalHex($row->[1]));
52335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($sum ne CanonicalHex($row->[3])) {
52345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      printf STDERR "ERROR: %s != %s - %s = %s\n", $sum,
52355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             $row->[0], $row->[1], $row->[3];
52365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++$fail_count;
52375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
52385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++$pass_count;
52395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
52405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
52415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  printf STDERR "AddressSub 64-bit tests: %d passes, %d failures\n",
52425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         $pass_count, $fail_count;
52435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $error_count += $fail_count;
52445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
52455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $error_count;
52465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
52475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
52485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
52495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Unit test for AddressInc:
52505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub AddressIncUnitTest {
52515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $test_data_8 = shift;
52525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $test_data_16 = shift;
52535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $error_count = 0;
52545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $fail_count = 0;
52555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $pass_count = 0;
52565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # print STDERR "AddressIncUnitTest: ", 1+$#{$test_data_8}, " tests\n";
52575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
52585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # First a few 8-nibble addresses.  Note that this implementation uses
52595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # plain old arithmetic, so a quick sanity check along with verifying what
52605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # happens to overflow (we want it to wrap):
52615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $address_length = 8;
52625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $row (@{$test_data_8}) {
52635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
52645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $sum = AddressInc ($row->[0]);
52655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($sum ne $row->[4]) {
52665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      printf STDERR "ERROR: %s != %s + 1 = %s\n", $sum,
52675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             $row->[0], $row->[4];
52685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++$fail_count;
52695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
52705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++$pass_count;
52715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
52725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
52735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  printf STDERR "AddressInc 32-bit tests: %d passes, %d failures\n",
52745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         $pass_count, $fail_count;
52755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $error_count = $fail_count;
52765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $fail_count = 0;
52775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $pass_count = 0;
52785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
52795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Now 16-nibble addresses.
52805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $address_length = 16;
52815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach my $row (@{$test_data_16}) {
52825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
52835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    my $sum = AddressInc (CanonicalHex($row->[0]));
52845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ($sum ne CanonicalHex($row->[4])) {
52855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      printf STDERR "ERROR: %s != %s + 1 = %s\n", $sum,
52865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             $row->[0], $row->[4];
52875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++$fail_count;
52885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
52895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++$pass_count;
52905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
52915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
52925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  printf STDERR "AddressInc 64-bit tests: %d passes, %d failures\n",
52935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         $pass_count, $fail_count;
52945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $error_count += $fail_count;
52955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
52965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return $error_count;
52975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
52985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
52995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
53005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Driver for unit tests.
53015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Currently just the address add/subtract/increment routines for 64-bit.
53025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sub RunUnitTests {
53035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $error_count = 0;
53045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
53055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # This is a list of tuples [a, b, a+b, a-b, a+1]
53065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $unit_test_data_8 = [
53075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    [qw(aaaaaaaa 50505050 fafafafa 5a5a5a5a aaaaaaab)],
53085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    [qw(50505050 aaaaaaaa fafafafa a5a5a5a6 50505051)],
53095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    [qw(ffffffff aaaaaaaa aaaaaaa9 55555555 00000000)],
53105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    [qw(00000001 ffffffff 00000000 00000002 00000002)],
53115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    [qw(00000001 fffffff0 fffffff1 00000011 00000002)],
53125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ];
53135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  my $unit_test_data_16 = [
53145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # The implementation handles data in 7-nibble chunks, so those are the
53155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # interesting boundaries.
53165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    [qw(aaaaaaaa 50505050
53175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        00_000000f_afafafa 00_0000005_a5a5a5a 00_000000a_aaaaaab)],
53185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    [qw(50505050 aaaaaaaa
53195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        00_000000f_afafafa ff_ffffffa_5a5a5a6 00_0000005_0505051)],
53205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    [qw(ffffffff aaaaaaaa
53215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        00_000001a_aaaaaa9 00_0000005_5555555 00_0000010_0000000)],
53225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    [qw(00000001 ffffffff
53235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        00_0000010_0000000 ff_ffffff0_0000002 00_0000000_0000002)],
53245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    [qw(00000001 fffffff0
53255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        00_000000f_ffffff1 ff_ffffff0_0000011 00_0000000_0000002)],
53265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
53275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    [qw(00_a00000a_aaaaaaa 50505050
53285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        00_a00000f_afafafa 00_a000005_a5a5a5a 00_a00000a_aaaaaab)],
53295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    [qw(0f_fff0005_0505050 aaaaaaaa
53305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        0f_fff000f_afafafa 0f_ffefffa_5a5a5a6 0f_fff0005_0505051)],
53315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    [qw(00_000000f_fffffff 01_800000a_aaaaaaa
53325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        01_800001a_aaaaaa9 fe_8000005_5555555 00_0000010_0000000)],
53335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    [qw(00_0000000_0000001 ff_fffffff_fffffff
53345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        00_0000000_0000000 00_0000000_0000002 00_0000000_0000002)],
53355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    [qw(00_0000000_0000001 ff_fffffff_ffffff0
53365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ff_fffffff_ffffff1 00_0000000_0000011 00_0000000_0000002)],
53375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ];
53385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
53395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $error_count += AddressAddUnitTest($unit_test_data_8, $unit_test_data_16);
53405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $error_count += AddressSubUnitTest($unit_test_data_8, $unit_test_data_16);
53415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  $error_count += AddressIncUnitTest($unit_test_data_8, $unit_test_data_16);
53425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ($error_count > 0) {
53435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print STDERR $error_count, " errors: FAILED\n";
53445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
53455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print STDERR "PASS\n";
53465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
53475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  exit ($error_count);
53485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5349