Skip to content

Commit 1b57f89

Browse files
authored
Add PoC speedscope -> collapsed converter (#422)
1 parent a53155c commit 1b57f89

File tree

1 file changed

+64
-0
lines changed

1 file changed

+64
-0
lines changed

gprofiler/utils/speedscope.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#
2+
# Copyright (c) Granulate. All rights reserved.
3+
# Licensed under the AGPL3 License. See LICENSE.md in the project root for license information.
4+
#
5+
6+
# speedscope -> collapsed converter, aimed to work for dotnet-trace.
7+
# speedscope spec:
8+
# https://github.com/jlfwong/speedscope/blob/639dae322b15fbcba5cd02c90335889fd285686a/src/lib/file-format-spec.ts
9+
10+
import json
11+
import math
12+
import random
13+
from collections import Counter
14+
from typing import Any, Dict, List, Tuple
15+
16+
from gprofiler.gprofiler_types import StackToSampleCount
17+
18+
19+
def _speedscope_frame_name(speedscope: Dict[str, Any], frame: int) -> str:
20+
name = speedscope["shared"]["frames"][frame]["name"]
21+
assert isinstance(name, str)
22+
return name
23+
24+
25+
def load_speedscope_as_collapsed(speedscope_path: str, frequncy_hz: int) -> StackToSampleCount:
26+
interval = 1 / frequncy_hz
27+
interval_ms = interval * 1000
28+
29+
with open(speedscope_path) as f:
30+
speedscope = json.load(f)
31+
32+
result_stacks: StackToSampleCount = Counter()
33+
34+
for profile in speedscope["profiles"]: # a profile per thread
35+
assert profile["type"] == "evented", profile["type"] # what dotnet-trace uses
36+
assert profile["unit"] == "milliseconds", profile["unit"] # what dotnet-trace uses
37+
stack: List[int] = []
38+
stacks: List[Tuple[int, ...]] = []
39+
40+
# needs to be a float, but dotnet-trace puts a string...
41+
last_ts = float(profile["startValue"]) # matches the ts of first event
42+
for event in profile["events"]:
43+
at = event["at"]
44+
elapsed = at - last_ts
45+
last_ts = at
46+
47+
if event["type"] == "O":
48+
stack.append(event["frame"])
49+
else:
50+
assert event["type"] == "C", f"unexpected event type: {event['type']}"
51+
assert stack[-1] == event["frame"]
52+
stack.pop()
53+
54+
frac, n = math.modf(elapsed / interval_ms)
55+
assert int(n) == n
56+
for _ in range(int(n)):
57+
stacks.append(tuple(stack))
58+
if random.random() <= frac:
59+
stacks.append(tuple(stack))
60+
61+
for s in stacks:
62+
result_stacks[";".join(map(lambda f: _speedscope_frame_name(speedscope, f), s))] += 1
63+
64+
return result_stacks

0 commit comments

Comments
 (0)