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 for event in tracing_track.GetEvents(): |
42 if event['cat'] != '__metadata' or event['name'] != 'process_name': | 45 if event.category != '__metadata' or event.name != 'process_name': |
43 continue | 46 continue |
44 if event['args']['name'] == 'Browser': | 47 if event.args['name'] == 'Browser': |
45 return event['pid'] | 48 return event.pid |
46 raise ValueError('couldn\'t find browser\'s PID') | 49 raise ValueError('couldn\'t find browser\'s PID') |
47 | 50 |
48 | 51 |
49 def _GetBrowserDumpEvents(trace): | 52 def _GetBrowserDumpEvents(tracing_track): |
50 """Get the browser memory dump events from a trace. | 53 """Get the browser memory dump events from a tracing track. |
51 | 54 |
52 Args: | 55 Args: |
53 trace: The cached trace. | 56 tracing_track: The tracing.TracingTrack. |
54 | 57 |
55 Returns: | 58 Returns: |
56 List of memory dump events. | 59 List of memory dump events. |
57 """ | 60 """ |
58 browser_pid = _GetBrowserPID(trace) | 61 browser_pid = _GetBrowserPID(tracing_track) |
59 browser_dumps_events = [] | 62 browser_dumps_events = [] |
60 for event in trace['traceEvents']: | 63 for event in tracing_track.GetEvents(): |
61 if event['cat'] != 'disabled-by-default-memory-infra': | 64 if event.category != 'disabled-by-default-memory-infra': |
62 continue | 65 continue |
63 if event['ph'] != 'v' or event['name'] != 'periodic_interval': | 66 if event.type != 'v' or event.name != 'periodic_interval': |
64 continue | 67 continue |
65 # Ignore dump events for processes other than the browser process | 68 # Ignore dump events for processes other than the browser process |
66 if event['pid'] != browser_pid: | 69 if event.pid != browser_pid: |
67 continue | 70 continue |
68 browser_dumps_events.append(event) | 71 browser_dumps_events.append(event) |
69 if len(browser_dumps_events) == 0: | 72 if len(browser_dumps_events) == 0: |
70 raise ValueError('No browser dump events found.') | 73 raise ValueError('No browser dump events found.') |
71 return browser_dumps_events | 74 return browser_dumps_events |
72 | 75 |
73 | 76 |
74 def _GetWebPageTrackedEvents(trace): | 77 def _GetWebPageTrackedEvents(tracing_track): |
75 """Get the web page's tracked events from a trace. | 78 """Get the web page's tracked events from a tracing track. |
76 | 79 |
77 Args: | 80 Args: |
78 trace: The cached trace. | 81 tracing_track: The tracing.TracingTrack. |
79 | 82 |
80 Returns: | 83 Returns: |
81 Dictionary all tracked events. | 84 Dictionary all tracked events. |
82 """ | 85 """ |
83 main_frame = None | 86 main_frame = None |
84 tracked_events = {} | 87 tracked_events = {} |
85 for event in trace['traceEvents']: | 88 for event in tracing_track.GetEvents(): |
86 if event['cat'] != 'blink.user_timing': | 89 if event.category != 'blink.user_timing': |
87 continue | 90 continue |
88 event_name = event['name'] | 91 event_name = event.name |
89 # Ignore events until about:blank's unloadEventEnd that give the main | 92 # Ignore events until about:blank's unloadEventEnd that give the main |
90 # frame id. | 93 # frame id. |
91 if not main_frame: | 94 if not main_frame: |
92 if event_name == 'unloadEventEnd': | 95 if event_name == 'unloadEventEnd': |
93 main_frame = event['args']['frame'] | 96 main_frame = event.args['frame'] |
94 logging.info('found about:blank\'s event \'unloadEventEnd\'') | 97 logging.info('found about:blank\'s event \'unloadEventEnd\'') |
95 continue | 98 continue |
96 # Ignore sub-frames events. requestStart don't have the frame set but it | 99 # 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. | 100 # is fine since tracking the first one after about:blank's unloadEventEnd. |
98 if 'frame' in event['args'] and event['args']['frame'] != main_frame: | 101 if 'frame' in event.args and event.args['frame'] != main_frame: |
99 continue | 102 continue |
100 if event_name in _TRACKED_EVENT_NAMES and event_name not in tracked_events: | 103 if event_name in _TRACKED_EVENT_NAMES and event_name not in tracked_events: |
101 logging.info('found url\'s event \'%s\'' % event_name) | 104 logging.info('found url\'s event \'%s\'' % event_name) |
102 tracked_events[event_name] = event | 105 tracked_events[event_name] = event |
103 assert len(tracked_events) == len(_TRACKED_EVENT_NAMES) | 106 assert len(tracked_events) == len(_TRACKED_EVENT_NAMES) |
104 return tracked_events | 107 return tracked_events |
105 | 108 |
106 | 109 |
107 def _PullMetricsFromTrace(trace): | 110 def _PullMetricsFromLoadingTrace(loading_trace): |
108 """Pulls all the metrics from a given trace. | 111 """Pulls all the metrics from a given trace. |
109 | 112 |
110 Args: | 113 Args: |
111 trace: The cached trace. | 114 loading_trace: loading_trace_module.LoadingTrace. |
112 | 115 |
113 Returns: | 116 Returns: |
114 Dictionary with all _CSV_FIELD_NAMES's field set (except the 'id'). | 117 Dictionary with all _CSV_FIELD_NAMES's field set (except the 'id'). |
115 """ | 118 """ |
116 browser_dump_events = _GetBrowserDumpEvents(trace) | 119 browser_dump_events = _GetBrowserDumpEvents(loading_trace.tracing_track) |
117 web_page_tracked_events = _GetWebPageTrackedEvents(trace) | 120 web_page_tracked_events = _GetWebPageTrackedEvents( |
| 121 loading_trace.tracing_track) |
118 | 122 |
119 browser_malloc_sum = 0 | 123 browser_malloc_sum = 0 |
120 browser_malloc_max = 0 | 124 browser_malloc_max = 0 |
121 for dump_event in browser_dump_events: | 125 for dump_event in browser_dump_events: |
122 attr = dump_event['args']['dumps']['allocators']['malloc']['attrs']['size'] | 126 attr = dump_event.args['dumps']['allocators']['malloc']['attrs']['size'] |
123 assert attr['units'] == 'bytes' | 127 assert attr['units'] == 'bytes' |
124 size = int(attr['value'], 16) | 128 size = int(attr['value'], 16) |
125 browser_malloc_sum += size | 129 browser_malloc_sum += size |
126 browser_malloc_max = max(browser_malloc_max, size) | 130 browser_malloc_max = max(browser_malloc_max, size) |
127 | 131 |
128 return { | 132 return { |
129 'total_load': (web_page_tracked_events['loadEventEnd']['ts'] - | 133 'total_load': (web_page_tracked_events['loadEventEnd'].start_msec - |
130 web_page_tracked_events['requestStart']['ts']), | 134 web_page_tracked_events['requestStart'].start_msec), |
131 'onload': (web_page_tracked_events['loadEventEnd']['ts'] - | 135 'onload': (web_page_tracked_events['loadEventEnd'].start_msec - |
132 web_page_tracked_events['loadEventStart']['ts']), | 136 web_page_tracked_events['loadEventStart'].start_msec), |
133 'browser_malloc_avg': browser_malloc_sum / float(len(browser_dump_events)), | 137 'browser_malloc_avg': browser_malloc_sum / float(len(browser_dump_events)), |
134 'browser_malloc_max': browser_malloc_max | 138 'browser_malloc_max': browser_malloc_max |
135 } | 139 } |
136 | 140 |
137 | 141 |
138 def _PullMetricsFromOutputDirectory(output_directory_path): | 142 def _PullMetricsFromOutputDirectory(output_directory_path): |
139 """Pulls all the metrics from all the traces of a sandwich run directory. | 143 """Pulls all the metrics from all the traces of a sandwich run directory. |
140 | 144 |
141 Args: | 145 Args: |
142 output_directory_path: The sandwich run's output directory to pull the | 146 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)): | 159 if not os.path.isdir(os.path.join(output_directory_path, node_name)): |
156 continue | 160 continue |
157 try: | 161 try: |
158 page_id = int(node_name) | 162 page_id = int(node_name) |
159 except ValueError: | 163 except ValueError: |
160 continue | 164 continue |
161 trace_path = os.path.join(output_directory_path, node_name, 'trace.json') | 165 trace_path = os.path.join(output_directory_path, node_name, 'trace.json') |
162 if not os.path.isfile(trace_path): | 166 if not os.path.isfile(trace_path): |
163 continue | 167 continue |
164 logging.info('processing \'%s\'' % trace_path) | 168 logging.info('processing \'%s\'' % trace_path) |
165 with open(trace_path) as trace_file: | 169 loading_trace = loading_trace_module.LoadingTrace.FromJsonFile(trace_path) |
166 trace = json.load(trace_file) | 170 trace_metrics = _PullMetricsFromLoadingTrace(loading_trace) |
167 trace_metrics = _PullMetricsFromTrace(trace) | 171 trace_metrics['id'] = page_id |
168 trace_metrics['id'] = page_id | 172 trace_metrics['url'] = run_infos['urls'][page_id] |
169 trace_metrics['url'] = run_infos['urls'][page_id] | 173 metrics.append(trace_metrics) |
170 metrics.append(trace_metrics) | |
171 assert len(metrics) > 0, ('Looks like \'{}\' was not a sandwich ' + | 174 assert len(metrics) > 0, ('Looks like \'{}\' was not a sandwich ' + |
172 'run directory.').format(output_directory_path) | 175 'run directory.').format(output_directory_path) |
173 return metrics | 176 return metrics |
174 | 177 |
175 | 178 |
176 def main(): | 179 def main(): |
177 logging.basicConfig(level=logging.INFO) | 180 logging.basicConfig(level=logging.INFO) |
178 | 181 |
179 parser = argparse.ArgumentParser() | 182 parser = argparse.ArgumentParser() |
180 parser.add_argument('output', type=str, | 183 parser.add_argument('output', type=str, |
181 help='Output directory of run_sandwich.py command.') | 184 help='Output directory of run_sandwich.py command.') |
182 args = parser.parse_args() | 185 args = parser.parse_args() |
183 | 186 |
184 trace_metrics_list = _PullMetricsFromOutputDirectory(args.output) | 187 trace_metrics_list = _PullMetricsFromOutputDirectory(args.output) |
185 trace_metrics_list.sort(key=lambda e: e['id']) | 188 trace_metrics_list.sort(key=lambda e: e['id']) |
186 cs_file_path = os.path.join(args.output, 'trace_analysis.csv') | 189 cs_file_path = os.path.join(args.output, 'trace_analysis.csv') |
187 with open(cs_file_path, 'w') as csv_file: | 190 with open(cs_file_path, 'w') as csv_file: |
188 writer = csv.DictWriter(csv_file, fieldnames=_CSV_FIELD_NAMES) | 191 writer = csv.DictWriter(csv_file, fieldnames=_CSV_FIELD_NAMES) |
189 writer.writeheader() | 192 writer.writeheader() |
190 for trace_metrics in trace_metrics_list: | 193 for trace_metrics in trace_metrics_list: |
191 writer.writerow(trace_metrics) | 194 writer.writerow(trace_metrics) |
192 return 0 | 195 return 0 |
193 | 196 |
194 | 197 |
195 if __name__ == '__main__': | 198 if __name__ == '__main__': |
196 sys.exit(main()) | 199 sys.exit(main()) |
OLD | NEW |