xref: /linux-6.15/tools/perf/scripts/python/gecko.py (revision 5aacd7f0)
11699d3efSAnup Sharma# firefox-gecko-converter.py - Convert perf record output to Firefox's gecko profile format
21699d3efSAnup Sharma# SPDX-License-Identifier: GPL-2.0
31699d3efSAnup Sharma#
41699d3efSAnup Sharma# The script converts perf.data to Gecko Profile Format,
51699d3efSAnup Sharma# which can be read by https://profiler.firefox.com/.
61699d3efSAnup Sharma#
71699d3efSAnup Sharma# Usage:
81699d3efSAnup Sharma#
91699d3efSAnup Sharma#     perf record -a -g -F 99 sleep 60
101699d3efSAnup Sharma#     perf script report gecko > output.json
111699d3efSAnup Sharma
121699d3efSAnup Sharmaimport os
131699d3efSAnup Sharmaimport sys
14*5aacd7f0SAnup Sharmafrom dataclasses import dataclass, field
15*5aacd7f0SAnup Sharmafrom typing import List, Dict, Optional, NamedTuple, Set, Tuple, Any
161699d3efSAnup Sharma
171699d3efSAnup Sharma# Add the Perf-Trace-Util library to the Python path
181699d3efSAnup Sharmasys.path.append(os.environ['PERF_EXEC_PATH'] + \
191699d3efSAnup Sharma	'/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
201699d3efSAnup Sharma
211699d3efSAnup Sharmafrom perf_trace_context import *
221699d3efSAnup Sharmafrom Core import *
231699d3efSAnup Sharma
24*5aacd7f0SAnup SharmaStringID = int
25*5aacd7f0SAnup SharmaStackID = int
26*5aacd7f0SAnup SharmaFrameID = int
27*5aacd7f0SAnup SharmaCategoryID = int
28*5aacd7f0SAnup SharmaMilliseconds = float
29*5aacd7f0SAnup Sharma
300a02e44cSAnup Sharma# start_time is intialiazed only once for the all event traces.
310a02e44cSAnup Sharmastart_time = None
320a02e44cSAnup Sharma
33*5aacd7f0SAnup Sharma# https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e26d7457fee1d66cd4e2737/src/types/gecko-profile.js#L156
34*5aacd7f0SAnup Sharmaclass Frame(NamedTuple):
35*5aacd7f0SAnup Sharma	string_id: StringID
36*5aacd7f0SAnup Sharma	relevantForJS: bool
37*5aacd7f0SAnup Sharma	innerWindowID: int
38*5aacd7f0SAnup Sharma	implementation: None
39*5aacd7f0SAnup Sharma	optimizations: None
40*5aacd7f0SAnup Sharma	line: None
41*5aacd7f0SAnup Sharma	column: None
42*5aacd7f0SAnup Sharma	category: CategoryID
43*5aacd7f0SAnup Sharma	subcategory: int
44*5aacd7f0SAnup Sharma
45*5aacd7f0SAnup Sharma# https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e26d7457fee1d66cd4e2737/src/types/gecko-profile.js#L216
46*5aacd7f0SAnup Sharmaclass Stack(NamedTuple):
47*5aacd7f0SAnup Sharma	prefix_id: Optional[StackID]
48*5aacd7f0SAnup Sharma	frame_id: FrameID
49*5aacd7f0SAnup Sharma
50*5aacd7f0SAnup Sharma# https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e26d7457fee1d66cd4e2737/src/types/gecko-profile.js#L90
51*5aacd7f0SAnup Sharmaclass Sample(NamedTuple):
52*5aacd7f0SAnup Sharma	stack_id: Optional[StackID]
53*5aacd7f0SAnup Sharma	time_ms: Milliseconds
54*5aacd7f0SAnup Sharma	responsiveness: int
55*5aacd7f0SAnup Sharma
56*5aacd7f0SAnup Sharma@dataclass
57*5aacd7f0SAnup Sharmaclass Thread:
58*5aacd7f0SAnup Sharma	"""A builder for a profile of the thread.
59*5aacd7f0SAnup Sharma
60*5aacd7f0SAnup Sharma	Attributes:
61*5aacd7f0SAnup Sharma		comm: Thread command-line (name).
62*5aacd7f0SAnup Sharma		pid: process ID of containing process.
63*5aacd7f0SAnup Sharma		tid: thread ID.
64*5aacd7f0SAnup Sharma		samples: Timeline of profile samples.
65*5aacd7f0SAnup Sharma		frameTable: interned stack frame ID -> stack frame.
66*5aacd7f0SAnup Sharma		stringTable: interned string ID -> string.
67*5aacd7f0SAnup Sharma		stringMap: interned string -> string ID.
68*5aacd7f0SAnup Sharma		stackTable: interned stack ID -> stack.
69*5aacd7f0SAnup Sharma		stackMap: (stack prefix ID, leaf stack frame ID) -> interned Stack ID.
70*5aacd7f0SAnup Sharma		frameMap: Stack Frame string -> interned Frame ID.
71*5aacd7f0SAnup Sharma		comm: str
72*5aacd7f0SAnup Sharma		pid: int
73*5aacd7f0SAnup Sharma		tid: int
74*5aacd7f0SAnup Sharma		samples: List[Sample] = field(default_factory=list)
75*5aacd7f0SAnup Sharma		frameTable: List[Frame] = field(default_factory=list)
76*5aacd7f0SAnup Sharma		stringTable: List[str] = field(default_factory=list)
77*5aacd7f0SAnup Sharma		stringMap: Dict[str, int] = field(default_factory=dict)
78*5aacd7f0SAnup Sharma		stackTable: List[Stack] = field(default_factory=list)
79*5aacd7f0SAnup Sharma		stackMap: Dict[Tuple[Optional[int], int], int] = field(default_factory=dict)
80*5aacd7f0SAnup Sharma		frameMap: Dict[str, int] = field(default_factory=dict)
81*5aacd7f0SAnup Sharma	"""
82*5aacd7f0SAnup Sharma	comm: str
83*5aacd7f0SAnup Sharma	pid: int
84*5aacd7f0SAnup Sharma	tid: int
85*5aacd7f0SAnup Sharma	samples: List[Sample] = field(default_factory=list)
86*5aacd7f0SAnup Sharma	frameTable: List[Frame] = field(default_factory=list)
87*5aacd7f0SAnup Sharma	stringTable: List[str] = field(default_factory=list)
88*5aacd7f0SAnup Sharma	stringMap: Dict[str, int] = field(default_factory=dict)
89*5aacd7f0SAnup Sharma	stackTable: List[Stack] = field(default_factory=list)
90*5aacd7f0SAnup Sharma	stackMap: Dict[Tuple[Optional[int], int], int] = field(default_factory=dict)
91*5aacd7f0SAnup Sharma	frameMap: Dict[str, int] = field(default_factory=dict)
92*5aacd7f0SAnup Sharma
93*5aacd7f0SAnup Sharma	def _to_json_dict(self) -> Dict:
94*5aacd7f0SAnup Sharma		"""Converts current Thread to GeckoThread JSON format."""
95*5aacd7f0SAnup Sharma		# Gecko profile format is row-oriented data as List[List],
96*5aacd7f0SAnup Sharma		# And a schema for interpreting each index.
97*5aacd7f0SAnup Sharma		# Schema:
98*5aacd7f0SAnup Sharma		# https://github.com/firefox-devtools/profiler/blob/main/docs-developer/gecko-profile-format.md
99*5aacd7f0SAnup Sharma		# https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e26d7457fee1d66cd4e2737/src/types/gecko-profile.js#L230
100*5aacd7f0SAnup Sharma		return {
101*5aacd7f0SAnup Sharma			"tid": self.tid,
102*5aacd7f0SAnup Sharma			"pid": self.pid,
103*5aacd7f0SAnup Sharma			"name": self.comm,
104*5aacd7f0SAnup Sharma			# https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e26d7457fee1d66cd4e2737/src/types/gecko-profile.js#L51
105*5aacd7f0SAnup Sharma			"markers": {
106*5aacd7f0SAnup Sharma				"schema": {
107*5aacd7f0SAnup Sharma					"name": 0,
108*5aacd7f0SAnup Sharma					"startTime": 1,
109*5aacd7f0SAnup Sharma					"endTime": 2,
110*5aacd7f0SAnup Sharma					"phase": 3,
111*5aacd7f0SAnup Sharma					"category": 4,
112*5aacd7f0SAnup Sharma					"data": 5,
113*5aacd7f0SAnup Sharma				},
114*5aacd7f0SAnup Sharma				"data": [],
115*5aacd7f0SAnup Sharma			},
116*5aacd7f0SAnup Sharma
117*5aacd7f0SAnup Sharma			# https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e26d7457fee1d66cd4e2737/src/types/gecko-profile.js#L90
118*5aacd7f0SAnup Sharma			"samples": {
119*5aacd7f0SAnup Sharma				"schema": {
120*5aacd7f0SAnup Sharma					"stack": 0,
121*5aacd7f0SAnup Sharma					"time": 1,
122*5aacd7f0SAnup Sharma					"responsiveness": 2,
123*5aacd7f0SAnup Sharma				},
124*5aacd7f0SAnup Sharma				"data": self.samples
125*5aacd7f0SAnup Sharma			},
126*5aacd7f0SAnup Sharma
127*5aacd7f0SAnup Sharma			# https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e26d7457fee1d66cd4e2737/src/types/gecko-profile.js#L156
128*5aacd7f0SAnup Sharma			"frameTable": {
129*5aacd7f0SAnup Sharma				"schema": {
130*5aacd7f0SAnup Sharma					"location": 0,
131*5aacd7f0SAnup Sharma					"relevantForJS": 1,
132*5aacd7f0SAnup Sharma					"innerWindowID": 2,
133*5aacd7f0SAnup Sharma					"implementation": 3,
134*5aacd7f0SAnup Sharma					"optimizations": 4,
135*5aacd7f0SAnup Sharma					"line": 5,
136*5aacd7f0SAnup Sharma					"column": 6,
137*5aacd7f0SAnup Sharma					"category": 7,
138*5aacd7f0SAnup Sharma					"subcategory": 8,
139*5aacd7f0SAnup Sharma				},
140*5aacd7f0SAnup Sharma				"data": self.frameTable,
141*5aacd7f0SAnup Sharma			},
142*5aacd7f0SAnup Sharma
143*5aacd7f0SAnup Sharma			# https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e26d7457fee1d66cd4e2737/src/types/gecko-profile.js#L216
144*5aacd7f0SAnup Sharma			"stackTable": {
145*5aacd7f0SAnup Sharma				"schema": {
146*5aacd7f0SAnup Sharma					"prefix": 0,
147*5aacd7f0SAnup Sharma					"frame": 1,
148*5aacd7f0SAnup Sharma				},
149*5aacd7f0SAnup Sharma				"data": self.stackTable,
150*5aacd7f0SAnup Sharma			},
151*5aacd7f0SAnup Sharma			"stringTable": self.stringTable,
152*5aacd7f0SAnup Sharma			"registerTime": 0,
153*5aacd7f0SAnup Sharma			"unregisterTime": None,
154*5aacd7f0SAnup Sharma			"processType": "default",
155*5aacd7f0SAnup Sharma		}
156*5aacd7f0SAnup Sharma
1571699d3efSAnup Sharma# Uses perf script python interface to parse each
1581699d3efSAnup Sharma# event and store the data in the thread builder.
1591699d3efSAnup Sharmadef process_event(param_dict: Dict) -> None:
1600a02e44cSAnup Sharma	global start_time
1610a02e44cSAnup Sharma	global tid_to_thread
1620a02e44cSAnup Sharma	time_stamp = (param_dict['sample']['time'] // 1000) / 1000
1630a02e44cSAnup Sharma	pid = param_dict['sample']['pid']
1640a02e44cSAnup Sharma	tid = param_dict['sample']['tid']
1650a02e44cSAnup Sharma	comm = param_dict['comm']
1660a02e44cSAnup Sharma
1670a02e44cSAnup Sharma	# Start time is the time of the first sample
1680a02e44cSAnup Sharma	if not start_time:
1690a02e44cSAnup Sharma		start_time = time_stamp
1701699d3efSAnup Sharma
1711699d3efSAnup Sharma# Trace_end runs at the end and will be used to aggregate
1721699d3efSAnup Sharma# the data into the final json object and print it out to stdout.
1731699d3efSAnup Sharmadef trace_end() -> None:
1741699d3efSAnup Sharma	pass
175