Files
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

334 lines
15 KiB
Markdown

# Python Modules
This document summarizes the Python modules added for the rocprof-trace-decoder
API and source-tree ATT helpers. The installable Python package is intentionally
small: only the reusable API under `rocprof_trace_decoder` is installed.
The Python API wraps the handle-based decoder functions from
`rocprof_trace_decoder.h` and the record types from `trace_decoder_types.h`.
The quick-scan and standalone trace extraction APIs are intentionally not
wrapped here.
The API can be installed as a normal Python package (`python3 -m pip install .`)
or with CMake when `BUILD_PYTHON=ON`. The CMake install path defaults to the
ROCm-prefix-relative Python site-packages directory `lib/python3/site-packages`.
This package is pure Python over `ctypes`, so the CMake install is shared by
supported Python 3 minor versions. Packagers can override
`ROCPROF_TRACE_DECODER_PYTHON_INSTALL_DIR`. Users running directly from a ROCm
prefix need that directory on `PYTHONPATH`; ROCm environment setup such as the
rocprofiler-sdk setup script/modulefile already prepends the matching ROCm
Python directory. TheRock wheel packaging exposes this package as a top-level
Python package from the profiler wheel instead of relying on a nested
ROCm-prefix path being present on `sys.path`.
## Module Overview
| Module | Role | API Status |
| --- | --- | --- |
| `rocprof_trace_decoder` | Public package entry point | Public |
| `rocprof_trace_decoder.records` | Python record/enumeration model for decoder output | Public |
| `rocprof_trace_decoder.bindings` | `ctypes` wrapper around `librocprof-trace-decoder` | Public for `Decoder`, internal for C mirrors |
| `rocprof_trace_decoder.code_index` | ISA lookup and instruction-stat accumulation | Public utility |
| `rocprof_trace_decoder.codegen` | Builds in-memory code metadata from explicit code object inputs | Public utility |
| `rocprof_trace_decoder.rcv` | ROCprof Compute Viewer JSON writer | Internal utility |
| `rocprof_trace_decoder.att` | High-level ATT decode orchestration with explicit trace metadata | Public utility |
| `generate_code.py` | Generates `code.json`, snapshots, and source copies from code objects | Tool module |
| `att_tool.py` | CLI script for ATT output generation | Tool script |
## `rocprof_trace_decoder`
`rocprof_trace_decoder` is the import root for users who want the Python API.
It re-exports the main wrapper class, the explicit code object helpers, the
code/stat index helpers, and the dataclasses/enums that represent decoded
records.
Typical direct API usage starts here:
```python
from rocprof_trace_decoder import CodeObject, Decoder, generate_code_artifacts
artifacts = generate_code_artifacts([
CodeObject(path="kernel.hsaco", code_object_id=0),
])
with Decoder() as decoder:
records = decoder.parse(att_bytes, isa=artifacts.code_index)
```
Users who want the decoder's built-in disassembly mode can keep one decoder
handle alive, upload code-object bytes to that handle, parse ATT bytes, and
unload by id:
```python
from rocprof_trace_decoder import Decoder
with Decoder() as decoder:
decoder.load_code_object(
code_object_bytes,
load_id=0,
load_addr=0x8000,
load_size=0x4000,
)
records = decoder.parse(att_bytes)
decoder.unload_code_object(0)
```
Built-in disassembly requires a decoder shared library built with the COMGR or
LLVM disassembly backend. Builds without that backend raise `DecoderError` with
`DecoderStatus.ERROR_NOT_IMPLEMENTED` when code objects are loaded or parsed
without a custom ISA provider.
The package also defines `__version__`. The value is stored in `_version.py`,
and the Python packaging metadata reads the same value through
`tool.setuptools.dynamic`, so wheel builds and runtime imports use one source.
## `rocprof_trace_decoder.records`
`records.py` is the public Python model for the decoder data. It contains
`IntEnum` classes for the decoder status, info, record, event, dispatch,
shader-data, wave-state, and instruction-category values, plus dataclasses for
the record payloads emitted by the C decoder.
The dataclasses are deliberately plain Python objects. They do not expose
`ctypes` fields or raw C pointers. This keeps downstream users insulated from
the callback lifetime rules and struct-layout details in the C API.
Important dataclasses include:
- `Pc`: `(address, code_object_id)` key used for ISA lookup and stats.
- `Instruction`: one decoded instruction execution event in a wave.
- `Wave`: wave lifetime, timeline states, and decoded instruction stream.
- `Occupancy`, `PerfEvent`, `ShaderData`, `Realtime`, `OtherSimdInstruction`,
`Event`, and `Dispatch`: direct Python equivalents of the public decoder
payloads.
- `TraceRecords`: the aggregate returned by `Decoder.parse*`, with one list per
record type and a `batches` list preserving callback batch order.
This is the best module for users to import when they want type names for
annotations or record inspection.
## `rocprof_trace_decoder.bindings`
`bindings.py` owns the direct `ctypes` interface to
`librocprof-trace-decoder`. It defines the private C struct mirrors, callback
signatures, status checking, shared-library discovery, and conversion from raw
callback batches into `records.py` objects.
The main public class is `Decoder`. It manages a handle created with
`rocprof_trace_decoder_create_handle()` and destroyed with
`rocprof_trace_decoder_destroy_handle()`. `Decoder` supports:
- `parse(data, isa=...)`, where `data` is ATT bytes.
- `parse_file(path, isa=...)`, a convenience wrapper around `parse(...)`.
- `parse_chunks(chunks, isa=...)`, for callers that intentionally decode each
chunk as a separate trace buffer.
- `load_code_object(data, load_id=..., load_addr=..., load_size=...)`, where
`data` is code-object bytes or a path. `load_size` is the loaded memory range
size and is intentionally distinct from the code-object byte length, which is
inferred from `data`.
- `load_code_object_file(path, load_id=..., load_addr=..., load_size=...)`.
- `load_code_object_data(load_id, load_addr, load_size, data)`, retained for
compatibility with the original wrapper spelling.
- `unload_code_object(...)`
- `info_string(...)`
- `status_string(...)`
`Decoder` is the public Python handle object. Raw C handle values stay private;
users keep per-handle state by keeping the `Decoder` instance alive.
For ATT tool usage, the high-level path normally passes a `CodeIndex` as the
ISA provider. Users who explicitly want the decoder's built-in disassembly mode
can load and unload code-object bytes on the same `Decoder` before parsing ATT
bytes. That mode requires a COMGR/LLVM-enabled decoder library; otherwise the
C API reports `ERROR_NOT_IMPLEMENTED`.
The module also contains the `IsaProvider` protocol. Any object with an
`isa_for_pc(pc) -> tuple[str, int] | None` method can provide instructions to
the decoder callback.
Shared-library lookup is ordered so user overrides win before ROCm defaults:
explicit `lib_path`, `ROCPROF_TRACE_DECODER_LIB`, non-empty `LD_LIBRARY_PATH`
entries, `ROCM_HOME/lib`, `ROCM_PATH/lib`, TheRock package library roots,
`/opt/rocm/lib`, then the platform loader fallback. The lookup accepts both the
unversioned library name and the decoder SONAME used by TheRock runtime wheels.
## `rocprof_trace_decoder.code_index`
`code_index.py` is the shared bridge between static code metadata and dynamic
trace records. It has two related responsibilities.
First, it provides ISA text to the decoder. A `CodeIndex` is normally produced
by `generate_code_artifacts()` from explicit code objects, or loaded from an
existing RCV `code.json` with `CodeIndex.from_code_json(path)`. It maps
`Pc(code_object_id, address)` to instruction text, source text, line number,
and estimated instruction size. Because `CodeIndex` implements `isa_for_pc`, it
can be passed directly to `Decoder.parse_file(..., isa=code_index)`.
Second, it accumulates instruction statistics from decoded waves. As each wave
is processed, `accumulate_wave()` updates per-instruction hit count, latency,
stall, and idle counters. The same implementation is used by the final ATT tool
and by `test/csv_test.py`, which keeps test validation and output generation on
one stats path.
The module can write updated `code.json` and `stats_*.csv` files. It can also
load control CSVs through `from_stats_csv()` and compare accumulated counters
with expected values through `validate_expected()`.
This module is intentionally not responsible for ROCprof Compute Viewer sidecar
JSON files. That output format belongs to `rcv.py`.
## `rocprof_trace_decoder.codegen`
`codegen.py` builds static code metadata directly from explicit code object
inputs. The public input type is `CodeObject(path=..., code_object_id=...)`.
`path` may be a string or `Path`. The module does not scan directories, infer
ids from names, write files, or require `code.json`. Those behaviors belong to
source-tree scripts or RCV writers.
The main helper, `generate_code_artifacts(...)`, returns a `CodeArtifacts`
object containing:
- `code_index`: a `CodeIndex` ready to use as the decoder ISA provider.
- `source_paths`: source files discovered from DWARF comments, suitable for
the RCV snapshot writer when the caller wants viewer source snapshots.
The module reads DWARF line information with `pyelftools`, disassembles with
`llvm-objdump`, and extracts labels from ELF symbols. Function symbols and
debug labels become RCV label rows using the viewer-compatible `; _...` form.
No caller needs to run `generate_code.py` before using this API. Scripts that
want `code.json` can call `generate_code_artifacts(...)` and then explicitly
write the returned `CodeIndex`.
## `rocprof_trace_decoder.rcv`
`rcv.py` writes ROCprof Compute Viewer sidecar JSON. Its main class,
`RcvOutputWriter`, receives decoded records grouped by shader engine and writes
viewer-facing files such as:
- `filenames.json`
- `occupancy.json`
- `realtime.json`
- `se*_perfcounter.json`
- `se*_sm*_sl*_wv*.json`
- `other_simd_se*_*.json`
- `shaderdata_*_*.json`
The writer also calls `CodeIndex.accumulate_wave()` while writing wave files, so
the final stats CSV and viewer files are produced from the same decoded waves.
This module is best treated as an internal formatting layer. Its JSON schema is
chosen to match the ROCprof Compute Viewer conventions, not to be a general
Python API for trace analysis.
`RcvOutputWriter` accepts only the exact output formats `json` and `csv`.
Schema literals such as `navi`, `filenames.json`, and `se*_...` file names are
viewer-format details and intentionally stay in this module.
## `rocprof_trace_decoder.att`
`att.py` is the high-level orchestration module for turning already identified
ATT traces into final CSV/JSON outputs. It does not parse trace file names.
Callers must pass explicit `AttTrace` objects containing the trace path, shader
engine id, and run id.
The primary reusable function is `generate_att_outputs(...)`. It takes a small
set of explicit inputs and handles the standard output file naming:
- input traces: `AttTrace(path=..., shader_engine=..., run=...)`
- optional `CodeIndex`
- optional explicit `CodeObject(path=..., code_object_id=...)` inputs
- optional explicit source paths for RCV snapshots
- output directories: `ui_output_<name><run>`
- stats files: `stats_<output-dir-name>.csv`
The `formats` argument accepts `json`, `csv`, or both as a comma-separated
string or iterable. Unknown names are rejected instead of being matched as
substrings.
This boundary is intentional. The reusable API can parse an ATT buffer from any
file name. Naming conventions such as `*_shader_engine_<se>_<run>.att` belong in
scripts that know how rocprofiler wrote a specific output directory. Existing
`code.json` files are likewise a script concern: scripts may load them with
`CodeIndex.from_code_json(path)` and pass the resulting object into
`generate_att_outputs(...)`. Callers that have code object files do not need an
intermediate `code.json`; they can pass explicit `CodeObject` values to
`generate_att_outputs(...)`, or call `generate_code_artifacts(...)` themselves
and pass the resulting `CodeIndex`.
## `generate_code.py`
`generate_code.py` is a source-tree helper for writing the RCV static code
files. It reads GPU code objects, extracts disassembly with
`llvm-objdump`, reads DWARF line information with `pyelftools`, and writes:
- `code.json`
- `snapshots.json` when source files are found
- `source_*` copies for snapshotted source files
The script is not part of the installable API and is not required by
`rocprof_trace_decoder.att`. The reusable code object API lives in
`rocprof_trace_decoder.codegen`.
When code objects are not provided explicitly, the command scans the current
directory for `.hsaco`, `.out`, `.co`, and `.o` ELF files. The script maps those
files to explicit `CodeObject` values before calling the reusable API.
The reusable codegen implementation normalizes branch operands printed by
`llvm-objdump` as symbolic labels back to the raw SOPP immediate form expected
by the decoder stitcher. This keeps both `generate_code.py` output and
in-memory `CodeIndex` generation compatible with the decoder's PC stitching
behavior.
## `att_tool.py`
`att_tool.py` is the user-facing command-line script. It accepts a mixed list of
`.att`, `.out`, `.co`, `.hsaco`, `code.json`, and directory inputs. Code
objects alone generate only `code.json`, `snapshots.json`, and source snapshots.
ATT inputs do not require `code.json` or code object files at argument
validation time.
When ATT inputs are present, the script uses `code.json` or code objects only
when they are provided by the user or found through an explicitly provided
directory input. Otherwise it calls
`rocprof_trace_decoder.att.generate_att_outputs()` without ISA metadata.
Whether a given trace can be decoded without ISA metadata is determined by the
decoder library.
When an ATT file follows the conventional
`*_shader_engine_<se>_<run>.att` naming convention, the script uses that
metadata. Arbitrarily named `.att` files are also accepted; they default to
run `1` and receive shader-engine ids in discovery order, skipping ids already
claimed by conventional names. Users who need exact metadata for nonstandard
multi-SE captures can bypass the script and call `generate_att_outputs()` with
explicit `AttTrace` objects.
Developers can run the source-tree script directly:
```bash
python3 python/att_tool.py trace.att code_object.out
```
from the checkout without installing the package first.
## Typical Flow
The one-command ATT path is:
1. `att_tool.py` discovers `.att` traces, `code.json`, and code objects from
explicit CLI inputs and directory inputs.
2. `att_tool.py` calls `rocprof_trace_decoder.codegen` when code objects are
available and a new `CodeIndex` is needed. If there are no ATT traces, the
command writes only RCV static code files.
3. `att_tool.py` turns ATT trace paths into explicit `AttTrace` objects, using
trace filename metadata when present and deterministic defaults otherwise.
4. When code metadata is available, `CodeIndex` becomes the decoder ISA
provider.
5. `Decoder` parses each `.att` file and returns `TraceRecords`.
6. `RcvOutputWriter` writes viewer JSON files and updates `CodeIndex` stats.
7. `CodeIndex` writes the final updated `code.json` and `stats_*.csv`.
This division keeps the wrapper reusable: users can stop at `Decoder` and
`TraceRecords` for custom analysis, use `CodeIndex` for CSV/stat workflows, or
use `generate_att_outputs()` for the complete ROCprof Compute Viewer output
pipeline.