OLD | NEW |
---|---|
1 #! /usr/bin/env python | 1 #! /usr/bin/env python |
2 # Copyright 2016 The Chromium Authors. All rights reserved. | 2 # Copyright 2016 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """Pull a sandwich run's output directory's metrics from traces into a CSV. | 6 """Pull a sandwich run's output directory's metrics from traces into a CSV. |
7 | 7 |
8 python pull_sandwich_metrics.py -h | 8 python pull_sandwich_metrics.py -h |
9 """ | 9 """ |
10 | 10 |
11 import argparse | 11 import argparse |
12 import csv | 12 import csv |
13 import json | 13 import json |
14 import logging | 14 import logging |
15 import os | 15 import os |
16 import sys | 16 import sys |
17 | 17 |
18 import loading_trace as loading_trace_module | |
19 import tracing | |
20 | |
18 | 21 |
19 CATEGORIES = ['blink.user_timing', 'disabled-by-default-memory-infra'] | 22 CATEGORIES = ['blink.user_timing', 'disabled-by-default-memory-infra'] |
20 | 23 |
21 _CSV_FIELD_NAMES = [ | 24 _CSV_FIELD_NAMES = [ |
22 'id', | 25 'id', |
23 'url', | 26 'url', |
24 'total_load', | 27 'total_load', |
25 'onload', | 28 'onload', |
26 'browser_malloc_avg', | 29 'browser_malloc_avg', |
27 'browser_malloc_max'] | 30 'browser_malloc_max'] |
28 | 31 |
29 _TRACKED_EVENT_NAMES = set(['requestStart', 'loadEventStart', 'loadEventEnd']) | 32 _TRACKED_EVENT_NAMES = set(['requestStart', 'loadEventStart', 'loadEventEnd']) |
30 | 33 |
31 | 34 |
32 def _GetBrowserPID(trace): | 35 def _GetBrowserPID(tracing_track): |
33 """Get the browser PID from a trace. | 36 """Get the browser PID from a trace. |
34 | 37 |
35 Args: | 38 Args: |
36 trace: The cached trace. | 39 tracing_track: The tracing.TracingTrack. |
37 | 40 |
38 Returns: | 41 Returns: |
39 The browser's PID as an integer. | 42 The browser's PID as an integer. |
40 """ | 43 """ |
41 for event in trace['traceEvents']: | 44 assert type(tracing_track) == tracing.TracingTrack |
42 if event['cat'] != '__metadata' or event['name'] != 'process_name': | 45 for event in tracing_track.GetEvents(): |
46 if event.category != '__metadata' or event.name != 'process_name': | |
43 continue | 47 continue |
44 if event['args']['name'] == 'Browser': | 48 if event.args['name'] == 'Browser': |
45 return event['pid'] | 49 return event.pid |
46 raise ValueError('couldn\'t find browser\'s PID') | 50 raise ValueError('couldn\'t find browser\'s PID') |
47 | 51 |
48 | 52 |
49 def _GetBrowserDumpEvents(trace): | 53 def _GetBrowserDumpEvents(tracing_track): |
50 """Get the browser memory dump events from a trace. | 54 """Get the browser memory dump events from a tracing track. |
51 | 55 |
52 Args: | 56 Args: |
53 trace: The cached trace. | 57 tracing_track: The tracing.TracingTrack. |
54 | 58 |
55 Returns: | 59 Returns: |
56 List of memory dump events. | 60 List of memory dump events. |
57 """ | 61 """ |
58 browser_pid = _GetBrowserPID(trace) | 62 assert type(tracing_track) == tracing.TracingTrack |
63 browser_pid = _GetBrowserPID(tracing_track) | |
59 browser_dumps_events = [] | 64 browser_dumps_events = [] |
60 for event in trace['traceEvents']: | 65 for event in tracing_track.GetEvents(): |
61 if event['cat'] != 'disabled-by-default-memory-infra': | 66 if event.category != 'disabled-by-default-memory-infra': |
62 continue | 67 continue |
63 if event['ph'] != 'v' or event['name'] != 'periodic_interval': | 68 if event.type != 'v' or event.name != 'periodic_interval': |
64 continue | 69 continue |
65 # Ignore dump events for processes other than the browser process | 70 # Ignore dump events for processes other than the browser process |
66 if event['pid'] != browser_pid: | 71 if event.pid != browser_pid: |
67 continue | 72 continue |
68 browser_dumps_events.append(event) | 73 browser_dumps_events.append(event) |
69 if len(browser_dumps_events) == 0: | 74 if len(browser_dumps_events) == 0: |
70 raise ValueError('No browser dump events found.') | 75 raise ValueError('No browser dump events found.') |
71 return browser_dumps_events | 76 return browser_dumps_events |
72 | 77 |
73 | 78 |
74 def _GetWebPageTrackedEvents(trace): | 79 def _GetWebPageTrackedEvents(tracing_track): |
75 """Get the web page's tracked events from a trace. | 80 """Get the web page's tracked events from a tracing track. |
76 | 81 |
77 Args: | 82 Args: |
78 trace: The cached trace. | 83 tracing_track: The tracing.TracingTrack. |
79 | 84 |
80 Returns: | 85 Returns: |
81 Dictionary all tracked events. | 86 Dictionary all tracked events. |
82 """ | 87 """ |
88 assert type(tracing_track) == tracing.TracingTrack | |
83 main_frame = None | 89 main_frame = None |
84 tracked_events = {} | 90 tracked_events = {} |
85 for event in trace['traceEvents']: | 91 for event in tracing_track.GetEvents(): |
86 if event['cat'] != 'blink.user_timing': | 92 if event.category != 'blink.user_timing': |
87 continue | 93 continue |
88 event_name = event['name'] | 94 event_name = event.name |
89 # Ignore events until about:blank's unloadEventEnd that give the main | 95 # Ignore events until about:blank's unloadEventEnd that give the main |
90 # frame id. | 96 # frame id. |
91 if not main_frame: | 97 if not main_frame: |
92 if event_name == 'unloadEventEnd': | 98 if event_name == 'unloadEventEnd': |
93 main_frame = event['args']['frame'] | 99 main_frame = event.args['frame'] |
94 logging.info('found about:blank\'s event \'unloadEventEnd\'') | 100 logging.info('found about:blank\'s event \'unloadEventEnd\'') |
95 continue | 101 continue |
96 # Ignore sub-frames events. requestStart don't have the frame set but it | 102 # Ignore sub-frames events. requestStart don't have the frame set but it |
97 # is fine since tracking the first one after about:blank's unloadEventEnd. | 103 # is fine since tracking the first one after about:blank's unloadEventEnd. |
98 if 'frame' in event['args'] and event['args']['frame'] != main_frame: | 104 if 'frame' in event.args and event.args['frame'] != main_frame: |
99 continue | 105 continue |
100 if event_name in _TRACKED_EVENT_NAMES and event_name not in tracked_events: | 106 if event_name in _TRACKED_EVENT_NAMES and event_name not in tracked_events: |
101 logging.info('found url\'s event \'%s\'' % event_name) | 107 logging.info('found url\'s event \'%s\'' % event_name) |
102 tracked_events[event_name] = event | 108 tracked_events[event_name] = event |
103 assert len(tracked_events) == len(_TRACKED_EVENT_NAMES) | 109 assert len(tracked_events) == len(_TRACKED_EVENT_NAMES) |
104 return tracked_events | 110 return tracked_events |
105 | 111 |
106 | 112 |
107 def _PullMetricsFromTrace(trace): | 113 def _PullMetricsFromLoadingTrace(loading_trace): |
108 """Pulls all the metrics from a given trace. | 114 """Pulls all the metrics from a given trace. |
109 | 115 |
110 Args: | 116 Args: |
111 trace: The cached trace. | 117 loading_trace: loading_trace_module.LoadingTrace. |
112 | 118 |
113 Returns: | 119 Returns: |
114 Dictionary with all _CSV_FIELD_NAMES's field set (except the 'id'). | 120 Dictionary with all _CSV_FIELD_NAMES's field set (except the 'id'). |
115 """ | 121 """ |
116 browser_dump_events = _GetBrowserDumpEvents(trace) | 122 assert type(loading_trace) == loading_trace_module.LoadingTrace |
mattcary
2016/02/19 14:41:05
This sort of check seems overly fussy. Worse, it p
gabadie
2016/02/19 15:04:35
Done.
| |
117 web_page_tracked_events = _GetWebPageTrackedEvents(trace) | 123 browser_dump_events = _GetBrowserDumpEvents(loading_trace.tracing_track) |
124 web_page_tracked_events = _GetWebPageTrackedEvents( | |
125 loading_trace.tracing_track) | |
118 | 126 |
119 browser_malloc_sum = 0 | 127 browser_malloc_sum = 0 |
120 browser_malloc_max = 0 | 128 browser_malloc_max = 0 |
121 for dump_event in browser_dump_events: | 129 for dump_event in browser_dump_events: |
122 attr = dump_event['args']['dumps']['allocators']['malloc']['attrs']['size'] | 130 attr = dump_event.args['dumps']['allocators']['malloc']['attrs']['size'] |
123 assert attr['units'] == 'bytes' | 131 assert attr['units'] == 'bytes' |
124 size = int(attr['value'], 16) | 132 size = int(attr['value'], 16) |
125 browser_malloc_sum += size | 133 browser_malloc_sum += size |
126 browser_malloc_max = max(browser_malloc_max, size) | 134 browser_malloc_max = max(browser_malloc_max, size) |
127 | 135 |
128 return { | 136 return { |
129 'total_load': (web_page_tracked_events['loadEventEnd']['ts'] - | 137 'total_load': (web_page_tracked_events['loadEventEnd'].start_msec - |
130 web_page_tracked_events['requestStart']['ts']), | 138 web_page_tracked_events['requestStart'].start_msec), |
131 'onload': (web_page_tracked_events['loadEventEnd']['ts'] - | 139 'onload': (web_page_tracked_events['loadEventEnd'].start_msec - |
132 web_page_tracked_events['loadEventStart']['ts']), | 140 web_page_tracked_events['loadEventStart'].start_msec), |
133 'browser_malloc_avg': browser_malloc_sum / float(len(browser_dump_events)), | 141 'browser_malloc_avg': browser_malloc_sum / float(len(browser_dump_events)), |
134 'browser_malloc_max': browser_malloc_max | 142 'browser_malloc_max': browser_malloc_max |
135 } | 143 } |
136 | 144 |
137 | 145 |
138 def _PullMetricsFromOutputDirectory(output_directory_path): | 146 def _PullMetricsFromOutputDirectory(output_directory_path): |
139 """Pulls all the metrics from all the traces of a sandwich run directory. | 147 """Pulls all the metrics from all the traces of a sandwich run directory. |
140 | 148 |
141 Args: | 149 Args: |
142 output_directory_path: The sandwich run's output directory to pull the | 150 output_directory_path: The sandwich run's output directory to pull the |
(...skipping 12 matching lines...) Expand all Loading... | |
155 if not os.path.isdir(os.path.join(output_directory_path, node_name)): | 163 if not os.path.isdir(os.path.join(output_directory_path, node_name)): |
156 continue | 164 continue |
157 try: | 165 try: |
158 page_id = int(node_name) | 166 page_id = int(node_name) |
159 except ValueError: | 167 except ValueError: |
160 continue | 168 continue |
161 trace_path = os.path.join(output_directory_path, node_name, 'trace.json') | 169 trace_path = os.path.join(output_directory_path, node_name, 'trace.json') |
162 if not os.path.isfile(trace_path): | 170 if not os.path.isfile(trace_path): |
163 continue | 171 continue |
164 logging.info('processing \'%s\'' % trace_path) | 172 logging.info('processing \'%s\'' % trace_path) |
165 with open(trace_path) as trace_file: | 173 loading_trace = loading_trace_module.LoadingTrace.FromJsonFile(trace_path) |
166 trace = json.load(trace_file) | 174 trace_metrics = _PullMetricsFromLoadingTrace(loading_trace) |
167 trace_metrics = _PullMetricsFromTrace(trace) | 175 trace_metrics['id'] = page_id |
168 trace_metrics['id'] = page_id | 176 trace_metrics['url'] = run_infos['urls'][page_id] |
169 trace_metrics['url'] = run_infos['urls'][page_id] | 177 metrics.append(trace_metrics) |
170 metrics.append(trace_metrics) | |
171 assert len(metrics) > 0, ('Looks like \'{}\' was not a sandwich ' + | 178 assert len(metrics) > 0, ('Looks like \'{}\' was not a sandwich ' + |
172 'run directory.').format(output_directory_path) | 179 'run directory.').format(output_directory_path) |
173 return metrics | 180 return metrics |
174 | 181 |
175 | 182 |
176 def main(): | 183 def main(): |
177 logging.basicConfig(level=logging.INFO) | 184 logging.basicConfig(level=logging.INFO) |
178 | 185 |
179 parser = argparse.ArgumentParser() | 186 parser = argparse.ArgumentParser() |
180 parser.add_argument('output', type=str, | 187 parser.add_argument('output', type=str, |
181 help='Output directory of run_sandwich.py command.') | 188 help='Output directory of run_sandwich.py command.') |
182 args = parser.parse_args() | 189 args = parser.parse_args() |
183 | 190 |
184 trace_metrics_list = _PullMetricsFromOutputDirectory(args.output) | 191 trace_metrics_list = _PullMetricsFromOutputDirectory(args.output) |
185 trace_metrics_list.sort(key=lambda e: e['id']) | 192 trace_metrics_list.sort(key=lambda e: e['id']) |
186 cs_file_path = os.path.join(args.output, 'trace_analysis.csv') | 193 cs_file_path = os.path.join(args.output, 'trace_analysis.csv') |
187 with open(cs_file_path, 'w') as csv_file: | 194 with open(cs_file_path, 'w') as csv_file: |
188 writer = csv.DictWriter(csv_file, fieldnames=_CSV_FIELD_NAMES) | 195 writer = csv.DictWriter(csv_file, fieldnames=_CSV_FIELD_NAMES) |
189 writer.writeheader() | 196 writer.writeheader() |
190 for trace_metrics in trace_metrics_list: | 197 for trace_metrics in trace_metrics_list: |
191 writer.writerow(trace_metrics) | 198 writer.writerow(trace_metrics) |
192 return 0 | 199 return 0 |
193 | 200 |
194 | 201 |
195 if __name__ == '__main__': | 202 if __name__ == '__main__': |
196 sys.exit(main()) | 203 sys.exit(main()) |
OLD | NEW |