24 from six.moves
import zip
28 TIME_FROM_SCOPE_START = object()
29 TIME_TO_SCOPE_END = object()
30 TIME_FROM_STACK_START = object()
31 TIME_TO_STACK_END = object()
32 TIME_FROM_LAST_IMPORTANT = object()
34 argp = argparse.ArgumentParser(
35 description=
'Process output of basic_prof builds')
36 argp.add_argument(
'--source', default=
'latency_trace.txt', type=str)
37 argp.add_argument(
'--fmt', choices=tabulate.tabulate_formats, default=
'simple')
38 argp.add_argument(
'--out', default=
'-', type=str)
39 args = argp.parse_args()
45 self.
tag = line[
'tag']
61 call_stack_builder.lines.append(self.
top_line)
66 line_item.end_time = line_item.start_time
70 assert line[
'tag'] == self.
top_line.tag, (
71 'expected %s, got %s; thread=%s; t0=%f t1=%f' %
72 (self.
top_line.tag, line[
'tag'], line[
'thd'],
73 self.
top_line.start_time, line[
't']))
74 final_time_stamp = line[
't']
75 assert self.
top_line.end_time
is None
76 self.
top_line.end_time = final_time_stamp
78 assert SELF_TIME
not in self.
top_line.times
80 SELF_TIME] = final_time_stamp - self.
top_line.start_time
82 if TIME_FROM_SCOPE_START
not in line.times:
84 TIME_FROM_SCOPE_START] = line.start_time - self.
top_line.start_time
85 line.times[TIME_TO_SCOPE_END] = final_time_stamp - line.end_time
96 start_time = self.
lines[0].start_time
97 end_time = self.
lines[0].end_time
99 last_important = start_time
100 for line
in self.
lines:
101 line.times[TIME_FROM_STACK_START] = line.start_time - start_time
102 line.times[TIME_TO_STACK_END] = end_time - line.end_time
104 TIME_FROM_LAST_IMPORTANT] = line.start_time - last_important
106 last_important = line.end_time
107 last_important = end_time
110 line_type = line[
'type']
116 elif line_type ==
'}':
118 'expected non-empty stack for closing %s; thread=%s; t=%f' %
119 (line[
'tag'], line[
'thd'], line[
't']))
125 elif line_type ==
'.' or line_type ==
'!':
127 self.
stk[-1].mark(line)
130 raise Exception(
'Unknown line type: \'%s\'' % line_type)
138 self.
lines = initial_call_stack_builder.lines
139 for line
in self.
lines:
140 for key, val
in list(line.times.items()):
141 line.times[key] = [val]
143 def add(self, call_stack_builder):
144 assert self.
signature == call_stack_builder.signature
146 assert len(self.
lines) ==
len(call_stack_builder.lines)
147 for lsum, line
in zip(self.
lines, call_stack_builder.lines):
148 assert lsum.tag == line.tag
149 assert list(lsum.times.keys()) == list(line.times.keys())
150 for k, lst
in list(lsum.times.items()):
151 lst.append(line.times[k])
154 for line
in self.
lines:
155 for lst
in list(line.times.values()):
159 builder = collections.defaultdict(CallStackBuilder)
160 call_stacks = collections.defaultdict(CallStack)
164 with open(args.source)
as f:
167 inf = json.loads(line)
171 if cs.signature
in call_stacks:
172 call_stacks[cs.signature].
add(cs)
174 call_stacks[cs.signature] =
CallStack(cs)
176 time_taken = time.time() - start
178 call_stacks = sorted(list(call_stacks.values()),
179 key=
lambda cs: cs.count,
182 for cs
in call_stacks:
183 total_stacks += cs.count
189 Find the percentile of an already sorted list of values.
191 @parameter N - is a list of values. MUST be already sorted.
192 @parameter percent - a float value from [0.0,1.0].
193 @parameter key - optional key function to compute value from each element of N.
195 @return - the percentile of the values
199 float_idx = (
len(N) - 1) * percent
204 result += (float_idx - idx) * (
key(N[idx + 1]) -
key(N[idx]))
209 if tag[0:10] ==
'GRPC_PTAG_':
215 num_values =
len(values)
222 def ent(line, idx=idx):
223 if idx
in line.times:
230 BANNER = {
'simple':
'Count: %(count)d',
'html':
'<h1>Count: %(count)d</h1>'}
233 (
'TAG',
lambda line:
'..' * line.indent +
tidy_tag(line.tag)),
234 (
'LOC',
lambda line:
'%s:%d' %
235 (line.filename[line.filename.rfind(
'/') + 1:], line.fileline)),
236 (
'IMP',
lambda line:
'*' if line.important
else ''),
237 (
'FROM_IMP',
time_format(TIME_FROM_LAST_IMPORTANT)),
238 (
'FROM_STACK_START',
time_format(TIME_FROM_STACK_START)),
241 (
'FROM_SCOPE_START',
time_format(TIME_FROM_SCOPE_START)),
248 out =
open(args.out,
'w')
250 if args.fmt ==
'html':
253 out.write(
'<title>Profile Report</title>')
257 for cs
in call_stacks:
259 if args.fmt
in BANNER:
260 out.write(BANNER[args.fmt] % {
263 header, _ = list(zip(*FORMAT))
265 for line
in cs.lines:
268 fields.append(
fn(line))
270 out.write(tabulate.tabulate(table, header, tablefmt=args.fmt))
271 accounted_for += cs.count
272 if accounted_for > .99 * total_stacks:
275 if args.fmt ==
'html':