Bug 1747299 - [UniqueBuffer] Use UniqueBuffer::Take instead of copy constructors...
[gecko.git] / tools / jprof / split-profile.py
blobf924e4a66680a3d6395a0fc8f5035df724f8bf06
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 from __future__ import absolute_import, print_function
57 import sys
58 import subprocess
59 import os.path
61 if len(sys.argv) < 5:
62 sys.stderr.write("Expected arguments: <jprof> <split-file> <program> <jprof-log>\n")
63 sys.exit(1)
65 jprof = sys.argv[1]
66 splitfile = sys.argv[2]
67 passthrough = sys.argv[3:]
69 for f in [jprof, splitfile]:
70 if not os.path.isfile(f):
71 sys.stderr.write("could not find file: {0}\n".format(f))
72 sys.exit(1)
75 def read_splits(splitfile):
76 """
77 Read splitfile (each line of which contains a name, a space, and
78 then a function name to split on), and return a list of pairs
79 representing exactly that. (Note that the name cannot contain
80 spaces, but the function name can, and often does.)
81 """
83 def line_to_split(line):
84 line = line.strip("\r\n")
85 idx = line.index(" ")
86 return (line[0:idx], line[idx + 1 :])
88 io = open(splitfile, "r")
89 result = [line_to_split(line) for line in io]
90 io.close()
91 return result
94 splits = read_splits(splitfile)
97 def generate_profile(options, destfile):
98 """
99 Run jprof to generate one split of the profile.
101 args = [jprof] + options + passthrough
102 print("Generating {}".format(destfile))
103 destio = open(destfile, "w")
104 # jprof expects the "jprof-map" file to be in its current working directory
105 cwd = None
106 for option in passthrough:
107 if option.find("jprof-log"):
108 cwd = os.path.dirname(option)
109 if cwd is None:
110 raise Exception("no jprof-log option given")
111 process = subprocess.Popen(args, stdout=destio, cwd=cwd)
112 process.wait()
113 destio.close()
114 if process.returncode != 0:
115 os.remove(destfile)
116 sys.stderr.write(
117 "Error {0} from command:\n {1}\n".format(
118 process.returncode, " ".join(args)
121 sys.exit(process.returncode)
124 def output_filename(number, splitname):
126 Return the filename (absolute path) we should use to output the
127 profile segment with the given number and splitname. Splitname
128 should be None for the complete profile and the remainder.
131 def pad_count(i):
132 result = str(i)
133 # 0-pad to the same length
134 result = "0" * (len(str(len(splits) + 1)) - len(result)) + result
135 return result
137 name = pad_count(number)
138 if splitname is not None:
139 name += "-" + splitname
141 return os.path.join(os.path.dirname(splitfile), "jprof-{0}.html".format(name))
144 # generate the complete profile
145 generate_profile([], output_filename(0, None))
147 # generate the listed splits
148 count = 1
149 excludes = []
150 for (splitname, splitfunction) in splits:
151 generate_profile(
152 excludes + ["-i" + splitfunction], output_filename(count, splitname)
154 excludes += ["-e" + splitfunction]
155 count = count + 1
157 # generate the remainder after the splits
158 generate_profile(excludes, output_filename(count, None))