# Contributing to ROCm Compute Profiler ## Getting Started ROCm Compute Profiler lives under `projects/rocprofiler-compute` in the [ROCm Systems super-repo](https://github.com/ROCm/rocm-systems). To set up your local environment, follow the [clone and setup instructions](https://github.com/ROCm/rocm-systems/blob/develop/CONTRIBUTING.md#getting-started) in the rocm-systems CONTRIBUTING.md. Sparse checkout is recommended for most contributors. ## Reporting Issues and Bugs - Search [existing issues](https://github.com/ROCm/rocm-systems/issues) before filing a new one — your bug may already be tracked. - If you don't find an existing issue, [open a new one](https://github.com/ROCm/rocm-systems/issues/new) with a clear description of the problem and steps to reproduce it. ## Submitting a Pull Request Follow the [pull request guidelines](https://github.com/ROCm/rocm-systems/blob/develop/CONTRIBUTING.md#pull-request-guidelines) in the rocm-systems CONTRIBUTING.md. > **Note for external contributors:** Please refer to the [ROCm contribution guide](https://rocm.docs.amd.com/en/develop/contribute/contributing.html) for instructions on contributing from a fork. ### Review and Labeling Labels and reviewer assignments are handled automatically based on the files you've changed. Reviewers for `projects/rocprofiler-compute` are defined in the top-level [CODEOWNERS](https://github.com/ROCm/rocm-systems/blob/develop/CODEOWNERS) file. ### CI Requirements All pull requests must pass CI checks before merging. For `rocprofiler-compute`, these currently include compilation checks, with correctness and performance tests being added over time. See the [CI documentation](https://github.com/ROCm/rocm-systems/blob/develop/docs/continuous-integration.md) for a full breakdown of what runs on each PR. > [!TIP] > Run our pre-commit hooks locally before pushing to catch formatting issues early. See [Using Pre-Commit Hooks](#using-pre-commit-hooks) below for setup instructions. ## Adding Experimental Features New features that aren't yet stable can be introduced behind the `--experimental` flag. This lets users opt in to preview functionality while keeping the default experience stable. ### How It Works The `--experimental` flag acts as a master toggle: - Experimental options are **hidden** from help output unless `--experimental` is passed. - Attempting to use an experimental flag without `--experimental` raises a clear error. - A warning is displayed when an experimental feature is active. To see available experimental features: ```bash rocprof-compute profile --experimental --help ``` ### Adding a New Experimental Feature Follow these three steps to add a new experimental flag. **Step 1 — Register it in the `--experimental` help text** In `src/argparser.py`, update the `add_general_group()` function: ```python general_group.add_argument( "--experimental", action="store_true", default=False, help=( "Enable experimental feature(s):\n" " Your feature name (--your-flag)\n" # Add this line ), ) ``` **Step 2 — Add the argument using `ExperimentalAction`** Add your flag to the relevant parser group (profile, analyze, etc.): ```python # For a flag that accepts a value profile_group.add_argument( "--your-flag", dest="your_flag", required=False, default=None, action=ExperimentalAction, experimental_enabled=experimental_enabled, feature_label="Your feature name", base_action="store", # Required — see supported actions below type=str, nargs="*", metavar="", help="\t\t\tDescription of your feature", ) # For a boolean toggle flag analyze_group.add_argument( "--your-flag", dest="your_flag", required=False, default=False, action=ExperimentalAction, experimental_enabled=experimental_enabled, feature_label="Your feature description", base_action="store_const", # Required — see supported actions below nargs=0, const=True, help="\t\tDescription of your feature", ) ``` The `base_action` parameter is required and must be one of: | Value | Behavior | |---|---| | `store` | Store a value (standard argparse default) | | `store_const` | Store a fixed constant; consumes no arguments | | `store_true` | Store `True` when the flag is present | | `store_false` | Store `False` when the flag is present | | `append` | Append each value to a list | | `append_const` | Append a constant to a list | | `count` | Count occurrences (e.g. `-vvv`) | | `extend` | Extend a list with multiple values | **Step 3 — Verify behavior** Confirm the flag is hidden without `--experimental` and visible with it: ```bash # Should not appear rocprof-compute profile --help # Should appear with EXPERIMENTAL: prefix rocprof-compute profile --experimental --help ``` ### Promoting a Feature to Stable When a feature is ready for general availability: 1. Remove it from the `--experimental` help text in `src/argparser.py`. 2. Replace `action=ExperimentalAction` with a standard argparse action (e.g. `action="store"`). 3. Remove the `experimental_enabled`, `feature_label`, and `base_action` parameters. 4. Update documentation and tests accordingly. ## Using Pre-Commit Hooks Pre-commit hooks automatically check your code for formatting issues before each commit, helping you catch problems before they reach CI. **Setup:** First, install [development dependencies](README.md#development-dependencies), then enable the hooks: ```bash cd rocprofiler-compute pre-commit install ``` Once installed, every commit will run the configured checks automatically: ![A screen capture showing terminal output from a pre-commit hook](docs/data/contributing/pre-commit-hook.png) See the [pre-commit documentation](https://pre-commit.com/#quick-start) for more details. ## Code Style and Formatting ROCm Compute Profiler uses [Ruff](https://docs.astral.sh/ruff/) for linting and formatting. All contributions to `src/` must pass Ruff checks before merging. Pre-commit hooks handle this automatically. **Style references:** | Topic | Source of Truth | |-------|-----------------| | Function design, naming, code organization | [Python Coding Style Guidelines](PYTHON_CODING_STYLE.md) | | Ruff configuration (enforced rules, ignores, formatting) | [`pyproject.toml`](pyproject.toml) | | YAML metric equation formatting | [YAML Metric Equation Formatting](#yaml-metric-equation-formatting) | ### Running Ruff Manually ```bash # Check for issues ruff check . ruff format --check . # Auto-fix most issues ruff check --fix . ruff format . ``` ### YAML Metric Equation Formatting Metric equations in YAML config files follow a canonical format. The canonical implementation is [`tools/format_yaml.py`](tools/format_yaml.py). **Scope.** Equation formatting applies to YAML files under: - `src/rocprof_compute_soc/analysis_configs/gfx*/*.yaml` - `src/rocprof_compute_tui/utils/gfx*/*.yaml` Template files (`*_template.yaml`) and build artifacts are excluded. Only values under these keys are treated as equations: `value`, `avg`, `min`, `max`, `peak`. **Rules.** - **Operator spacing** — all binary operators (`+`, `-`, `*`, `/`) have exactly one space on each side: `SUM(x) / SUM(y)`. - **Constant factoring** — when an aggregation (`SUM`, `AVG`, `MIN`, `MAX`) multiplies by a numeric constant, factor it out: `128 * SUM(x)`, not `SUM(x * 128)`. Addition inside aggregations is left as-is. - **Minimal parentheses** — remove parentheses made redundant by operator precedence (`a + b + c`, not `(a + b) + c`), but preserve those required for correct evaluation (`a - (b - c)`, `(a + b) * c`). - **Unary negation** — negation of a compound expression keeps its parentheses: `-(a + b)`, not `-a + b`. - **Literal values** — `N/A`, `None`, `null`, `true`, `false`, and `Peak (Empirical)` are never parsed as equations and must be written exactly as shown (the space before `(` in `Peak (Empirical)` is required). Equations with unsupported syntax (unknown characters, unmatched parentheses) are left unchanged; the formatter never corrupts an equation it cannot fully parse. **Enforcement.** The `yaml-format-fix` pre-commit hook runs `tools/format_yaml.py --fix` on staged config files and auto-corrects equations in place. Re-stage the modified files and commit again. ```bash # Show proposed changes python tools/format_yaml.py --diff src/rocprof_compute_soc/analysis_configs/gfx950/*.yaml # Auto-fix in place python tools/format_yaml.py --fix src/rocprof_compute_soc/analysis_configs/gfx950/*.yaml ``` ## Documentation Changes For instructions on building and testing changes to files under the `docs/` folder, see the [ROCm documentation contributing guide](https://rocm.docs.amd.com/en/latest/contribute/contributing.html). ## Metrics Management If your PR modifies **metric configurations** — panel YAMLs under `src/rocprof_compute_soc/analysis_configs/gfx/*.yaml` or metric descriptions in `docs/data/metrics_description.yaml` — follow the metric management workflow: 1. Edit the relevant panel YAMLs. 2. Validate them with `python tools/config_management/master_config_workflow_script.py --validate-only`. 3. Refresh the hash DB with `python tools/config_management/hash_manager.py --compute-all src/rocprof_compute_soc/analysis_configs` and confirm CI tests pass. For full details, see the [metric config management README](./tools/config_management/README.md). ## Vendoring External Dependencies rocprofiler-compute vendors certain Python dependencies (via git submodules) to eliminate external dependencies in profile mode. This improves portability and reliability on HPC systems. **We vendor:** - Pure Python packages used in profile code path - Stable packages with permissive licenses For detailed vendoring workflow (adding/updating packages), see [`src/vendored/README.md`](./src/vendored/README.md). ## AI Agent Guidelines This project uses AI coding assistants (Claude Code, Cursor, GitHub Copilot). All AI-specific guidelines live in [`AGENTS.md`](AGENTS.md), which serves as the single source of truth. Tool-specific adapter files (e.g., `CLAUDE.md`, `.github/copilot-instructions.md`) reference `AGENTS.md` without duplicating content. To add or update AI guidelines, edit the appropriate file under `.ai/` and add a reference in `AGENTS.md`. ## Profile Mode Dependency Policy Profile mode code should not use non-standard Python libraries. ### Why This Matters Profile mode uses only stdlib to: 1. **Avoid dependency conflicts** - Users can profile without creating virtual environments or worrying about package version conflicts with their own projects 2. **Work everywhere** - No `pip install` needed: - HPC systems with locked-down Python environments - Security-sensitive systems requiring minimal dependencies - Any system with Python 3.8+ installed ### Enforcement **Python Version Requirement:** - Import enforcement requires Python 3.10+ (uses `sys.stdlib_module_names`) - Python 3.8-3.9: Tests run but import enforcement is disabled (warning issued) - CI uses Python 3.10+ to ensure full enforcement coverage Enforcement is automatically done for tests which execute profile pipeline using main function instead of subprocess (unlike tests using --call-binary and multi mpi rank tests). These tests are automatically protected by guards in `tests/conftest.py` when testing with Python 3.10 and above. The guard intercepts ALL imports (direct, nested, dynamic) and fails tests immediately if non-stdlib packages are imported. ### What's Allowed **Python standard library** (Python 3.8+): - `json`, `csv`, `sqlite3`, `subprocess`, `pathlib`, `logging`, `sys`, `os`, etc. **Project modules**: - `rocprof_compute`, `utils`, `vendored.*`, `roofline`, `config`, `argparser` **ROCm system libraries** (bundled with ROCm, not pip packages): - `amdsmi` - AMD System Management Interface - `hip` - HIP runtime Python bindings - `rocprofv3` - rocprofv3 Python bindings ### What's NOT Allowed These are forbidden in profile mode **External packages**: - `pandas`, `yaml`, `numpy`, `plotly`, `dash`, `textual`, etc. - Anything from `requirements.txt` ### Common Mistakes **Don't do this in profile code:** ```python import pandas # External package import yaml # Use json or vendored.pyyaml instead import numpy # Use stdlib math/statistics ``` **Do this instead:** ```python import json # Stdlib for config/data import csv # Stdlib for CSV operations import sqlite3 # Stdlib for data manipulation from vendored.pyyaml import yaml # Vendored package (if needed) ``` ### If Your Test Fails **Error message:** ``` PROFILE MODE DEPENDENCY VIOLATION Forbidden package: pandas ``` **How to fix:** 1. **Move import to analyze mode** - Move the import and relevant code to analysis mode 2. **Use stdlib alternative** - `pandas` → `csv` module + `sqlite3` for dataframes - `yaml` → `json` module (or `vendored.pyyaml` if YAML required) - `numpy` → `math`/`statistics` modules