IQ.Pilot Release Commit @ 0798119

This commit is contained in:
IQ.Lvbs history cleanup
2026-08-22 23:42:42 -05:00
commit b42569dbca
4529 changed files with 1132125 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
# SQTT Profiling
## Getting SQ Thread Trace
`VIZ=2` to enable SQTT profiling.
`SQTT_ITRACE_SE_MASK=X` to select shader engines for instruction tracing, -1 = all, 0 = disabled, >0 = SE bitmask, default 0b11.
`SQTT_BUFFER_SIZE=X` to change size of SQTT buffer (per shader engine, 6 SEs on 7900xtx) in megabytes, default 256.
## Viewing the traces
- Web UI: `tinygrad/viz/serve.py`
- Command line: `python -m tinygrad.renderer.amd.sqtt`

View File

@@ -0,0 +1,27 @@
import os, subprocess, sys, shlex
from pathlib import Path
from tinygrad.helpers import temp, getenv
EXAMPLES_DIR = Path(__file__).parent
PROFILE_PATH = Path(temp("profile.pkl", append_user=True))
EXAMPLES = {
"empty":"test/backend/test_custom_kernel.py TestCustomKernel.test_empty",
"plus":"test/test_tiny.py TestTiny.test_plus",
"gemm":"-c \"from tinygrad import Tensor; (Tensor.empty(N:=32, N)@Tensor.empty(N, N)).realize()\"",
"sync":"test/amd/test_custom_kernel.py TestCustomKernel.test_lds_sync",
"handwritten":"test/amd/test_custom_kernel.py TestCustomKernel.test_handwritten",
}
if __name__ == "__main__":
arch = subprocess.check_output(["python", "-c", "from tinygrad import Device; print(Device['AMD'].arch)"], text=True,
env={**os.environ, "DEBUG":"0"}).rstrip()
(EXAMPLES_DIR/arch).mkdir(exist_ok=True)
for name,test in EXAMPLES.items():
if getenv("NAME", name) != name: continue
for i in range(2):
# AM_RESET=1 gets a clear trace, does not work on mi300 machines
subprocess.run([sys.executable, *shlex.split(test)], cwd=EXAMPLES_DIR.parent.parent.parent,
env={**os.environ, "DEV":"AMD", "AM_RESET":"1" if not arch.startswith("gfx9") else "0", "VIZ":"-2", "PYTHONPATH":"."})
PROFILE_PATH.rename(dest:=EXAMPLES_DIR/arch/f"profile_{name}_run_{i}.pkl")
print(f"saved SQTT trace to {dest}")

View File

@@ -0,0 +1,23 @@
#!/usr/bin/env python3
import os, platform, shutil, subprocess
from pathlib import Path
from tinygrad.helpers import fetch, OSX
VERSION = "0.1.6"
DEST = Path("/usr/local/lib")
DEST.mkdir(exist_ok=True)
if __name__ == "__main__":
if OSX:
arch = "arm64" if platform.machine() == "arm64" else "x86_64"
dmg = fetch(f"https://github.com/ROCm/rocprof-trace-decoder/releases/download/{VERSION}/rocprof-trace-decoder-macos-{arch}-{VERSION}-Darwin.dmg")
mnt = Path(subprocess.check_output(["hdiutil", "attach", "-nobrowse", "-readonly", "-mountrandom", "/tmp", str(dmg)],
text=True).split("\t")[-1].strip())
try: shutil.copy2(next(mnt.rglob("librocprof-trace-decoder.dylib")), DEST)
finally: subprocess.run(["hdiutil", "detach", str(mnt)], check=True)
lib = DEST/"librocprof-trace-decoder.dylib"
else:
lib = DEST/"librocprof-trace-decoder.so"
os.system(f"sudo curl -L https://github.com/ROCm/rocprof-trace-decoder/raw/{VERSION}/releases/linux_glibc_2_28_x86_64/librocprof-trace-decoder.so -o {lib}")
os.system("sudo ldconfig")
print(f"Installed {lib.name} ({VERSION}) to", DEST)

View File

@@ -0,0 +1,347 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse, ctypes, struct, hashlib, pickle, code, typing, functools
import tinygrad.runtime.autogen.sqtt as sqtt
from tinygrad.device import ProfileEvent, ProfileDeviceEvent, ProfileProgramEvent
from tinygrad.runtime.ops_amd import ProfileSQTTEvent
from tinygrad.helpers import round_up, flatten, all_same, temp
from dataclasses import dataclass
CHUNK_CLASSES = {
sqtt.SQTT_FILE_CHUNK_TYPE_ASIC_INFO: sqtt.struct_sqtt_file_chunk_asic_info,
sqtt.SQTT_FILE_CHUNK_TYPE_SQTT_DESC: sqtt.struct_sqtt_file_chunk_sqtt_desc,
sqtt.SQTT_FILE_CHUNK_TYPE_SQTT_DATA: sqtt.struct_sqtt_file_chunk_sqtt_data,
sqtt.SQTT_FILE_CHUNK_TYPE_API_INFO: sqtt.struct_sqtt_file_chunk_api_info,
sqtt.SQTT_FILE_CHUNK_TYPE_QUEUE_EVENT_TIMINGS: sqtt.struct_sqtt_file_chunk_queue_event_timings,
sqtt.SQTT_FILE_CHUNK_TYPE_CLOCK_CALIBRATION: sqtt.struct_sqtt_file_chunk_clock_calibration,
sqtt.SQTT_FILE_CHUNK_TYPE_CPU_INFO: sqtt.struct_sqtt_file_chunk_cpu_info,
sqtt.SQTT_FILE_CHUNK_TYPE_SPM_DB: sqtt.struct_sqtt_file_chunk_spm_db,
sqtt.SQTT_FILE_CHUNK_TYPE_CODE_OBJECT_DATABASE: sqtt.struct_sqtt_file_chunk_code_object_database,
sqtt.SQTT_FILE_CHUNK_TYPE_CODE_OBJECT_LOADER_EVENTS: sqtt.struct_sqtt_file_chunk_code_object_loader_events,
sqtt.SQTT_FILE_CHUNK_TYPE_PSO_CORRELATION: sqtt.struct_sqtt_file_chunk_pso_correlation,
}
def pretty(val, pad=0) -> str:
if isinstance(val, (ctypes.Structure, ctypes.Union)):
nl = '\n' # old python versions don't support \ in f-strings
return f"{val.__class__.__name__}({nl}{' '*(pad+2)}{(f', {nl}'+' '*(pad+2)).join([f'{field[0]}={pretty(getattr(val, field[0]), pad=pad+2)}' for field in val._fields_])}{nl}{' '*pad})"
if isinstance(val, ctypes.Array):
return f"[{', '.join(map(pretty, val))}]"
if isinstance(val, int) and val >= 1024: return hex(val)
return repr(val)
@dataclass(frozen=True)
class RGPChunk:
header: sqtt.Structure
data: list[typing.Any]|list[tuple[typing.Any, bytes]]|bytes|None = None
def print(self):
print(pretty(self.header))
# if isinstance(self.data, bytes): print(repr(self.data))
if isinstance(self.data, list):
for dchunk in self.data:
if isinstance(dchunk, tuple):
print(pretty(dchunk[0]))
# print(repr(dchunk[1]))
else:
print(pretty(dchunk))
# TODO: `def fixup` and true immutability
def to_bytes(self, offset:int) -> bytes:
cid = self.header.header.chunk_id.type
match cid:
case _ if cid in {sqtt.SQTT_FILE_CHUNK_TYPE_ASIC_INFO, sqtt.SQTT_FILE_CHUNK_TYPE_CPU_INFO, sqtt.SQTT_FILE_CHUNK_TYPE_API_INFO, sqtt.SQTT_FILE_CHUNK_TYPE_SQTT_DESC}:
self.header.header.size_in_bytes = ctypes.sizeof(self.header)
return bytes(self.header)
case sqtt.SQTT_FILE_CHUNK_TYPE_SQTT_DATA:
assert isinstance(self.data, bytes)
self.header.header.size_in_bytes = ctypes.sizeof(self.header) + len(self.data)
self.header.offset = offset+ctypes.sizeof(self.header)
self.header.size = len(self.data)
return bytes(self.header) + self.data
case sqtt.SQTT_FILE_CHUNK_TYPE_CODE_OBJECT_DATABASE:
assert isinstance(self.data, list)
data_codb = typing.cast(list[tuple[sqtt.struct_sqtt_code_object_database_record, bytes]], self.data)
ret = bytearray()
sz = ctypes.sizeof(self.header)+sum([ctypes.sizeof(record_hdr)+round_up(len(record_blob), 4) for record_hdr,record_blob in data_codb])
self.header.header.size_in_bytes = sz
self.header.offset = offset
self.header.record_count = len(data_codb)
self.header.size = sz
ret += self.header
for record_hdr,record_blob in data_codb:
record_hdr.size = round_up(len(record_blob), 4)
ret += record_hdr
ret += record_blob.ljust(4, b'\x00')
return ret
case sqtt.SQTT_FILE_CHUNK_TYPE_CODE_OBJECT_LOADER_EVENTS:
assert isinstance(self.data, list)
data_lev = typing.cast(list[tuple[sqtt.struct_sqtt_code_object_loader_events_record]], self.data)
self.header.header.size_in_bytes = ctypes.sizeof(self.header)+ctypes.sizeof(sqtt.struct_sqtt_code_object_loader_events_record)*len(data_lev)
self.header.offset = offset
self.header.record_size = ctypes.sizeof(sqtt.struct_sqtt_code_object_loader_events_record)
self.header.record_count = len(data_lev)
return bytes(self.header) + b''.join(map(bytes, data_lev))
case sqtt.SQTT_FILE_CHUNK_TYPE_PSO_CORRELATION:
assert isinstance(self.data, list)
data_pso = typing.cast(list[tuple[sqtt.struct_sqtt_pso_correlation_record]], self.data)
self.header.header.size_in_bytes = ctypes.sizeof(self.header)+ctypes.sizeof(sqtt.struct_sqtt_pso_correlation_record)*len(data_pso)
self.header.offset = offset
self.header.record_size = ctypes.sizeof(sqtt.struct_sqtt_pso_correlation_record)
self.header.record_count = len(data_pso)
return bytes(self.header) + b''.join(map(bytes, data_pso))
case _: raise NotImplementedError(pretty(self.header))
@dataclass(frozen=True)
class RGP:
header: sqtt.struct_sqtt_file_header
chunks: list[RGPChunk]
@staticmethod
def from_bytes(blob: bytes) -> RGP:
file_header = sqtt.struct_sqtt_file_header.from_buffer_copy(blob)
assert file_header.magic_number == sqtt.SQTT_FILE_MAGIC_NUMBER and file_header.version_major == sqtt.SQTT_FILE_VERSION_MAJOR
i = file_header.chunk_offset
chunks = []
while i < len(blob):
assert i%4==0, hex(i)
hdr = sqtt.struct_sqtt_file_chunk_header.from_buffer_copy(blob, i)
cid = hdr.chunk_id.type
header: ctypes.Structure
match cid:
case _ if cid in {sqtt.SQTT_FILE_CHUNK_TYPE_RESERVED, sqtt.SQTT_FILE_CHUNK_TYPE_QUEUE_EVENT_TIMINGS, sqtt.SQTT_FILE_CHUNK_TYPE_CLOCK_CALIBRATION, sqtt.SQTT_FILE_CHUNK_TYPE_SPM_DB}:
chunk = None
case sqtt.SQTT_FILE_CHUNK_TYPE_CODE_OBJECT_DATABASE:
header = sqtt.struct_sqtt_file_chunk_code_object_database.from_buffer_copy(blob, i)
j = header.offset + ctypes.sizeof(header)
data: list = []
while j < header.offset + header.size:
rec_hdr: ctypes.Structure = sqtt.struct_sqtt_code_object_database_record.from_buffer_copy(blob, j)
data.append((rec_hdr, elf:=blob[j+ctypes.sizeof(rec_hdr):j+ctypes.sizeof(rec_hdr)+rec_hdr.size]))
assert elf[:4] == b'\x7fELF', repr(elf[:16])
j += ctypes.sizeof(rec_hdr)+rec_hdr.size
assert len(data) == header.record_count
chunk = RGPChunk(header, data)
case sqtt.SQTT_FILE_CHUNK_TYPE_CODE_OBJECT_LOADER_EVENTS:
header = sqtt.struct_sqtt_file_chunk_code_object_loader_events.from_buffer_copy(blob, i)
data = [sqtt.struct_sqtt_code_object_loader_events_record.from_buffer_copy(blob, header.offset+ctypes.sizeof(header)+j*header.record_size)
for j in range(header.record_count)]
chunk = RGPChunk(header, data)
case sqtt.SQTT_FILE_CHUNK_TYPE_PSO_CORRELATION:
header = sqtt.struct_sqtt_file_chunk_pso_correlation.from_buffer_copy(blob, i)
data = [sqtt.struct_sqtt_pso_correlation_record.from_buffer_copy(blob, header.offset+ctypes.sizeof(header)+j*header.record_size)
for j in range(header.record_count)]
chunk = RGPChunk(header, data)
case sqtt.SQTT_FILE_CHUNK_TYPE_SQTT_DATA:
header = sqtt.struct_sqtt_file_chunk_sqtt_data.from_buffer_copy(blob, i)
chunk = RGPChunk(header, blob[header.offset:header.offset+header.size])
case _ if cid in {sqtt.SQTT_FILE_CHUNK_TYPE_ASIC_INFO, sqtt.SQTT_FILE_CHUNK_TYPE_CPU_INFO, sqtt.SQTT_FILE_CHUNK_TYPE_API_INFO,
sqtt.SQTT_FILE_CHUNK_TYPE_SQTT_DESC}:
chunk = RGPChunk(CHUNK_CLASSES[cid].from_buffer_copy(blob, i))
case _:
chunk = None
print(f"unknown chunk id {cid}")
if chunk is not None: chunks.append(chunk)
i += hdr.size_in_bytes
assert i == len(blob), f'{i} != {len(blob)}'
return RGP(file_header, chunks)
@staticmethod
def from_profile(profile_pickled, device:str|None=None):
profile: list[ProfileEvent] = pickle.loads(profile_pickled)
def _is_base_dev(d): return all(p.isdigit() for p in d.split(":")[1:])
device_events = {x.device:x for x in profile if isinstance(x, ProfileDeviceEvent) and x.device.startswith('AMD') and _is_base_dev(x.device)}
if device is None:
if len(device_events) == 0: raise RuntimeError('No supported devices found in profile')
if len(device_events) > 1: raise RuntimeError(f"More than one supported device found, select which one to export: {', '.join(device_events.keys())}")
_, device_event = device_events.popitem()
else:
if device not in device_events: raise RuntimeError(f"Device {device} not found in profile, devices in profile: {', '.join(device_events.keys())} ")
device_event = device_events[device]
sqtt_events = [x for x in profile if isinstance(x, ProfileSQTTEvent) and x.device == device_event.device]
device_props = device_event.props
# merge events per SE
merged_sqtt_events:dict[int, ProfileSQTTEvent] = {}
for ev in sqtt_events:
if ev.se not in merged_sqtt_events: merged_sqtt_events[ev.se] = ev
else:
merged_sqtt_events[ev.se] = ProfileSQTTEvent(
device=ev.device,
kern=ev.kern,
se=ev.se,
itrace=merged_sqtt_events[ev.se].itrace or ev.itrace,
blob=merged_sqtt_events[ev.se].blob + ev.blob,
exec_tag=0,
)
sqtt_events = list(merged_sqtt_events.values())
if len(sqtt_events) == 0: raise RuntimeError(f"Device {device_event.device} doesn't contain SQTT data")
gfx_ver = device_props['gfx_target_version'] // 10000
gfx_iplvl = getattr(sqtt, f"SQTT_GFXIP_LEVEL_GFXIP_{device_props['gfx_target_version']//10000}_{(device_props['gfx_target_version']//100)%100}",
getattr(sqtt, f"SQTT_GFXIP_LEVEL_GFXIP_{device_props['gfx_target_version']//10000}", None))
sqtt_itrace_enabled = any([event.itrace for event in sqtt_events])
sqtt_itrace_masked = not all_same([event.itrace for event in sqtt_events])
sqtt_itrace_se_mask = functools.reduce(lambda a,b: a|b, [int(event.itrace) << event.se for event in sqtt_events], 0) if sqtt_itrace_masked else 0
load_events = [x for x in profile if isinstance(x, ProfileProgramEvent) and x.device == device_event.device]
loads = [(event.base, struct.unpack('<Q', hashlib.md5(event.lib).digest()[:8])*2) for event in load_events if event.base is not None and event.lib is not None]
code_objects = list(dict.fromkeys([x.lib for x in load_events if x.lib is not None]).keys())
if len(loads) == 0: raise RuntimeError('No load events in profile')
# TODO: tons of stuff hardcoded for 7900xtx
file_header = sqtt.struct_sqtt_file_header(
magic_number=sqtt.SQTT_FILE_MAGIC_NUMBER,
version_major=sqtt.SQTT_FILE_VERSION_MAJOR,
version_minor=sqtt.SQTT_FILE_VERSION_MINOR,
flags=sqtt.struct_sqtt_file_header_flags(value=1,),
chunk_offset=ctypes.sizeof(sqtt.struct_sqtt_file_header),
)
chunks = [
RGPChunk(sqtt.struct_sqtt_file_chunk_cpu_info(
header=sqtt.struct_sqtt_file_chunk_header(
chunk_id=sqtt.struct_sqtt_file_chunk_id(type=sqtt.SQTT_FILE_CHUNK_TYPE_CPU_INFO),
major_version=0, minor_version=0,
),
cpu_timestamp_freq=1000000000,
clock_speed=2994, # in mhz???
num_logical_cores=64,
num_physical_cores=32,
system_ram_size=256*1024, # in mb???
)),
RGPChunk(sqtt.struct_sqtt_file_chunk_asic_info(
header=sqtt.struct_sqtt_file_chunk_header(
chunk_id=sqtt.struct_sqtt_file_chunk_id(type=sqtt.SQTT_FILE_CHUNK_TYPE_ASIC_INFO),
major_version=0, minor_version=5,
),
flags=0,
trace_shader_core_clock=0x93f05080,
trace_memory_clock=0x4a723a40,
device_id={110000: 0x744c, 110003: 0x7480, 120001: 0x7550, 120000: 0x7550}[device_props['gfx_target_version']],
device_revision_id=0xc8,
vgprs_per_simd=1536,
sgprs_per_simd=128*16,
shader_engines=device_props['array_count'] // device_props['simd_arrays_per_engine'],
compute_unit_per_shader_engine=device_props['simd_count'] // device_props['simd_per_cu'] // (device_props['array_count'] // device_props['simd_arrays_per_engine']),
simd_per_compute_unit=device_props['simd_per_cu'],
wavefronts_per_simd=device_props['max_waves_per_simd'],
minimum_vgpr_alloc=4,
vgpr_alloc_granularity=8,
minimum_sgpr_alloc=128,
sgpr_alloc_granularity=128,
hardware_contexts=8,
gpu_type=sqtt.SQTT_GPU_TYPE_DISCRETE,
gfxip_level=gfx_iplvl,
gpu_index=0,
gds_size=0,
gds_per_shader_engine=0,
ce_ram_size=0,
ce_ram_size_graphics=0,
ce_ram_size_compute=0,
max_number_of_dedicated_cus=0,
vram_size=24 * 1024 * 1024 * 1024, # 24 GB
vram_bus_width=384, # 384-bit
l2_cache_size=6 * 1024 * 1024, # 6 MB
l1_cache_size=32 * 1024, # 32 KB per SIMD (?)
lds_size=device_props['lds_size_in_kb'] * 1024,
gpu_name=b'NAVI31',
alu_per_clock=0,
texture_per_clock=0,
prims_per_clock=6,
pixels_per_clock=0,
gpu_timestamp_frequency=100000000, # 100 MHz
max_shader_core_clock=2500000000, # 2.5 GHz (boost clock)
max_memory_clock=1250000000, # 1.25 GHz
memory_ops_per_clock=16,
memory_chip_type=sqtt.SQTT_MEMORY_TYPE_GDDR6,
lds_granularity=512,
cu_mask=((255, 255),)*6 + ((0,0),)*(32-6),
gl1_cache_size=256 * 1024, # 256 KB
instruction_cache_size=32 * 1024, # 32 KB
scalar_cache_size=16 * 1024, # 16 KB
mall_cache_size=96 * 1024 * 1024, # 96 MB
)),
RGPChunk(sqtt.struct_sqtt_file_chunk_api_info(
header=sqtt.struct_sqtt_file_chunk_header(
chunk_id=sqtt.struct_sqtt_file_chunk_id(type=sqtt.SQTT_FILE_CHUNK_TYPE_API_INFO),
major_version=0,
minor_version=2,
),
api_type=5, # HIP, not in enum
major_version=12, minor_version=0,
profiling_mode=sqtt.SQTT_PROFILING_MODE_PRESENT,
instruction_trace_mode=sqtt.SQTT_INSTRUCTION_TRACE_FULL_FRAME if sqtt_itrace_enabled else sqtt.SQTT_INSTRUCTION_TRACE_DISABLED,
instruction_trace_data=sqtt.union_sqtt_instruction_trace_data(
shader_engine_filter=sqtt.union_sqtt_instruction_trace_data_shader_engine_filter(mask=sqtt_itrace_se_mask),
),
)),
*flatten([(
RGPChunk(sqtt.struct_sqtt_file_chunk_sqtt_desc(
header=sqtt.struct_sqtt_file_chunk_header(
chunk_id=sqtt.struct_sqtt_file_chunk_id(type=sqtt.SQTT_FILE_CHUNK_TYPE_SQTT_DESC, index=sqtt_event.se),
major_version=0, minor_version=2,
),
shader_engine_index=sqtt_event.se,
sqtt_version={11: sqtt.SQTT_VERSION_3_2, 12: sqtt.SQTT_VERSION_3_3}.get(gfx_ver),
v1=sqtt.struct_sqtt_file_chunk_sqtt_desc_v1(
instrumentation_spec_version=1,
instrumentation_api_version=0,
compute_unit_index=0,
)
)),
RGPChunk(sqtt.struct_sqtt_file_chunk_sqtt_data(
header=sqtt.struct_sqtt_file_chunk_header(
chunk_id=sqtt.struct_sqtt_file_chunk_id(type=sqtt.SQTT_FILE_CHUNK_TYPE_SQTT_DATA, index=sqtt_event.se),
major_version=0, minor_version=0,
),
), sqtt_event.blob),
) for sqtt_event in sqtt_events]),
RGPChunk(sqtt.struct_sqtt_file_chunk_code_object_database(
header=sqtt.struct_sqtt_file_chunk_header(
chunk_id=sqtt.struct_sqtt_file_chunk_id(type=sqtt.SQTT_FILE_CHUNK_TYPE_CODE_OBJECT_DATABASE),
major_version=0, minor_version=0,
),
), [(sqtt.struct_sqtt_code_object_database_record(), lib) for lib in code_objects]),
RGPChunk(sqtt.struct_sqtt_file_chunk_code_object_loader_events(
header=sqtt.struct_sqtt_file_chunk_header(
chunk_id=sqtt.struct_sqtt_file_chunk_id(type=sqtt.SQTT_FILE_CHUNK_TYPE_CODE_OBJECT_LOADER_EVENTS),
major_version=1, minor_version=0,
),
), [sqtt.struct_sqtt_code_object_loader_events_record(base_address=base, code_object_hash=hash) for base,hash in loads]),
RGPChunk(sqtt.struct_sqtt_file_chunk_pso_correlation(
header=sqtt.struct_sqtt_file_chunk_header(
chunk_id=sqtt.struct_sqtt_file_chunk_id(type=sqtt.SQTT_FILE_CHUNK_TYPE_PSO_CORRELATION),
major_version=0, minor_version=0,
),
), [sqtt.struct_sqtt_pso_correlation_record(api_pso_hash=hash[0], pipeline_hash=hash) for _,hash in loads])
]
return RGP(file_header, chunks)
def to_bytes(self) -> bytes:
ret = bytearray()
ret += self.header
for chunk in self.chunks:
ret += chunk.to_bytes(len(ret))
return bytes(ret)
def print(self):
print(pretty(self.header))
for chunk in self.chunks: chunk.print()
if __name__ == '__main__':
parser = argparse.ArgumentParser(prog='rgptool', description='A tool to create (from pickled tinygrad profile), inspect and modify Radeon GPU Profiler files')
parser.add_argument('command')
parser.add_argument('input', nargs='?', default=temp("profile.pkl", append_user=True))
parser.add_argument('-d', '--device')
parser.add_argument('-o', '--output')
args = parser.parse_args()
with open(args.input, 'rb') as fd: input_bytes = fd.read()
match args.command:
case 'print':
rgp = RGP.from_bytes(input_bytes)
rgp.print()
case 'create':
rgp = RGP.from_profile(input_bytes, device=args.device)
# rgp.to_bytes() # fixup
# rgp.print()
case 'repl':
rgp = RGP.from_bytes(input_bytes)
code.interact(local=locals())
case _: raise RuntimeError(args.command)
if args.output is not None:
with open(args.output, 'wb+') as fd: fd.write(rgp.to_bytes())
print(f"Saved to {args.output}")

241
tinygrad_repo/extra/sqtt/roc.py Executable file
View File

@@ -0,0 +1,241 @@
#!/usr/bin/env python3
import ctypes, pathlib, argparse, pickle, dataclasses, threading, itertools
from decimal import Decimal
from typing import Generator
from tinygrad.helpers import temp, unwrap, DEBUG
from tinygrad.runtime.ops_amd import ProfileSQTTEvent
from tinygrad.runtime.autogen import rocprof
from tinygrad.renderer.amd.dsl import Inst
from tinygrad.helpers import ProfileEvent, ProfileRangeEvent, ProfilePointEvent
from tinygrad.device import ProfileProgramEvent
from test.amd.disasm import disasm
@dataclasses.dataclass(frozen=True)
class InstExec:
typ:str
pc:int
stall:int
dur:int
time:int
@dataclasses.dataclass(frozen=True)
class WaveSlot:
wave_id:int
cu:int
simd:int
se:int
@property
def cu_loc(self) -> str: return f"SE:{self.se} CU:{self.cu}"
@property
def wave_loc(self) -> str: return f"{self.cu_loc} SIMD:{self.simd} W:{self.wave_id}"
@dataclasses.dataclass(frozen=True)
class WaveExec(WaveSlot):
begin_time:int
end_time:int
insts:bytearray
def unpack_insts(self) -> Generator[InstExec, None, None]:
sz = ctypes.sizeof(struct:=rocprof.rocprofiler_thread_trace_decoder_inst_t)
insts_array = (struct*(len(self.insts)//sz)).from_buffer(self.insts)
for inst in insts_array:
inst_typ = rocprof.enum_rocprofiler_thread_trace_decoder_inst_category_t.get(inst.category)
yield InstExec(inst_typ, inst.pc.address, inst.stall, inst.duration, inst.time)
@dataclasses.dataclass(frozen=True)
class OccEvent(WaveSlot):
time:int
start:int
RunKey = tuple[str, int]
class _ROCParseCtx:
def __init__(self, sqtt_evs:list[ProfileSQTTEvent], disasms:dict[str, dict[int, Inst]]):
self.sqtt_evs, self.disasms = iter(sqtt_evs), {k:{k2:(disasm(v2), v2.size()) for k2,v2 in v.items()} for k,v in disasms.items()}
self.inst_execs:dict[RunKey, list[WaveExec]] = {}
self.occ_events:dict[RunKey, list[OccEvent]] = {}
def next_sqtt(self):
x = next(self.sqtt_evs, None)
self.active_run = (x.kern, x.exec_tag) if x is not None else None
self.active_se = x.se if x is not None else None
self.active_blob = (ctypes.c_ubyte * len(x.blob)).from_buffer_copy(x.blob) if x is not None else None
return self.active_blob
def on_occupancy_ev(self, ev:rocprof.rocprofiler_thread_trace_decoder_occupancy_t):
if DEBUG >= 5: print(f"OCC {ev.time=} {self.active_se=} {ev.cu=} {ev.simd=} {ev.wave_id=} {ev.start=}")
self.occ_events.setdefault(unwrap(self.active_run), []).append(OccEvent(ev.wave_id, ev.cu, ev.simd, unwrap(self.active_se), ev.time, ev.start))
def on_wave_ev(self, ev:rocprof.rocprofiler_thread_trace_decoder_wave_t):
if DEBUG >= 5: print(f"WAVE {ev.wave_id=} {self.active_se=} {ev.cu=} {ev.simd=} {ev.contexts=} {ev.begin_time=} {ev.end_time=}")
# Skip wave events without instruction timings, occupancy events give the start and duration.
if ev.instructions_size == 0: return
insts_blob = bytearray(sz:=ev.instructions_size * ctypes.sizeof(rocprof.rocprofiler_thread_trace_decoder_inst_t))
ctypes.memmove((ctypes.c_char * sz).from_buffer(insts_blob), ev.instructions_array, sz)
self.inst_execs.setdefault(unwrap(self.active_run), []).append(WaveExec(ev.wave_id, ev.cu, ev.simd, unwrap(self.active_se), ev.begin_time,
ev.end_time, insts_blob))
def decode(sqtt_evs:list[ProfileSQTTEvent], disasms:dict[str, dict[int, Inst]]) -> _ROCParseCtx:
ROCParseCtx = _ROCParseCtx(sqtt_evs, disasms)
@rocprof.rocprof_trace_decoder_se_data_callback_t
def copy_cb(buf, buf_size, _):
if (prof_info:=ROCParseCtx.next_sqtt()) is None: return 0
buf[0] = ctypes.cast(prof_info, ctypes.POINTER(ctypes.c_ubyte))
buf_size[0] = len(prof_info)
return len(prof_info)
@rocprof.rocprof_trace_decoder_trace_callback_t
def trace_cb(record_type, events_ptr, n, _):
match record_type:
case rocprof.ROCPROFILER_THREAD_TRACE_DECODER_RECORD_OCCUPANCY:
for ev in (rocprof.rocprofiler_thread_trace_decoder_occupancy_t * n).from_address(events_ptr): ROCParseCtx.on_occupancy_ev(ev)
case rocprof.ROCPROFILER_THREAD_TRACE_DECODER_RECORD_WAVE:
for ev in (rocprof.rocprofiler_thread_trace_decoder_wave_t * n).from_address(events_ptr): ROCParseCtx.on_wave_ev(ev)
case rocprof.ROCPROFILER_THREAD_TRACE_DECODER_RECORD_REALTIME:
if DEBUG >= 5:
pairs = [(ev.shader_clock, ev.realtime_clock) for ev in (rocprof.rocprofiler_thread_trace_decoder_realtime_t * n).from_address(events_ptr)]
print(f"REALTIME {pairs}")
case _:
if DEBUG >= 5: print(rocprof.enum_rocprofiler_thread_trace_decoder_record_type_t.get(record_type), events_ptr, n)
return rocprof.ROCPROFILER_THREAD_TRACE_DECODER_STATUS_SUCCESS
@rocprof.rocprof_trace_decoder_isa_callback_t
def isa_cb(instr_ptr, mem_size_ptr, size_ptr, pc, _):
instr, mem_size_ptr[0] = ROCParseCtx.disasms[unwrap(ROCParseCtx.active_run)[0]][pc.address]
# this is the number of bytes to next instruction, set to 0 for end_pgm
if instr == "s_endpgm": mem_size_ptr[0] = 0
if (max_sz:=size_ptr[0]) == 0: return rocprof.ROCPROFILER_THREAD_TRACE_DECODER_STATUS_ERROR_OUT_OF_RESOURCES
# truncate the instr if it doesn't fit
if (str_sz:=len(instr_bytes:=instr.encode()))+1 > max_sz: str_sz = max_sz
ctypes.memmove(instr_ptr, instr_bytes, str_sz)
size_ptr[0] = str_sz
return rocprof.ROCPROFILER_THREAD_TRACE_DECODER_STATUS_SUCCESS
exc:Exception|None = None
def worker():
nonlocal exc
try: rocprof.rocprof_trace_decoder_parse_data(copy_cb, trace_cb, isa_cb, None)
except AttributeError as e:
exc = RuntimeError("Failed to find rocprof-trace-decoder. Run sudo ./extra/sqtt/install_rocprof_decoder.py to install")
exc.__cause__ = e
(t:=threading.Thread(target=worker, daemon=True)).start()
t.join()
if exc is not None:
raise exc
return ROCParseCtx
def unpack_occ(viz_data, i:int, j:int, key:tuple[str, int], data:list, p:ProfileProgramEvent, target:str) -> dict:
from tinygrad.viz.serve import amd_decode, create_step, row_tuple
steps = viz_data.ctxs[i]["steps"]
if len(steps[j+1:]) > 0: return {"steps":[{k:v for k,v in s.items() if k != "data"} for s in steps[j+1:]]}
base = unwrap(p.base)
disasm:dict[int, Inst] = {addr+base:inst for addr,inst in amd_decode(unwrap(p.lib), target).items()}
rctx = decode(data, {p.tag:disasm})
cu_events:dict[str, list[ProfileEvent]] = {}
# ** inst traces
wave_insts:dict[str, dict[str, dict]] = {}
inst_units:dict[str, itertools.count] = {}
for w in rctx.inst_execs.get(key, []):
if (u:=w.wave_loc) not in inst_units: inst_units[u] = itertools.count(0)
n = next(inst_units[u])
if (events:=cu_events.get(w.cu_loc)) is None: cu_events[w.cu_loc] = events = []
events.append(ProfileRangeEvent(f"SIMD:{w.simd}", loc:=f"INST WAVE:{w.wave_id} N:{n}", Decimal(w.begin_time), Decimal(w.end_time)))
wave_insts.setdefault(w.cu_loc, {})[f"{u} N:{n}"] = {"wave":w, "disasm":disasm, "prg":p, "run_number":n, "loc":loc}
# ** occ traces (only WAVESTART/WAVEEND)
units:dict[str, itertools.count] = {}
wave_start:dict[str, int] = {}
for occ in rctx.occ_events.get(key, []):
if (u:=occ.wave_loc) not in units: units[u] = itertools.count(0)
if u in inst_units: continue
if occ.start: wave_start[u] = occ.time
else:
if (events:=cu_events.get(occ.cu_loc)) is None: cu_events[occ.cu_loc] = events = []
events.append(ProfileRangeEvent(f"SIMD:{occ.simd}", f"OCC WAVE:{occ.wave_id} N:{next(units[u])}", Decimal(wave_start.pop(u)),Decimal(occ.time)))
# ** split graph by CU
for cu in sorted(cu_events, key=row_tuple):
steps.append(create_step(f"{cu} {len(cu_events[cu])}", ("/cu-sqtt", i, len(steps)), depth=1,
data=[ProfilePointEvent(unit, "start", unit, ts=Decimal(0)) for unit in units]+cu_events[cu]))
for k in sorted(wave_insts.get(cu, []), key=row_tuple):
wd = wave_insts[cu][k]
steps.append(create_step(k.replace(cu, ""), ("/amd-sqtt-insts", i, len(steps)), loc=wd["loc"], depth=2,
data={"fxn":unpack_insts, "args":(wd,)}))
return {"steps":[{k:v for k,v in s.items() if k != "data"} for s in steps[j+1:]]}
def unpack_insts(viz_data, i:int, j:int, data:dict) -> dict:
columns = ["PC", "Instruction", "Hits", "Cycles", "Stall", "Type"]
inst_columns = ["N", "Clk", "Idle", "Dur", "Stall"]
# Idle: The total time gap between the completion of previous instruction and the beginning of the current instruction.
# The idle time can be caused by:
# * Arbiter loss
# * Source or destination register dependency
# * Instruction cache miss
# Stall: The total number of cycles the hardware pipe couldn't issue an instruction.
# Duration: Total latency in cycles, defined as "Stall time + Issue time" for gfx9 or "Stall time + Execute time" for gfx10+.
prev_instr = (w:=data["wave"]).begin_time
pc_to_inst = data["disasm"]
start_pc = None
rows:dict[int, dict] = {}
for pc, inst in pc_to_inst.items():
if start_pc is None: start_pc = pc
rows[pc] = {"pc":pc-start_pc, "inst":str(inst), "hit_count":0, "dur":0, "stall":0, "type":"", "hits":{"cols":inst_columns, "rows":[]}}
for e in w.unpack_insts():
if not (inst:=rows[e.pc]).get("type"): inst["type"] = str(e.typ).split("_")[-1]
inst["hit_count"] += 1
inst["dur"] += e.dur
inst["stall"] += e.stall
inst["hits"]["rows"].append((inst["hit_count"]-1, e.time, max(0, e.time-prev_instr), e.dur, e.stall))
prev_instr = max(prev_instr, e.time + e.dur)
summary = [{"label":"Total Cycles", "value":w.end_time-w.begin_time}, {"label":"SE", "value":w.se}, {"label":"CU", "value":w.cu},
{"label":"SIMD", "value":w.simd}, {"label":"Wave ID", "value":w.wave_id}, {"label":"Run number", "value":data["run_number"]}]
return {"rows":[tuple(v.values()) for v in rows.values()], "cols":columns, "metadata":[summary], "ref":viz_data.ref_map.get(data["prg"].name)}
def print_data(data:dict) -> None:
from tabulate import tabulate
# plaintext
if "src" in data: print(data["src"])
# table format
elif "cols" in data:
print(tabulate([r[:len(data["cols"])] for r in data["rows"]], headers=data["cols"], tablefmt="github"))
def main() -> None:
import tinygrad.viz.serve as viz
from tinygrad.uop.ops import RewriteTrace
data = viz.VizData()
parser = argparse.ArgumentParser()
parser.add_argument('--profile', type=pathlib.Path, metavar="PATH", help='Path to profile (optional file, default: latest profile)',
default=pathlib.Path(temp("profile.pkl", append_user=True)))
parser.add_argument('--kernel', type=str, default=None, metavar="NAME", help='Kernel to focus on (optional name, default: all kernels)')
parser.add_argument('-n', type=int, default=3, metavar="NUM", help='Max traces to print (optional number, default: 3 traces)')
args = parser.parse_args()
with args.profile.open("rb") as f: profile = pickle.load(f)
viz.get_profile(profile, data=data)
# List all kernels
if args.kernel is None:
for c in data.ctxs:
print(c["name"])
for s in c["steps"]: print(" "+s["name"])
return None
# Find kernel trace
trace = next((c for c in data.ctxs if c["name"] == f"SQTT {args.kernel}"), None)
if not trace: raise RuntimeError(f"no matching trace for {args.kernel}")
n = 0
for s in trace["steps"]:
if "PKTS" in s["name"]: continue
print(s["name"])
ret = viz.get_render(data, s["query"])
print_data(ret)
n += 1
if n > args.n: break
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,845 @@
#include <stdint.h>
// Original definition in pal is in c++ and clang2py can't autogen it correctly
// Most of this is copy pasted from mesa/src/amd/common/ac_rgp.{h, c}
/*
* Copyright 2020 Advanced Micro Devices, Inc.
* Copyright 2020 Valve Corporation
*
* SPDX-License-Identifier: MIT
*/
#define SQTT_FILE_MAGIC_NUMBER 0x50303042
#define SQTT_FILE_VERSION_MAJOR 1
#define SQTT_FILE_VERSION_MINOR 5
#define SQTT_GPU_NAME_MAX_SIZE 256
#define SQTT_MAX_NUM_SE 32
#define SQTT_SA_PER_SE 2
#define SQTT_ACTIVE_PIXEL_PACKER_MASK_DWORDS 4
struct sqtt_data_info {
uint32_t cur_offset;
uint32_t trace_status;
union {
uint32_t gfx9_write_counter;
uint32_t gfx10_dropped_cntr;
};
};
struct sqtt_data_se {
struct sqtt_data_info info;
void *data_ptr;
uint32_t shader_engine;
uint32_t compute_unit;
};
enum sqtt_version
{
SQTT_VERSION_NONE = 0x0,
SQTT_VERSION_2_2 = 0x5, /* GFX8 */
SQTT_VERSION_2_3 = 0x6, /* GFX9 */
SQTT_VERSION_2_4 = 0x7, /* GFX10+ */
SQTT_VERSION_3_2 = 0xb, /* GFX11+ */
SQTT_VERSION_3_3 = 0xc, /* GFX12+ */
};
enum sqtt_file_chunk_type
{
SQTT_FILE_CHUNK_TYPE_ASIC_INFO,
SQTT_FILE_CHUNK_TYPE_SQTT_DESC,
SQTT_FILE_CHUNK_TYPE_SQTT_DATA,
SQTT_FILE_CHUNK_TYPE_API_INFO,
SQTT_FILE_CHUNK_TYPE_RESERVED,
SQTT_FILE_CHUNK_TYPE_QUEUE_EVENT_TIMINGS,
SQTT_FILE_CHUNK_TYPE_CLOCK_CALIBRATION,
SQTT_FILE_CHUNK_TYPE_CPU_INFO,
SQTT_FILE_CHUNK_TYPE_SPM_DB,
SQTT_FILE_CHUNK_TYPE_CODE_OBJECT_DATABASE,
SQTT_FILE_CHUNK_TYPE_CODE_OBJECT_LOADER_EVENTS,
SQTT_FILE_CHUNK_TYPE_PSO_CORRELATION,
SQTT_FILE_CHUNK_TYPE_INSTRUMENTATION_TABLE,
SQTT_FILE_CHUNK_TYPE_COUNT
};
struct sqtt_file_chunk_id {
int32_t type : 8;
int32_t index : 8;
int32_t reserved : 16;
};
struct sqtt_file_chunk_header {
struct sqtt_file_chunk_id chunk_id;
uint16_t minor_version;
uint16_t major_version;
int32_t size_in_bytes;
int32_t padding;
};
struct sqtt_file_header_flags {
union {
struct {
uint32_t is_semaphore_queue_timing_etw : 1;
uint32_t no_queue_semaphore_timestamps : 1;
uint32_t reserved : 30;
};
uint32_t value;
};
};
struct sqtt_file_header {
uint32_t magic_number;
uint32_t version_major;
uint32_t version_minor;
struct sqtt_file_header_flags flags;
int32_t chunk_offset;
int32_t second;
int32_t minute;
int32_t hour;
int32_t day_in_month;
int32_t month;
int32_t year;
int32_t day_in_week;
int32_t day_in_year;
int32_t is_daylight_savings;
};
struct sqtt_file_chunk_cpu_info {
struct sqtt_file_chunk_header header;
uint32_t vendor_id[4];
uint32_t processor_brand[12];
uint32_t reserved[2];
uint64_t cpu_timestamp_freq;
uint32_t clock_speed;
uint32_t num_logical_cores;
uint32_t num_physical_cores;
uint32_t system_ram_size;
};
enum sqtt_file_chunk_asic_info_flags
{
SQTT_FILE_CHUNK_ASIC_INFO_FLAG_SC_PACKER_NUMBERING = (1 << 0),
SQTT_FILE_CHUNK_ASIC_INFO_FLAG_PS1_EVENT_TOKENS_ENABLED = (1 << 1)
};
enum sqtt_gpu_type
{
SQTT_GPU_TYPE_UNKNOWN = 0x0,
SQTT_GPU_TYPE_INTEGRATED = 0x1,
SQTT_GPU_TYPE_DISCRETE = 0x2,
SQTT_GPU_TYPE_VIRTUAL = 0x3
};
enum sqtt_gfxip_level
{
SQTT_GFXIP_LEVEL_NONE = 0x0,
SQTT_GFXIP_LEVEL_GFXIP_6 = 0x1,
SQTT_GFXIP_LEVEL_GFXIP_7 = 0x2,
SQTT_GFXIP_LEVEL_GFXIP_8 = 0x3,
SQTT_GFXIP_LEVEL_GFXIP_8_1 = 0x4,
SQTT_GFXIP_LEVEL_GFXIP_9 = 0x5,
SQTT_GFXIP_LEVEL_GFXIP_10_1 = 0x7,
SQTT_GFXIP_LEVEL_GFXIP_10_3 = 0x9,
SQTT_GFXIP_LEVEL_GFXIP_11_0 = 0xc,
SQTT_GFXIP_LEVEL_GFXIP_11_5 = 0xd,
SQTT_GFXIP_LEVEL_GFXIP_12 = 0x10,
};
enum sqtt_memory_type
{
SQTT_MEMORY_TYPE_UNKNOWN = 0x0,
SQTT_MEMORY_TYPE_DDR = 0x1,
SQTT_MEMORY_TYPE_DDR2 = 0x2,
SQTT_MEMORY_TYPE_DDR3 = 0x3,
SQTT_MEMORY_TYPE_DDR4 = 0x4,
SQTT_MEMORY_TYPE_DDR5 = 0x5,
SQTT_MEMORY_TYPE_GDDR3 = 0x10,
SQTT_MEMORY_TYPE_GDDR4 = 0x11,
SQTT_MEMORY_TYPE_GDDR5 = 0x12,
SQTT_MEMORY_TYPE_GDDR6 = 0x13,
SQTT_MEMORY_TYPE_HBM = 0x20,
SQTT_MEMORY_TYPE_HBM2 = 0x21,
SQTT_MEMORY_TYPE_HBM3 = 0x22,
SQTT_MEMORY_TYPE_LPDDR4 = 0x30,
SQTT_MEMORY_TYPE_LPDDR5 = 0x31,
};
struct sqtt_file_chunk_asic_info {
struct sqtt_file_chunk_header header;
uint64_t flags;
uint64_t trace_shader_core_clock;
uint64_t trace_memory_clock;
int32_t device_id;
int32_t device_revision_id;
int32_t vgprs_per_simd;
int32_t sgprs_per_simd;
int32_t shader_engines;
int32_t compute_unit_per_shader_engine;
int32_t simd_per_compute_unit;
int32_t wavefronts_per_simd;
int32_t minimum_vgpr_alloc;
int32_t vgpr_alloc_granularity;
int32_t minimum_sgpr_alloc;
int32_t sgpr_alloc_granularity;
int32_t hardware_contexts;
enum sqtt_gpu_type gpu_type;
enum sqtt_gfxip_level gfxip_level;
int32_t gpu_index;
int32_t gds_size;
int32_t gds_per_shader_engine;
int32_t ce_ram_size;
int32_t ce_ram_size_graphics;
int32_t ce_ram_size_compute;
int32_t max_number_of_dedicated_cus;
int64_t vram_size;
int32_t vram_bus_width;
int32_t l2_cache_size;
int32_t l1_cache_size;
int32_t lds_size;
char gpu_name[SQTT_GPU_NAME_MAX_SIZE];
float alu_per_clock;
float texture_per_clock;
float prims_per_clock;
float pixels_per_clock;
uint64_t gpu_timestamp_frequency;
uint64_t max_shader_core_clock;
uint64_t max_memory_clock;
uint32_t memory_ops_per_clock;
enum sqtt_memory_type memory_chip_type;
uint32_t lds_granularity;
uint16_t cu_mask[SQTT_MAX_NUM_SE][SQTT_SA_PER_SE];
char reserved1[128];
uint32_t active_pixel_packer_mask[SQTT_ACTIVE_PIXEL_PACKER_MASK_DWORDS];
char reserved2[16];
uint32_t gl1_cache_size;
uint32_t instruction_cache_size;
uint32_t scalar_cache_size;
uint32_t mall_cache_size;
char padding[4];
};
enum sqtt_api_type
{
SQTT_API_TYPE_DIRECTX_12,
SQTT_API_TYPE_VULKAN,
SQTT_API_TYPE_GENERIC,
SQTT_API_TYPE_OPENCL
};
enum sqtt_instruction_trace_mode
{
SQTT_INSTRUCTION_TRACE_DISABLED = 0x0,
SQTT_INSTRUCTION_TRACE_FULL_FRAME = 0x1,
SQTT_INSTRUCTION_TRACE_API_PSO = 0x2,
};
enum sqtt_profiling_mode
{
SQTT_PROFILING_MODE_PRESENT = 0x0,
SQTT_PROFILING_MODE_USER_MARKERS = 0x1,
SQTT_PROFILING_MODE_INDEX = 0x2,
SQTT_PROFILING_MODE_TAG = 0x3,
};
union sqtt_profiling_mode_data {
struct {
char start[256];
char end[256];
} user_marker_profiling_data;
struct {
uint32_t start;
uint32_t end;
} index_profiling_data;
struct {
uint32_t begin_hi;
uint32_t begin_lo;
uint32_t end_hi;
uint32_t end_lo;
} tag_profiling_data;
};
union sqtt_instruction_trace_data {
struct {
uint64_t api_pso_filter;
} api_pso_data;
struct {
uint32_t mask;
} shader_engine_filter;
};
struct sqtt_file_chunk_api_info {
struct sqtt_file_chunk_header header;
enum sqtt_api_type api_type;
uint16_t major_version;
uint16_t minor_version;
enum sqtt_profiling_mode profiling_mode;
uint32_t reserved;
union sqtt_profiling_mode_data profiling_mode_data;
enum sqtt_instruction_trace_mode instruction_trace_mode;
uint32_t reserved2;
union sqtt_instruction_trace_data instruction_trace_data;
};
struct sqtt_code_object_database_record {
uint32_t size;
};
struct sqtt_file_chunk_code_object_database {
struct sqtt_file_chunk_header header;
uint32_t offset;
uint32_t flags;
uint32_t size;
uint32_t record_count;
};
struct sqtt_code_object_loader_events_record {
uint32_t loader_event_type;
uint32_t reserved;
uint64_t base_address;
uint64_t code_object_hash[2];
uint64_t time_stamp;
};
struct sqtt_file_chunk_code_object_loader_events {
struct sqtt_file_chunk_header header;
uint32_t offset;
uint32_t flags;
uint32_t record_size;
uint32_t record_count;
};
struct sqtt_pso_correlation_record {
uint64_t api_pso_hash;
uint64_t pipeline_hash[2];
char api_level_obj_name[64];
};
struct sqtt_file_chunk_pso_correlation {
struct sqtt_file_chunk_header header;
uint32_t offset;
uint32_t flags;
uint32_t record_size;
uint32_t record_count;
};
struct sqtt_file_chunk_sqtt_desc {
struct sqtt_file_chunk_header header;
int32_t shader_engine_index;
enum sqtt_version sqtt_version;
union {
struct {
int32_t instrumentation_version;
} v0;
struct {
int16_t instrumentation_spec_version;
int16_t instrumentation_api_version;
int32_t compute_unit_index;
} v1;
};
};
struct sqtt_file_chunk_sqtt_data {
struct sqtt_file_chunk_header header;
int32_t offset; /* in bytes */
int32_t size; /* in bytes */
};
struct sqtt_file_chunk_queue_event_timings {
struct sqtt_file_chunk_header header;
uint32_t queue_info_table_record_count;
uint32_t queue_info_table_size;
uint32_t queue_event_table_record_count;
uint32_t queue_event_table_size;
};
enum sqtt_queue_type {
SQTT_QUEUE_TYPE_UNKNOWN = 0x0,
SQTT_QUEUE_TYPE_UNIVERSAL = 0x1,
SQTT_QUEUE_TYPE_COMPUTE = 0x2,
SQTT_QUEUE_TYPE_DMA = 0x3,
};
enum sqtt_engine_type {
SQTT_ENGINE_TYPE_UNKNOWN = 0x0,
SQTT_ENGINE_TYPE_UNIVERSAL = 0x1,
SQTT_ENGINE_TYPE_COMPUTE = 0x2,
SQTT_ENGINE_TYPE_EXCLUSIVE_COMPUTE = 0x3,
SQTT_ENGINE_TYPE_DMA = 0x4,
SQTT_ENGINE_TYPE_HIGH_PRIORITY_UNIVERSAL = 0x7,
SQTT_ENGINE_TYPE_HIGH_PRIORITY_GRAPHICS = 0x8,
};
struct sqtt_queue_hardware_info {
union {
struct {
int32_t queue_type : 8;
int32_t engine_type : 8;
uint32_t reserved : 16;
};
uint32_t value;
};
};
struct sqtt_queue_info_record {
uint64_t queue_id;
uint64_t queue_context;
struct sqtt_queue_hardware_info hardware_info;
uint32_t reserved;
};
enum sqtt_queue_event_type {
SQTT_QUEUE_TIMING_EVENT_CMDBUF_SUBMIT,
SQTT_QUEUE_TIMING_EVENT_SIGNAL_SEMAPHORE,
SQTT_QUEUE_TIMING_EVENT_WAIT_SEMAPHORE,
SQTT_QUEUE_TIMING_EVENT_PRESENT
};
struct sqtt_queue_event_record {
enum sqtt_queue_event_type event_type;
uint32_t sqtt_cb_id;
uint64_t frame_index;
uint32_t queue_info_index;
uint32_t submit_sub_index;
uint64_t api_id;
uint64_t cpu_timestamp;
uint64_t gpu_timestamps[2];
};
struct sqtt_file_chunk_clock_calibration {
struct sqtt_file_chunk_header header;
uint64_t cpu_timestamp;
uint64_t gpu_timestamp;
uint64_t reserved;
};
enum elf_gfxip_level
{
EF_AMDGPU_MACH_AMDGCN_GFX801 = 0x028,
EF_AMDGPU_MACH_AMDGCN_GFX900 = 0x02c,
EF_AMDGPU_MACH_AMDGCN_GFX1010 = 0x033,
EF_AMDGPU_MACH_AMDGCN_GFX1030 = 0x036,
EF_AMDGPU_MACH_AMDGCN_GFX1100 = 0x041,
EF_AMDGPU_MACH_AMDGCN_GFX1150 = 0x043,
EF_AMDGPU_MACH_AMDGCN_GFX1200 = 0x04e,
};
struct sqtt_file_chunk_spm_db {
struct sqtt_file_chunk_header header;
uint32_t flags;
uint32_t preamble_size;
uint32_t num_timestamps;
uint32_t num_spm_counter_info;
uint32_t spm_counter_info_size;
uint32_t sample_interval;
};
/**
* Identifiers for RGP SQ thread-tracing markers (Table 1)
*/
enum rgp_sqtt_marker_identifier
{
RGP_SQTT_MARKER_IDENTIFIER_EVENT = 0x0,
RGP_SQTT_MARKER_IDENTIFIER_CB_START = 0x1,
RGP_SQTT_MARKER_IDENTIFIER_CB_END = 0x2,
RGP_SQTT_MARKER_IDENTIFIER_BARRIER_START = 0x3,
RGP_SQTT_MARKER_IDENTIFIER_BARRIER_END = 0x4,
RGP_SQTT_MARKER_IDENTIFIER_USER_EVENT = 0x5,
RGP_SQTT_MARKER_IDENTIFIER_GENERAL_API = 0x6,
RGP_SQTT_MARKER_IDENTIFIER_SYNC = 0x7,
RGP_SQTT_MARKER_IDENTIFIER_PRESENT = 0x8,
RGP_SQTT_MARKER_IDENTIFIER_LAYOUT_TRANSITION = 0x9,
RGP_SQTT_MARKER_IDENTIFIER_RENDER_PASS = 0xA,
RGP_SQTT_MARKER_IDENTIFIER_RESERVED2 = 0xB,
RGP_SQTT_MARKER_IDENTIFIER_BIND_PIPELINE = 0xC,
RGP_SQTT_MARKER_IDENTIFIER_RESERVED4 = 0xD,
RGP_SQTT_MARKER_IDENTIFIER_RESERVED5 = 0xE,
RGP_SQTT_MARKER_IDENTIFIER_RESERVED6 = 0xF
};
/**
* Command buffer IDs used in RGP SQ thread-tracing markers (only 20 bits).
*/
union rgp_sqtt_marker_cb_id {
struct {
uint32_t per_frame : 1; /* Must be 1, frame-based command buffer ID. */
uint32_t frame_index : 7;
uint32_t cb_index : 12; /* Command buffer index within the frame. */
uint32_t reserved : 12;
} per_frame_cb_id;
struct {
uint32_t per_frame : 1; /* Must be 0, global command buffer ID. */
uint32_t cb_index : 19; /* Global command buffer index. */
uint32_t reserved : 12;
} global_cb_id;
uint32_t all;
};
/**
* RGP SQ thread-tracing marker for the start of a command buffer. (Table 2)
*/
struct rgp_sqtt_marker_cb_start {
union {
struct {
uint32_t identifier : 4;
uint32_t ext_dwords : 3;
uint32_t cb_id : 20;
uint32_t queue : 5;
};
uint32_t dword01;
};
union {
uint32_t device_id_low;
uint32_t dword02;
};
union {
uint32_t device_id_high;
uint32_t dword03;
};
union {
uint32_t queue_flags;
uint32_t dword04;
};
};
/**
*
* RGP SQ thread-tracing marker for the end of a command buffer. (Table 3)
*/
struct rgp_sqtt_marker_cb_end {
union {
struct {
uint32_t identifier : 4;
uint32_t ext_dwords : 3;
uint32_t cb_id : 20;
uint32_t reserved : 5;
};
uint32_t dword01;
};
union {
uint32_t device_id_low;
uint32_t dword02;
};
union {
uint32_t device_id_high;
uint32_t dword03;
};
};
/**
* API types used in RGP SQ thread-tracing markers for the "General API"
* packet.
*/
enum rgp_sqtt_marker_general_api_type
{
ApiCmdBindPipeline = 0,
ApiCmdBindDescriptorSets = 1,
ApiCmdBindIndexBuffer = 2,
ApiCmdBindVertexBuffers = 3,
ApiCmdDraw = 4,
ApiCmdDrawIndexed = 5,
ApiCmdDrawIndirect = 6,
ApiCmdDrawIndexedIndirect = 7,
ApiCmdDrawIndirectCountAMD = 8,
ApiCmdDrawIndexedIndirectCountAMD = 9,
ApiCmdDispatch = 10,
ApiCmdDispatchIndirect = 11,
ApiCmdCopyBuffer = 12,
ApiCmdCopyImage = 13,
ApiCmdBlitImage = 14,
ApiCmdCopyBufferToImage = 15,
ApiCmdCopyImageToBuffer = 16,
ApiCmdUpdateBuffer = 17,
ApiCmdFillBuffer = 18,
ApiCmdClearColorImage = 19,
ApiCmdClearDepthStencilImage = 20,
ApiCmdClearAttachments = 21,
ApiCmdResolveImage = 22,
ApiCmdWaitEvents = 23,
ApiCmdPipelineBarrier = 24,
ApiCmdBeginQuery = 25,
ApiCmdEndQuery = 26,
ApiCmdResetQueryPool = 27,
ApiCmdWriteTimestamp = 28,
ApiCmdCopyQueryPoolResults = 29,
ApiCmdPushConstants = 30,
ApiCmdBeginRenderPass = 31,
ApiCmdNextSubpass = 32,
ApiCmdEndRenderPass = 33,
ApiCmdExecuteCommands = 34,
ApiCmdSetViewport = 35,
ApiCmdSetScissor = 36,
ApiCmdSetLineWidth = 37,
ApiCmdSetDepthBias = 38,
ApiCmdSetBlendConstants = 39,
ApiCmdSetDepthBounds = 40,
ApiCmdSetStencilCompareMask = 41,
ApiCmdSetStencilWriteMask = 42,
ApiCmdSetStencilReference = 43,
ApiCmdDrawIndirectCount = 44,
ApiCmdDrawIndexedIndirectCount = 45,
/* gap */
ApiCmdDrawMeshTasksEXT = 47,
ApiCmdDrawMeshTasksIndirectCountEXT = 48,
ApiCmdDrawMeshTasksIndirectEXT = 49,
ApiRayTracingSeparateCompiled = 0x800000,
ApiInvalid = 0xffffffff
};
/**
* RGP SQ thread-tracing marker for a "General API" instrumentation packet.
*/
struct rgp_sqtt_marker_general_api {
union {
struct {
uint32_t identifier : 4;
uint32_t ext_dwords : 3;
uint32_t api_type : 20;
uint32_t is_end : 1;
uint32_t reserved : 4;
};
uint32_t dword01;
};
};
/**
* API types used in RGP SQ thread-tracing markers (Table 16).
*/
enum rgp_sqtt_marker_event_type
{
EventCmdDraw = 0,
EventCmdDrawIndexed = 1,
EventCmdDrawIndirect = 2,
EventCmdDrawIndexedIndirect = 3,
EventCmdDrawIndirectCountAMD = 4,
EventCmdDrawIndexedIndirectCountAMD = 5,
EventCmdDispatch = 6,
EventCmdDispatchIndirect = 7,
EventCmdCopyBuffer = 8,
EventCmdCopyImage = 9,
EventCmdBlitImage = 10,
EventCmdCopyBufferToImage = 11,
EventCmdCopyImageToBuffer = 12,
EventCmdUpdateBuffer = 13,
EventCmdFillBuffer = 14,
EventCmdClearColorImage = 15,
EventCmdClearDepthStencilImage = 16,
EventCmdClearAttachments = 17,
EventCmdResolveImage = 18,
EventCmdWaitEvents = 19,
EventCmdPipelineBarrier = 20,
EventCmdResetQueryPool = 21,
EventCmdCopyQueryPoolResults = 22,
EventRenderPassColorClear = 23,
EventRenderPassDepthStencilClear = 24,
EventRenderPassResolve = 25,
EventInternalUnknown = 26,
EventCmdDrawIndirectCount = 27,
EventCmdDrawIndexedIndirectCount = 28,
/* gap */
EventCmdTraceRaysKHR = 30,
EventCmdTraceRaysIndirectKHR = 31,
EventCmdBuildAccelerationStructuresKHR = 32,
EventCmdBuildAccelerationStructuresIndirectKHR = 33,
EventCmdCopyAccelerationStructureKHR = 34,
EventCmdCopyAccelerationStructureToMemoryKHR = 35,
EventCmdCopyMemoryToAccelerationStructureKHR = 36,
/* gap */
EventCmdDrawMeshTasksEXT = 41,
EventCmdDrawMeshTasksIndirectCountEXT = 42,
EventCmdDrawMeshTasksIndirectEXT = 43,
EventUnknown = 0x7fff,
EventInvalid = 0xffffffff
};
/**
* "Event (Per-draw/dispatch)" RGP SQ thread-tracing marker. (Table 4)
*/
struct rgp_sqtt_marker_event {
union {
struct {
uint32_t identifier : 4;
uint32_t ext_dwords : 3;
uint32_t api_type : 24;
uint32_t has_thread_dims : 1;
};
uint32_t dword01;
};
union {
struct {
uint32_t cb_id : 20;
uint32_t vertex_offset_reg_idx : 4;
uint32_t instance_offset_reg_idx : 4;
uint32_t draw_index_reg_idx : 4;
};
uint32_t dword02;
};
union {
uint32_t cmd_id;
uint32_t dword03;
};
};
/**
* Per-dispatch specific marker where workgroup dims are included.
*/
struct rgp_sqtt_marker_event_with_dims {
struct rgp_sqtt_marker_event event;
uint32_t thread_x;
uint32_t thread_y;
uint32_t thread_z;
};
/**
* "Barrier Start" RGP SQTT instrumentation marker (Table 5)
*/
struct rgp_sqtt_marker_barrier_start {
union {
struct {
uint32_t identifier : 4;
uint32_t ext_dwords : 3;
uint32_t cb_id : 20;
uint32_t reserved : 5;
};
uint32_t dword01;
};
union {
struct {
uint32_t driver_reason : 31;
uint32_t internal : 1;
};
uint32_t dword02;
};
};
/**
* "Barrier End" RGP SQTT instrumentation marker (Table 6)
*/
struct rgp_sqtt_marker_barrier_end {
union {
struct {
uint32_t identifier : 4;
uint32_t ext_dwords : 3;
uint32_t cb_id : 20;
uint32_t wait_on_eop_ts : 1;
uint32_t vs_partial_flush : 1;
uint32_t ps_partial_flush : 1;
uint32_t cs_partial_flush : 1;
uint32_t pfp_sync_me : 1;
};
uint32_t dword01;
};
union {
struct {
uint32_t sync_cp_dma : 1;
uint32_t inval_tcp : 1;
uint32_t inval_sqI : 1;
uint32_t inval_sqK : 1;
uint32_t flush_tcc : 1;
uint32_t inval_tcc : 1;
uint32_t flush_cb : 1;
uint32_t inval_cb : 1;
uint32_t flush_db : 1;
uint32_t inval_db : 1;
uint32_t num_layout_transitions : 16;
uint32_t inval_gl1 : 1;
uint32_t wait_on_ts : 1;
uint32_t eop_ts_bottom_of_pipe : 1;
uint32_t eos_ts_ps_done : 1;
uint32_t eos_ts_cs_done : 1;
uint32_t reserved : 1;
};
uint32_t dword02;
};
};
/**
* "Layout Transition" RGP SQTT instrumentation marker (Table 7)
*/
struct rgp_sqtt_marker_layout_transition {
union {
struct {
uint32_t identifier : 4;
uint32_t ext_dwords : 3;
uint32_t depth_stencil_expand : 1;
uint32_t htile_hiz_range_expand : 1;
uint32_t depth_stencil_resummarize : 1;
uint32_t dcc_decompress : 1;
uint32_t fmask_decompress : 1;
uint32_t fast_clear_eliminate : 1;
uint32_t fmask_color_expand : 1;
uint32_t init_mask_ram : 1;
uint32_t reserved1 : 17;
};
uint32_t dword01;
};
union {
struct {
uint32_t reserved2 : 32;
};
uint32_t dword02;
};
};
/**
* "User Event" RGP SQTT instrumentation marker (Table 8)
*/
struct rgp_sqtt_marker_user_event {
union {
struct {
uint32_t identifier : 4;
uint32_t reserved0 : 8;
uint32_t data_type : 8;
uint32_t reserved1 : 12;
};
uint32_t dword01;
};
};
struct rgp_sqtt_marker_user_event_with_length {
struct rgp_sqtt_marker_user_event user_event;
uint32_t length;
};
enum rgp_sqtt_marker_user_event_type
{
UserEventTrigger = 0,
UserEventPop,
UserEventPush,
UserEventObjectName,
};
/**
* "Pipeline bind" RGP SQTT instrumentation marker (Table 12)
*/
struct rgp_sqtt_marker_pipeline_bind {
union {
struct {
uint32_t identifier : 4;
uint32_t ext_dwords : 3;
uint32_t bind_point : 1;
uint32_t cb_id : 20;
uint32_t reserved : 4;
};
uint32_t dword01;
};
union {
uint32_t api_pso_hash[2];
struct {
uint32_t dword02;
uint32_t dword03;
};
};
};