Bug 1082265 - Rewrite split-profile.pl in python.
This adds arguments for the paths to jprof, the program being profiled,
and the jprof profile itself, so I don't need to modify my local copy of
split-profile.pl to fix those.
DONTBUILD
deleted file mode 100755
--- a/tools/jprof/split-profile.pl
+++ /dev/null
@@ -1,100 +0,0 @@
-#!/usr/bin/perl
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-
-# split-profile.pl Documentation:
-#
-# This script uses jprof's includes (-i) and excludes (-e) options to
-# split profiles into segments. It takes as input a single text file,
-# and from that text file creates a series of jprof profiles in the
-# directory in which it is run. It expects the application binaries
-# with which the profile was made, including jprof, and the jprof
-# profile data, to be in a directory called "bin" that is a subdirectory
-# of the current directory, and it will output the profiles into the
-# current directory.
-#
-# The input file format looks like the following:
-#
-# poll g_main_poll
-# GetRuleCascade CSSRuleProcessor::GetRuleCascade(nsPresContext *, nsIAtom *)
-# RuleProcessorData RuleProcessorData::RuleProcessorData(nsPresContext *, nsIContent *, nsRuleWalker *, nsCompatibility *)
-#
-# From this input file, the script will construct a profile called
-# 00.html that contains the whole profile, a profile called 01-poll.html
-# that includes only stacks with g_main_poll, a profile called
-# 02-GetRuleCascade.html that includes only stacks that have
-# GetRuleCascade and do not have g_main_poll, a profile called
-# 03-RuleProcessorData.html that includes only stacks that have the
-# RuleProcessorData constructor and do not have GetRuleCascade or
-# g_main_poll, and a profile called 04.html that includes only stacks
-# that do not have any of the three functions in them.
-#
-# This means that all of the segments of the profile, except 00.html,
-# are mutually exclusive. Thus clever ordering of the functions in the
-# input file can lead to a logical splitting of the profile into
-# segments.
-
-
-use strict;
-
-my @names;
-my @sigs;
-
-sub read_info($) {
- my ($fname) = @_;
-
- open(INFO, "<$fname");
- my $i = 0;
- while (<INFO>) {
- chop;
- my $line = $_;
- my $idx = index($line, " ");
- my $name = substr($line, 0, $idx);
- my $sig = substr($line, $idx+1);
-
- $names[$i] = $name;
- $sigs[$i] = $sig;
- ++$i;
- }
-}
-
-sub run_profile($$) {
- my ($options, $outfile) = @_;
-
- print "./jprof$options mozilla-bin jprof-log > ../$outfile.html\n";
- system "./jprof$options mozilla-bin jprof-log > ../$outfile.html";
-}
-
-sub run_profiles() {
- run_profile("", "00");
-
- for (my $i = 0; $i <= $#names + 1; ++$i) {
- my $options = "";
- for (my $j = 0; $j < $i; ++$j) {
- $options .= " -e\"$sigs[$j]\"";
- }
- if ($i <= $#names) {
- $options .= " -i\"$sigs[$i]\"";
- }
- my $num;
- my $n = $i + 1;
- if ($n < 10) {
- $num = "0$n";
- } else {
- $num = "$n";
- }
- if ($i <= $#names) {
- run_profile($options, "$num-$names[$i]");
- } else {
- run_profile($options, "$num");
- }
- }
-}
-
-($#ARGV == 0) || die "Usage: split-profile.pl <info-file>\n";
-
-read_info($ARGV[0]);
-chdir "bin" || die "Can't change directory to bin.";
-run_profiles();
new file mode 100755
--- /dev/null
+++ b/tools/jprof/split-profile.py
@@ -0,0 +1,140 @@
+#!/usr/bin/python
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# This program splits up a jprof profile into multiple files based on a
+# list of functions in a text file. First, a complete profile is
+# generated. Then, for each line in the text file, a profile is
+# generated containing only stacks that go through that line, and also
+# excluding all stacks in earlier lines in the text file. This means
+# that the text file, from start to end, is splitting out pieces of the
+# profile in their own file. Finally, a final profile containing the
+# remainder is produced.
+
+# The program takes four arguments:
+# (1) The path to jprof.
+# (2) The path to the text file describing the splits. The output
+# will be placed in the same directory as this file.
+# (3) The program that was profiled.
+# (4) The jprof-log file generated by the profile, to be split up.
+# (Really, all arguments from (3) and later are passed through to
+# jprof, so additional arguments could be provided if you want to pass
+# additional arguments to jprof.)
+
+# In slightly more detail:
+#
+# This script uses jprof's includes (-i) and excludes (-e) options to
+# split profiles into segments. It takes as input a single text file,
+# and from that text file creates a series of jprof profiles in the
+# directory in which it is run. It expects the application binaries
+# with which the profile was made, including jprof, and the jprof
+# profile data, to be in a directory called "bin" that is a subdirectory
+# of the current directory, and it will output the profiles into the
+# current directory.
+#
+# The input file format looks like the following:
+#
+# poll g_main_poll
+# GetRuleCascade CSSRuleProcessor::GetRuleCascade(nsPresContext *, nsIAtom *)
+# RuleProcessorData RuleProcessorData::RuleProcessorData(nsPresContext *, nsIContent *, nsRuleWalker *, nsCompatibility *)
+#
+# From this input file, the script will construct a profile called
+# jprof-0.html that contains the whole profile, a profile called
+# jprof-1-poll.html that includes only stacks with g_main_poll, a
+# profile called jprof-2-GetRuleCascade.html that includes only stacks
+# that have GetRuleCascade and do not have g_main_poll, a profile called
+# jprof-3-RuleProcessorData.html that includes only stacks that have the
+# RuleProcessorData constructor and do not have GetRuleCascade or
+# g_main_poll, and a profile called jprof-4.html that includes only
+# stacks that do not have any of the three functions in them.
+#
+# This means that all of the segments of the profile, except
+# jprof-0.html, are mutually exclusive. Thus clever ordering of the
+# functions in the input file can lead to a logical splitting of the
+# profile into segments.
+
+import sys
+import subprocess
+import os.path
+
+if len(sys.argv) < 5:
+ sys.stderr.write("Expected arguments: <jprof> <split-file> <program> <jprof-log>\n")
+ sys.exit(1)
+
+jprof = sys.argv[1]
+splitfile = sys.argv[2]
+passthrough = sys.argv[3:]
+
+for f in [jprof, splitfile]:
+ if not os.path.isfile(f):
+ sys.stderr.write("could not find file: {0}\n".format(f))
+ sys.exit(1)
+
+def read_splits(splitfile):
+ """
+ Read splitfile (each line of which contains a name, a space, and
+ then a function name to split on), and return a list of pairs
+ representing exactly that. (Note that the name cannot contain
+ spaces, but the function name can, and often does.)
+ """
+ def line_to_split(line):
+ line = line.strip("\r\n")
+ idx = line.index(" ")
+ return (line[0:idx], line[idx+1:])
+
+ io = open(splitfile, "r")
+ result = [line_to_split(line) for line in io]
+ io.close()
+ return result
+
+splits = read_splits(splitfile)
+
+def generate_profile(options, destfile):
+ """
+ Run jprof to generate one split of the profile.
+ """
+ args = [jprof] + options + passthrough
+ print "Generating {0}".format(destfile)
+ destio = open(destfile, "w")
+ process = subprocess.Popen(args, stdout=destio)
+ process.wait()
+ destio.close()
+ if process.returncode != 0:
+ os.remove(destfile)
+ sys.stderr.write("Error {0} from command:\n {1}\n".format(process.returncode, " ".join(args)))
+ sys.exit(process.returncode)
+
+def output_filename(number, splitname):
+ """
+ Return the filename (absolute path) we should use to output the
+ profile segment with the given number and splitname. Splitname
+ should be None for the complete profile and the remainder.
+ """
+ def pad_count(i):
+ result = str(i)
+ # 0-pad to the same length
+ result = "0" * (len(str(len(splits) + 1)) - len(result)) + result
+ return result
+
+ name = pad_count(number)
+ if splitname is not None:
+ name += "-" + splitname
+
+ return os.path.join(os.path.dirname(splitfile),
+ "jprof-{0}.html".format(name))
+
+# generate the complete profile
+generate_profile([], output_filename(0, None))
+
+# generate the listed splits
+count = 1
+excludes = []
+for (splitname, splitfunction) in splits:
+ generate_profile(excludes + ["-i" + splitfunction],
+ output_filename(count, splitname))
+ excludes += ["-e" + splitfunction]
+ count = count + 1
+
+# generate the remainder after the splits
+generate_profile(excludes, output_filename(count, None))