mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2026-05-01 16:48:07 +02:00
intel/tools: add intel_measure.py
We are moving away from the INTEL_MEASURE tool, replacing it with utrace. Utrace is better maintained and provides similar debug data. The eventual plan is to EOL INTEL_MEASURE from the driver. This python script reinterprets the dumped utrace data into the traditional INTEL_MEASURE csv file format. Usage: MESA_GPU_TRACES=print_csv MESA_GPU_TRACEFILE=/tmp/ut.csv INTEL_DEBUG=stall <cmd> intel_measure.py /tmp/ut.csv > im.csv Reviewed-by: Casey Bowman <casey.g.bowman@intel.com> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/34662>
This commit is contained in:
parent
eea3ed6a37
commit
e75c0160df
2 changed files with 178 additions and 0 deletions
172
src/intel/tools/intel_measure.py
Executable file
172
src/intel/tools/intel_measure.py
Executable file
|
|
@ -0,0 +1,172 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import re
|
||||
import sys
|
||||
|
||||
def error(msg, err_code=1):
|
||||
print('ERROR: ' + msg)
|
||||
sys.exit(err_code)
|
||||
|
||||
class Utrace_Parser:
|
||||
def __init__(self, cl_args: argparse.Namespace):
|
||||
self.cl_args = cl_args
|
||||
self.events: Dict[str, float] = {}
|
||||
self.renderpass: int = 0
|
||||
self.next_renderpass: int = 1
|
||||
self.idx: int = 0
|
||||
self.prev_end_ts_ns: float = 0
|
||||
|
||||
self.func: str
|
||||
self.args: str
|
||||
self.vs: str
|
||||
self.fs: str
|
||||
self.cs: str
|
||||
|
||||
def get_header(self) -> str:
|
||||
if self.cl_args.verbose:
|
||||
return 'start_ts,end_ts,frame,cmdbuf,rp,idx,event,count,vs,fs,cs,gap_us,time_us,details'
|
||||
return 'frame,cmdbuf,rp,idx,event,count,vs,fs,cs,gap_us,time_us'
|
||||
|
||||
def extract_count(self) -> None:
|
||||
if 'count' in self.args:
|
||||
filter = r'(.*)count=(\w+);?(.*)'
|
||||
match = re.search(filter, self.args)
|
||||
if match:
|
||||
self.count = match.group(2)
|
||||
self.args = match.group(1) + match.group(3)
|
||||
elif 'compute' in self.func:
|
||||
filter = r'(.*)group_x=(\w+); group_y=(\w+); group_z=(\w+);?(.*)'
|
||||
match = re.search(filter, self.args)
|
||||
if match:
|
||||
self.count = int(match.group(2)) * int(match.group(3)) * int(match.group(4))
|
||||
self.func += f'({match.group(2)}x{match.group(3)}x{match.group(4)})'
|
||||
self.args = match.group(1) + match.group(5)
|
||||
|
||||
def extract_shaders(self) -> None:
|
||||
filter = r'(.*)(vs_hash=[0-9a-fx]+); (fs_hash=[0-9a-fx]+);?(.*)'
|
||||
match = re.search(filter, self.args)
|
||||
if match:
|
||||
self.vs = match.group(2).split('=')[1]
|
||||
self.fs = match.group(3).split('=')[1]
|
||||
self.args = match.group(1) + match.group(4)
|
||||
return
|
||||
|
||||
filter = r'(.*)(cs_hash=0x[0-9a-f]+);?(.*)'
|
||||
match = re.search(filter, self.args)
|
||||
if match:
|
||||
self.cs = match.group(2).split('=')[1]
|
||||
self.args = match.group(1) + match.group(3)
|
||||
return
|
||||
|
||||
def extract_blorp_op(self) -> None:
|
||||
filter = r'(.*)op=([A-Z_]+);?(.*)'
|
||||
match = re.search(filter, self.args)
|
||||
if match:
|
||||
self.func = match.group(2).lower()
|
||||
self.args = match.group(1) + match.group(3)
|
||||
|
||||
def parse_line(self, line) -> str:
|
||||
if line.count(',') < 4:
|
||||
return None
|
||||
(frame, cmd_buf, timestamp, name, args) = line.strip().split(',', 4)
|
||||
|
||||
if name.count('_') < 2:
|
||||
return None
|
||||
(intel, begin_or_end, self.func) = name.split('_', 2)
|
||||
|
||||
if self.func == 'render_pass':
|
||||
if begin_or_end == 'begin':
|
||||
self.renderpass = self.next_renderpass
|
||||
return
|
||||
self.next_renderpass = self.renderpass + 1
|
||||
self.renderpass = 0
|
||||
return
|
||||
|
||||
ignore_funcs = {'frame': True, 'cmd_buffer': True}
|
||||
if ignore_funcs.get(self.func, None):
|
||||
if self.prev_end_ts_ns == 0:
|
||||
self.prev_end_ts_ns = float(timestamp)
|
||||
return None
|
||||
|
||||
if begin_or_end == 'begin':
|
||||
self.events[self.func] = float(timestamp)
|
||||
return None
|
||||
|
||||
# end event
|
||||
begin_ts_ns = self.events.pop(self.func, 0)
|
||||
end_ts_ns = float(timestamp)
|
||||
gap_ts_us = (begin_ts_ns - self.prev_end_ts_ns) / 1000
|
||||
delta_ts_us = (end_ts_ns - begin_ts_ns) / 1000
|
||||
|
||||
self.count = 0
|
||||
self.vs = ''
|
||||
self.fs = ''
|
||||
self.cs = ''
|
||||
self.args = args.replace(',', ';').strip(';')
|
||||
|
||||
self.extract_count()
|
||||
if 'draw' in name or 'compute' in name:
|
||||
self.extract_shaders()
|
||||
self.idx += 1
|
||||
elif 'blorp' in name:
|
||||
self.extract_blorp_op()
|
||||
self.idx += 1
|
||||
self.args = self.args.strip().lstrip('+')
|
||||
|
||||
if self.cl_args.verbose:
|
||||
out = [f'{begin_ts_ns:.0f}', f'{end_ts_ns:.0f}', int(frame) + 1,
|
||||
int(cmd_buf) + 1, self.renderpass, self.idx, self.func,
|
||||
self.count, self.vs, self.fs, self.cs,
|
||||
f'{gap_ts_us:.3f}', f'{delta_ts_us:.3f}', self.args]
|
||||
else:
|
||||
out = [int(frame) + 1, int(cmd_buf) + 1, self.renderpass, self.idx,
|
||||
self.func, self.count, self.vs, self.fs, self.cs,
|
||||
f'{gap_ts_us:.3f}', f'{delta_ts_us:.3f}']
|
||||
|
||||
self.prev_end_ts_ns = end_ts_ns
|
||||
result = ','.join(v if isinstance(v, str) else str(v) for v in out)
|
||||
return result
|
||||
|
||||
class CustomArgumentParser(argparse.ArgumentParser):
|
||||
examples = """
|
||||
Examples
|
||||
========
|
||||
> Generate csv of gpu events while running <cmd>. Use stalls to avoid overlapping events:
|
||||
|
||||
MESA_GPU_TRACES=print_csv MESA_GPU_TRACEFILE=/tmp/ut.csv INTEL_DEBUG=stall <cmd>
|
||||
intel_measure.py /tmp/ut.csv > im.csv
|
||||
|
||||
"""
|
||||
def format_help(self):
|
||||
return super().format_help() + self.examples
|
||||
|
||||
def main():
|
||||
cl_parser = CustomArgumentParser()
|
||||
cl_parser.add_argument('utrace_log', type=str, help='path to utrace log to parse')
|
||||
cl_parser.add_argument('-v', '--verbose', default=False, action='store_true', help='dump all fields to output')
|
||||
cl_parser.add_argument('-f', '--file', type=str, default=None, help='save results to file')
|
||||
|
||||
cl_args = cl_parser.parse_args()
|
||||
parser = Utrace_Parser(cl_args)
|
||||
|
||||
if cl_args.file:
|
||||
file = open(cl_args.file, 'w')
|
||||
else:
|
||||
file = sys.stdout
|
||||
|
||||
with open(cl_args.utrace_log, 'r') as f:
|
||||
print(parser.get_header(), file=file)
|
||||
for line in f.readlines():
|
||||
result = parser.parse_line(line)
|
||||
if result:
|
||||
print(result, file=file)
|
||||
|
||||
if cl_args.file:
|
||||
file.close()
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
|
@ -238,3 +238,9 @@ if with_intel_tools
|
|||
install : true
|
||||
)
|
||||
endif
|
||||
|
||||
install_data(
|
||||
'intel_measure.py',
|
||||
install_dir : get_option('bindir'),
|
||||
install_mode : 'rwxr-xr-x'
|
||||
)
|
||||
Loading…
Add table
Reference in a new issue