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
178 lines
7.7 KiB
Markdown
178 lines
7.7 KiB
Markdown
# Hotswap failure reducer
|
|
|
|
`utils/hotswap/hotswap_reduce.py` turns a failing hotswap corpus bundle into a
|
|
small, standalone reproducer. It only accepts a transformation when a
|
|
caller-provided interestingness command continues to return the configured exit
|
|
code. The original inputs are never modified and the output directory is
|
|
published atomically.
|
|
|
|
The reducer works from the outside in:
|
|
|
|
1. Delta-debug the list of code objects.
|
|
2. Delta-debug the top-level `kernels`, `cases`, and `arguments` metadata
|
|
lists, followed by each retained case's `arguments` list.
|
|
3. Optionally remove explicitly allowed, non-allocated ELF sections with
|
|
`llvm-objcopy`.
|
|
4. Repeat the hierarchy until a complete pass makes no change. This gives each
|
|
retained dimension another reduction opportunity after lower-level changes.
|
|
|
|
The section pass gets section names and flags from `llvm-readobj` JSON. It does
|
|
not parse ELF itself. Allocated sections and the default runtime-critical
|
|
section set are always protected. Section removal is disabled unless at least
|
|
one `--allow-remove-section` glob is provided. If multiple sections have the
|
|
same name, one allocated or protected instance protects every instance because
|
|
`llvm-objcopy` removes sections by name. Every transformed output is parsed
|
|
again to verify that it is still AMDGPU ELF, every requested section is gone,
|
|
and no unrequested section disappeared.
|
|
|
|
## Interestingness command
|
|
|
|
The command is an argv vector, not a shell string. Pass the executable with
|
|
`--predicate` and repeat `--predicate-arg` for its arguments. These literal
|
|
placeholders are expanded for each candidate:
|
|
|
|
- `{bundle}`: candidate `bundle.json`
|
|
- `{input}`: the sole candidate code object (only valid with one object)
|
|
- `{metadata}`: candidate `metadata.json`
|
|
- `{workspace}`: candidate directory
|
|
|
|
Exit zero means interesting by default. Use `--interesting-exit-code` when the
|
|
reproducer intentionally reports a failure with a nonzero exit. A timeout,
|
|
launch error, or exit-code disagreement between `--predicate-runs` repetitions
|
|
rejects a candidate. Predicate output retained in the log is bounded to avoid
|
|
unbounded memory use. Timed-out predicate process trees are terminated before
|
|
their temporary workspace is removed.
|
|
|
|
Stable outcomes are cached by the complete candidate content and predicate
|
|
configuration; `--cache-file` makes that cache persistent. The resolved
|
|
predicate executable and regular-file argv items are identified by path,
|
|
content, and file mode. Repeat `--cache-dependency` for dynamically loaded
|
|
libraries, reference data, or other file inputs, and repeat `--cache-tag` for
|
|
relevant environment or configuration identity. A dependency that changes
|
|
during a run aborts the reduction instead of consulting a stale cache entry.
|
|
Predicates must be deterministic and must not rely on side effects outside
|
|
their candidate workspace because a cache hit replaces the process execution.
|
|
|
|
For example:
|
|
|
|
```console
|
|
python3 utils/hotswap/hotswap_reduce.py \
|
|
--bundle failing-bundle.json \
|
|
--output reduced \
|
|
--predicate /usr/bin/python3 \
|
|
--predicate-arg=/absolute/path/is_interesting.py \
|
|
--predicate-arg='{bundle}' \
|
|
--predicate-runs 3 \
|
|
--timeout 20 \
|
|
--allow-remove-section='.debug_*' \
|
|
--allow-remove-section='.comment'
|
|
```
|
|
|
|
No argument is evaluated by a shell. Supplying an absolute predicate and
|
|
absolute script path makes the recorded reproduction argv unambiguous on the
|
|
same filesystem. Existing regular-file argv items are resolved to absolute
|
|
paths before the first predicate run.
|
|
|
|
## PR #3646 offline differential workflow
|
|
|
|
An A0 runtime failure is not required. The predicate can be any deterministic
|
|
offline check, which makes the reducer useful while validating the hotswap
|
|
implementation in PR #3646. For a discrepant hipBLASLt or hipSOLVER case,
|
|
first put the involved code objects and launch records in a regular bundle.
|
|
Then use one of these predicate shapes:
|
|
|
|
- A wrapper rewrites the candidate with the PR #3646 build, emits its
|
|
structural manifest, compares it with a fixed PR #3598 reference manifest,
|
|
and exits with the interesting code while a difference remains.
|
|
- A wrapper runs `hotswap-audit` over every retained object and exits with the
|
|
interesting code while the audit reports an invariant violation.
|
|
- A wrapper runs `hotswap-semcheck` for the retained kernels and exits with the
|
|
interesting code while it returns a counterexample.
|
|
|
|
For example, an offline manifest differential can be driven as:
|
|
|
|
```console
|
|
python3 utils/hotswap/hotswap_reduce.py \
|
|
--bundle hipblaslt-discrepancy.json \
|
|
--output reduced-3646-difference \
|
|
--predicate /absolute/path/manifest-diff-predicate \
|
|
--predicate-arg=/absolute/path/pr3598-reference.json \
|
|
--predicate-arg='{bundle}' \
|
|
--predicate-runs 2
|
|
```
|
|
|
|
The wrapper owns the meaning of the comparison and the selected build. The
|
|
reducer only sees an argv vector and an exit code, so no PR-specific opcode,
|
|
kernel name, or library test name is encoded in its reduction logic. Keep the
|
|
reference manifest outside the candidate bundle so ddmin cannot reduce the
|
|
oracle itself.
|
|
|
|
## Input and output bundle
|
|
|
|
Input bundles use this versioned JSON form. Object paths and a string-valued
|
|
`metadata` path are relative to the bundle file and cannot escape its directory,
|
|
including through a symlink. Use direct `--code-object` or `--worklist` input
|
|
when the object files intentionally live elsewhere.
|
|
|
|
```json
|
|
{
|
|
"format": "comgr-hotswap-reducer-bundle",
|
|
"version": 1,
|
|
"code_objects": [
|
|
{"id": "hipblaslt-case-17", "path": "objects/case 17.co"}
|
|
],
|
|
"metadata": {
|
|
"kernels": [{"name": "kernel"}],
|
|
"cases": [{"name": "case", "arguments": [1, 2, 3]}],
|
|
"arguments": []
|
|
}
|
|
}
|
|
```
|
|
|
|
`--code-object` can be repeated instead of providing a bundle, with optional
|
|
metadata from `--metadata`.
|
|
|
|
An inventory or test-selection pipeline can provide a NUL-delimited worklist:
|
|
|
|
```console
|
|
python3 utils/hotswap/hotswap_reduce.py \
|
|
--worklist unique-code-objects.list \
|
|
--metadata selection.json \
|
|
--output reduced \
|
|
--predicate /absolute/path/offline-predicate \
|
|
--predicate-arg='{bundle}'
|
|
```
|
|
|
|
Every worklist entry is treated as a path, never shell text. Absolute paths
|
|
from the inventory utility work directly; relative paths are resolved from the
|
|
worklist's directory. Empty, unterminated, or duplicate entries are rejected.
|
|
A selector document can be passed directly as metadata. In addition to
|
|
`kernels`, `cases`, and `arguments`, a top-level `selected_tests` list is
|
|
delta-debugged generically; all unknown selector fields are preserved.
|
|
|
|
The output contains:
|
|
|
|
- `bundle.json`, `metadata.json`, and the retained objects under `objects/`
|
|
- `reduction-log.json`, a versioned deterministic record of every accepted or
|
|
rejected transformation
|
|
- a final reproduction argv and interesting exit code in that log
|
|
|
|
The log also records original and final content digests. It intentionally
|
|
contains no timestamps, elapsed times, or cache-hit state so identical inputs
|
|
and stable predicates produce identical logs.
|
|
|
|
## Safety limits
|
|
|
|
- Existing output directories are never overwritten.
|
|
- Cache files cannot overwrite an input (including external bundle metadata),
|
|
predicate identity, dependency, or live inside the atomically published
|
|
output directory.
|
|
- Malformed bundles and metadata fail before a predicate runs.
|
|
- Section pruning requires AMDGPU ELF input and available LLVM tools.
|
|
- Protected or allocated sections cannot be opted into removal.
|
|
- Temporary candidates are removed after interruption, timeout, or failure.
|
|
- `llvm-reduce` is not run on linked ELF code objects; it is an IR/MIR reducer.
|
|
Use it before linking when a retained bundle also has LLVM IR or MIR source.
|
|
- Persistent cache writes are atomic but are not merged across concurrent
|
|
reducer processes; give each concurrent process a distinct cache file.
|