Files
project_6/cccl_upstream/ci/compile_time/prepare_traces.py
muh-bot 2a7ca101d7 feat(cccl): integrate missing CCCL directories — python/, ci/, .agent/, docs/, test/
Sparse-checkout from NVIDIA/cccl main branch to complete cccl_upstream:

Added:
- python/cuda_cccl/ (226 files) — Python bindings for device-level algorithms
  Critical for muh toolchain: cuda.compute.reduce_into, scan, radix_sort, etc.
  Includes 204 .py files with full test coverage for all 27 algorithms
- ci/ (163 files) — Build/test infrastructure
  build_cub.sh, test_cub.sh, build_and_test_targets.sh, matrix.yaml
  Directly maps to our [INFRA-CI] and [INFRA-BUILD] items
- .agent/skills/ (7 files) — NVIDIA's own agent skills for CCCL
  cccl-style/SKILL.md, cccl-test/SKILL.md, sass-diff/SKILL.md
- docs/ (491 files) — Official CCCL documentation
  CI references, CMake guides, Python compute docs, libcudacxx PTX docs
- test/ (12 files) — Top-level integration tests (cuda_smoke, stdpar)
- Root configs: .clang-format, .clang-tidy, CONTRIBUTING.md, pyproject.toml
- CLAUDE.md symlink → AGENTS.md (NVIDIA's standard)

cccl_upstream now mirrors full NVIDIA/cccl structure:
  Before: 42M (cub + thrust + libcudacxx + cudax + c + examples + benchmarks)
  After:  53M (+python +ci +docs +.agent +test +configs)

This completes the CCCL base needed for:
- [muh-bench] items: ci/util/build_and_test_targets.sh for targeted builds
- [CCCL-verify] items: python/cuda_cccl/tests/ as reference implementations
- [CCCL-test] items: ci/test_cub.sh, ci/test_thrust.sh
- Agent workflow: .agent/skills/ for consistent style and test patterns
2026-08-07 02:34:33 +00:00

168 lines
4.6 KiB
Python
Executable File

#!/usr/bin/env python3
import argparse
import json
from pathlib import Path
DETAIL_EVENT_NAMES = {
"Code Generation Function",
"CodeGen Function",
"ExecuteCompiler",
"Frontend",
"Instantiating Template Class",
"Instantiating Template Function",
"InstantiateClass",
"InstantiateFunction",
"OptFunction",
"ParseClass",
"PerformPendingInstantiations",
"Processing Header File",
"RunPass",
"Scanning Function Body",
"Source",
}
DETAIL_PREFIXES_TO_STRIP = (
"libcudacxx/include/",
"cudax/include/",
"c/parallel/include/",
)
DETAIL_PREFIXES_TO_COLLAPSE = (
("cub/cub/", "cub/"),
("thrust/thrust/", "thrust/"),
)
def normalize_detail(detail: str, repo_root: Path) -> str:
detail_path = Path(detail)
if detail_path.is_absolute():
try:
rel = detail_path.resolve(strict=False).relative_to(repo_root)
detail = rel.as_posix()
except ValueError:
pass
for prefix in DETAIL_PREFIXES_TO_STRIP:
if detail.startswith(prefix):
detail = detail[len(prefix) :]
break
for prefix, replacement in DETAIL_PREFIXES_TO_COLLAPSE:
if detail.startswith(prefix):
detail = replacement + detail[len(prefix) :]
break
return detail
def display_detail(detail: str, repo_root: Path, max_detail_len: int | None) -> str:
detail = normalize_detail(detail, repo_root)
if (
max_detail_len is not None
and max_detail_len > 0
and len(detail) > max_detail_len
):
return detail[: max_detail_len - 1] + "..."
return detail
def rewrite_event_name(
event: dict, repo_root: Path, max_detail_len: int | None
) -> bool:
name = event.get("name")
if name not in DETAIL_EVENT_NAMES:
return False
args = event.get("args")
if not isinstance(args, dict):
return False
detail = args.get("detail")
if not detail:
return False
args.setdefault("original_name", name)
event["name"] = f"{name}: {display_detail(str(detail), repo_root, max_detail_len)}"
return True
def prepare_trace(
input_path: Path, output_path: Path, repo_root: Path, max_detail_len: int | None
) -> int:
with input_path.open(encoding="utf-8") as f:
trace = json.load(f)
rewritten = 0
for event in trace.get("traceEvents", []):
if rewrite_event_name(event, repo_root, max_detail_len):
rewritten += 1
output_path.parent.mkdir(parents=True, exist_ok=True)
with output_path.open("w", encoding="utf-8") as f:
json.dump(trace, f, separators=(",", ":"))
return rewritten
def iter_input_traces(input_path: Path) -> list[Path]:
if input_path.is_file():
return [input_path]
return sorted(input_path.rglob("*.json"))
def output_path_for(input_trace: Path, input_root: Path, output_path: Path) -> Path:
if input_root.is_file():
if output_path.is_dir() or not output_path.suffix:
return output_path / f"{input_trace.stem}.perfetto.json"
return output_path
rel = input_trace.relative_to(input_root)
return output_path / rel.parent / f"{rel.stem}.perfetto.json"
def main() -> None:
parser = argparse.ArgumentParser(
description="Prepare NVCC device-time-trace JSON files for Perfetto by promoting args.detail into event names."
)
parser.add_argument(
"--input", required=True, type=Path, help="Input trace JSON file or directory"
)
parser.add_argument(
"--output", required=True, type=Path, help="Output trace JSON file or directory"
)
parser.add_argument(
"--repo-root", default=Path(__file__).resolve().parents[2], type=Path
)
parser.add_argument(
"--max-detail-len",
default=0,
type=int,
help="Truncate promoted detail text to this many characters; 0 keeps full details",
)
args = parser.parse_args()
input_path = args.input.resolve(strict=False)
output_path = args.output.resolve(strict=False)
repo_root = args.repo_root.resolve(strict=False)
max_detail_len = args.max_detail_len if args.max_detail_len > 0 else None
traces = iter_input_traces(input_path)
if not traces:
raise SystemExit(f"no JSON traces found under {args.input}")
total_rewritten = 0
for trace_path in traces:
total_rewritten += prepare_trace(
trace_path,
output_path_for(trace_path, input_path, output_path),
repo_root,
max_detail_len,
)
print(f"prepared {len(traces)} trace(s); renamed {total_rewritten} event(s)")
if __name__ == "__main__":
main()