| # 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. |
| |
| """Filters a big trace keeping only the last memory-infra dumps.""" |
| |
| from __future__ import print_function |
| |
| import collections |
| import gzip |
| import json |
| |
| |
| def FormatBytes(value): |
| units = ['B', 'KiB', 'MiB', 'GiB'] |
| while abs(value) >= 1024 and len(units) > 1: |
| value /= 1024 |
| units = units.pop(0) |
| return '%3.1f %s' % (value, units[0]) |
| |
| |
| def Main(argv): |
| if len(argv) < 2: |
| print('Usage: %s trace.json[.gz]' % argv[0]) |
| return 1 |
| |
| in_path = argv[1] |
| if in_path.lower().endswith('.gz'): |
| fin = gzip.open(in_path, 'rb') |
| else: |
| fin = open(in_path, 'r') |
| with fin: |
| print('Loading trace (can take 1 min on a z620 for a 1GB trace)...') |
| trace = json.load(fin) |
| print('Done. Read ' + FormatBytes(fin.tell())) |
| |
| print('Filtering events') |
| phase_count = collections.defaultdict(int) |
| out_events = [] |
| global_dumps = collections.OrderedDict() |
| if isinstance(trace, dict): |
| in_events = trace.get('traceEvents', []) |
| elif isinstance(trace, list) and isinstance(trace[0], dict): |
| in_events = trace |
| |
| for evt in in_events: |
| phase = evt.get('ph', '?') |
| phase_count[phase] += 1 |
| |
| # Drop all diagnostic events for memory-infra debugging. |
| if phase not in ('v', 'V') and evt.get('cat', '').endswith('memory-infra'): |
| continue |
| |
| # pass-through all the other non-memory-infra events |
| if phase != 'v': |
| out_events.append(evt) |
| continue |
| |
| # Recreate the global dump groups |
| event_id = evt['id'] |
| global_dumps.setdefault(event_id, []) |
| global_dumps[event_id].append(evt) |
| |
| |
| print('Detected %d memory-infra global dumps' % len(global_dumps)) |
| if global_dumps: |
| max_procs = max(len(x) for x in global_dumps.values()) |
| print('Max number of processes seen: %d' % max_procs) |
| |
| ndumps = 2 |
| print('Preserving the last %d memory-infra dumps' % ndumps) |
| detailed_dumps = [] |
| non_detailed_dumps = [] |
| for global_dump in global_dumps.values(): |
| try: |
| level_of_detail = global_dump[0]['args']['dumps']['level_of_detail'] |
| except KeyError: |
| level_of_detail = None |
| if level_of_detail == 'detailed': |
| detailed_dumps.append(global_dump) |
| else: |
| non_detailed_dumps.append(global_dump) |
| |
| dumps_to_preserve = detailed_dumps[-ndumps:] |
| ndumps -= len(dumps_to_preserve) |
| if ndumps: |
| dumps_to_preserve += non_detailed_dumps[-ndumps:] |
| |
| for global_dump in dumps_to_preserve: |
| out_events += global_dump |
| |
| print('\nEvents histogram for the original trace (count by phase)') |
| print('--------------------------------------------------------') |
| for phase, count in sorted(list(phase_count.items()), key=lambda x: x[1]): |
| print('%s %d' % (phase, count)) |
| |
| out_path = in_path.split('.json')[0] + '-filtered.json' |
| print('\nWriting filtered trace to ' + out_path, end='') |
| with open(out_path, 'w') as fout: |
| json.dump({'traceEvents': out_events}, fout) |
| num_bytes_written = fout.tell() |
| print(' (%s written)' % FormatBytes(num_bytes_written)) |