Files
TheRock/docs/development/ccache_troubleshooting.md
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

331 lines
14 KiB
Markdown

# CCache Troubleshooting
This page covers how to diagnose and fix ccache hit rate issues in TheRock CI.
## Introduction
[ccache](https://ccache.dev/) is a compiler cache that speeds up
recompilation by caching previous compilation results and detecting when
the same compilation is being done again. TheRock uses ccache for both
local developer builds and CI builds, with a shared remote cache server
so that cache entries can be reused across CI runners.
[sccache](https://github.com/mozilla/sccache) is an alternative compiler
cache with similar goals. We are evaluating both ccache and sccache for
use across ROCm and downstream frameworks like PyTorch (see
`build_tools/setup_sccache_rocm.py`). The goal is to standardize on a
cache setup that works well across our full ecosystem of projects using
shared infrastructure (e.g., cache servers). This page focuses on ccache,
which is the current default for TheRock CI.
### Key files
| File | Description |
| ------------------------------------------------------------------------------------------------ | ----------------------------------------------- |
| [`build_tools/setup_ccache.py`](../../build_tools/setup_ccache.py) | Generates ccache config for local and CI builds |
| [`build_tools/posix_ccache_compiler_check.py`](../../build_tools/posix_ccache_compiler_check.py) | Custom compiler fingerprinting for POSIX |
| [`build_tools/hack/ccache/`](../../build_tools/hack/ccache/) | Sanity tests and analysis scripts |
## CI infrastructure
### Remote cache servers
CI uses [bazel-remote](https://github.com/buchgr/bazel-remote) as a shared
remote cache, accessed via ccache's `remote_storage` option with
`layout=bazel`:
| Preset | Server | Used by |
| -------------------- | ----------------------------------------------------------- | --------------------- |
| `github-oss-dev` | `bazelremote-svc.bazelremote-ns.svc.cluster.local:8080` | PR builds, postsubmit |
| `github-oss-release` | `bazelremote-svc-rel.bazelremote-ns.svc.cluster.local:8080` | Release builds |
Both servers are on the Kubernetes cluster, accessible without
authentication from any pod in the cluster.
### Namespace version
`CCACHE_NAMESPACE_VERSION` in `setup_ccache.py` controls the cache
namespace. Bump it when making hash-affecting config changes (sloppiness,
compiler_check, etc.) to isolate from stale entries. All entries in the
old namespace become unreachable — the cache starts cold, so the first
CI run after a bump will be all misses.
Currently a single namespace is shared across all repos, platforms, and
build configurations. ccache's own hashing (compiler binary, flags,
source content) partitions entries naturally, but if cross-repo or
cross-platform pollution becomes an issue, per-repo or per-platform
namespaces could be introduced.
### Platform differences
| | Linux | Windows |
| ---------------- | ------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
| ccache version | Pinned in [manylinux Dockerfile](../../dockerfiles/build_manylinux_x86_64.Dockerfile) (4.11.2 as of 2026-05) | Installed via chocolatey in the [Windows workflow](../../.github/workflows/multi_arch_build_windows_artifacts.yml) (4.13.6 as of 2026-05) |
| `compiler_check` | Custom script (hashes binary + shared libs via sha256sum) | `content` (hashes binary content) |
| Build container | Same manylinux Docker image for all jobs | Ephemeral VMs with unique workspace GUIDs |
| Workspace path | `/__w/TheRock/TheRock/` (consistent) | `C:\home\runner\_work\TheRock\TheRock\` (consistent) |
| Build directory | `/__w/TheRock/TheRock/build/` | `B:\build` (volume mount) |
### Windows drive mounts
Windows CI runners use Azure VMs where `B:\` is a temporary disk mounted
as a volume mount point to `C:\{GUID}\`. The GUID is unique per VM, so
`B:\build` resolves to different absolute paths on different runners.
The `-resource-dir` flag is explicitly passed to clang in the toolchain
setup (`cmake/therock_subproject.cmake`) to prevent clang from
auto-detecting its resource directory through the resolved mount path.
### Cross-repo cache sharing
Multiple ROCm repos (TheRock, SPIRV-LLVM-Translator, rocm-libraries,
rocm-systems, etc.) use the same `setup_ccache.py` and share the same
bazelremote cache servers. Each repo gets a different workspace path
(e.g., `/_work/{repo}/{repo}` on Linux, `C:\home\runner\_work\{repo}\{repo}`
on Windows). The namespace isolates entries to some extent, but be aware
of cross-repo pollution — entries from one repo may share manifests with
another if they compile the same source files with compatible flags.
### Release builds use the local cache only (no remote)
Prerelease (release-candidate) and `nightly-bkc` builds use only the local
cache. The `--release-type prerelease` and `--release-type nightly-bkc` options
automatically select the `local` config preset. Users can select the same
local-only behavior explicitly with `--config-preset=local`.
Stable releases are a repackage of the prerelease artifacts and reading only
from the local cache guards against cache poisoning attacks. The local cache is
kept because some builds (notably Windows) need at least a local cache for
performance and runner stability.
Expect zero remote hits in prerelease and `nightly-bkc` "Report" steps; local
hits are expected and fine. Nightly, `dev-bkc`, `dev`, and `ci` builds are
unaffected and continue to use a shared remote cache.
> [!TIP]
> To confirm which cache a run actually used, look for the `Cache mode` line in
> the "Setup ccache" step. It is read back from the generated `ccache.conf`, so
> it reports what was written rather than only the requested options:
>
> ```text
> [setup_ccache] Cache mode: release_type=prerelease preset=local remote=disabled local=/.../ccache
> [setup_ccache] Cache mode: release_type=nightly-bkc preset=local remote=disabled local=/.../ccache
> [setup_ccache] Cache mode: release_type=nightly preset=github-oss-release remote=http://bazelremote-svc-rel...:8080|layout=bazel|connect-timeout=50 local=/.../ccache
> ```
## Downloading and inspecting CI logs
### Finding a workflow run
The helper scripts in `build_tools/` can find runs and artifacts
automatically:
```bash
# Find the latest successful run for an artifact group
python build_tools/find_latest_artifacts.py --artifact-group gfx110X-all -v
# Find artifacts for a specific commit
python build_tools/find_artifacts_for_commit.py --commit abc123 \
--artifact-group gfx110X-all
```
For more manual exploration using the GitHub API directly:
```bash
# Find recent multi-arch CI runs
gh api "repos/ROCm/TheRock/actions/workflows/210763103/runs?per_page=10" \
-q '.workflow_runs[] | "\(.id) \(.created_at[:16]) \(.conclusion) \(.head_sha[:10])"'
# Find math-libs jobs in a specific run
gh api "repos/ROCm/TheRock/actions/runs/{RUN_ID}/jobs?per_page=100" --paginate \
-q '.jobs[] | select(.name | contains("math-libs") and contains("gfx1151")) | "\(.id) \(.name) \(.conclusion)"'
```
### Checking ccache stats from a job
The ccache stats are printed in the "Report" step of each build job:
```bash
gh api repos/ROCm/TheRock/actions/jobs/{JOB_ID}/logs 2>/dev/null \
| grep -A 25 "Cacheable calls"
```
### Downloading ccache logs from S3
Build logs are uploaded to S3 as artifacts. The ccache log archive
contains `ccache.log` (detailed per-compilation log) and
`ccache_stats.log` (per-file statistics).
```
# URL pattern
https://therock-ci-artifacts.s3.amazonaws.com/{RUN_ID}-{PLATFORM}/logs/{STAGE}/{GFX_FAMILY}/ccache_logs.tar.zst
# Example
https://therock-ci-artifacts.s3.amazonaws.com/25465494022-windows/logs/math-libs/gfx1151/ccache_logs.tar.zst
```
To download and extract:
```bash
curl -sL -o ccache_logs.tar.zst \
"https://therock-ci-artifacts.s3.amazonaws.com/{RUN_ID}-windows/logs/math-libs/gfx1151/ccache_logs.tar.zst"
```
```python
# Extract (requires zstandard package)
import tarfile, io, zstandard
dctx = zstandard.ZstdDecompressor()
with open("ccache_logs.tar.zst", "rb") as f:
reader = dctx.stream_reader(f)
with tarfile.open(fileobj=reader, mode="r|") as tf:
tf.extractall("output_dir", filter="tar")
```
### Using the analysis scripts
```bash
# Analyze a CI run's ccache logs (downloads from S3 automatically)
python build_tools/hack/ccache/analyze_ccache_logs.py \
--run-id 25465494022 --stage math-libs --gfx gfx1151
# Analyze a local log file
python build_tools/hack/ccache/analyze_ccache_logs.py \
--log-file /path/to/ccache.log
# Compare hit rates per subproject between two logs (e.g., Linux vs Windows)
python build_tools/hack/ccache/compare_ccache_by_project.py \
/path/to/linux/ccache.log /path/to/windows/ccache.log
```
### Other log files
The S3 index page lists all available logs for a run:
```
https://therock-ci-artifacts.s3.amazonaws.com/{RUN_ID}-{PLATFORM}/logs/{STAGE}/{GFX_FAMILY}/index.html
```
This includes per-subproject build/configure/install logs, ninja logs,
and the ccache logs archive.
## Symptoms of poor cache hit rates
### Symptom: Low overall hit rate
Check the "Report" step output. A healthy warm-cache run should show
~95%+ hit rates for cacheable calls.
**What to check:**
1. **Is the cache warm?** The first run after a namespace bump or config
change will be all misses. Check the second run.
1. **Which compiler is missing?** Use `analyze_ccache_logs.py` to break
down hits/misses by compiler binary. `cl.exe` and `clang++` may have
different hit rates.
### Symptom: "can't be read" entries in ccache log
```
C:\{GUID}\build\...\__stdarg_va_copy.h is mentioned in a manifest
entry but can't be read (No such file or directory)
```
This means manifest entries reference absolute paths from a different
runner that no longer exist on the current runner.
**Likely cause:** Path instability — the build directory resolves to a
different absolute path on each runner (e.g., GUID-based volume mounts
on Windows).
**Fix:** Ensure all tools see and use stable paths. Passing an explicit
`-resource-dir` to clang (in `therock_subproject.cmake`) can help.
### Symptom: Manifest found but no entries match
ccache finds the manifest (same source + command line + compiler hash)
but all result entries fail verification.
**Likely causes:**
- **Unstable include file paths:** Header paths change between runs
(see "can't be read" above)
- **Generated headers with volatile content:** Version headers that
embed git hashes or timestamps change on every build. See
`THEROCK_FLAG_STAMP_LIBRARY_GIT_VERSIONS` in `FLAGS.cmake` — this
should be OFF for non-release builds to avoid embedding git hashes
in `hip_version.h`.
- **Re-staged headers with fresh mtime:** The build system may rewrite
headers during the stage/install step even when content is unchanged.
See [issue #5009](https://github.com/ROCm/TheRock/issues/5009).
### Symptom: Different manifest keys across runs for the same file
The direct hash (manifest key) differs between runs even for unchanged
source files.
**Likely causes:**
- **GUID in compiler flags:** Check if `-D` defines or other flags
contain runner-specific paths (e.g., `-DHIP_COMPILER_FLAGS` embedding
the resolved `C:\{GUID}\...` path)
- **Different compiler binary:** If the compiler is rebuilt from source
and `compiler_check = content` is used, verify the binary is
byte-identical across runs. Use the analysis scripts or compare
SHA256 hashes of the compiler binary from the CI artifacts.
- **Different ccache version:** Different ccache versions use different
hash formats. Entries from one version can't be used by another.
## Validating fixes
### Verifying compiler binary reproducibility
Download the compiler binary from two different CI runs and compare:
```bash
# Download from S3 (streams ~600MB, extracts just clang++)
python -c "
import tarfile, io, urllib.request, zstandard, hashlib
url = 'https://therock-ci-artifacts.s3.amazonaws.com/{RUN_ID}-windows/amd-llvm_run_generic.tar.zst'
with urllib.request.urlopen(url) as resp:
data = resp.read()
dctx = zstandard.ZstdDecompressor()
reader = dctx.stream_reader(io.BytesIO(data))
with tarfile.open(fileobj=reader, mode='r|') as tf:
for member in tf:
if 'clang++.exe' in member.name and member.isfile():
f = tf.extractfile(member)
print(f'{hashlib.sha256(f.read()).hexdigest()} {member.name}')
break
"
```
Both runs should produce the same SHA256. If they don't, check that
`/Brepro` is being applied (see `therock_subproject.cmake`).
### Verifying path stability
Check the ccache log for GUID or runner-specific paths:
```bash
# Count "can't be read" entries (should be 0)
grep -c "can't be read" ccache.log
# Check for GUID patterns in the log
grep -oP "[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}" ccache.log | sort -u
# Check the setup_ccache.py log output for resolved paths
grep "setup_ccache" job_log.txt
```
### Verifying flag stability
Compare command lines for the same source file across two runs using the
ccache logs. Key things to check:
- `-I` and `-isystem` paths should be consistent
- `-D` defines should not contain runner-specific values
- `-resource-dir`, `--hip-path`, and `--hip-device-lib-path` should use stable
paths