michael@0: # Copyright (c) 2012 The Chromium Authors. All rights reserved. michael@0: # Use of this source code is governed by a BSD-style license that can be michael@0: # found in the LICENSE file. michael@0: michael@0: michael@0: """A helper module for parsing JSON objects from perf tests results.""" michael@0: michael@0: import json michael@0: michael@0: michael@0: def GetAverageRunInfo(json_data, name): michael@0: """Summarizes TraceEvent JSON data for performance metrics. michael@0: michael@0: Example JSON Inputs (More tags can be added but these are required): michael@0: Measuring Duration: michael@0: [ michael@0: { "cat": "Java", michael@0: "ts": 10000000000, michael@0: "ph": "S", michael@0: "name": "TestTrace" michael@0: }, michael@0: { "cat": "Java", michael@0: "ts": 10000004000, michael@0: "ph": "F", michael@0: "name": "TestTrace" michael@0: }, michael@0: ... michael@0: ] michael@0: michael@0: Measuring Call Frequency (FPS): michael@0: [ michael@0: { "cat": "Java", michael@0: "ts": 10000000000, michael@0: "ph": "I", michael@0: "name": "TestTraceFPS" michael@0: }, michael@0: { "cat": "Java", michael@0: "ts": 10000004000, michael@0: "ph": "I", michael@0: "name": "TestTraceFPS" michael@0: }, michael@0: ... michael@0: ] michael@0: michael@0: Args: michael@0: json_data: A list of dictonaries each representing a JSON object. michael@0: name: The 'name' tag to filter on in the JSON file. michael@0: michael@0: Returns: michael@0: A dictionary of result data with the following tags: michael@0: min: The minimum value tracked. michael@0: max: The maximum value tracked. michael@0: average: The average of all the values tracked. michael@0: count: The number of times the category/name pair was tracked. michael@0: type: The type of tracking ('Instant' for instant tags and 'Span' for michael@0: begin/end tags. michael@0: category: The passed in category filter. michael@0: name: The passed in name filter. michael@0: data_points: A list of all of the times used to generate this data. michael@0: units: The units for the values being reported. michael@0: michael@0: Raises: michael@0: Exception: if entry contains invalid data. michael@0: """ michael@0: michael@0: def EntryFilter(entry): michael@0: return entry['cat'] == 'Java' and entry['name'] == name michael@0: filtered_entries = filter(EntryFilter, json_data) michael@0: michael@0: result = {} michael@0: michael@0: result['min'] = -1 michael@0: result['max'] = -1 michael@0: result['average'] = 0 michael@0: result['count'] = 0 michael@0: result['type'] = 'Unknown' michael@0: result['category'] = 'Java' michael@0: result['name'] = name michael@0: result['data_points'] = [] michael@0: result['units'] = '' michael@0: michael@0: total_sum = 0 michael@0: michael@0: last_val = 0 michael@0: val_type = None michael@0: for entry in filtered_entries: michael@0: if not val_type: michael@0: if 'mem' in entry: michael@0: val_type = 'mem' michael@0: michael@0: def GetVal(entry): michael@0: return entry['mem'] michael@0: michael@0: result['units'] = 'kb' michael@0: elif 'ts' in entry: michael@0: val_type = 'ts' michael@0: michael@0: def GetVal(entry): michael@0: return float(entry['ts']) / 1000.0 michael@0: michael@0: result['units'] = 'ms' michael@0: else: michael@0: raise Exception('Entry did not contain valid value info: %s' % entry) michael@0: michael@0: if not val_type in entry: michael@0: raise Exception('Entry did not contain expected value type "%s" ' michael@0: 'information: %s' % (val_type, entry)) michael@0: val = GetVal(entry) michael@0: if (entry['ph'] == 'S' and michael@0: (result['type'] == 'Unknown' or result['type'] == 'Span')): michael@0: result['type'] = 'Span' michael@0: last_val = val michael@0: elif ((entry['ph'] == 'F' and result['type'] == 'Span') or michael@0: (entry['ph'] == 'I' and (result['type'] == 'Unknown' or michael@0: result['type'] == 'Instant'))): michael@0: if last_val > 0: michael@0: delta = val - last_val michael@0: if result['min'] == -1 or result['min'] > delta: michael@0: result['min'] = delta michael@0: if result['max'] == -1 or result['max'] < delta: michael@0: result['max'] = delta michael@0: total_sum += delta michael@0: result['count'] += 1 michael@0: result['data_points'].append(delta) michael@0: if entry['ph'] == 'I': michael@0: result['type'] = 'Instant' michael@0: last_val = val michael@0: if result['count'] > 0: result['average'] = total_sum / result['count'] michael@0: michael@0: return result michael@0: michael@0: michael@0: def GetAverageRunInfoFromJSONString(json_string, name): michael@0: """Returns the results from GetAverageRunInfo using a JSON string. michael@0: michael@0: Args: michael@0: json_string: The string containing JSON. michael@0: name: The 'name' tag to filter on in the JSON file. michael@0: michael@0: Returns: michael@0: See GetAverageRunInfo Returns section. michael@0: """ michael@0: return GetAverageRunInfo(json.loads(json_string), name) michael@0: michael@0: michael@0: def GetAverageRunInfoFromFile(json_file, name): michael@0: """Returns the results from GetAverageRunInfo using a JSON file. michael@0: michael@0: Args: michael@0: json_file: The path to a JSON file. michael@0: name: The 'name' tag to filter on in the JSON file. michael@0: michael@0: Returns: michael@0: See GetAverageRunInfo Returns section. michael@0: """ michael@0: with open(json_file, 'r') as f: michael@0: data = f.read() michael@0: perf = json.loads(data) michael@0: michael@0: return GetAverageRunInfo(perf, name)