Files
TheRock/rocm-systems/projects/rocprof-trace-decoder/python/rocprof_trace_decoder/bindings.py
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

836 lines
26 KiB
Python

from __future__ import annotations
import ctypes
import importlib
import importlib.util
import os
from pathlib import Path
from typing import Callable, Iterable, Protocol, Union
from ._version import __version__ as _PACKAGE_VERSION
from .records import (
DecoderStatus,
Dispatch,
Event,
EventPayload,
Instruction,
Occupancy,
OtherSimdInstruction,
Pc,
PerfEvent,
Realtime,
RecordType,
ShaderData,
TraceRecords,
Wave,
WaveState,
)
BytesLike = Union[bytes, bytearray, memoryview]
class DecoderError(RuntimeError):
def __init__(self, status: int, message: str | None = None):
self.status = int(status)
super().__init__(message or f"rocprof-trace-decoder returned status {status}")
class IsaProvider(Protocol):
def isa_for_pc(self, pc: Pc) -> tuple[str, int] | None:
"""Return (instruction text, instruction byte size) for *pc*."""
class _PcInfo(ctypes.Structure):
_fields_ = [
("address", ctypes.c_uint64),
("code_object_id", ctypes.c_uint64),
]
class _PerfEvent(ctypes.Structure):
_fields_ = [
("time", ctypes.c_int64),
("events0", ctypes.c_uint16),
("events1", ctypes.c_uint16),
("events2", ctypes.c_uint16),
("events3", ctypes.c_uint16),
("CU", ctypes.c_uint8),
("bank", ctypes.c_uint8),
]
class _Occupancy(ctypes.Structure):
_fields_ = [
("pc", _PcInfo),
("time", ctypes.c_uint64),
("reserved", ctypes.c_uint8),
("cu", ctypes.c_uint8),
("simd", ctypes.c_uint8),
("wave_id", ctypes.c_uint8),
("_bits", ctypes.c_uint32),
]
class _WaveState(ctypes.Structure):
_fields_ = [("type", ctypes.c_int32), ("duration", ctypes.c_int32)]
class _Instruction(ctypes.Structure):
_fields_ = [
("_cat_stall", ctypes.c_uint32),
("duration", ctypes.c_int32),
("time", ctypes.c_int64),
("pc", _PcInfo),
]
class _Wave(ctypes.Structure):
_fields_ = [
("cu", ctypes.c_uint8),
("simd", ctypes.c_uint8),
("wave_id", ctypes.c_uint8),
("contexts", ctypes.c_uint8),
("dispatcher", ctypes.c_uint8),
("workgroup_id", ctypes.c_uint8),
("cluster_id", ctypes.c_uint8),
("reserved", ctypes.c_uint8),
("size", ctypes.c_uint64),
("begin_time", ctypes.c_int64),
("end_time", ctypes.c_int64),
("timeline_size", ctypes.c_uint64),
("instructions_size", ctypes.c_uint64),
("timeline_array", ctypes.POINTER(_WaveState)),
("instructions_array", ctypes.POINTER(_Instruction)),
]
class _Realtime(ctypes.Structure):
_fields_ = [
("shader_clock", ctypes.c_int64),
("realtime_clock", ctypes.c_uint64),
("reserved", ctypes.c_uint64),
]
class _ShaderData(ctypes.Structure):
_fields_ = [
("time", ctypes.c_int64),
("value", ctypes.c_uint64),
("cu", ctypes.c_uint8),
("simd", ctypes.c_uint8),
("wave_id", ctypes.c_uint8),
("flags", ctypes.c_uint8),
("reserved", ctypes.c_uint32),
]
class _OtherSimdInstruction(ctypes.Structure):
_fields_ = [
("size", ctypes.c_uint64),
("time", ctypes.c_int64),
("cycles", ctypes.c_uint16),
("wgp", ctypes.c_uint8),
("category", ctypes.c_uint8),
]
class _ClusterBarrier(ctypes.Structure):
_fields_ = [("cluster_id", ctypes.c_int32), ("barrier_id", ctypes.c_int32)]
class _EventPayload(ctypes.Union):
_fields_ = [
("raw", ctypes.c_uint64),
("code_object_id", ctypes.c_uint64),
("cluster_barrier", _ClusterBarrier),
]
class _Event(ctypes.Structure):
_fields_ = [
("size", ctypes.c_uint64),
("time", ctypes.c_int64),
("type", ctypes.c_int),
("me_id", ctypes.c_uint8),
("pipe_id", ctypes.c_uint8),
("flags", ctypes.c_uint16),
("payload", _EventPayload),
("byte_offset", ctypes.c_uint64),
]
class _Dispatch(ctypes.Structure):
_fields_ = [
("size", ctypes.c_uint64),
("time", ctypes.c_int64),
("me_id", ctypes.c_uint8),
("pipe_id", ctypes.c_uint8),
("user_sgprs", ctypes.c_uint16),
("flags", ctypes.c_int),
("vgprs", ctypes.c_uint32),
("sgprs", ctypes.c_uint32),
("lds_size", ctypes.c_uint32),
("thread_dim_x", ctypes.c_uint32),
("thread_dim_y", ctypes.c_uint32),
("thread_dim_z", ctypes.c_uint32),
("dispatch_pkt_addr", ctypes.c_uint64),
("byte_offset", ctypes.c_uint64),
("entry_point", _PcInfo),
]
class _Handle(ctypes.Structure):
_fields_ = [("handle", ctypes.c_uint64)]
_TRACE_CB = ctypes.CFUNCTYPE(
ctypes.c_int,
ctypes.c_int,
ctypes.c_void_p,
ctypes.c_uint64,
ctypes.c_void_p,
)
_ISA_CB = ctypes.CFUNCTYPE(
ctypes.c_int,
ctypes.c_void_p,
ctypes.POINTER(ctypes.c_uint64),
ctypes.POINTER(ctypes.c_uint64),
_PcInfo,
ctypes.c_void_p,
)
_SE_DATA_CB = ctypes.CFUNCTYPE(
ctypes.c_uint64,
ctypes.POINTER(ctypes.POINTER(ctypes.c_uint8)),
ctypes.POINTER(ctypes.c_uint64),
ctypes.c_void_p,
)
def _pc(c_pc: _PcInfo) -> Pc:
return Pc(address=int(c_pc.address), code_object_id=int(c_pc.code_object_id))
def _find_library(explicit: str | os.PathLike[str] | None = None) -> str:
if explicit:
return str(explicit)
env = os.environ.get("ROCPROF_TRACE_DECODER_LIB")
if env:
return env
names = _library_names()
for root in _library_roots():
for name in names:
candidate = root / name
if candidate.exists():
return str(candidate)
for candidate in sorted(root.glob("librocprof-trace-decoder.so.*"), reverse=True):
if candidate.is_file():
return str(candidate)
# Let the platform loader try its normal search path.
return "librocprof-trace-decoder.so"
def _library_names() -> list[str]:
names = ["librocprof-trace-decoder.so", "librocprof-trace-decoder.dylib"]
version_parts = _PACKAGE_VERSION.split(".")
if len(version_parts) >= 2:
names.append(f"librocprof-trace-decoder.so.{version_parts[0]}.{version_parts[1]}")
return names
def _library_roots() -> list[Path]:
roots: list[Path] = []
seen: set[Path] = set()
def add(path: Path) -> None:
if path not in seen:
roots.append(path)
seen.add(path)
for raw in os.environ.get("LD_LIBRARY_PATH", "").split(os.pathsep):
if raw:
add(Path(raw).expanduser())
for var in ("ROCM_HOME", "ROCM_PATH"):
value = os.environ.get(var)
if value:
add(Path(value).expanduser() / "lib")
for root in _therock_package_roots():
add(root / "lib")
add(Path("/opt/rocm/lib"))
return roots
def _therock_package_roots() -> list[Path]:
roots: list[Path] = []
seen: set[Path] = set()
def add_package_root(package_name: str) -> None:
spec = importlib.util.find_spec(package_name)
if spec is None or spec.origin is None:
return
root = Path(spec.origin).resolve().parent
if root not in seen:
roots.append(root)
seen.add(root)
for dist_info_name in (
"rocm_sdk_core._dist_info",
"rocm_profiler._dist_info",
"rocm_sdk._dist_info",
):
try:
dist_info = importlib.import_module(dist_info_name)
except Exception:
continue
for logical_name in ("core", "devel", "profiler"):
entry = getattr(dist_info, "ALL_PACKAGES", {}).get(logical_name)
if entry is None or getattr(entry, "is_target_specific", False):
continue
try:
add_package_root(entry.get_py_package_name())
except Exception:
continue
for package_name in ("_rocm_sdk_core", "_rocm_sdk_devel", "_rocm_profiler"):
add_package_root(package_name)
return roots
class Decoder:
def __init__(self, lib_path: str | os.PathLike[str] | None = None):
self.lib_path = _find_library(lib_path)
self._lib = ctypes.CDLL(self.lib_path)
self._configure_library()
self._handle = _Handle()
self._closed = False
self._isa_cb: _ISA_CB | None = None
self._trace_cb: _TRACE_CB | None = None
status = self._lib.rocprof_trace_decoder_create_handle(ctypes.byref(self._handle))
self._check(status)
def __enter__(self) -> "Decoder":
return self
def __exit__(self, *_exc: object) -> None:
self.close()
def close(self) -> None:
if self._closed:
return
status = self._lib.rocprof_trace_decoder_destroy_handle(self._handle)
self._check(status)
self._closed = True
self._isa_cb = None
self._trace_cb = None
def _ensure_open(self) -> None:
if self._closed:
raise DecoderError(DecoderStatus.ERROR_INVALID_ARGUMENT, "Decoder handle is closed")
def _configure_library(self) -> None:
lib = self._lib
lib.rocprof_trace_decoder_create_handle.argtypes = [ctypes.POINTER(_Handle)]
lib.rocprof_trace_decoder_create_handle.restype = ctypes.c_int
lib.rocprof_trace_decoder_destroy_handle.argtypes = [_Handle]
lib.rocprof_trace_decoder_destroy_handle.restype = ctypes.c_int
lib.rocprof_trace_decoder_codeobj_load.argtypes = [
_Handle,
ctypes.c_uint64,
ctypes.c_uint64,
ctypes.c_uint64,
ctypes.c_void_p,
ctypes.c_uint64,
]
lib.rocprof_trace_decoder_codeobj_load.restype = ctypes.c_int
lib.rocprof_trace_decoder_codeobj_unload.argtypes = [_Handle, ctypes.c_uint64]
lib.rocprof_trace_decoder_codeobj_unload.restype = ctypes.c_int
lib.rocprof_trace_decoder_set_isa_callback.argtypes = [_Handle, _ISA_CB, ctypes.c_void_p]
lib.rocprof_trace_decoder_set_isa_callback.restype = ctypes.c_int
lib.rocprof_trace_decoder_set_se_data_callback.argtypes = [
_Handle,
_SE_DATA_CB,
ctypes.c_void_p,
]
lib.rocprof_trace_decoder_set_se_data_callback.restype = ctypes.c_int
lib.rocprof_trace_decoder_parse.argtypes = [
_Handle,
ctypes.c_void_p,
ctypes.c_uint64,
_TRACE_CB,
ctypes.c_void_p,
]
lib.rocprof_trace_decoder_parse.restype = ctypes.c_int
lib.rocprof_trace_decoder_get_info_string.argtypes = [ctypes.c_int]
lib.rocprof_trace_decoder_get_info_string.restype = ctypes.c_char_p
lib.rocprof_trace_decoder_get_status_string.argtypes = [ctypes.c_int]
lib.rocprof_trace_decoder_get_status_string.restype = ctypes.c_char_p
def _check(self, status: int) -> None:
if int(status) == int(DecoderStatus.SUCCESS):
return
message = None
try:
raw = self._lib.rocprof_trace_decoder_get_status_string(int(status))
if raw:
message = raw.decode("utf-8", errors="replace")
except Exception:
pass
raise DecoderError(status, message)
def info_string(self, info: int) -> str:
raw = self._lib.rocprof_trace_decoder_get_info_string(int(info))
return raw.decode("utf-8", errors="replace") if raw else ""
def status_string(self, status: int) -> str:
raw = self._lib.rocprof_trace_decoder_get_status_string(int(status))
return raw.decode("utf-8", errors="replace") if raw else ""
def load_code_object_data(
self,
load_id: int,
load_addr: int,
load_size: int,
data: BytesLike,
) -> None:
"""Compatibility wrapper for the original data-loading argument order.
*load_size* is the loaded memory range size, not the code-object byte
length.
"""
self._load_code_object_bytes(
data,
load_id=load_id,
load_addr=load_addr,
load_size=load_size,
)
def load_code_object(
self,
data: BytesLike | str | os.PathLike[str],
load_id: int,
load_addr: int,
load_size: int,
) -> None:
"""Load a code object into this decoder handle.
*data* may be a bytes-like object or a filesystem path. New code that
wants to be explicit about paths can use `load_code_object_file()`.
*load_size* is the size of the loaded memory range and is intentionally
separate from the code-object byte length passed to the native API.
"""
if _is_bytes_like(data):
self._load_code_object_bytes(
data,
load_id=load_id,
load_addr=load_addr,
load_size=load_size,
)
else:
self.load_code_object_file(
data,
load_id=load_id,
load_addr=load_addr,
load_size=load_size,
)
def load_code_object_file(
self,
path: str | os.PathLike[str],
load_id: int,
load_addr: int,
load_size: int,
) -> None:
"""Load a code object from a file path.
*load_size* is the loaded memory range size, not the file length.
"""
self._load_code_object_bytes(
Path(path).read_bytes(),
load_id=load_id,
load_addr=load_addr,
load_size=load_size,
)
def _load_code_object_bytes(
self,
data: BytesLike,
*,
load_id: int,
load_addr: int,
load_size: int,
) -> None:
self._ensure_open()
blob = bytes(data)
buf = ctypes.create_string_buffer(blob)
status = self._lib.rocprof_trace_decoder_codeobj_load(
self._handle, load_id, load_addr, load_size, buf, len(blob)
)
self._check(status)
def unload_code_object(self, load_id: int) -> None:
self._ensure_open()
status = self._lib.rocprof_trace_decoder_codeobj_unload(self._handle, load_id)
self._check(status)
def parse_file(
self,
path: str | os.PathLike[str],
isa: IsaProvider | None = None,
on_batch: Callable[[RecordType, list[object] | int], None] | None = None,
) -> TraceRecords:
return self.parse(Path(path).read_bytes(), isa=isa, on_batch=on_batch)
def parse_chunks(
self,
chunks: Iterable[BytesLike],
isa: IsaProvider | None = None,
on_batch: Callable[[RecordType, list[object] | int], None] | None = None,
) -> TraceRecords:
out = TraceRecords()
for chunk in chunks:
partial = self.parse(chunk, isa=isa, on_batch=on_batch)
merge_records(out, partial)
return out
def parse(
self,
data: BytesLike,
isa: IsaProvider | None = None,
on_batch: Callable[[RecordType, list[object] | int], None] | None = None,
) -> TraceRecords:
"""Decode a binary ATT payload into Python records.
*isa* is called by the native decoder when it needs instruction text
for a PC. *on_batch*, when provided, receives each converted native
record batch before this method returns the accumulated TraceRecords.
"""
self._ensure_open()
raw = bytes(data)
records = TraceRecords()
callback_error: list[BaseException] = []
if isa is not None:
self._isa_cb = self._make_isa_callback(isa)
else:
self._isa_cb = _ISA_CB()
self._check(
self._lib.rocprof_trace_decoder_set_isa_callback(self._handle, self._isa_cb, None)
)
def _trace(record_type: int, events: int | None, size: int, _userdata: int | None) -> int:
try:
batch_type = RecordType(record_type)
batch = self._convert_batch(batch_type, events, int(size))
_append_batch(records, batch_type, batch)
if on_batch is not None:
on_batch(batch_type, batch)
return int(DecoderStatus.SUCCESS)
except BaseException as exc:
callback_error.append(exc)
return int(DecoderStatus.ERROR)
trace_cb = _TRACE_CB(_trace)
self._trace_cb = trace_cb
if raw:
data_buf = (ctypes.c_uint8 * len(raw)).from_buffer_copy(raw)
data_ptr = ctypes.cast(data_buf, ctypes.c_void_p)
data_size = len(raw)
else:
data_buf = None
data_ptr = None
data_size = 0
status = self._lib.rocprof_trace_decoder_parse(
self._handle, data_ptr, data_size, trace_cb, None
)
if callback_error:
raise callback_error[0]
self._check(status)
return records
def _make_isa_callback(self, isa: IsaProvider) -> _ISA_CB:
def _isa(
instr_buf: int,
mem_size_p: ctypes.POINTER(ctypes.c_uint64),
size_p: ctypes.POINTER(ctypes.c_uint64),
c_pc: _PcInfo,
_userdata: int | None,
) -> int:
pc = _pc(c_pc)
resolved = isa.isa_for_pc(pc)
if resolved is None:
return int(DecoderStatus.ERROR_INVALID_ARGUMENT)
text, memory_size = resolved
encoded = text.encode("utf-8")
available = int(size_p[0])
size_p[0] = len(encoded)
mem_size_p[0] = max(int(memory_size), 4)
if len(encoded) > available:
return int(DecoderStatus.ERROR_OUT_OF_RESOURCES)
if encoded:
ctypes.memmove(instr_buf, encoded, len(encoded))
return int(DecoderStatus.SUCCESS)
return _ISA_CB(_isa)
def _convert_batch(
self, record_type: RecordType, events: int | None, size: int
) -> list[object] | int:
if record_type == RecordType.GFXIP:
return int(events or 0)
if size <= 0:
return []
if not events:
return []
if record_type == RecordType.INFO:
ptr = ctypes.cast(events, ctypes.POINTER(ctypes.c_int))
return [int(ptr[i]) for i in range(size)]
if record_type == RecordType.OCCUPANCY:
ptr = ctypes.cast(events, ctypes.POINTER(_Occupancy))
return [_convert_occupancy(ptr[i]) for i in range(size)]
if record_type == RecordType.PERFEVENT:
ptr = ctypes.cast(events, ctypes.POINTER(_PerfEvent))
return [_convert_perf(ptr[i]) for i in range(size)]
if record_type == RecordType.WAVE:
ptr = ctypes.cast(events, ctypes.POINTER(_Wave))
return [_convert_wave(ptr[i]) for i in range(size)]
if record_type == RecordType.EVENT:
ptr = ctypes.cast(events, ctypes.POINTER(_Event))
return [_convert_event(ptr[i]) for i in range(size)]
if record_type == RecordType.SHADERDATA:
ptr = ctypes.cast(events, ctypes.POINTER(_ShaderData))
return [_convert_shaderdata(ptr[i]) for i in range(size)]
if record_type == RecordType.REALTIME:
ptr = ctypes.cast(events, ctypes.POINTER(_Realtime))
return [_convert_realtime(ptr[i]) for i in range(size)]
if record_type == RecordType.RT_FREQUENCY:
ptr = ctypes.cast(events, ctypes.POINTER(ctypes.c_uint64))
return int(ptr[0])
if record_type == RecordType.INST_OTHER_SIMD:
ptr = ctypes.cast(events, ctypes.POINTER(_OtherSimdInstruction))
return [_convert_other_simd(ptr[i]) for i in range(size)]
if record_type == RecordType.DISPATCH:
ptr = ctypes.cast(events, ctypes.POINTER(_Dispatch))
return [_convert_dispatch(ptr[i]) for i in range(size)]
return []
def _convert_perf(c: _PerfEvent) -> PerfEvent:
return PerfEvent(
time=int(c.time),
events0=int(c.events0),
events1=int(c.events1),
events2=int(c.events2),
events3=int(c.events3),
cu=int(c.CU),
bank=int(c.bank),
)
def _convert_occupancy(c: _Occupancy) -> Occupancy:
bits = int(c._bits)
return Occupancy(
pc=_pc(c.pc),
time=int(c.time),
reserved=int(c.reserved),
cu=int(c.cu),
simd=int(c.simd),
wave_id=int(c.wave_id),
start=bits & 0x1,
me_id=(bits >> 1) & 0x7,
pipe_id=(bits >> 4) & 0xF,
is_ext=(bits >> 8) & 0x1,
workgroup_id=(bits >> 9) & 0x7F,
cluster_id=(bits >> 16) & 0x1F,
)
def _convert_instruction(c: _Instruction) -> Instruction:
raw = int(c._cat_stall)
return Instruction(
category=raw & 0xFF,
stall=(raw >> 8) & 0xFFFFFF,
duration=int(c.duration),
time=int(c.time),
pc=_pc(c.pc),
)
def _convert_wave(c: _Wave) -> Wave:
timeline = []
if c.timeline_array:
timeline = [
WaveState(
type=int(c.timeline_array[i].type),
duration=int(c.timeline_array[i].duration),
)
for i in range(int(c.timeline_size))
]
instructions = []
if c.instructions_array:
instructions = [
_convert_instruction(c.instructions_array[i]) for i in range(int(c.instructions_size))
]
return Wave(
cu=int(c.cu),
simd=int(c.simd),
wave_id=int(c.wave_id),
contexts=int(c.contexts),
dispatcher=int(c.dispatcher),
workgroup_id=int(c.workgroup_id),
cluster_id=int(c.cluster_id),
reserved=int(c.reserved),
size=int(c.size),
begin_time=int(c.begin_time),
end_time=int(c.end_time),
timeline=timeline,
instructions=instructions,
)
def _convert_realtime(c: _Realtime) -> Realtime:
return Realtime(
shader_clock=int(c.shader_clock),
realtime_clock=int(c.realtime_clock),
reserved=int(c.reserved),
)
def _convert_shaderdata(c: _ShaderData) -> ShaderData:
return ShaderData(
time=int(c.time),
value=int(c.value),
cu=int(c.cu),
simd=int(c.simd),
wave_id=int(c.wave_id),
flags=int(c.flags),
reserved=int(c.reserved),
)
def _convert_other_simd(c: _OtherSimdInstruction) -> OtherSimdInstruction:
return OtherSimdInstruction(
size=int(c.size),
time=int(c.time),
cycles=int(c.cycles),
wgp=int(c.wgp),
category=int(c.category),
)
def _convert_event(c: _Event) -> Event:
payload = EventPayload(
raw=int(c.payload.raw),
code_object_id=int(c.payload.code_object_id),
cluster_id=int(c.payload.cluster_barrier.cluster_id),
barrier_id=int(c.payload.cluster_barrier.barrier_id),
)
return Event(
size=int(c.size),
time=int(c.time),
type=int(c.type),
me_id=int(c.me_id),
pipe_id=int(c.pipe_id),
flags=int(c.flags),
payload=payload,
byte_offset=int(c.byte_offset),
)
def _convert_dispatch(c: _Dispatch) -> Dispatch:
return Dispatch(
size=int(c.size),
time=int(c.time),
me_id=int(c.me_id),
pipe_id=int(c.pipe_id),
user_sgprs=int(c.user_sgprs),
flags=int(c.flags),
vgprs=int(c.vgprs),
sgprs=int(c.sgprs),
lds_size=int(c.lds_size),
thread_dim_x=int(c.thread_dim_x),
thread_dim_y=int(c.thread_dim_y),
thread_dim_z=int(c.thread_dim_z),
dispatch_pkt_addr=int(c.dispatch_pkt_addr),
byte_offset=int(c.byte_offset),
entry_point=_pc(c.entry_point),
)
def _append_batch(
records: TraceRecords,
record_type: RecordType,
batch: list[object] | int,
) -> None:
records.batches.append((record_type, batch))
if record_type == RecordType.GFXIP:
records.gfxip = int(batch)
elif record_type == RecordType.INFO:
records.info.extend(batch) # type: ignore[arg-type]
elif record_type == RecordType.OCCUPANCY:
records.occupancy.extend(batch) # type: ignore[arg-type]
elif record_type == RecordType.PERFEVENT:
records.perf_events.extend(batch) # type: ignore[arg-type]
elif record_type == RecordType.WAVE:
records.waves.extend(batch) # type: ignore[arg-type]
elif record_type == RecordType.EVENT:
records.events.extend(batch) # type: ignore[arg-type]
elif record_type == RecordType.SHADERDATA:
records.shaderdata.extend(batch) # type: ignore[arg-type]
elif record_type == RecordType.REALTIME:
records.realtime.extend(batch) # type: ignore[arg-type]
elif record_type == RecordType.RT_FREQUENCY:
records.realtime_frequency = int(batch)
elif record_type == RecordType.INST_OTHER_SIMD:
records.other_simd.extend(batch) # type: ignore[arg-type]
elif record_type == RecordType.DISPATCH:
records.dispatches.extend(batch) # type: ignore[arg-type]
def merge_records(dst: TraceRecords, src: TraceRecords) -> TraceRecords:
if src.gfxip is not None:
dst.gfxip = src.gfxip
if src.realtime_frequency is not None:
dst.realtime_frequency = src.realtime_frequency
dst.info.extend(src.info)
dst.occupancy.extend(src.occupancy)
dst.perf_events.extend(src.perf_events)
dst.waves.extend(src.waves)
dst.events.extend(src.events)
dst.shaderdata.extend(src.shaderdata)
dst.realtime.extend(src.realtime)
dst.other_simd.extend(src.other_simd)
dst.dispatches.extend(src.dispatches)
dst.batches.extend(src.batches)
return dst
def _is_bytes_like(value: object) -> bool:
return isinstance(value, (bytes, bytearray, memoryview))
__all__ = ["BytesLike", "Decoder", "DecoderError", "IsaProvider", "merge_records"]