Bug 1885489 - Part 5: Add SnapshotIterator::readInt32(). r=iain
[gecko.git] / tools / jprof / split-profile.py
blob2e5fa89cafc1a6c5808e7090a3df879d84041f17
1 #!/usr/bin/python
2 # This Source Code Form is subject to the terms of the Mozilla Public
3 # License, v. 2.0. If a copy of the MPL was not distributed with this
4 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 # This program splits up a jprof profile into multiple files based on a
7 # list of functions in a text file. First, a complete profile is
8 # generated. Then, for each line in the text file, a profile is
9 # generated containing only stacks that go through that line, and also
10 # excluding all stacks in earlier lines in the text file. This means
11 # that the text file, from start to end, is splitting out pieces of the
12 # profile in their own file. Finally, a final profile containing the
13 # remainder is produced.
15 # The program takes four arguments:
16 # (1) The path to jprof.
17 # (2) The path to the text file describing the splits. The output
18 # will be placed in the same directory as this file.
19 # (3) The program that was profiled.
20 # (4) The jprof-log file generated by the profile, to be split up.
21 # (Really, all arguments from (3) and later are passed through to
22 # jprof, so additional arguments could be provided if you want to pass
23 # additional arguments to jprof.)
25 # In slightly more detail:
27 # This script uses jprof's includes (-i) and excludes (-e) options to
28 # split profiles into segments. It takes as input a single text file,
29 # and from that text file creates a series of jprof profiles in the
30 # directory the text file is in.
32 # The input file format looks like the following:
34 # poll g_main_poll
35 # GetRuleCascade CSSRuleProcessor::GetRuleCascade(nsPresContext *, nsAtom *)
36 # RuleProcessorData RuleProcessorData::RuleProcessorData
37 # (nsPresContext *, nsIContent *, nsRuleWalker *, nsCompatibility *)
40 # From this input file, the script will construct a profile called
41 # jprof-0.html that contains the whole profile, a profile called
42 # jprof-1-poll.html that includes only stacks with g_main_poll, a
43 # profile called jprof-2-GetRuleCascade.html that includes only stacks
44 # that have GetRuleCascade and do not have g_main_poll, a profile called
45 # jprof-3-RuleProcessorData.html that includes only stacks that have the
46 # RuleProcessorData constructor and do not have GetRuleCascade or
47 # g_main_poll, and a profile called jprof-4.html that includes only
48 # stacks that do not have any of the three functions in them.
50 # This means that all of the segments of the profile, except
51 # jprof-0.html, are mutually exclusive. Thus clever ordering of the
52 # functions in the input file can lead to a logical splitting of the
53 # profile into segments.
55 import os.path
56 import subprocess
57 import sys
59 if len(sys.argv) < 5:
60 sys.stderr.write("Expected arguments: <jprof> <split-file> <program> <jprof-log>\n")
61 sys.exit(1)
63 jprof = sys.argv[1]
64 splitfile = sys.argv[2]
65 passthrough = sys.argv[3:]
67 for f in [jprof, splitfile]:
68 if not os.path.isfile(f):
69 sys.stderr.write("could not find file: {0}\n".format(f))
70 sys.exit(1)
73 def read_splits(splitfile):
74 """
75 Read splitfile (each line of which contains a name, a space, and
76 then a function name to split on), and return a list of pairs
77 representing exactly that. (Note that the name cannot contain
78 spaces, but the function name can, and often does.)
79 """
81 def line_to_split(line):
82 line = line.strip("\r\n")
83 idx = line.index(" ")
84 return (line[0:idx], line[idx + 1 :])
86 io = open(splitfile, "r")
87 result = [line_to_split(line) for line in io]
88 io.close()
89 return result
92 splits = read_splits(splitfile)
95 def generate_profile(options, destfile):
96 """
97 Run jprof to generate one split of the profile.
98 """
99 args = [jprof] + options + passthrough
100 print("Generating {}".format(destfile))
101 destio = open(destfile, "w")
102 # jprof expects the "jprof-map" file to be in its current working directory
103 cwd = None
104 for option in passthrough:
105 if option.find("jprof-log"):
106 cwd = os.path.dirname(option)
107 if cwd is None:
108 raise Exception("no jprof-log option given")
109 process = subprocess.Popen(args, stdout=destio, cwd=cwd)
110 process.wait()
111 destio.close()
112 if process.returncode != 0:
113 os.remove(destfile)
114 sys.stderr.write(
115 "Error {0} from command:\n {1}\n".format(
116 process.returncode, " ".join(args)
119 sys.exit(process.returncode)
122 def output_filename(number, splitname):
124 Return the filename (absolute path) we should use to output the
125 profile segment with the given number and splitname. Splitname
126 should be None for the complete profile and the remainder.
129 def pad_count(i):
130 result = str(i)
131 # 0-pad to the same length
132 result = "0" * (len(str(len(splits) + 1)) - len(result)) + result
133 return result
135 name = pad_count(number)
136 if splitname is not None:
137 name += "-" + splitname
139 return os.path.join(os.path.dirname(splitfile), "jprof-{0}.html".format(name))
142 # generate the complete profile
143 generate_profile([], output_filename(0, None))
145 # generate the listed splits
146 count = 1
147 excludes = []
148 for splitname, splitfunction in splits:
149 generate_profile(
150 excludes + ["-i" + splitfunction], output_filename(count, splitname)
152 excludes += ["-e" + splitfunction]
153 count = count + 1
155 # generate the remainder after the splits
156 generate_profile(excludes, output_filename(count, None))