Skip to content

Commit b68ce9b

Browse files
committed
Add PoC speedscope -> collapsed converter
1 parent 5c6b501 commit b68ce9b

File tree

1 file changed

+63
-0
lines changed

1 file changed

+63
-0
lines changed

gprofiler/utils/speedscope.py

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

0 commit comments

Comments
 (0)