| Index: tools/android/loading/pull_sandwich_metrics.py
|
| diff --git a/tools/android/loading/pull_sandwich_metrics.py b/tools/android/loading/pull_sandwich_metrics.py
|
| new file mode 100755
|
| index 0000000000000000000000000000000000000000..15582431ab1672c3a25ce1834d58d36e7aa938e4
|
| --- /dev/null
|
| +++ b/tools/android/loading/pull_sandwich_metrics.py
|
| @@ -0,0 +1,190 @@
|
| +#! /usr/bin/env python
|
| +# Copyright 2016 The Chromium Authors. All rights reserved.
|
| +# Use of this source code is governed by a BSD-style license that can be
|
| +# found in the LICENSE file.
|
| +
|
| +"""Pull a sandwich run's output directory's metrics from traces into a CSV.
|
| +
|
| +python pull_sandwich_metrics.py -h
|
| +"""
|
| +
|
| +import argparse
|
| +import csv
|
| +import json
|
| +import logging
|
| +import os
|
| +import sys
|
| +
|
| +
|
| +CATEGORIES = ['blink.user_timing', 'disabled-by-default-memory-infra']
|
| +
|
| +_CSV_FIELD_NAMES = [
|
| + 'id',
|
| + 'total_load',
|
| + 'onload',
|
| + 'browser_malloc_avg',
|
| + 'browser_malloc_max']
|
| +
|
| +_TRACKED_EVENT_NAMES = set(['requestStart', 'loadEventStart', 'loadEventEnd'])
|
| +
|
| +
|
| +def _GetBrowserPID(trace):
|
| + """Get the browser PID from a trace.
|
| +
|
| + Args:
|
| + trace: The cached trace.
|
| +
|
| + Returns:
|
| + The browser's PID as an integer.
|
| + """
|
| + for event in trace['traceEvents']:
|
| + if event['cat'] != '__metadata' or event['name'] != 'process_name':
|
| + continue
|
| + if event['args']['name'] == 'Browser':
|
| + return event['pid']
|
| + raise ValueError('couldn\'t find browser\'s PID')
|
| +
|
| +
|
| +def _GetBrowserDumpEvents(trace):
|
| + """Get the browser memory dump events from a trace.
|
| +
|
| + Args:
|
| + trace: The cached trace.
|
| +
|
| + Returns:
|
| + List of memory dump events.
|
| + """
|
| + browser_pid = _GetBrowserPID(trace)
|
| + browser_dumps_events = []
|
| + for event in trace['traceEvents']:
|
| + if event['cat'] != 'disabled-by-default-memory-infra':
|
| + continue
|
| + if event['ph'] != 'v' or event['name'] != 'periodic_interval':
|
| + continue
|
| + # Ignore dump events for processes other than the browser process
|
| + if event['pid'] != browser_pid:
|
| + continue
|
| + browser_dumps_events.append(event)
|
| + if len(browser_dumps_events) == 0:
|
| + raise ValueError('No browser dump events found.')
|
| + return browser_dumps_events
|
| +
|
| +
|
| +def _GetWebPageTrackedEvents(trace):
|
| + """Get the web page's tracked events from a trace.
|
| +
|
| + Args:
|
| + trace: The cached trace.
|
| +
|
| + Returns:
|
| + Dictionary all tracked events.
|
| + """
|
| + main_frame = None
|
| + tracked_events = {}
|
| + for event in trace['traceEvents']:
|
| + if event['cat'] != 'blink.user_timing':
|
| + continue
|
| + event_name = event['name']
|
| + # Ignore events until about:blank's unloadEventEnd that give the main
|
| + # frame id.
|
| + if not main_frame:
|
| + if event_name == 'unloadEventEnd':
|
| + main_frame = event['args']['frame']
|
| + logging.info('found about:blank\'s event \'unloadEventEnd\'')
|
| + continue
|
| + # Ignore sub-frames events. requestStart don't have the frame set but it
|
| + # is fine since tracking the first one after about:blank's unloadEventEnd.
|
| + if 'frame' in event['args'] and event['args']['frame'] != main_frame:
|
| + continue
|
| + if event_name in _TRACKED_EVENT_NAMES and event_name not in tracked_events:
|
| + logging.info('found url\'s event \'%s\'' % event_name)
|
| + tracked_events[event_name] = event
|
| + assert len(tracked_events) == len(_TRACKED_EVENT_NAMES)
|
| + return tracked_events
|
| +
|
| +
|
| +def _PullMetricsFromTrace(trace):
|
| + """Pulls all the metrics from a given trace.
|
| +
|
| + Args:
|
| + trace: The cached trace.
|
| +
|
| + Returns:
|
| + Dictionary with all _CSV_FIELD_NAMES's field set (except the 'id').
|
| + """
|
| + browser_dump_events = _GetBrowserDumpEvents(trace)
|
| + web_page_tracked_events = _GetWebPageTrackedEvents(trace)
|
| +
|
| + browser_malloc_sum = 0
|
| + browser_malloc_max = 0
|
| + for dump_event in browser_dump_events:
|
| + attr = dump_event['args']['dumps']['allocators']['malloc']['attrs']['size']
|
| + assert attr['units'] == 'bytes'
|
| + size = int(attr['value'], 16)
|
| + browser_malloc_sum += size
|
| + browser_malloc_max = max(browser_malloc_max, size)
|
| +
|
| + return {
|
| + 'total_load': (web_page_tracked_events['loadEventEnd']['ts'] -
|
| + web_page_tracked_events['requestStart']['ts']),
|
| + 'onload': (web_page_tracked_events['loadEventEnd']['ts'] -
|
| + web_page_tracked_events['loadEventStart']['ts']),
|
| + 'browser_malloc_avg': browser_malloc_sum / float(len(browser_dump_events)),
|
| + 'browser_malloc_max': browser_malloc_max
|
| + }
|
| +
|
| +
|
| +def _PullMetricsFromOutputDirectory(output_directory_path):
|
| + """Pulls all the metrics from all the traces of a sandwich run directory.
|
| +
|
| + Args:
|
| + output_directory_path: The sandwich run's output directory to pull the
|
| + metrics from.
|
| +
|
| + Returns:
|
| + List of dictionaries with all _CSV_FIELD_NAMES's field set.
|
| + """
|
| + assert os.path.isdir(output_directory_path)
|
| + metrics = []
|
| + for node_name in os.listdir(output_directory_path):
|
| + if not os.path.isdir(os.path.join(output_directory_path, node_name)):
|
| + continue
|
| + try:
|
| + page_id = int(node_name)
|
| + except ValueError:
|
| + continue
|
| + trace_path = os.path.join(output_directory_path, node_name, 'trace.json')
|
| + if not os.path.isfile(trace_path):
|
| + continue
|
| + logging.info('processing \'%s\'' % trace_path)
|
| + with open(trace_path) as trace_file:
|
| + trace = json.load(trace_file)
|
| + trace_metrics = _PullMetricsFromTrace(trace)
|
| + trace_metrics['id'] = page_id
|
| + metrics.append(trace_metrics)
|
| + assert len(metrics) > 0, ('Looks like \'{}\' was not a sandwich ' +
|
| + 'run directory.').format(output_directory_path)
|
| + return metrics
|
| +
|
| +
|
| +def main():
|
| + logging.basicConfig(level=logging.INFO)
|
| +
|
| + parser = argparse.ArgumentParser()
|
| + parser.add_argument('output', type=str,
|
| + help='Output directory of run_sandwich.py command.')
|
| + args = parser.parse_args()
|
| +
|
| + trace_metrics_list = _PullMetricsFromOutputDirectory(args.output)
|
| + trace_metrics_list.sort(key=lambda e: e['id'])
|
| + cs_file_path = os.path.join(args.output, 'trace_analysis.csv')
|
| + with open(cs_file_path, 'w') as csv_file:
|
| + writer = csv.DictWriter(csv_file, fieldnames=_CSV_FIELD_NAMES)
|
| + writer.writeheader()
|
| + for trace_metrics in trace_metrics_list:
|
| + writer.writerow(trace_metrics)
|
| + return 0
|
| +
|
| +
|
| +if __name__ == '__main__':
|
| + sys.exit(main())
|
|
|