Skip to content

[TRTLLM-12838][infra] enhance function-level code coverage for catching subprocess data#16229

Open
crazydemo wants to merge 6 commits into
NVIDIA:mainfrom
crazydemo:cbts-coverage-utils
Open

[TRTLLM-12838][infra] enhance function-level code coverage for catching subprocess data#16229
crazydemo wants to merge 6 commits into
NVIDIA:mainfrom
crazydemo:cbts-coverage-utils

Conversation

@crazydemo

@crazydemo crazydemo commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Added CBTS-based, per-test function coverage collection for eligible post-merge GPU test stages.
    • Added consolidated SQLite, JSON, and HTML coverage reports for easier analysis.
    • Added coverage artifact collection and upload for CI results.
  • Bug Fixes

    • Improved coverage handling for MPI workers and subprocesses.
    • Coverage failures remain non-blocking and are safely skipped when no data is available.
  • Documentation

    • Added setup, usage, output, and troubleshooting guidance for CBTS coverage tools.

Description

Test Coverage

PR Checklist

Please review the following before submitting your PR:

  • PR description clearly explains what and why. If using CodeRabbit's summary, please make sure it makes sense.

  • PR Follows TRT-LLM CODING GUIDELINES to the best of your knowledge.

  • Test cases are provided for new code paths (see test instructions)

  • If PR introduces API changes, an appropriate PR label is added - either api-compatible or api-breaking. For api-breaking, include BREAKING in the PR title.

  • Any new dependencies have been scanned for license and vulnerabilities

  • CODEOWNERS updated if ownership changes

  • Documentation updated as needed

  • Update tava architecture diagram if there is a significant design change in PR.

  • The reviewers assigned automatically/manually are appropriate for the PR.

  • Please check this after reviewing the above items as appropriate for this PR.

GitHub Bot Help

To see a list of available CI bot commands, please comment /bot help.

…ge via sys.monitoring

Collect per-test code coverage on single-GPU L0 stages to feed change-based test selection.
A sys.monitoring PY_START tracker (cbts_pystart.py) records, per test, the product functions
each test enters; overhead scales with functions entered, not lines, so it is far cheaper than
line tracing. sitecustomize bootstraps the tracker in every process (incl. subprocesses /
mpi4py pool workers / long-lived servers); cbts_plugin drives per-test context switching.
Per-process data is written as binary SQLite (skipped by the artifact guardword/secret
scanners) and merged by pystart_report.py into an indexed touch DB, a per-file split HTML
report, and a file/function coverage rate against tensorrt_llm/.

Gated by isCbtsStage() to single-GPU non-perf/TensorRT/CPP/AutoDeploy stages (multi-GPU
enabled incrementally later); ENABLE_CBTS_COVERAGE is a global kill-switch.

Signed-off-by: Ivy Zhang <25222398+crazydemo@users.noreply.github.com>
…bprocess imports

The cbts-mpi-patcher daemon imported pytest (via cbts_plugin) at process startup,
concurrently with a plain CLI subprocess's own imports (e.g. trtllm-bench importing
tensorrt_llm -> huggingface_hub). CPython's partial-module deadlock-avoidance then
handed the main thread a half-initialized huggingface_hub.utils -> NameError name sys
is not defined, failing test_offline_benchmark.

Fix: (1) cbts_plugin binds pytest and its hooks only when pytest is already loaded, so
importing it for install_mpi_pool_patch never drags in the pytest graph; (2) the
mpi-patcher daemon defers every import until the target modules are already in
sys.modules, so it never runs a concurrent import during the host's startup phase;
(3) harden the PY_START callback and tracker startup so a tracker fault can never
propagate into host code.

Signed-off-by: Ivy Zhang <25222398+crazydemo@users.noreply.github.com>
The full single-GPU run produces thousands of per-process .cbtscov.*.sqlite files with millions of touch records; pystart_report loaded them all into in-memory dicts/sets (per_test + file_funcs), which at full scale can reach multiple GB. Stream every input file straight into the output SQLite (dedup on disk via a UNIQUE index, one file's rows in memory at a time), compute counts/rate with SQL, and render the split HTML by querying per file. Peak RSS is ~29 MB for a 4M-row / 464 MB stress input. CLI and outputs unchanged.

Signed-off-by: Ivy Zhang <25222398+crazydemo@users.noreply.github.com>
…uardword scan

The merged cbts_touchmap.sqlite was uploaded raw. SQLite stores its TEXT columns as
plaintext, so the per-test (file, qualname) rows carry product paths (wan, marlin, ...)
verbatim in the file bytes; the publish-artifacts guardword scanner byte-matches raw
uploaded files and flagged them. The scanner does not recurse into archives (the HTML
report .tar.gz with the same qualnames was never flagged), so bundle the touch DB into
cbts_pystart_report.tar.gz alongside the report and upload only the tarball. Correct the
stale 'binary sqlite is skipped' notes in cbts_pystart.py and README.

Signed-off-by: Ivy Zhang <25222398+crazydemo@users.noreply.github.com>
@crazydemo crazydemo requested review from a team as code owners July 10, 2026 08:08
…c sed

Collapse the multi-line comment blocks across the CBTS coverage utils and
Jenkins glue to single lines, fix stale .json/post-merge references, and
remove the no-op @TRTLLM_SRC_PATH@ sed (the template has no such placeholder).

Signed-off-by: Ivy Zhang <25222398+crazydemo@users.noreply.github.com>
@crazydemo crazydemo force-pushed the cbts-coverage-utils branch from eb56591 to ed6dd1c Compare July 10, 2026 08:10
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The PR replaces generic coverage collection with CBTS PY_START coverage, adds per-test runtime tracking and reporting utilities, gates instrumentation to eligible Jenkins stages, and publishes merged CBTS touch maps and HTML reports.

Changes

CBTS coverage

Layer / File(s) Summary
Pipeline gating and launch wiring
jenkins/L0_Test.groovy, jenkins/scripts/cbts/coverage_utils/coveragerc.template, jenkins/scripts/cbts/coverage_utils/make_coveragerc.sh, jenkins/scripts/slurm_run.sh
Eligible post-merge stages render CBTS configuration, enable the pytest plugin, collect per-stage artifacts, and update wheel-path substitution.
PY_START runtime tracking
jenkins/scripts/cbts/coverage_utils/cbts_pystart.py, jenkins/scripts/cbts/coverage_utils/sitecustomize.py
Runtime instrumentation records per-test function entries, handles context changes and forks, persists SQLite data, and manages background saves and marker polling.
Pytest and MPI test attribution
jenkins/scripts/cbts/coverage_utils/cbts_plugin.py
The pytest plugin sets test contexts, writes marker files, and extends MPI worker environment propagation.
Coverage merge and publication
jenkins/scripts/cbts/coverage_utils/pystart_report.py, jenkins/L0_MergeRequest.groovy, jenkins/scripts/cbts/coverage_utils/README.md
Per-process SQLite inputs are deduplicated into touch maps, optional JSON and HTML reports are generated, and CBTS reports are archived and uploaded. The README documents the tooling and artifacts.

Estimated code review effort: 5 (Critical) | ~90 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Jenkins
  participant Pytest
  participant sitecustomize
  participant PyStartTracker
  participant pystart_report.py
  participant ArtifactStore
  Jenkins->>Pytest: launch eligible stage with cbts_plugin
  Pytest->>sitecustomize: initialize CBTS coverage
  Pytest->>PyStartTracker: switch test context
  PyStartTracker->>PyStartTracker: record PY_START entries
  PyStartTracker->>ArtifactStore: save per-process SQLite files
  Jenkins->>pystart_report.py: merge .cbtscov.*.sqlite files
  pystart_report.py->>ArtifactStore: write touch DB and HTML report
  Jenkins->>ArtifactStore: upload CBTS report archive
Loading
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description keeps the template placeholders but leaves the Description and Test Coverage sections empty, so it is largely incomplete. Add a short problem/solution summary and list the tests or CI coverage that validate the new CBTS pipeline and reporting changes.
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the PR's main goal of enhancing function-level coverage, including subprocess capture, and follows the required ticket/type format.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

…ge pipeline

Enable per-test coverage only when JOB_NAME carries L0_PostMerge, instead of
on every pipeline. Single-GPU stage selection is unchanged (the existing
Perf / TensorRT / CPP / AutoDeploy / multi-GPU skip logic).

Signed-off-by: Ivy Zhang <25222398+crazydemo@users.noreply.github.com>
@crazydemo

crazydemo commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator Author

Description

This PR adds CBTS Layer C — per-test function/class-level coverage capture for single-GPU L0 stages — and gates it to the official post-merge pipeline only.

What it does

  • Captures, per test, the set of product functions each test entered (via sys.monitoring PY_START, Python 3.12+), including subprocesses (mpi4py.futures pool workers, trtllm-serve, nested pytests).
  • Merges every per-process SQLite into one indexed touch(test, file, qualname) DB (the change-impact selector artifact) plus a split HTML report, uploaded compressed.
  • CI-only, fully gated: nothing ships in the product wheel; every file is a no-op unless CBTS_COVERAGE_CONFIG is set. isCbtsStage() restricts capture to single-GPU, non-Perf, non-TensorRT/CPP/AutoDeploy stages, and CBTS_PIPELINE_ELIGIBLE is true only when JOB_NAME carries L0_PostMerge. Global kill-switch: ENABLE_CBTS_COVERAGE.

Post-merge execution-time overhead

Measured on L0_MergeRequest #47070 (CBTS on) vs L0_PostMerge #2827 (no CBTS), zero product-code diff, per-case matched on l_e2e_time_ms (both sides PASSED), 41,232 matched cases. corresponding pipeline: https://prod.blsm.nvidia.com/sw-tensorrt-top-1/job/LLM/job/main/job/L0_MergeRequest_PR/47070/
https://nv/trt-llm-cicd/job/main/job/L0_MergeRequest_PR/47160/

Total matched-case time by stage (large-sample stages, ≥1500 cases — variance averaged out)

Stage 2827 (s) 47070 (s) Δ (s) Ratio Cases
B300-PyTorch-2 11112 11078 -34 1.00 8047
DGX_B200-PyTorch-4 11542 11708 +166 1.01 6553
B300-PyTorch-1 10400 10640 +240 1.02 4800
DGX_B200-PyTorch-1 10278 10174 -105 0.99 3712
DGX_H100-PyTorch-4 7725 7653 -72 0.99 3293
DGX_B200-PyTorch-3 4504 4667 +163 1.04 1716
A10-PyTorch-1 5371 5291 -80 0.99 1679
A10-PyTorch-2 5583 5596 +13 1.00 1591
Total (all 41 stages) 195261 203872 +8610 1.044 41232

Test Coverage

CI-infrastructure change with no product code path. Validated via:

  • Post-merge dry run showing CBTS_PIPELINE_ELIGIBLE is: true and non-zero touch counts per single-GPU stage.
  • The Test Coverage merge stage self-skips (no PY_START data files) on non-eligible pipelines, so pre-merge PR runs are unaffected.
  • Kill-switch verified: with ENABLE_CBTS_COVERAGE=false no stage instruments.

@crazydemo crazydemo requested a review from QiJune July 10, 2026 08:29
@crazydemo

Copy link
Copy Markdown
Collaborator Author

/bot run

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Nitpick comments (2)
jenkins/scripts/cbts/coverage_utils/pystart_report.py (1)

29-32: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add type annotations to all new Python functions.

The repository’s Python guidelines require parameter and return annotations, but every function in this new module is currently unannotated. Add annotations throughout the public and internal helpers.

As per coding guidelines, Python functions must always be annotated.

Also applies to: 35-50, 52-80, 83-107, 120-121, 124-189, 192-222, 225-303

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@jenkins/scripts/cbts/coverage_utils/pystart_report.py` around lines 29 - 32,
The new functions in pystart_report.py, including canon and all helpers in the
noted ranges, lack required type annotations. Add parameter and return type
annotations to every public and internal function, using appropriate concrete or
generic types consistent with each function’s inputs and outputs.

Source: Coding guidelines

jenkins/scripts/cbts/coverage_utils/make_coveragerc.sh (1)

35-41: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the unused make_coveragerc.sh helper

jenkins/L0_Test.groovy renders .coveragerc inline, and this script has no in-repo callers. Keeping both paths creates two sources of truth for the same template; remove the helper or route the pipeline through it consistently.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@jenkins/scripts/cbts/coverage_utils/make_coveragerc.sh` around lines 35 - 41,
Remove the unused make_coveragerc.sh helper and any associated references, since
jenkins/L0_Test.groovy already renders .coveragerc inline; do not maintain two
implementations of the same generation logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@jenkins/L0_MergeRequest.groovy`:
- Around line 1245-1264: Add the standard NVIDIA copyright/license header to the
modified Groovy file, using the latest meaningful modification year (including
the current year if applicable), while preserving the existing pipeline logic.
- Around line 1245-1251: Update the coverage-file collection commands in the
CBTS coverage stage to remove the blanket “|| true” suppression and propagate or
explicitly report find/mv failures. Restrict the find operation to source result
locations while excluding cov/ so generated files are not reprocessed, and
ensure collection errors fail clearly before the fileCount check in the
surrounding coverage logic.

In `@jenkins/scripts/cbts/coverage_utils/coveragerc.template`:
- Line 1: The new coveragerc.template file is missing the required NVIDIA
copyright header. Prepend the standard repository NVIDIA copyright comment block
before the existing configuration comment, preserving the template’s valid
configparser syntax and the existing [run] settings.

In `@jenkins/scripts/cbts/coverage_utils/pystart_report.py`:
- Around line 120-121: The _page_name function generates colliding filenames by
replacing non-alphanumeric characters with underscores. Make page names
collision-free by appending a stable digest derived from the original file path,
or by using an injective encoding, while preserving the .html suffix and
updating any dependent links consistently.
- Around line 64-75: In the file-processing loop around `_iter_rows`, `canon`,
and the `con.executemany` calls, restrict exception recovery to input parsing
errors only (such as `OSError` and `ValueError`). Ensure `sqlite3.Error` raised
by destination-database writes propagates and aborts the merge instead of being
swallowed.
- Around line 246-249: Update the temporary database path logic in the report
entrypoint around merge_to_sqlite: when a.out_sqlite is unset, create a unique
path using tempfile.mkstemp() or NamedTemporaryFile(delete=False), close the
returned file descriptor, and track that exact path for cleanup after
processing. Preserve the user-provided output path unchanged and ensure only the
invocation-specific temporary database is removed.
- Around line 83-107: Update enumerate_defs() to recursively traverse all AST
child nodes, including functions and methods inside control-flow blocks and
nested classes, while preserving qualified names consistent with PY_START;
refactor visit() so it receives the file identifier explicitly instead of
closing over rel, resolving Ruff B023.

In `@jenkins/scripts/cbts/coverage_utils/README.md`:
- Around line 1-6: Add the standard mandated NVIDIA copyright and license header
at the beginning of the README, before the title and existing documentation
content.

In `@jenkins/scripts/slurm_run.sh`:
- Line 37: Fix the whitespace replacement for trtllmWhlPath and the matching
sanitization pattern later in jenkins/scripts/slurm_run.sh: use extended sed
regex with sed -E, escape the repetition operator appropriately, or replace it
with shell substitution so consecutive whitespace characters are converted to
underscores and the resulting paths are sanitized correctly.

---

Nitpick comments:
In `@jenkins/scripts/cbts/coverage_utils/make_coveragerc.sh`:
- Around line 35-41: Remove the unused make_coveragerc.sh helper and any
associated references, since jenkins/L0_Test.groovy already renders .coveragerc
inline; do not maintain two implementations of the same generation logic.

In `@jenkins/scripts/cbts/coverage_utils/pystart_report.py`:
- Around line 29-32: The new functions in pystart_report.py, including canon and
all helpers in the noted ranges, lack required type annotations. Add parameter
and return type annotations to every public and internal function, using
appropriate concrete or generic types consistent with each function’s inputs and
outputs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 2acd0c0a-1d71-431a-aaff-472e166451c1

📥 Commits

Reviewing files that changed from the base of the PR and between e2ce7a6 and a38d4e5.

📒 Files selected for processing (10)
  • jenkins/L0_MergeRequest.groovy
  • jenkins/L0_Test.groovy
  • jenkins/scripts/cbts/coverage_utils/README.md
  • jenkins/scripts/cbts/coverage_utils/cbts_plugin.py
  • jenkins/scripts/cbts/coverage_utils/cbts_pystart.py
  • jenkins/scripts/cbts/coverage_utils/coveragerc.template
  • jenkins/scripts/cbts/coverage_utils/make_coveragerc.sh
  • jenkins/scripts/cbts/coverage_utils/pystart_report.py
  • jenkins/scripts/cbts/coverage_utils/sitecustomize.py
  • jenkins/scripts/slurm_run.sh

Comment on lines +1245 to 1251
// Collect the per-process PY_START data files from every stage's results tarball.
sh "find . -type f -name '.cbtscov.*.sqlite' -exec mv -t cov/ {} + || true"
sh "cd cov && ls -la"
def fileCount = sh(returnStdout: true, script: 'find cov -name ".cbtscov.*.sqlite" | wc -l').replaceAll("\\s","").toInteger()
if (fileCount == 0) {
echo "Test coverage is skipped because there is no test data file."
echo "CBTS coverage skipped: no PY_START data files."
return

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Do not hide coverage-file collection failures.

|| true masks every find/mv error. A partial move can still produce a report from incomplete data, while a total failure is reported as “no PY_START data files” and silently skipped. Fail or explicitly report collection errors, and exclude cov/ from the search to avoid reprocessing generated files.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@jenkins/L0_MergeRequest.groovy` around lines 1245 - 1251, Update the
coverage-file collection commands in the CBTS coverage stage to remove the
blanket “|| true” suppression and propagate or explicitly report find/mv
failures. Restrict the find operation to source result locations while excluding
cov/ so generated files are not reprocessed, and ensure collection errors fail
clearly before the fileCount check in the surrounding coverage logic.

Comment on lines +1245 to +1264
// Collect the per-process PY_START data files from every stage's results tarball.
sh "find . -type f -name '.cbtscov.*.sqlite' -exec mv -t cov/ {} + || true"
sh "cd cov && ls -la"
def fileCount = sh(returnStdout: true, script: 'find cov -name ".cbtscov.*.sqlite" | wc -l').replaceAll("\\s","").toInteger()
if (fileCount == 0) {
echo "Test coverage is skipped because there is no test data file."
echo "CBTS coverage skipped: no PY_START data files."
return
}
trtllm_utils.llmExecStepWithRetry(pipeline, script: "pip3 install coverage")
sh "coverage --version"

sh "cp llm/examples/openai_triton/manual_plugin/fmha_triton.py llm/examples/openai_triton/plugin_autogen/"
def coverageConfigFile = "cov/.coveragerc"
// Merge into the indexed touch DB, a per-file HTML report, and the coverage rate.
sh """
echo '[paths]' > ${coverageConfigFile}
echo 'source1=\n ${CUR_PATH}/llm/examples/\n */TensorRT-LLM/src/examples/' >> ${coverageConfigFile}
echo 'source2=\n ${CUR_PATH}/llm/tensorrt_llm/\n */tensorrt_llm/' >> ${coverageConfigFile}
cat ${coverageConfigFile}
python3 llm/jenkins/scripts/cbts/coverage_utils/pystart_report.py \
--glob 'cov/.cbtscov.*.sqlite' \
--out-sqlite cov/cbts_touchmap.sqlite \
--out-dir cov/cbts_report \
--source-root llm/tensorrt_llm
"""

sh "cd cov && coverage combine"
sh "cd cov && find . -type f"
sh "cd cov && coverage report -i" // -i: ignore errors. Ignore the error that the source code file cannot be found.
sh "cd cov && coverage html -d test_coverage_html -i"
trtllm_utils.uploadArtifacts("cov/test_coverage_html/*", "${UPLOAD_PATH}/test-results/coverage-report/")
echo "Test coverage report: https://urm.nvidia.com/artifactory/${UPLOAD_PATH}/test-results/coverage-report/index.html"
// Upload compressed only: the guardword scanner byte-matches raw files but not archives.
sh "cd cov && tar czf cbts_pystart_report.tar.gz cbts_touchmap.sqlite cbts_report"
trtllm_utils.uploadArtifacts("cov/cbts_pystart_report.tar.gz", "${UPLOAD_PATH}/cbts-coverage/")
echo "CBTS coverage (touch DB + report): https://urm.nvidia.com/artifactory/${UPLOAD_PATH}/cbts-coverage/cbts_pystart_report.tar.gz"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add the required NVIDIA copyright header.

This modified Groovy file has no NVIDIA copyright/license header or current-year update.

As per coding guidelines, all modified files must contain an NVIDIA copyright header with the latest meaningful modification year.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@jenkins/L0_MergeRequest.groovy` around lines 1245 - 1264, Add the standard
NVIDIA copyright/license header to the modified Groovy file, using the latest
meaningful modification year (including the current year if applicable), while
preserving the existing pipeline logic.

Source: Coding guidelines

@@ -0,0 +1,6 @@
# CBTS runtime config template; only [run] source and data_file are consumed by sitecustomize.py.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Missing NVIDIA copyright header on this new file.

Per repo guidelines, all new files must carry the NVIDIA copyright header. Since # lines are treated as comments by configparser, prepending the header block is safe and won't affect the [run] parsing done by sitecustomize.py.

As per coding guidelines: "Add an NVIDIA copyright header to all new files."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@jenkins/scripts/cbts/coverage_utils/coveragerc.template` at line 1, The new
coveragerc.template file is missing the required NVIDIA copyright header.
Prepend the standard repository NVIDIA copyright comment block before the
existing configuration comment, preserving the template’s valid configparser
syntax and the existing [run] settings.

Source: Coding guidelines

Comment on lines +64 to +75
for fp in files:
batch = []
try:
for test, file, qual in _iter_rows(fp):
batch.append((test, canon(file), qual))
if len(batch) >= 20000:
con.executemany("INSERT OR IGNORE INTO touch VALUES (?, ?, ?)", batch)
batch = []
if batch:
con.executemany("INSERT OR IGNORE INTO touch VALUES (?, ?, ?)", batch)
except (OSError, ValueError, sqlite3.Error):
continue

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Do not swallow destination-database failures.

The sqlite3.Error handler covers both malformed input files and writes to the output database. A locked, corrupt, or full destination can therefore be skipped as if it were bad input, producing and publishing an incomplete touch DB with misleading coverage rates. Scope recovery to input parsing and let destination write failures abort the merge.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@jenkins/scripts/cbts/coverage_utils/pystart_report.py` around lines 64 - 75,
In the file-processing loop around `_iter_rows`, `canon`, and the
`con.executemany` calls, restrict exception recovery to input parsing errors
only (such as `OSError` and `ValueError`). Ensure `sqlite3.Error` raised by
destination-database writes propagates and aborts the merge instead of being
swallowed.

Comment on lines +83 to +107
def enumerate_defs(source_root):
"""Return (coverable_files, funcs) for the coverage-rate denominator, matching what the tracker records."""
funcs = set()
for dirpath, _dirs, names in os.walk(source_root):
for nm in names:
if not nm.endswith(".py"):
continue
full = os.path.join(dirpath, nm)
rel = canon(os.path.abspath(full))
try:
tree = ast.parse(open(full, encoding="utf-8").read())
except (OSError, SyntaxError):
continue

def visit(node, prefix):
for child in node.body:
if isinstance(child, (ast.FunctionDef, ast.AsyncFunctionDef)):
funcs.add((rel, prefix + child.name))
elif isinstance(child, ast.ClassDef):
for m in child.body:
if isinstance(m, (ast.FunctionDef, ast.AsyncFunctionDef)):
funcs.add((rel, f"{prefix}{child.name}.{m.name}"))

visit(tree, "")
return {f for f, _q in funcs}, funcs

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n -C3 'co_qualname|PY_START' jenkins/scripts/cbts/coverage_utils/cbts_pystart.py

Repository: NVIDIA/TensorRT-LLM

Length of output: 1433


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '== file list ==\n'
git ls-files jenkins/scripts/cbts/coverage_utils

printf '\n== outline pystart_report.py ==\n'
ast-grep outline jenkins/scripts/cbts/coverage_utils/pystart_report.py --view expanded

printf '\n== relevant lines pystart_report.py ==\n'
sed -n '1,220p' jenkins/scripts/cbts/coverage_utils/pystart_report.py

printf '\n== relevant lines cbts_pystart.py ==\n'
sed -n '1,220p' jenkins/scripts/cbts/coverage_utils/cbts_pystart.py

Repository: NVIDIA/TensorRT-LLM

Length of output: 14859


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,220p' jenkins/scripts/cbts/coverage_utils/pystart_report.py
sed -n '1,220p' jenkins/scripts/cbts/coverage_utils/cbts_pystart.py

Repository: NVIDIA/TensorRT-LLM

Length of output: 14013


🏁 Script executed:

#!/bin/bash
set -euo pipefail

nl -ba jenkins/scripts/cbts/coverage_utils/pystart_report.py | sed -n '1,220p'
printf '\n---\n'
nl -ba jenkins/scripts/cbts/coverage_utils/cbts_pystart.py | sed -n '1,180p'

Repository: NVIDIA/TensorRT-LLM

Length of output: 197


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '== pystart_report.py ==\n'
nl -ba jenkins/scripts/cbts/coverage_utils/pystart_report.py | sed -n '1,220p'

printf '\n== references to all_funcs / enumerate_defs ==\n'
rg -n -C2 'all_funcs|enumerate_defs|funcs' jenkins/scripts/cbts/coverage_utils

printf '\n== tracker qualname filtering ==\n'
nl -ba jenkins/scripts/cbts/coverage_utils/cbts_pystart.py | sed -n '45,90p'

Repository: NVIDIA/TensorRT-LLM

Length of output: 221


Walk the AST recursively. enumerate_defs() only visits direct module bodies and direct class bodies, so it drops functions/methods nested under control-flow blocks or nested classes even though PY_START still records them. That makes the coverage denominator too small. The inner visit() also closes over rel, which is what Ruff B023 is flagging.

🧰 Tools
🪛 ast-grep (0.44.1)

[warning] 92-92: File path is request-/variable-derived; validate and normalize to prevent path traversal.
Context: open(full, encoding="utf-8")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(open-filename-from-request)

🪛 Ruff (0.15.20)

[warning] 100-100: Function definition does not bind loop variable rel

(B023)


[warning] 104-104: Function definition does not bind loop variable rel

(B023)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@jenkins/scripts/cbts/coverage_utils/pystart_report.py` around lines 83 - 107,
Update enumerate_defs() to recursively traverse all AST child nodes, including
functions and methods inside control-flow blocks and nested classes, while
preserving qualified names consistent with PY_START; refactor visit() so it
receives the file identifier explicitly instead of closing over rel, resolving
Ruff B023.

Source: Linters/SAST tools

Comment on lines +120 to +121
def _page_name(file):
return re.sub(r"[^A-Za-z0-9]", "_", file) + ".html"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make HTML page names collision-free.

Replacing every non-alphanumeric character with _ maps distinct files such as a-b.py and a_b.py to the same page. The later write overwrites the earlier report, and both index links show incorrect content. Append a stable digest of the original path or use an injective encoding.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@jenkins/scripts/cbts/coverage_utils/pystart_report.py` around lines 120 -
121, The _page_name function generates colliding filenames by replacing
non-alphanumeric characters with underscores. Make page names collision-free by
appending a stable digest derived from the original file path, or by using an
injective encoding, while preserving the .html suffix and updating any dependent
links consistently.

Comment on lines +246 to +249
# Merge streams into a SQLite DB (bounded memory). Use --out-sqlite as that DB directly, else a temp file.
keep = a.out_sqlite is not None
db_path = a.out_sqlite if keep else os.path.join(tempfile.gettempdir(), "cbts_merge_tmp.sqlite")
con, n_data_files = merge_to_sqlite(a.glob, db_path)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Use a unique temporary database per invocation.

When --out-sqlite is omitted, every process uses /tmp/cbts_merge_tmp.sqlite and removes it before opening it. Concurrent report jobs can delete or overwrite each other’s database and publish mixed or empty artifacts. Use tempfile.mkstemp()/NamedTemporaryFile(delete=False) and clean up that specific path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@jenkins/scripts/cbts/coverage_utils/pystart_report.py` around lines 246 -
249, Update the temporary database path logic in the report entrypoint around
merge_to_sqlite: when a.out_sqlite is unset, create a unique path using
tempfile.mkstemp() or NamedTemporaryFile(delete=False), close the returned file
descriptor, and track that exact path for cleanup after processing. Preserve the
user-provided output path unchanged and ensure only the invocation-specific
temporary database is removed.

Comment on lines +1 to +6
# CBTS Layer C — Coverage Utils

CI tooling that captures per-test **function/class-level** coverage (which product functions each
test entered), including subprocesses, on single-GPU L0 stages. CI infrastructure only — nothing
ships in the product wheel, and every file is a no-op unless `CBTS_COVERAGE_CONFIG` is set in the
environment.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add the required NVIDIA copyright header.

This new Markdown file does not include the mandated NVIDIA copyright/license header.

As per coding guidelines, all new files must include the NVIDIA copyright header.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@jenkins/scripts/cbts/coverage_utils/README.md` around lines 1 - 6, Add the
standard mandated NVIDIA copyright and license header at the beginning of the
README, before the title and existing documentation content.

Source: Coding guidelines

# get trtllm wheel path and add to pytest command
# Wheel path for the CBTS .coveragerc @TRTLLM_WHEEL_PATH@ substitution below.
trtllmWhlPath=$(pip3 show tensorrt_llm | grep Location | cut -d ' ' -f 2)
trtllmWhlPath=$(echo "$trtllmWhlPath" | sed 's/[[:space:]]+/_/g')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '--- jenkins/scripts/slurm_run.sh (selected lines) ---\n'
nl -ba jenkins/scripts/slurm_run.sh | sed -n '1,140p'

printf '\n--- grep for similar sed expressions ---\n'
rg -n "sed 's/\[\[:space:\]\]\+/_/g'" jenkins/scripts/slurm_run.sh || true

printf '\n--- probe sed BRE vs ERE behavior ---\n'
python3 - <<'PY'
import subprocess, textwrap

tests = [
    ("BRE default", ["sed", "s/[[:space:]]+/_/g"], "a b"),
    ("ERE", ["sed", "-E", "s/[[:space:]]+/_/g"], "a b"),
    ("BRE escaped plus", ["sed", "s/[[:space:]]\\+/_/g"], "a b"),
]
for name, cmd, inp in tests:
    p = subprocess.run(cmd, input=inp.encode(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    print(name, "->", p.stdout.decode().strip(), "| rc=", p.returncode, "| stderr=", p.stderr.decode().strip())
PY

Repository: NVIDIA/TensorRT-LLM

Length of output: 248


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the surrounding context and test the regex semantics directly.
nl -ba jenkins/scripts/slurm_run.sh | sed -n '30,60p'

python3 - <<'PY'
import subprocess

cases = [
    ("default BRE", ["sed", "s/[[:space:]]+/_/g"], "a b"),
    ("ERE", ["sed", "-E", "s/[[:space:]]+/_/g"], "a b"),
    ("BRE escaped +", ["sed", "s/[[:space:]]\\+/_/g"], "a b"),
]
for label, cmd, data in cases:
    p = subprocess.run(cmd, input=data.encode(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    print(f"{label}: {p.stdout.decode().strip()!r} rc={p.returncode} stderr={p.stderr.decode().strip()!r}")
PY

Repository: NVIDIA/TensorRT-LLM

Length of output: 197


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- jenkins/scripts/slurm_run.sh (lines 30-60) ---'
sed -n '30,60p' jenkins/scripts/slurm_run.sh

echo
echo '--- matching sed expressions ---'
grep -n "sed 's/\[\[:space:\]\]\+/_/g'" jenkins/scripts/slurm_run.sh || true

echo
echo '--- regex behavior probe ---'
python3 - <<'PY'
import subprocess

cases = [
    ("default BRE", ["sed", "s/[[:space:]]+/_/g"], "a b"),
    ("ERE", ["sed", "-E", "s/[[:space:]]+/_/g"], "a b"),
    ("BRE escaped +", ["sed", "s/[[:space:]]\\+/_/g"], "a b"),
]
for label, cmd, data in cases:
    p = subprocess.run(cmd, input=data.encode(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    print(f"{label}: stdout={p.stdout.decode().rstrip()!r} rc={p.returncode} stderr={p.stderr.decode().rstrip()!r}")
PY

Repository: NVIDIA/TensorRT-LLM

Length of output: 1822


Fix the whitespace-collapse regex sed is using basic regex here, so [[:space:]]+ treats + literally and leaves spaces unchanged. That makes the wheel-path and LD_LIBRARY_PATH sanitization ineffective; switch to sed -E, escape +, or use shell substitution. Same pattern appears later in the file.

🧰 Tools
🪛 Shellcheck (0.11.0)

[style] 37-37: See if you can use ${variable//search/replace} instead.

(SC2001)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@jenkins/scripts/slurm_run.sh` at line 37, Fix the whitespace replacement for
trtllmWhlPath and the matching sanitization pattern later in
jenkins/scripts/slurm_run.sh: use extended sed regex with sed -E, escape the
repetition operator appropriately, or replace it with shell substitution so
consecutive whitespace characters are converted to underscores and the resulting
paths are sanitized correctly.

Source: Linters/SAST tools

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #58645 [ run ] triggered by Bot. Commit: a38d4e5 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #58645 [ run ] completed with state FAILURE. Commit: a38d4e5
/LLM/main/L0_MergeRequest_PR pipeline #47232 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants