servo/etc/profilicate.py
zefr0x c96de69e80
Use ruff to enforce python code formatting (#37117)
Requires servo/servo#37045 for deps and config.

Testing: No need for tests to test tests.
Fixes: servo/servo#37041

---------

Signed-off-by: zefr0x <zer0-x.7ty50@aleeas.com>
2025-05-26 11:54:43 +00:00

155 lines
4.3 KiB
Python

#!/usr/bin/env python3
# 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/main/docs-developer/processed-profile-format.md
# [this script]: https://github.com/firefox-devtools/profiler/blob/main/src/profile-logic/import/linux-perf.js
# [this documentation]: https://github.com/firefox-devtools/profiler/blob/main/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(list(self.table.keys()))
def contents(self):
return sorted(list(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(iter(thread_data.items()), 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 frame["name"] not 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))