diff options
author | Josh Matthews <josh@joshmatthews.net> | 2019-03-22 13:15:50 -0400 |
---|---|---|
committer | Josh Matthews <josh@joshmatthews.net> | 2019-03-26 16:35:08 -0400 |
commit | 90f67c11e5de39341b8b212a022ce997f9382eb3 (patch) | |
tree | 391e8643504f957e26d6abaf1eee62eba3f52513 /etc | |
parent | db7bb2a5101ea6042654b59b3b81725e2da65891 (diff) | |
download | servo-90f67c11e5de39341b8b212a022ce997f9382eb3.tar.gz servo-90f67c11e5de39341b8b212a022ce997f9382eb3.zip |
Add a sampling profiler and a script to generate profiles for use with Gecko tooling.
Diffstat (limited to 'etc')
-rw-r--r-- | etc/profilicate.py | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/etc/profilicate.py b/etc/profilicate.py new file mode 100644 index 00000000000..44c734aef99 --- /dev/null +++ b/etc/profilicate.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python + +# Copyright 2018 The Servo Project Developers. See the COPYRIGHT +# file at the top-level directory of this distribution. +# +# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +# Script to take raw sample output from Servo sampling profiler and +# output a [processed profile]. Based largely on [this script] and +# [this documentation]. +# +# [processed profile]: https://github.com/firefox-devtools/profiler/blob/master/docs-developer/processed-profile-format.md +# [this script]: https://github.com/firefox-devtools/profiler/blob/master/src/profile-logic/import/linux-perf.js +# [this documentation]: https://github.com/firefox-devtools/profiler/blob/master/src/types/profile.js + +from collections import defaultdict +import json +import sys + + +class StringTable: + def __init__(self): + self.table = {} + self.idx = 0 + + def get(self, s): + assert s + if s not in self.table: + self.table[s] = self.idx + self.idx += 1 + return self.table[s] + + def length(self): + return len(self.table.keys()) + + def contents(self): + return sorted(self.table.keys(), key=self.table.__getitem__) + + +with open(sys.argv[1]) as f: + profile = json.load(f) + rate = profile["rate"] + samples = profile["data"] + startTime = profile["start"] + +frames = {} +stacks = {} + +thread_data = defaultdict(list) +thread_order = {} +for sample in samples: + if sample['name']: + name = sample['name'] + else: + name = "%s %d %d" % (sample['type'], sample['namespace'], sample['index']) + thread_data[name].append((sample['time'], sample['frames'])) + if name not in thread_order: + thread_order[name] = (sample['namespace'], sample['index']) + +tid = 0 +threads = [] +for (name, raw_samples) in sorted(thread_data.iteritems(), key=lambda x: thread_order[x[0]]): + string_table = StringTable() + tid += 1 + + stackMap = {} + stacks = [] + frameMap = {} + frames = [] + + samples = [] + + for sample in raw_samples: + prefix = None + for frame in sample[1]: + if not frame['name']: + continue + if not frame['name'] in frameMap: + frameMap[frame['name']] = len(frames) + frame_index = string_table.get(frame['name']) + frames.append([frame_index]) + frame = frameMap[frame['name']] + + stack_key = "%d,%d" % (frame, prefix) if prefix else str(frame) + if stack_key not in stackMap: + stackMap[stack_key] = len(stacks) + stacks.append([frame, prefix]) + stack = stackMap[stack_key] + prefix = stack + samples.append([stack, sample[0]]) + + threads.append({ + 'tid': tid, + 'name': name, + 'markers': { + 'schema': { + 'name': 0, + 'time': 1, + 'data': 2, + }, + 'data': [], + }, + 'samples': { + 'schema': { + 'stack': 0, + 'time': 1, + 'responsiveness': 2, + 'rss': 2, + 'uss': 4, + 'frameNumber': 5, + }, + 'data': samples, + }, + 'frameTable': { + 'schema': { + 'location': 0, + 'implementation': 1, + 'optimizations': 2, + 'line': 3, + 'category': 4, + }, + 'data': frames, + }, + 'stackTable': { + 'schema': { + 'frame': 0, + 'prefix': 1, + }, + 'data': stacks, + }, + 'stringTable': string_table.contents(), + }) + + +output = { + 'meta': { + 'interval': rate, + 'processType': 0, + 'product': 'Servo', + 'stackwalk': 1, + 'startTime': startTime, + 'version': 4, + 'presymbolicated': True, + }, + 'libs': [], + 'threads': threads, +} + +print(json.dumps(output)) |