mirror of
https://github.com/tinygrad/tinygrad.git
synced 2026-08-29 11:56:08 +00:00
get cdna sqtt working (#14301)
* get cdna sqtt working * cnd aprser * wavestart/waveend * names * cdna * test that
This commit is contained in:
@@ -132,7 +132,7 @@ class PacketType:
|
||||
return inst
|
||||
|
||||
def __repr__(self) -> str:
|
||||
fields_str = ", ".join(f"{k}={getattr(self, k)}" for k in self._fields if not k.startswith('_'))
|
||||
fields_str = ", ".join(f"{k}={getattr(self, k)}" for k in self._fields if not k.startswith('_') and k != 'encoding')
|
||||
return f"{self.__class__.__name__}({fields_str})"
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
"""SQTT (SQ Thread Trace) packet decoder for CDNA/MI300 GPUs.
|
||||
|
||||
CDNA uses a completely different 16-bit header format from RDNA's nibble-based encoding.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
from typing import Iterator
|
||||
from extra.assembly.amd.dsl import bits
|
||||
from extra.assembly.amd.sqtt import PacketType
|
||||
|
||||
# CDNA pkt_fmt -> size in bytes (extracted from rocprof hash table)
|
||||
CDNA_PKT_SIZES = {0: 2, 1: 8, 2: 8, 3: 4, 4: 2, 5: 6, 6: 2, 7: 2, 8: 2, 9: 2, 10: 2, 11: 8, 12: 6, 13: 4, 14: 8, 15: 6}
|
||||
|
||||
class CDNA_DELTA(PacketType):
|
||||
"""pkt_fmt=0: 16-bit timestamp delta packet"""
|
||||
encoding = bits[3:0] == 0
|
||||
delta = bits[11:4] # (data >> 4) & 0xff
|
||||
unk_0 = bits[12:12] # (data >> 0xc) & 1
|
||||
unk_1 = bits[15:13] # (data >> 0xd)
|
||||
|
||||
class CDNA_TIMESTAMP(PacketType):
|
||||
"""pkt_fmt=1: 64-bit timestamp packet (case 0x0)"""
|
||||
encoding = bits[3:0] == 1
|
||||
unk_0 = bits[15:4]
|
||||
timestamp = bits[63:16] # stored as (data_word >> 0x10) in low 46 bits of local_58
|
||||
|
||||
class CDNA_PKT_2(PacketType):
|
||||
"""pkt_fmt=2: 64-bit packet (case 0x4)"""
|
||||
encoding = bits[3:0] == 2
|
||||
unk_0 = bits[6:5] # (data >> 5) & 3
|
||||
unk_1 = bits[7:7] # (data >> 7) + 1 & 1
|
||||
unk_padding = bits[63:8]
|
||||
|
||||
class CDNA_WAVESTART(PacketType):
|
||||
"""pkt_fmt=3: 32-bit WAVESTART packet (case 0x8)"""
|
||||
encoding = bits[3:0] == 3
|
||||
unk_0 = bits[5:5] # (data >> 5) & 1
|
||||
unk_1 = bits[9:6] # (data >> 6) & 0xf
|
||||
wave = bits[13:10] # (data >> 10) & 0xf
|
||||
simd = bits[15:14] # (data >> 0xe) & 3
|
||||
cu = bits[17:16] # (data >> 0x10) & 3
|
||||
unk_5 = bits[19:18] # (data >> 0x12) & 3
|
||||
unk_6 = bits[28:22] # (data >> 0x16) & 0x7f
|
||||
unk_padding = bits[31:29]
|
||||
|
||||
class CDNA_PKT_4(PacketType):
|
||||
"""pkt_fmt=4: 16-bit packet (case 0xc, same as 0x8/0x14)"""
|
||||
encoding = bits[3:0] == 4
|
||||
unk_0 = bits[5:5] # (data_word >> 5) & 1
|
||||
unk_1 = bits[9:6] # (data_word >> 6) & 0xf
|
||||
unk_2 = bits[13:10] # (data_word >> 10) & 0xf
|
||||
unk_3 = bits[15:14] # (data_word >> 0xe)
|
||||
|
||||
class CDNA_PKT_5(PacketType):
|
||||
"""pkt_fmt=5: 48-bit packet (case 0x10)"""
|
||||
encoding = bits[3:0] == 5
|
||||
unk_0 = bits[6:5] # (data >> 5) & 3
|
||||
unk_1 = bits[7:7] # (data >> 7) + 1 & 1
|
||||
unk_2 = bits[15:9] # (data >> 9) & 0x7f
|
||||
unk_padding = bits[47:16]
|
||||
|
||||
class CDNA_WAVEEND(PacketType):
|
||||
"""pkt_fmt=6: 16-bit WAVEEND packet (case 0x14, same as 0x8/0xc)"""
|
||||
encoding = bits[3:0] == 6
|
||||
unk_0 = bits[5:5] # (data_word >> 5) & 1
|
||||
unk_1 = bits[9:6] # (data_word >> 6) & 0xf
|
||||
wave = bits[13:10] # (data_word >> 10) & 0xf
|
||||
simd = bits[15:14] # (data_word >> 0xe)
|
||||
|
||||
class CDNA_EXEC(PacketType):
|
||||
"""pkt_fmt=10: 16-bit EXEC packet (case 0x24)"""
|
||||
encoding = bits[3:0] == 10
|
||||
unk_0 = bits[8:5] # (data_word >> 5) & 0xf
|
||||
unk_1 = bits[10:9] # (data_word >> 9) & 3
|
||||
unk_2 = bits[15:11] # (data_word >> 0xb)
|
||||
|
||||
class CDNA_PKT_11(PacketType):
|
||||
"""pkt_fmt=11: 64-bit packet (case 0x28)"""
|
||||
encoding = bits[3:0] == 11
|
||||
unk_0 = bits[8:5] # (data_word >> 5) & 0xf
|
||||
unk_1 = bits[10:9] # (data_word >> 9) & 3
|
||||
unk_2 = bits[15:15] # (data_word >> 0xf) & 1
|
||||
unk_padding = bits[63:16]
|
||||
|
||||
class CDNA_INST(PacketType):
|
||||
"""pkt_fmt=13: 32-bit INST packet (case 0x30)"""
|
||||
encoding = bits[3:0] == 13
|
||||
unk_0 = bits[6:5] # (data >> 5) & 3
|
||||
unk_1 = bits[9:8] # (data >> 8) & 3
|
||||
unk_2 = bits[11:10] # (data >> 10) & 3
|
||||
unk_3 = bits[13:12] # (data >> 0xc) & 3
|
||||
unk_4 = bits[15:14] # (data >> 0xe) & 3
|
||||
unk_5 = bits[19:18] # (data >> 0x12) & 3
|
||||
unk_6 = bits[21:20] # (data >> 0x14) & 3
|
||||
unk_7 = bits[23:22] # (data >> 0x16) & 3
|
||||
unk_8 = bits[25:24] # (data >> 0x18) & 3
|
||||
unk_9 = bits[27:26] # (data >> 0x1a) & 3
|
||||
unk_padding = bits[31:28]
|
||||
|
||||
class CDNA_PKT_14(PacketType):
|
||||
"""pkt_fmt=14: 64-bit packet (case 0x34)"""
|
||||
encoding = bits[3:0] == 14
|
||||
unk_0 = bits[5:5] # (data >> 5) & 1
|
||||
unk_1 = bits[9:6] # (data >> 6) & 0xf
|
||||
unk_2 = bits[11:10] # (data >> 10) & 3
|
||||
unk_3 = bits[24:12] # (data >> 0xc) & 0x1fff
|
||||
unk_4 = bits[37:25] # (data >> 0x19) & 0x1fff
|
||||
unk_5 = bits[50:38] # (data >> 0x26) & 0x1fff
|
||||
unk_6 = bits[51:51] # (data >> 0x33) & 1
|
||||
unk_padding = bits[63:52]
|
||||
|
||||
class CDNA_PKT_15(PacketType):
|
||||
"""pkt_fmt=15: 48-bit packet (case 0x38, same as 0x10)"""
|
||||
encoding = bits[3:0] == 15
|
||||
unk_0 = bits[6:5] # (data >> 5) & 3
|
||||
unk_1 = bits[7:7] # (data >> 7) + 1 & 1
|
||||
unk_2 = bits[15:9] # (data >> 9) & 0x7f
|
||||
unk_padding = bits[47:16]
|
||||
|
||||
CDNA_PKT_TYPES: dict[int, type[PacketType]] = {
|
||||
0: CDNA_DELTA, 1: CDNA_TIMESTAMP, 2: CDNA_PKT_2, 3: CDNA_WAVESTART, 4: CDNA_PKT_4,
|
||||
5: CDNA_PKT_5, 6: CDNA_WAVEEND, 10: CDNA_EXEC, 11: CDNA_PKT_11, 13: CDNA_INST, 14: CDNA_PKT_14, 15: CDNA_PKT_15,
|
||||
}
|
||||
|
||||
# Validate CDNA packet definitions
|
||||
for pkt_fmt, pkt_cls in CDNA_PKT_TYPES.items():
|
||||
assert pkt_cls.encoding.default == pkt_fmt, f"{pkt_cls.__name__} encoding {pkt_cls.encoding.default} != pkt_fmt {pkt_fmt}"
|
||||
assert CDNA_PKT_SIZES[pkt_fmt] * 2 == pkt_cls._size_nibbles, f"{pkt_cls.__name__} size {pkt_cls._size_nibbles//2} != {CDNA_PKT_SIZES[pkt_fmt]}"
|
||||
|
||||
def decode(data: bytes) -> Iterator[PacketType]:
|
||||
"""Decode CDNA SQTT blob using 16-bit header format."""
|
||||
pos, time, ts_offset = 0, 0, None
|
||||
while pos + 2 <= len(data):
|
||||
header = int.from_bytes(data[pos:pos+2], 'little')
|
||||
pkt_fmt = header & 0xf
|
||||
pkt_size = CDNA_PKT_SIZES[pkt_fmt]
|
||||
if pos + pkt_size > len(data): break
|
||||
|
||||
raw = int.from_bytes(data[pos:pos+pkt_size], 'little')
|
||||
# pkt_fmt=0 has delta in bits[11:4], accumulate it
|
||||
if pkt_fmt == 0: time += ((raw >> 4) & 0xff) * 4
|
||||
# pkt_fmt=1 with unk_0=0 is absolute timestamp - use it to anchor time
|
||||
if pkt_fmt == 1 and ((raw >> 4) & 0xfff) == 0:
|
||||
abs_ts = raw >> 16
|
||||
if ts_offset is None: ts_offset = abs_ts - time # first timestamp: save offset
|
||||
else: time = ((abs_ts - ts_offset) & ~3) - 4 # subsequent: compute time, align to 4, subtract 4
|
||||
pkt_cls = CDNA_PKT_TYPES[pkt_fmt]
|
||||
yield pkt_cls.from_raw(raw, time)
|
||||
pos += pkt_size
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys, pickle
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python sqtt_cdna.py <pkl_file>")
|
||||
sys.exit(1)
|
||||
with open(sys.argv[1], "rb") as f:
|
||||
data = pickle.load(f)
|
||||
sqtt_events = [e for e in data if type(e).__name__ == "ProfileSQTTEvent"]
|
||||
for i, event in enumerate(sqtt_events):
|
||||
print(f"\n=== event {i} ===")
|
||||
for pkt in decode(event.blob):
|
||||
print(f"{pkt._time:8}: {pkt}")
|
||||
@@ -1,244 +1,170 @@
|
||||
"""Tests comparing sqtt.py PACKET_TYPES_L3/L4 against AMD's rocprof-trace-decoder binary."""
|
||||
import unittest
|
||||
import unittest, struct, ctypes, pickle
|
||||
from pathlib import Path
|
||||
import ctypes
|
||||
|
||||
ROCPROF_LIB = "/usr/lib/librocprof-trace-decoder.so"
|
||||
ROCPROF_LIB = Path("/usr/lib/librocprof-trace-decoder.so")
|
||||
EXAMPLES_DIR = Path(__file__).parent.parent.parent.parent / "sqtt/examples"
|
||||
|
||||
def _find_rw_segment():
|
||||
"""Find the rw- segment of the loaded library."""
|
||||
def _find_segment(perms: str):
|
||||
"""Find a segment of the loaded library with given permissions (e.g. 'rw-p', 'r--p')."""
|
||||
with open('/proc/self/maps', 'r') as f:
|
||||
for line in f:
|
||||
if 'librocprof-trace-decoder.so' in line and ' rw-p ' in line:
|
||||
if 'librocprof-trace-decoder.so' in line and f' {perms} ' in line:
|
||||
parts = line.split()
|
||||
rw_base = int(parts[0].split('-')[0], 16)
|
||||
rw_file_offset = int(parts[2], 16)
|
||||
return rw_base, rw_file_offset
|
||||
return int(parts[0].split('-')[0], 16), int(parts[2], 16)
|
||||
return None, None
|
||||
|
||||
def _read_array(file_offset: int, count: int):
|
||||
"""Read an array of uint8 at file_offset from the loaded library."""
|
||||
base, seg_offset = _find_segment('rw-p')
|
||||
if base is None: return None
|
||||
return list((ctypes.c_uint8 * count).from_address(base + (file_offset - seg_offset)))
|
||||
|
||||
def _load_lib():
|
||||
if not ROCPROF_LIB.exists(): return False
|
||||
ctypes.CDLL(str(ROCPROF_LIB))
|
||||
return True
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# RDNA EXTRACTION (nibble-based format)
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
def extract_bit_tables():
|
||||
"""Extract bit budget tables by loading librocprof-trace-decoder.so at runtime.
|
||||
|
||||
Returns (layout2, layout3, layout4). CDNA uses a different 16-bit header format, not nibble-based.
|
||||
"""
|
||||
if not Path(ROCPROF_LIB).exists():
|
||||
return None, None, None
|
||||
|
||||
ctypes.CDLL(ROCPROF_LIB)
|
||||
rw_base, rw_file_offset = _find_rw_segment()
|
||||
if rw_base is None:
|
||||
return None, None, None
|
||||
|
||||
# Bit tables at file offsets 0x2d220, 0x2d280, 0x2d2c0
|
||||
layout2 = list((ctypes.c_uint8 * 32).from_address(rw_base + (0x2d220 - rw_file_offset)))
|
||||
layout3 = list((ctypes.c_uint8 * 32).from_address(rw_base + (0x2d280 - rw_file_offset)))
|
||||
layout4 = list((ctypes.c_uint8 * 32).from_address(rw_base + (0x2d2c0 - rw_file_offset)))
|
||||
return layout2, layout3, layout4
|
||||
|
||||
def _find_ro_segment():
|
||||
"""Find the r--p segment containing .rodata of the loaded library."""
|
||||
with open('/proc/self/maps', 'r') as f:
|
||||
for line in f:
|
||||
if 'librocprof-trace-decoder.so' in line and ' r--p ' in line:
|
||||
parts = line.split()
|
||||
base = int(parts[0].split('-')[0], 16)
|
||||
file_offset = int(parts[2], 16)
|
||||
# The delta table is at file offset 0x26dc0, which is in .rodata at 0x26000
|
||||
if file_offset <= 0x26dc0:
|
||||
return base, file_offset
|
||||
return None, None
|
||||
"""Extract bit budget tables. Returns (layout2, layout3, layout4) or None."""
|
||||
if not _load_lib(): return None
|
||||
return _read_array(0x2d220, 32), _read_array(0x2d280, 32), _read_array(0x2d2c0, 32)
|
||||
|
||||
def extract_delta_fields():
|
||||
"""Extract delta bitfield tables from .rodata section.
|
||||
"""Extract delta bitfield tables. Returns (layout2, layout3, layout4) dicts mapping type_id -> (lo, hi)."""
|
||||
if not _load_lib(): return None
|
||||
ro_base, ro_offset = _find_segment('r--p')
|
||||
if ro_base is None: return None
|
||||
|
||||
Returns (layout2_table, layout3_table, layout4_table) where each is dict mapping type_id -> (delta_lo, delta_hi).
|
||||
The delta field is at bits[delta_hi-1:delta_lo], extracted as: (reg >> delta_lo) & ((1 << (delta_hi - delta_lo)) - 1)
|
||||
"""
|
||||
if not Path(ROCPROF_LIB).exists():
|
||||
return None, None, None
|
||||
def read_table(file_offset, num_entries):
|
||||
addr = ro_base + (file_offset - ro_offset)
|
||||
data = bytes((ctypes.c_uint8 * (num_entries * 12)).from_address(addr))
|
||||
return {type_id: (lo, hi) for j in range(0, len(data), 12)
|
||||
for type_id, lo, hi in [struct.unpack('<III', data[j:j+12])] if type_id < 32}
|
||||
|
||||
ctypes.CDLL(ROCPROF_LIB)
|
||||
ro_base, ro_file_offset = _find_ro_segment()
|
||||
if ro_base is None:
|
||||
return None, None, None
|
||||
|
||||
import struct
|
||||
|
||||
def read_table(file_offset, num_entries=25):
|
||||
table_addr = ro_base + (file_offset - ro_file_offset)
|
||||
table_size = num_entries * 12
|
||||
data = bytes((ctypes.c_uint8 * table_size).from_address(table_addr))
|
||||
delta_fields = {}
|
||||
for j in range(0, table_size, 12):
|
||||
type_id, delta_lo, delta_hi = struct.unpack('<III', data[j:j+12])
|
||||
if type_id < 32:
|
||||
delta_fields[type_id] = (delta_lo, delta_hi)
|
||||
return delta_fields
|
||||
|
||||
# Delta tables: Layout 2 at 0x26800, Layout 3 at 0x26dc0, Layout 4 at 0x27300
|
||||
layout2 = read_table(0x26800, 24) # L2 has 24 entries (no type 25)
|
||||
layout3 = read_table(0x26dc0, 25)
|
||||
layout4 = read_table(0x27300, 27) # L4 has more entries
|
||||
return layout2, layout3, layout4
|
||||
|
||||
def _read_encodings_from_vector(rw_base, rw_file_offset, vec_offset):
|
||||
"""Read packet encodings from a registration vector at given file offset."""
|
||||
vec_start_addr = rw_base + (vec_offset - rw_file_offset)
|
||||
vec_end_addr = rw_base + (vec_offset + 8 - rw_file_offset)
|
||||
|
||||
vec_start = ctypes.c_void_p.from_address(vec_start_addr).value
|
||||
vec_end = ctypes.c_void_p.from_address(vec_end_addr).value
|
||||
if not vec_start or not vec_end:
|
||||
return {}
|
||||
|
||||
# Each entry is 32 bytes: type_id at offset 0, pattern_start at 8, pattern_end at 16
|
||||
encodings = {}
|
||||
for i in range((vec_end - vec_start) // 32):
|
||||
entry_addr = vec_start + i * 32
|
||||
type_id = ctypes.c_uint8.from_address(entry_addr).value
|
||||
pattern_start = ctypes.c_void_p.from_address(entry_addr + 8).value
|
||||
pattern_end = ctypes.c_void_p.from_address(entry_addr + 16).value
|
||||
|
||||
if pattern_start and pattern_end:
|
||||
pattern_len = pattern_end - pattern_start
|
||||
if 0 < pattern_len <= 8:
|
||||
pattern = list((ctypes.c_uint8 * pattern_len).from_address(pattern_start))
|
||||
mask = sum(1 << j for j in range(pattern_len))
|
||||
value = sum(b << j for j, b in enumerate(pattern))
|
||||
encodings[type_id] = (mask, value)
|
||||
|
||||
return encodings
|
||||
return read_table(0x26800, 24), read_table(0x26dc0, 25), read_table(0x27300, 27)
|
||||
|
||||
def extract_packet_encodings():
|
||||
"""Extract packet type encodings from runtime packet type registrations.
|
||||
"""Extract packet encodings. Returns (L2, L3, L4) dicts mapping type_id -> (mask, value)."""
|
||||
if not _load_lib(): return None
|
||||
rw_base, rw_offset = _find_segment('rw-p')
|
||||
if rw_base is None: return None
|
||||
|
||||
Returns (L2_encodings, L3_encodings, L4_encodings) - each is dict mapping type_id -> (mask, value).
|
||||
L2 and L4 have layout-specific overrides on top of L3 base encodings.
|
||||
"""
|
||||
if not Path(ROCPROF_LIB).exists():
|
||||
return None, None, None
|
||||
# Read base encodings from registration vector at 0x2d340
|
||||
vec_start = ctypes.c_void_p.from_address(rw_base + (0x2d340 - rw_offset)).value
|
||||
vec_end = ctypes.c_void_p.from_address(rw_base + (0x2d348 - rw_offset)).value
|
||||
base = {}
|
||||
if vec_start and vec_end:
|
||||
for i in range((vec_end - vec_start) // 32):
|
||||
addr = vec_start + i * 32
|
||||
type_id = ctypes.c_uint8.from_address(addr).value
|
||||
pat_start = ctypes.c_void_p.from_address(addr + 8).value
|
||||
pat_end = ctypes.c_void_p.from_address(addr + 16).value
|
||||
if pat_start and pat_end and 0 < (n := pat_end - pat_start) <= 8:
|
||||
pat = list((ctypes.c_uint8 * n).from_address(pat_start))
|
||||
base[type_id] = (sum(1 << j for j in range(n)), sum(b << j for j, b in enumerate(pat)))
|
||||
|
||||
ctypes.CDLL(ROCPROF_LIB)
|
||||
rw_base, rw_file_offset = _find_rw_segment()
|
||||
if rw_base is None:
|
||||
return None, None, None
|
||||
return {**base, 17: (0x7f, 0x51), 25: (0x7f, 0x31)}, base, {**base} # L2 has overrides
|
||||
|
||||
# Base packet registrations vector at file offset 0x2d340 (shared by all layouts)
|
||||
base_encodings = _read_encodings_from_vector(rw_base, rw_file_offset, 0x2d340)
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# CDNA EXTRACTION (16-bit header format)
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
# L2 overrides: type 17 and 25 have different encodings (from ghidra lines 25633-25657)
|
||||
# Type 17: pattern [1,0,0,0,1,0,1] = mask 0x7f, value 0x51
|
||||
# Type 25: pattern [1,0,0,0,1,1,0] = mask 0x7f, value 0x31
|
||||
l2_encodings = {**base_encodings, 17: (0x7f, 0x51), 25: (0x7f, 0x31)}
|
||||
def extract_cdna_packet_sizes():
|
||||
"""Extract CDNA pkt_fmt -> size mapping by running rocprof decoder to populate its hash table."""
|
||||
from extra.assembly.amd.test.test_sqtt_examples import run_rocprof_decoder
|
||||
|
||||
# L3 uses base encodings directly
|
||||
l3_encodings = base_encodings
|
||||
if not (pkl_path := next((EXAMPLES_DIR / "gfx950").glob("*.pkl"), None)): return None
|
||||
with open(pkl_path, "rb") as f: data = pickle.load(f)
|
||||
sqtt_events = [e for e in data if type(e).__name__ == "ProfileSQTTEvent"]
|
||||
prg = next((e for e in data if type(e).__name__ == "ProfileProgramEvent"), None)
|
||||
if not sqtt_events or not prg: return None
|
||||
|
||||
# L4 uses same encodings as L3 - only field positions/sizes differ
|
||||
l4_encodings = {**base_encodings}
|
||||
# Run decoder to trigger hash table initialization
|
||||
run_rocprof_decoder([e.blob for e in sqtt_events], prg.lib, prg.base, "gfx950")
|
||||
|
||||
return l2_encodings, l3_encodings, l4_encodings
|
||||
# Extract hash table: head at 0x2d4f0, nodes are 16 bytes (next[8], key[4], value[4])
|
||||
rw_base, rw_offset = _find_segment('rw-p')
|
||||
if not (head := ctypes.c_void_p.from_address(rw_base + (0x2d4f0 - rw_offset)).value if rw_base else None): return None
|
||||
|
||||
pkt_sizes, node, seen = {}, head, set()
|
||||
while node and node not in seen and len(pkt_sizes) < 20:
|
||||
seen.add(node)
|
||||
key, val = ctypes.c_uint32.from_address(node + 8).value, ctypes.c_uint32.from_address(node + 12).value
|
||||
if key < 16 and val in (0x10, 0x20, 0x30, 0x40): pkt_sizes[key] = {0x10: 2, 0x20: 4, 0x30: 6, 0x40: 8}[val]
|
||||
node = ctypes.c_void_p.from_address(node).value
|
||||
return pkt_sizes if len(pkt_sizes) == 16 else None
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# TESTS
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
@unittest.skipUnless(Path(ROCPROF_LIB).exists(), "rocprof-trace-decoder not installed")
|
||||
class TestSQTTMatchesBinary(unittest.TestCase):
|
||||
def _test_bit_counts_match_layout(self, layout_num: int):
|
||||
def test_bit_counts_match_layout3(self): self._test_bit_counts(3)
|
||||
def test_bit_counts_match_layout4(self): self._test_bit_counts(4)
|
||||
def test_encodings_match_layout3(self): self._test_encodings(3)
|
||||
def test_encodings_match_layout4(self): self._test_encodings(4)
|
||||
def test_delta_fields_match_layout3(self): self._test_delta_fields(3)
|
||||
def test_delta_fields_match_layout4(self): self._test_delta_fields(4)
|
||||
|
||||
def test_cdna_packet_sizes(self):
|
||||
"""Extract and verify CDNA pkt_fmt -> size mapping from rocprof's hash table."""
|
||||
if not (EXAMPLES_DIR / "gfx950").exists(): self.skipTest("no CDNA examples")
|
||||
pkt_sizes = extract_cdna_packet_sizes()
|
||||
self.assertIsNotNone(pkt_sizes, "failed to extract CDNA packet sizes")
|
||||
from extra.assembly.amd.sqtt_cdna import CDNA_PKT_SIZES
|
||||
for pkt_fmt, size in CDNA_PKT_SIZES.items():
|
||||
with self.subTest(pkt_fmt=pkt_fmt): self.assertEqual(pkt_sizes.get(pkt_fmt), size)
|
||||
|
||||
def _test_bit_counts(self, layout: int):
|
||||
if not (tables := extract_bit_tables()): self.skipTest("rocprof-trace-decoder not installed")
|
||||
from extra.assembly.amd.sqtt import PACKET_TYPES_L3, PACKET_TYPES_L4
|
||||
layout2, layout3, layout4 = extract_bit_tables()
|
||||
layout = {3: layout3, 4: layout4}[layout_num]
|
||||
packet_types = {3: PACKET_TYPES_L3, 4: PACKET_TYPES_L4}[layout_num]
|
||||
|
||||
for type_id, pkt_cls in packet_types.items():
|
||||
expected_bits, actual_bits = layout[type_id], pkt_cls._size_nibbles * 4
|
||||
for type_id, pkt_cls in {3: PACKET_TYPES_L3, 4: PACKET_TYPES_L4}[layout].items():
|
||||
with self.subTest(packet=pkt_cls.__name__):
|
||||
self.assertEqual(actual_bits, expected_bits, f"{pkt_cls.__name__}: {actual_bits} bits != expected {expected_bits}")
|
||||
self.assertEqual(pkt_cls._size_nibbles * 4, tables[layout - 2][type_id])
|
||||
|
||||
# NOTE: CDNA uses a completely different 16-bit header format, not nibble-based - not tested here
|
||||
def test_bit_counts_match_layout3(self): self._test_bit_counts_match_layout(3)
|
||||
def test_bit_counts_match_layout4(self): self._test_bit_counts_match_layout(4)
|
||||
|
||||
def _test_encodings_match_layout(self, layout_num: int):
|
||||
"""Verify each PACKET_TYPE encoding matches rocprof-trace-decoder for given layout."""
|
||||
def _test_encodings(self, layout: int):
|
||||
if not (encodings := extract_packet_encodings()): self.skipTest("rocprof-trace-decoder not installed")
|
||||
from extra.assembly.amd.sqtt import PACKET_TYPES_L3, PACKET_TYPES_L4
|
||||
packet_types = {3: PACKET_TYPES_L3, 4: PACKET_TYPES_L4}[layout_num]
|
||||
|
||||
l2_enc, l3_enc, l4_enc = extract_packet_encodings()
|
||||
encodings = {3: l3_enc, 4: l4_enc}[layout_num]
|
||||
|
||||
for type_id, pkt_cls in packet_types.items():
|
||||
enc = (pkt_cls.encoding.mask, pkt_cls.encoding.default)
|
||||
for type_id, pkt_cls in {3: PACKET_TYPES_L3, 4: PACKET_TYPES_L4}[layout].items():
|
||||
with self.subTest(packet=pkt_cls.__name__):
|
||||
self.assertIn(type_id, encodings, f"{pkt_cls.__name__}: type_id {type_id} not in binary")
|
||||
self.assertEqual(enc, encodings[type_id],
|
||||
f"{pkt_cls.__name__}: encoding mismatch (ours=0x{enc[0]:02x}/0x{enc[1]:02x}, binary=0x{encodings[type_id][0]:02x}/0x{encodings[type_id][1]:02x})")
|
||||
self.assertEqual((pkt_cls.encoding.mask, pkt_cls.encoding.default), encodings[layout - 2][type_id])
|
||||
|
||||
# NOTE: CDNA uses a completely different 16-bit header format, not nibble-based - not tested here
|
||||
def test_encodings_match_layout3(self): self._test_encodings_match_layout(3)
|
||||
def test_encodings_match_layout4(self): self._test_encodings_match_layout(4)
|
||||
|
||||
def _test_delta_fields_match_layout(self, layout_num: int):
|
||||
def _test_delta_fields(self, layout: int):
|
||||
if not (deltas := extract_delta_fields()): self.skipTest("rocprof-trace-decoder not installed")
|
||||
from extra.assembly.amd.sqtt import PACKET_TYPES_L3, PACKET_TYPES_L4
|
||||
packet_types = {3: PACKET_TYPES_L3, 4: PACKET_TYPES_L4}[layout_num]
|
||||
|
||||
layout2_deltas, layout3_deltas, layout4_deltas = extract_delta_fields()
|
||||
delta_fields = {3: layout3_deltas, 4: layout4_deltas}[layout_num]
|
||||
|
||||
for type_id, pkt_cls in packet_types.items():
|
||||
if type_id not in delta_fields:
|
||||
continue
|
||||
expected_lo, expected_hi = delta_fields[type_id]
|
||||
delta_field = getattr(pkt_cls, 'delta', None)
|
||||
if delta_field is None:
|
||||
# NOP has no delta field, rocprof has (0, 0)
|
||||
actual_lo, actual_hi = 0, 0
|
||||
else:
|
||||
actual_lo = delta_field.lo
|
||||
# Our BitField hi is inclusive, rocprof's is exclusive, so convert
|
||||
actual_hi = delta_field.hi + 1
|
||||
with self.subTest(packet=pkt_cls.__name__):
|
||||
self.assertEqual((actual_lo, actual_hi), (expected_lo, expected_hi),
|
||||
f"{pkt_cls.__name__}: delta bits[{actual_hi}:{actual_lo}] != expected bits[{expected_hi}:{expected_lo}]")
|
||||
|
||||
# NOTE: CDNA uses a completely different 16-bit header format, not nibble-based - not tested here
|
||||
def test_delta_fields_match_layout3(self): self._test_delta_fields_match_layout(3)
|
||||
def test_delta_fields_match_layout4(self): self._test_delta_fields_match_layout(4)
|
||||
for type_id, pkt_cls in {3: PACKET_TYPES_L3, 4: PACKET_TYPES_L4}[layout].items():
|
||||
if type_id not in deltas[layout - 2]: continue
|
||||
delta = getattr(pkt_cls, 'delta', None)
|
||||
actual = (0, 0) if delta is None else (delta.lo, delta.hi + 1)
|
||||
with self.subTest(packet=pkt_cls.__name__): self.assertEqual(actual, deltas[layout - 2][type_id])
|
||||
|
||||
if __name__ == "__main__":
|
||||
layout2, layout3, layout4 = extract_bit_tables()
|
||||
l2_enc, l3_enc, l4_enc = extract_packet_encodings()
|
||||
delta2, delta3, delta4 = extract_delta_fields()
|
||||
tables = extract_bit_tables()
|
||||
encodings = extract_packet_encodings()
|
||||
deltas = extract_delta_fields()
|
||||
|
||||
TYPE_NAMES = {
|
||||
1: 'VALUINST', 2: 'VMEMEXEC', 3: 'ALUEXEC', 4: 'IMMEDIATE', 5: 'IMMEDIATE_MASK',
|
||||
6: 'WAVERDY', 7: 'TS_DELTA_S8_W3', 8: 'WAVEEND', 9: 'WAVESTART', 10: 'TS_DELTA_S5_W2',
|
||||
11: 'WAVEALLOC', 12: 'TS_DELTA_S5_W3', 13: 'PERF', 14: 'UTILCTR', 15: 'TS_DELTA_SHORT',
|
||||
16: 'NOP', 17: 'TS_WAVE_STATE', 18: 'EVENT', 19: 'EVENT_BIG', 20: 'REG',
|
||||
21: 'SNAPSHOT', 22: 'TS_DELTA_OR_MARK', 23: 'LAYOUT_HEADER', 24: 'INST', 25: 'UNK_25',
|
||||
26: 'UNK_26', 27: 'UNK_27', 28: 'UNK_28',
|
||||
}
|
||||
TYPE_NAMES = {1: 'VALUINST', 2: 'VMEMEXEC', 3: 'ALUEXEC', 4: 'IMMEDIATE', 5: 'IMMEDIATE_MASK', 6: 'WAVERDY',
|
||||
7: 'TS_DELTA_S8_W3', 8: 'WAVEEND', 9: 'WAVESTART', 10: 'TS_DELTA_S5_W2', 11: 'WAVEALLOC', 12: 'TS_DELTA_S5_W3',
|
||||
13: 'PERF', 14: 'UTILCTR', 15: 'TS_DELTA_SHORT', 16: 'NOP', 17: 'TS_WAVE_STATE', 18: 'EVENT', 19: 'EVENT_BIG',
|
||||
20: 'REG', 21: 'SNAPSHOT', 22: 'TS_DELTA_OR_MARK', 23: 'LAYOUT_HEADER', 24: 'INST', 25: 'UNK_25'}
|
||||
|
||||
print("L2:", layout2)
|
||||
print("L3:", layout3)
|
||||
print("L4:", layout4)
|
||||
|
||||
if l3_enc and layout3:
|
||||
print("\nPacket type registrations from rocprof-trace-decoder:\n")
|
||||
print(f"{'TypeID':>6} {'Name':>18} {'L2 enc':>12} {'L3 enc':>12} {'L4 enc':>12} {'L2':>4} {'L3':>4} {'L4':>4} {'L2 delta':>12} {'L3 delta':>12} {'L4 delta':>12}")
|
||||
print("L2:", tables[0], "\nL3:", tables[1], "\nL4:", tables[2])
|
||||
if encodings and tables:
|
||||
print(f"\n{'TypeID':>6} {'Name':>18} {'L2 enc':>12} {'L3 enc':>12} {'L4 enc':>12} {'L2':>4} {'L3':>4} {'L4':>4} {'L2 delta':>12} {'L3 delta':>12} {'L4 delta':>12}")
|
||||
print("-" * 140)
|
||||
all_type_ids = sorted(set(l2_enc.keys()) | set(l3_enc.keys()) | set(l4_enc.keys()))
|
||||
for type_id in all_type_ids:
|
||||
for type_id in sorted(set(encodings[0]) | set(encodings[1]) | set(encodings[2])):
|
||||
name = TYPE_NAMES.get(type_id, f'UNK_{type_id}')
|
||||
l2 = layout2[type_id] if type_id < len(layout2) else 0
|
||||
l3 = layout3[type_id] if type_id < len(layout3) else 0
|
||||
l4 = layout4[type_id] if type_id < len(layout4) else 0
|
||||
d2 = delta2.get(type_id, (0, 0)) if delta2 else (0, 0)
|
||||
d3 = delta3.get(type_id, (0, 0)) if delta3 else (0, 0)
|
||||
d4 = delta4.get(type_id, (0, 0)) if delta4 else (0, 0)
|
||||
d2_str = f"[{d2[1]-1}:{d2[0]}]" if d2[1] > d2[0] else "-"
|
||||
d3_str = f"[{d3[1]-1}:{d3[0]}]" if d3[1] > d3[0] else "-"
|
||||
d4_str = f"[{d4[1]-1}:{d4[0]}]" if d4[1] > d4[0] else "-"
|
||||
l2_enc_str = f"0x{l2_enc[type_id][0]:02x}/0x{l2_enc[type_id][1]:02x}" if type_id in l2_enc else "-"
|
||||
l3_enc_str = f"0x{l3_enc[type_id][0]:02x}/0x{l3_enc[type_id][1]:02x}" if type_id in l3_enc else "-"
|
||||
l4_enc_str = f"0x{l4_enc[type_id][0]:02x}/0x{l4_enc[type_id][1]:02x}" if type_id in l4_enc else "-"
|
||||
print(f"{type_id:6d} {name:>18} {l2_enc_str:>12} {l3_enc_str:>12} {l4_enc_str:>12} {l2:4d} {l3:4d} {l4:4d} {d2_str:>12} {d3_str:>12} {d4_str:>12}")
|
||||
bits = [tables[i][type_id] if type_id < len(tables[i]) else 0 for i in range(3)]
|
||||
enc_strs = [f"0x{encodings[i][type_id][0]:02x}/0x{encodings[i][type_id][1]:02x}" if type_id in encodings[i] else "-" for i in range(3)]
|
||||
delta_strs = [f"[{d[1]-1}:{d[0]}]" if (d := deltas[i].get(type_id, (0, 0)))[1] > d[0] else "-" for i in range(3)]
|
||||
print(f"{type_id:6d} {name:>18} {enc_strs[0]:>12} {enc_strs[1]:>12} {enc_strs[2]:>12} {bits[0]:4d} {bits[1]:4d} {bits[2]:4d} {delta_strs[0]:>12} {delta_strs[1]:>12} {delta_strs[2]:>12}")
|
||||
|
||||
cdna = extract_cdna_packet_sizes()
|
||||
if cdna: print(f"\nCDNA packet sizes: {cdna}")
|
||||
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user