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