Skip to content

Add signal-reachability checker and source quality checks (qualityChecks task) - #893

Open
jbachorik wants to merge 7 commits into
mainfrom
signal-reachability-checker
Open

jbachorik wants to merge 7 commits into
mainfrom
signal-reachability-checker

Conversation

@jbachorik

@jbachorik jbachorik commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

What does this PR do?

Adds deterministic quality checks enforcing the AGENTS.md signal-safety and
documentation rules:

  • utils/check-signal-reachability.py: builds a per-function call graph from
    the compiled ELF objects (function symbols + call/TLS relocations, COMDAT
    sections, C1/C2 constructor aliases) and reports every path from a signal
    handler or malloc/free hook to an async-signal-unsafe sink (allocation,
    blocking locks, TLS access, stdio, loader/JNI). Found the two real
    violations now tracked as PROF-16313 and PROF-16314 (both in the built-in
    allowlist until fixed).
  • utils/check-{jvm-boundary-includes,doc-index,sigsetjmp-shape,tracked-artifacts}.py:
    the cheap source-level checks — JVM boundary includes against the documented
    exception list, doc index coverage + PascalCase naming in both directions,
    sigsetjmp landing-pad shape, and no tracked session artifacts.
  • utils/tests/signal-reachability-fixture.cpp: planted-violation fixture;
    --self-test compiles it and asserts every planted violation is reported,
    allowlisted ones suppressed, negative controls clean. Wired into
    qualityChecks (Linux only; the checker is ELF-only).
  • Gradle: opt-in :ddprof-lib:checkSignalReachability (runs on the debug
    objects; SKIPPED on non-ELF/macOS) and :ddprof-lib:qualityChecks
    aggregating everything. Neither is wired into check/build yet, because
    PROF-16313/PROF-16314 are open.

Motivation

The AGENTS.md invariants (async-signal safety, JVM abstraction boundary, doc
index rules) had no mechanical enforcement — violations surfaced only through
manual review. A per-file regex lint is useless here (measured ~99% false
positives: a file is not a context; reachability is), so the core check works
on the call graph instead. The checker found 2 real signal-path violations on
its first run across macOS arm64, Linux arm64 and Linux x64 objects.

Additional Notes

  • Every check is also runnable standalone; exit codes: 0 clean, 1 findings,
    2 environment error. --self-test keeps the checker honest across
    toolchain/objdump changes.
  • Verified: exactly the 2 allowlisted findings on aarch64 and emulated x86-64
    debug objects; allowlist-emptied task fails; mutation-injected violations in
    every check are caught.
  • Reviewed with the harness agentic review (sphinx, quick effort): 20
    findings; the 3 HIGH ones were untracked scratch never committed; the rest
    addressed in c966c5ea7 (sink coverage for aligned new/delete and libc
    aliases, allowlist encoding/strictness, self-test wiring, doc-index reverse
    check, CXX argument handling, docs/sphinx boundary, named-task wiring).
  • Known limitation: the reachability walk stops at the profiler singletons,
    assuming they initialize before handlers are installed; that ordering is not
    verifiable from relocations and is documented in the script.

How to test the change?

./gradlew :ddprof-lib:qualityChecks (source checks everywhere; self-test on
Linux) and ./gradlew :ddprof-lib:checkSignalReachability (needs ELF debug
objects). Both are opt-in. Mutation testing: inject malloc, a thread_local,
or SpinLock::lock() into any signal-handler-reachable function and the
checker reports it.

For Datadog employees:

  • If this PR touches code that signs or publishes builds or packages, or handles
    credentials of any kind, I've requested a security review (run the dd:platform-security-review
    skill, or file a request via the PSEC review form).
    bewaire also runs automatically on every PR.
  • This PR doesn't touch any of that.
  • JIRA: PROF-16313, PROF-16314 — this PR adds detection only; the two violations it found are allowlisted here (not fixed) and must be resolved before the checker is wired into check.

utils/check-signal-reachability.py builds a call graph from the
relocations of ELF object files (objdump -dr), walks it from the signal
handlers and malloc hooks, and reports every reachable function that
calls an async-signal-unsafe symbol (heap, locks, stdio, dynamic loader,
dynamic TLS, logging). Calls through section symbols are resolved to
the local function they target, and edges are attributed to constructor
and destructor aliases. Entry points, unsafe symbols and the known
findings (PROF-16313, PROF-16314) are data lists at the top of the
script; --allowlist replaces the known findings from a file.

--self-test compiles utils/tests/signal-reachability-fixture.cpp and
checks its planted violations, the allowlist and negative controls.

Exit codes: 0 clean, 1 new findings, 2 usage or environment error.
Registers :ddprof-lib:checkSignalReachability (verification group),
which runs utils/check-signal-reachability.py on the debug objects after
compileDebug. It is not wired into check or build. The task is skipped
when the debug objects are missing or not ELF (e.g. macOS builds).
Four standalone stdlib-only checks under utils/, each printing one line
per violation and exiting 0 clean, 1 violations, 2 environment error:

- check-jvm-boundary-includes.py: shared code in ddprof-lib/src/main/cpp
  includes no hotspot/, j9/ or zing/ header outside the boundary files
  and the known exceptions, which mirror the JvmSupportLayer.md table;
  stale exceptions are reported too.
- check-doc-index.py: every tracked doc/**/*.md is linked from
  doc/README.md and has a PascalCase file name.
- check-sigsetjmp-shape.py: every sigsetjmp() has a JmpCtxScope in
  effect, savemask 1, and a landing pad that starts with
  SIGNAL_HANDLER_UNWIND_AFTER_LONGJMP() then <scope>.restore(). The glibc
  cancellation buffer in libraryPatcher_linux.cpp is the one exception.
- check-tracked-artifacts.py: no tracked docs/sphinx/, .sphinx/ or
  *.scratch* path.

:ddprof-lib:qualityChecks depends on checkSignalReachability, runs all
four and fails if any of them fails. It is not wired into check or build.

To make the doc index check pass, index the NativeMemoryProfiling,
SanityChecks, SigsegvPatching and Versioning docs in doc/README.md, and
rename the ThreadContext benchmark report to ThreadContextBenchmark.md
under a new Reports section.
@jbachorik jbachorik added the AI label Oct 23, 2026
…ontext

The guard constructor runs in signal handlers; its NESTED_DEPTH-overflow
warning used Log::warn (stdio, not async-signal-safe). Replace the
once-per-process latch with the REFCOUNT_NESTED_OVERFLOW counter, surfaced
in the stop-time report like every other signal-path anomaly (PROF-16314).
Drop the now-fixed entry from the checker allowlist.
@jbachorik
jbachorik marked this pull request as ready for review October 23, 2026 16:06
@jbachorik
jbachorik requested a review from a team as a code owner October 23, 2026 16:06
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-23T16:11:42.162235Z 03a07a7 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@jbachorik

Copy link
Copy Markdown
Collaborator Author

@DataDog please fix my ci

@datadog-prod-us1-5

Copy link
Copy Markdown

I can only run on private repositories.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 03a07a75a6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread utils/check-signal-reachability.py Outdated
Comment thread ddprof-lib/build.gradle.kts Outdated
Comment thread ddprof-lib/src/main/cpp/refCountGuard.cpp
Comment thread ddprof-lib/build.gradle.kts Outdated

@datadog-official datadog-official Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bits Code Review: FAIL

Linux qualityChecks fails on a healthy checkout because its self-test treats stale allowlist entries as findings. The source checks also reject valid brace initialization and miss angle-bracket JVM includes.

Open Bits AI session

🤖 Bits Code Review · Commit 03a07a7

Comment thread utils/check-signal-reachability.py Outdated
Comment thread utils/check-sigsetjmp-shape.py Outdated
Comment thread utils/check-jvm-boundary-includes.py Outdated
@dd-octo-sts

dd-octo-sts Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

CI Test Results

Run: #39993890183 | Commit: 7d7bb29 | Duration: 19m 51s (longest job)

✅ All 32 test jobs passed

Status Overview

JDK glibc-aarch64/debug glibc-amd64/debug musl-aarch64/debug musl-amd64/debug
8 - ✅ - -
8-ibm - ✅ - -
8-j9 ✅ ✅ - -
8-librca - - ✅ ✅
8-orcl - ✅ - -
11 - ✅ - -
11-j9 ✅ ✅ - -
11-librca - - ✅ ✅
17 ✅ ✅ - -
17-graal ✅ ✅ - -
17-j9 ✅ ✅ - -
17-librca - - ✅ ✅
21 ✅ ✅ - -
21-graal ✅ ✅ - -
21-librca - - ✅ ✅
25 ✅ ✅ - -
25-graal ✅ ✅ - -
25-librca - - ✅ ✅

Legend: ✅ passed | ❌ failed | ⚪ skipped | 🚫 cancelled

Summary: Total: 32 | Passed: 32 | Failed: 0


Updated: 2026-10-23 17:52:36 UTC

@dd-octo-sts

dd-octo-sts Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

✅ All 40 integration tests passed

📊 Dashboard · 👷 Pipeline · 📦 2ee769c4

@jbachorik

Copy link
Copy Markdown
Collaborator Author

Auto-expanded scope: 2 sibling issue(s) fixed in the same pass:

  • utils/README.md:204 — doc mirrors quoted-only include pattern
  • doc/architecture/JvmSupportLayer.md:238 — doc mirrors quoted-only include pattern

This branch has not been deployed

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant