Skip to content

Use sigsetjmp/siglongjmp instead of setjmp/longjmp#686

Open
zhengyu123 wants to merge 10 commits into
mainfrom
zgu/setjmp
Open

Use sigsetjmp/siglongjmp instead of setjmp/longjmp#686
zhengyu123 wants to merge 10 commits into
mainfrom
zgu/setjmp

Conversation

@zhengyu123

@zhengyu123 zhengyu123 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?:
To perform a non-local jump out of a signal handler in POSIX C, you should use sigsetjmp() and siglongjmp() rather than standard setjmp() and longjmp(). [1, 2]

While longjmp() safely unwinds the execution stack, it does not consistently restore the process's signal mask. This means the signal that triggered your handler could remain blocked indefinitely. Using siglongjmp() ensures that your signal mask returns to its exact state prior to the signal delivery. [1, 2, 3, 4, 5]

Motivation:
Improve stability

Additional Notes:
-Regular CI tests

How to test the change?:

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-15501

Unsure? Have a question? Request a review!

Copilot AI review requested due to automatic review settings July 24, 2026 21:09

Copilot AI 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.

Pull request overview

This PR aims to make HotSpot crash-protection jumps signal-safe by migrating from setjmp/longjmp to sigsetjmp/siglongjmp in the native stack-walking fault-recovery path.

Changes:

  • Switched the per-thread stored jump context type from jmp_buf* to sigjmp_buf*.
  • Updated HotSpot fault recovery to use siglongjmp() when unwinding from the SEGV handler.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
ddprof-lib/src/main/cpp/threadLocalData.h Stores the crash-protection jump context as sigjmp_buf* instead of jmp_buf*.
ddprof-lib/src/main/cpp/hotspot/hotspotSupport.cpp Uses sigjmp_buf for the crash-protection context and siglongjmp() for recovery.
Comments suppressed due to low confidence (1)

ddprof-lib/src/main/cpp/hotspot/hotspotSupport.cpp:258

  • This comment references setjmp/longjmp, but the code now uses sigjmp_buf/siglongjmp. Update the wording to match the new control-flow mechanism.
    // Should be preserved across setjmp/longjmp

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread ddprof-lib/src/main/cpp/hotspot/hotspotSupport.cpp
@dd-octo-sts

dd-octo-sts Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

CI Test Results

Run: #30256437751 | Commit: 72e5659 | Duration: 14m 15s (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-07-27 10:42:44 UTC

Copilot AI review requested due to automatic review settings July 24, 2026 21:56

Copilot AI 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.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (4)

ddprof-lib/src/test/cpp/hotspot_crash_protection_ut.cpp:17

  • The header comment still refers to jmp_buf* and longjmp() even though the implementation has been switched to sigsetjmp/siglongjmp. This is misleading (and suggests the wrong buffer type is stored on ProfiledThread).

This issue also appears in the following locations of the same file:

  • line 283
  • line 294
 * Crash recovery inside walkVM relies on sigsetjmp/siglongjmp:
 *   1. walkVM stores a jmp_buf* on ProfiledThread (setJmpCtx/getJmpCtx),
 *      chaining it with whatever context was already installed so a
 *      signal-based sampler interrupting a non-signal-based sampler's own
 *      in-flight walkVM() call doesn't clobber the outer call's context.

ddprof-lib/src/test/cpp/hotspot_crash_protection_ut.cpp:298

  • This test now uses sigsetjmp/siglongjmp, but the jump buffers are still declared as jmp_buf/jmp_buf*. Use sigjmp_buf consistently to avoid relying on platform typedef quirks and to match the production API (ProfiledThread::setJmpCtx(sigjmp_buf*)). Also update the nearby comments/FAIL message to mention siglongjmp.
    if (sigsetjmp(outer_ctx, 1) != 0) {
        outer_landed++;
    } else {
        _pt->setJmpCtx(&outer_ctx);

ddprof-lib/src/test/cpp/faultInjection_ut.cpp:150

  • The test still uses jmp_buf + setjmp, but the crash-protection path has moved to sigsetjmp/siglongjmp. Mixing setjmp with siglongjmp is undefined/implementation-specific. Update the buffer type and call to sigjmp_buf/sigsetjmp, and rename the test to avoid implying the non-signal variant is used.
// (c2) walkVM path: a raw dereference of an injected poison pointer must be
// caught by the sigsetjmp/siglongjmp crash protection, returning control to setjmp.
TEST_F(FaultInjectionTest, WalkVmSetjmpRecoversFromInjectedFault) {
  ProfiledThread* t = ProfiledThread::current();
  ASSERT_NE(t, nullptr);

ddprof-lib/src/test/cpp/hotspot_crash_protection_ut.cpp:286

  • Comment still says checkFault() "longjmps" even though the code path is now siglongjmp. Update wording to match the behavior being tested.
// End-to-end with real sigsetjmp/siglongjmp: a fault inside the inner frame must
// land in the inner frame's own recovery branch — checkFault() always
// longjmps through whatever is currently installed — and once the inner
// frame has recovered and restored the outer's context, the outer frame must

Comment thread ddprof-lib/src/test/cpp/faultInjection_ut.cpp Outdated
Copilot AI review requested due to automatic review settings July 24, 2026 22:08

Copilot AI 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.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (5)

ddprof-lib/src/test/cpp/hotspot_crash_protection_ut.cpp:17

  • The documentation here still references jmp_buf/longjmp, but ProfiledThread::setJmpCtx/getJmpCtx now use sigjmp_buf and recovery uses siglongjmp. This is misleading and also risks type mismatches on platforms where jmp_buf and sigjmp_buf differ.

This issue also appears in the following locations of the same file:

  • line 14
  • line 283
  • line 309
 * Crash recovery inside walkVM relies on sigsetjmp/siglongjmp:
 *   1. walkVM stores a jmp_buf* on ProfiledThread (setJmpCtx/getJmpCtx),
 *      chaining it with whatever context was already installed so a
 *      signal-based sampler interrupting a non-signal-based sampler's own
 *      in-flight walkVM() call doesn't clobber the outer call's context.

ddprof-lib/src/test/cpp/hotspot_crash_protection_ut.cpp:302

  • jmp_buf/jmp_buf* are still used here with sigsetjmp/siglongjmp. Since ProfiledThread::setJmpCtx/getJmpCtx now use sigjmp_buf, this can break compilation/portability on platforms where jmp_buf and sigjmp_buf are distinct types.
// End-to-end with real sigsetjmp/siglongjmp: a fault inside the inner frame must
// land in the inner frame's own recovery branch — checkFault() always
// longjmps through whatever is currently installed — and once the inner
// frame has recovered and restored the outer's context, the outer frame must
// be left exactly as it was, never having been unwound itself.
TEST_F(JmpCtxChainingTest, FaultInInnerFrameDoesNotDisturbOuterFrame) {
    jmp_buf outer_ctx;
    jmp_buf* outer_prev = _pt->getJmpCtx();
    int outer_landed = 0;
    int inner_landed = 0;

    if (sigsetjmp(outer_ctx, 1) != 0) {
        outer_landed++;
    } else {
        _pt->setJmpCtx(&outer_ctx);

        // --- inner "walkVM" call, interrupted mid-flight by a fault ---
        jmp_buf inner_ctx;
        jmp_buf* inner_prev = _pt->getJmpCtx();
        ASSERT_EQ(&outer_ctx, inner_prev);

ddprof-lib/src/test/cpp/hotspot_crash_protection_ut.cpp:312

  • This block still refers to longjmp in comments/error text, but the code path uses siglongjmp. Keeping wording consistent makes failures easier to interpret.
            // Simulate checkFault(): longjmp through whatever is currently
            // installed — this must hit the inner frame, not the outer.
            siglongjmp(*_pt->getJmpCtx(), 1);
            FAIL() << "unreachable: longjmp does not return";

ddprof-lib/src/test/cpp/faultInjection_ut.cpp:162

  • Later in this test (outside the diff hunk) the comments and assertion message still say setjmp/longjmp. Since the code path now uses sigsetjmp/siglongjmp, update that wording to avoid confusion when diagnosing failures.
  sigjmp_buf ctx;
  if (sigsetjmp(ctx) != 0) {
    recovered = true;                  // returned here via checkFault -> siglongjmp
    faults++;
  }

ddprof-lib/src/test/cpp/hotspot_crash_protection_ut.cpp:16

  • ProfiledThread::setJmpCtx/getJmpCtx now take/return sigjmp_buf*, but several tests earlier in this file still declare jmp_buf and pass jmp_buf* to these APIs (e.g., SetAndGetRoundTrip / SingleFrameRestoresPreviousOnExit / NestedFrames…). That can fail to compile on platforms where jmp_buf and sigjmp_buf are distinct types; consider switching those remaining jmp_buf declarations/comments to sigjmp_buf for consistency/portability.
 *   1. walkVM stores a jmp_buf* on ProfiledThread (setJmpCtx/getJmpCtx),
 *      chaining it with whatever context was already installed so a
 *      signal-based sampler interrupting a non-signal-based sampler's own

Comment thread ddprof-lib/src/main/cpp/threadLocalData.h
Comment thread ddprof-lib/src/test/cpp/faultInjection_ut.cpp
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 24, 2026 22:15

Copilot AI 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.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (3)

ddprof-lib/src/test/cpp/faultInjection_ut.cpp:162

  • sigsetjmp requires the savesigs argument (typically 1). As written, this call will not compile (or will call the wrong overload/macro) on most platforms.
  sigjmp_buf ctx;
  if (sigsetjmp(ctx, 1) != 0) {
    recovered = true;                  // returned here via checkFault -> siglongjmp
    faults++;
  }

ddprof-lib/src/test/cpp/faultInjection_ut.cpp:149

  • The test name and comment still refer to setjmp, but the implementation now uses sigsetjmp/siglongjmp. Renaming keeps intent and failure output accurate.
// (c2) walkVM path: a raw dereference of an injected poison pointer must be
// caught by the sigsetjmp/siglongjmp crash protection, returning control to setjmp.
TEST_F(FaultInjectionTest, WalkVmSetjmpRecoversFromInjectedFault) {
  ProfiledThread* t = ProfiledThread::current();

ddprof-lib/src/test/cpp/hotspot_crash_protection_ut.cpp:302

  • Same issue for the inner frame: use sigjmp_buf consistently so sigsetjmp/siglongjmp and setJmpCtx/getJmpCtx all agree on the buffer type.
        jmp_buf inner_ctx;
        jmp_buf* inner_prev = _pt->getJmpCtx();
        ASSERT_EQ(&outer_ctx, inner_prev);

Comment thread ddprof-lib/src/test/cpp/hotspot_crash_protection_ut.cpp Outdated
Comment thread ddprof-lib/src/test/cpp/hotspot_crash_protection_ut.cpp Outdated
Comment thread ddprof-lib/src/test/cpp/hotspot_crash_protection_ut.cpp
Copilot AI review requested due to automatic review settings July 24, 2026 22:19

Copilot AI 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.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (5)

ddprof-lib/src/test/cpp/hotspot_crash_protection_ut.cpp:20

  • The header comment was updated to mention sigsetjmp/siglongjmp, but it still refers to storing a jmp_buf* and calling longjmp(). This is now inconsistent with the implementation (ProfiledThread uses sigjmp_buf* and checkFault uses siglongjmp), which makes the documentation misleading.
 * Crash recovery inside walkVM relies on sigsetjmp/siglongjmp:
 *   1. walkVM stores a jmp_buf* on ProfiledThread (setJmpCtx/getJmpCtx),
 *      chaining it with whatever context was already installed so a
 *      signal-based sampler interrupting a non-signal-based sampler's own
 *      in-flight walkVM() call doesn't clobber the outer call's context.
 *   2. If a fault fires during the walk, checkFault() detects the live
 *      context via ProfiledThread::isProtected() and calls longjmp() to
 *      unwind through whatever context is currently installed.

ddprof-lib/src/test/cpp/hotspot_crash_protection_ut.cpp:312

  • This comment and failure message still refer to longjmp, but the code now uses siglongjmp(). Updating them keeps the test intent clear and consistent with the new crash-protection mechanism.
            // Simulate checkFault(): longjmp through whatever is currently
            // installed — this must hit the inner frame, not the outer.
            siglongjmp(*_pt->getJmpCtx(), 1);
            FAIL() << "unreachable: longjmp does not return";

ddprof-lib/src/test/cpp/faultInjection_ut.cpp:147

  • The comment says control returns to setjmp, but this test now uses sigsetjmp/siglongjmp. Update wording to match the actual mechanism being tested.
// (c2) walkVM path: a raw dereference of an injected poison pointer must be
// caught by the sigsetjmp/siglongjmp crash protection, returning control to setjmp.

ddprof-lib/src/test/cpp/hotspot_crash_protection_ut.cpp:294

  • outer_ctx/outer_prev are declared as jmp_buf/jmp_buf*, but this path now uses sigsetjmp(…, 1) and ProfiledThread::setJmpCtx/getJmpCtx are sigjmp_buf*-based. On libcs where jmp_buf and sigjmp_buf differ, passing the wrong buffer type can be incorrect (and could even corrupt the stack if sigsetjmp writes a larger env). Prefer using sigjmp_buf consistently here.
    if (sigsetjmp(outer_ctx, 1) != 0) {

ddprof-lib/src/test/cpp/hotspot_crash_protection_ut.cpp:304

  • inner_ctx/inner_prev are declared as jmp_buf/jmp_buf* but are used with sigsetjmp(…, 1) and stored via ProfiledThread::setJmpCtx(sigjmp_buf*). Use sigjmp_buf consistently to match the sigsetjmp/siglongjmp mechanism and avoid libc-dependent type mismatches.
        if (sigsetjmp(inner_ctx, 1) != 0) {

Comment thread ddprof-lib/src/main/cpp/hotspot/hotspotSupport.cpp Outdated
@dd-octo-sts

dd-octo-sts Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results (commit 75d514d)

Pipeline: https://gitlab.ddbuild.io/DataDog/apm-reliability/benchmarking-platform/-/pipelines/126927848 Commit: 75d514df63aa3dab872ca4056bf4a6f41762f1dc

✅ Within expected boundaries

No significant runtime deltas (all within run-to-run noise) and no internal-counter outliers.

Runtime details (per benchmark × JDK)
Benchmark JDK Latest Dev Δ (dev vs latest) Issues L/D
akka-uct 21 ✅ 10354 ms (21 iters) ✅ 10274 ms (21 iters) ≈ -0.8% (±11.6%) — / —
akka-uct 25 ✅ 8864 ms (24 iters) ✅ 8897 ms (24 iters) ≈ +0.4% (±10.2%) — / —
finagle-chirper 21 ✅ 6012 ms (33 iters) ✅ 6012 ms (33 iters) ≈ 0% (±25.4%) ⚠️ W:4 / ⚠️ W:3
finagle-chirper 25 ✅ 5458 ms (36 iters) ✅ 5501 ms (36 iters) ≈ +0.8% (±24.7%) ⚠️ W:3 / ⚠️ W:3
fj-kmeans 21 ✅ 2794 ms (67 iters) ✅ 2801 ms (67 iters) ≈ +0.3% (±2.7%) — / —
fj-kmeans 25 ✅ 2821 ms (66 iters) ✅ 2757 ms (67 iters) ≈ -2.3% (±2.6%) — / —
future-genetic 21 ✅ 2065 ms (90 iters) ✅ 2106 ms (88 iters) ≈ +2% (±2.9%) — / —
future-genetic 25 ✅ 2012 ms (93 iters) ✅ 2048 ms (91 iters) ≈ +1.8% (±2.7%) — / —
naive-bayes 21 ✅ 1266 ms (135 iters) ✅ 1264 ms (135 iters) ≈ -0.2% (±32.9%) — / —
naive-bayes 25 ✅ 1026 ms (166 iters) ✅ 999 ms (171 iters) ≈ -2.6% (±31.4%) — / —
reactors 21 ✅ 16138 ms (15 iters) ✅ 15993 ms (15 iters) ≈ -0.9% (±9.4%) — / —
reactors 25 ✅ 18295 ms (15 iters) ✅ 18564 ms (15 iters) ≈ +1.5% (±5.5%) — / —
Internal counter details (ddprof)

ddprof internal counters, latest / dev (✅ = 0, · = unavailable):

Benchmark JDK Dropped rec Dropped jvmti Dropped trace Skipped WC AGCT fail Unwind fail
akka-uct 21 ✅ / ✅ ✅ / ✅ 2 / 3 1933 / 1972 ✅ / ✅ ✅ / ✅
akka-uct 25 ✅ / ✅ ✅ / ✅ ✅ / 3 2315 / 2396 ✅ / ✅ ✅ / ✅
finagle-chirper 21 ✅ / ✅ ✅ / ✅ 3 / 1 8943 / 8589 ✅ / ✅ ✅ / ✅
finagle-chirper 25 ✅ / ✅ ✅ / ✅ 1 / 2 8109 / 8671 ✅ / ✅ ✅ / ✅
fj-kmeans 21 ✅ / ✅ ✅ / ✅ ✅ / ✅ ✅ / ✅ ✅ / ✅ ✅ / ✅
fj-kmeans 25 ✅ / ✅ ✅ / ✅ 3 / 1 1274 / 1252 ✅ / ✅ ✅ / ✅
future-genetic 21 ✅ / ✅ ✅ / ✅ 4 / 2 2996 / 2999 ✅ / ✅ ✅ / ✅
future-genetic 25 ✅ / ✅ ✅ / ✅ ✅ / 1 2974 / 2898 ✅ / ✅ ✅ / ✅
naive-bayes 21 ✅ / ✅ ✅ / ✅ 4 / 4 3505 / 3470 ✅ / ✅ ✅ / ✅
naive-bayes 25 ✅ / ✅ ✅ / ✅ 3 / ✅ 3479 / 3471 ✅ / ✅ ✅ / ✅
reactors 21 ✅ / ✅ ✅ / ✅ 1 / ✅ 1683 / 1785 ✅ / ✅ ✅ / ✅
reactors 25 ✅ / ✅ ✅ / ✅ 2 / ✅ 1874 / 1943 ✅ / ✅ ✅ / ✅

Copilot AI review requested due to automatic review settings July 25, 2026 00:56

Copilot AI 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.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (6)

ddprof-lib/src/test/cpp/hotspot_crash_protection_ut.cpp:17

  • The file header comment was partially updated to sigsetjmp/siglongjmp but still refers to jmp_buf* and longjmp(). This is now inconsistent with the code/API (sigjmp_buf* + siglongjmp()) and may confuse future maintenance.
 * Crash recovery inside walkVM relies on sigsetjmp/siglongjmp:
 *   1. walkVM stores a jmp_buf* on ProfiledThread (setJmpCtx/getJmpCtx),
 *      chaining it with whatever context was already installed so a
 *      signal-based sampler interrupting a non-signal-based sampler's own
 *      in-flight walkVM() call doesn't clobber the outer call's context.

ddprof-lib/src/test/cpp/hotspot_crash_protection_ut.cpp:287

  • This comment still says checkFault() "longjmps" even though the recovery mechanism is now siglongjmp(). Update the wording to match the implementation.
// End-to-end with real sigsetjmp/siglongjmp: a fault inside the inner frame must
// land in the inner frame's own recovery branch — checkFault() always
// longjmps through whatever is currently installed — and once the inner
// frame has recovered and restored the outer's context, the outer frame must
// be left exactly as it was, never having been unwound itself.

ddprof-lib/src/test/cpp/hotspot_crash_protection_ut.cpp:310

  • The inline comment describes the simulated jump as longjmp, but the code now calls siglongjmp(). Keeping the comment aligned helps avoid confusion when debugging these tests.
            // Simulate checkFault(): longjmp through whatever is currently
            // installed — this must hit the inner frame, not the outer.

ddprof-lib/src/test/cpp/hotspot_crash_protection_ut.cpp:292

  • ProfiledThread::getJmpCtx() now returns sigjmp_buf*, but this test file still contains earlier tests using jmp_buf/jmp_buf* with setJmpCtx/getJmpCtx (e.g., around the SetAndGetRoundTrip and NestedFramesChainAndUnwindInOrder tests). Those will no longer compile cleanly and should be updated to sigjmp_buf/sigjmp_buf* consistently throughout the file.
    sigjmp_buf outer_ctx;
    sigjmp_buf* outer_prev = _pt->getJmpCtx();
    int outer_landed = 0;
    int inner_landed = 0;

ddprof-lib/src/test/cpp/faultInjection_ut.cpp:147

  • This comment still says control returns to setjmp, but the test now uses sigsetjmp/siglongjmp. Update the wording for accuracy.
// caught by the sigsetjmp/siglongjmp crash protection, returning control to setjmp.

ddprof-lib/src/test/cpp/faultInjection_ut.cpp:163

  • The test body now uses sigsetjmp/siglongjmp, but the later comment and assertion message still say setjmp/longjmp. Since this shows up directly in test output, it should be kept consistent with the updated recovery mechanism.
  sigjmp_buf ctx;
  if (sigsetjmp(ctx, 1) != 0) {
    recovered = true;                  // returned here via checkFault -> siglongjmp
    faults++;
  }
  t->setJmpCtx(&ctx);

Comment thread ddprof-lib/src/main/cpp/threadLocalData.h
@dd-octo-sts

dd-octo-sts Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results (commit d2773a4)

Pipeline: https://gitlab.ddbuild.io/DataDog/apm-reliability/benchmarking-platform/-/pipelines/126937456 Commit: d2773a4fa0dd5c43ebd9975a2409a9c63d8bb57e

⚠️ Significant outliers

  • 🟢 future-genetic (JDK 21): runtime -2.6% (2133→2077 ms)
  • 🟢 future-genetic (JDK 25): runtime -5.2% (2080→1972 ms)
Runtime details (per benchmark × JDK)
Benchmark JDK Latest Dev Δ (dev vs latest) Issues L/D
akka-uct 21 ✅ 10522 ms (21 iters) ✅ 10247 ms (21 iters) ≈ -2.6% (±11.3%) — / —
akka-uct 25 ✅ 9035 ms (24 iters) ✅ 8862 ms (24 iters) ≈ -1.9% (±10.7%) — / —
finagle-chirper 21 ✅ 5998 ms (33 iters) ✅ 5954 ms (33 iters) ≈ -0.7% (±24.5%) ⚠️ W:3 / ⚠️ W:3
finagle-chirper 25 ✅ 5490 ms (36 iters) ✅ 5511 ms (36 iters) ≈ +0.4% (±24.7%) ⚠️ W:3 / ⚠️ W:3
fj-kmeans 21 ✅ 2787 ms (67 iters) ✅ 2853 ms (66 iters) ≈ +2.4% (±2.6%) — / —
fj-kmeans 25 ✅ 2813 ms (66 iters) ✅ 2807 ms (66 iters) ≈ -0.2% (±2.6%) — / —
future-genetic 21 ✅ 2133 ms (87 iters) ✅ 2077 ms (89 iters) 🟢 -2.6% — / —
future-genetic 25 ✅ 2080 ms (90 iters) ✅ 1972 ms (95 iters) 🟢 -5.2% — / —
naive-bayes 21 ✅ 1245 ms (137 iters) ✅ 1275 ms (134 iters) ≈ +2.4% (±33.2%) — / —
naive-bayes 25 ✅ 979 ms (174 iters) ✅ 1013 ms (169 iters) ≈ +3.5% (±32.2%) — / —
reactors 21 ✅ 16261 ms (15 iters) ✅ 16277 ms (15 iters) ≈ +0.1% (±8.2%) — / —
reactors 25 ✅ 18391 ms (15 iters) ✅ 18398 ms (15 iters) ≈ +0% (±3%) — / —
Internal counter details (ddprof)

ddprof internal counters, latest / dev (✅ = 0, · = unavailable):

Benchmark JDK Dropped rec Dropped jvmti Dropped trace Skipped WC AGCT fail Unwind fail
akka-uct 21 ✅ / ✅ ✅ / ✅ ✅ / 2 1977 / 2008 ✅ / ✅ ✅ / ✅
akka-uct 25 ✅ / ✅ ✅ / ✅ ✅ / 1 2312 / 2189 ✅ / ✅ ✅ / ✅
finagle-chirper 21 ✅ / ✅ ✅ / ✅ 3 / 2 8725 / 8783 ✅ / ✅ ✅ / ✅
finagle-chirper 25 ✅ / ✅ ✅ / ✅ 2 / 2 8724 / 8493 ✅ / ✅ ✅ / ✅
fj-kmeans 21 ✅ / ✅ ✅ / ✅ ✅ / 2 1277 / 1278 ✅ / ✅ ✅ / ✅
fj-kmeans 25 ✅ / ✅ ✅ / ✅ 1 / 1 1274 / 1274 ✅ / ✅ ✅ / ✅
future-genetic 21 ✅ / ✅ ✅ / ✅ 2 / 2 2916 / 2861 ✅ / ✅ ✅ / ✅
future-genetic 25 ✅ / ✅ ✅ / ✅ 3 / 1 2887 / 2863 ✅ / ✅ ✅ / ✅
naive-bayes 21 ✅ / ✅ ✅ / ✅ 1 / 1 3465 / 3536 ✅ / ✅ ✅ / ✅
naive-bayes 25 ✅ / ✅ ✅ / ✅ 5 / 1 3492 / 3486 ✅ / ✅ ✅ / ✅
reactors 21 ✅ / ✅ ✅ / ✅ 2 / 2 1826 / 1586 ✅ / ✅ ✅ / ✅
reactors 25 ✅ / ✅ ✅ / ✅ ✅ / 2 1859 / 1858 ✅ / ✅ ✅ / ✅

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 25, 2026 21:08

Copilot AI 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.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (4)

ddprof-lib/src/main/cpp/threadLocalData.h:251

  • _jmp_buf is a std::atomic, but these helpers rely on implicit atomic conversions/assignments. Using explicit load/store with acquire/release makes the publish/observe intent clearer (and avoids depending on implicit operator overloads), which is particularly important since this is read from an async signal handler path.
  inline void setJmpCtx(sigjmp_buf* buf) {
    _jmp_buf = buf;
  }

  inline sigjmp_buf* getJmpCtx() const {
    return _jmp_buf;
  }

  inline bool isProtected() const {
    return _jmp_buf != nullptr;
  }

ddprof-lib/src/test/cpp/faultInjection_ut.cpp:148

  • This comment still says control returns to setjmp, but the test now uses sigsetjmp/siglongjmp. Updating the wording avoids confusion when grepping for the intended mechanism.
// (c2) walkVM path: a raw dereference of an injected poison pointer must be
// caught by the sigsetjmp/siglongjmp crash protection, returning control to setjmp.
TEST_F(FaultInjectionTest, WalkVmSetjmpRecoversFromInjectedFault) {

ddprof-lib/src/test/cpp/hotspot_crash_protection_ut.cpp:287

  • These comments still describe the recovery as longjmp even though the test has been switched to sigsetjmp/siglongjmp. Keeping terminology consistent will make the chaining behavior easier to follow.
// End-to-end with real sigsetjmp/siglongjmp: a fault inside the inner frame must
// land in the inner frame's own recovery branch — checkFault() always
// longjmps through whatever is currently installed — and once the inner
// frame has recovered and restored the outer's context, the outer frame must
// be left exactly as it was, never having been unwound itself.

ddprof-lib/src/test/cpp/hotspot_crash_protection_ut.cpp:312

  • The inline comment says "Simulate checkFault(): longjmp" but the code calls siglongjmp now.
            _pt->setJmpCtx(&inner_ctx);
            // Simulate checkFault(): longjmp through whatever is currently
            // installed — this must hit the inner frame, not the outer.
            siglongjmp(*_pt->getJmpCtx(), 1);
            FAIL() << "unreachable: siglongjmp does not return";

@zhengyu123
zhengyu123 marked this pull request as ready for review July 25, 2026 21:15
@zhengyu123
zhengyu123 requested a review from a team as a code owner July 25, 2026 21:15
@dd-octo-sts

dd-octo-sts Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results (commit adad103)

Pipeline: https://gitlab.ddbuild.io/DataDog/apm-reliability/benchmarking-platform/-/pipelines/126983712 Commit: adad103c67ddfce3591070dd4855c0fb88df75fb

⚠️ Significant outliers

  • 🔴 fj-kmeans (JDK 21): runtime +3.9% (2721→2826 ms)
  • 🔴 future-genetic (JDK 21): runtime +2.6% (2055→2109 ms)
  • 🔴 future-genetic (JDK 25): runtime +4.4% (2021→2110 ms)
Runtime details (per benchmark × JDK)
Benchmark JDK Latest Dev Δ (dev vs latest) Issues L/D
akka-uct 21 ✅ 10337 ms (21 iters) ✅ 10307 ms (21 iters) ≈ -0.3% (±11.6%) — / —
akka-uct 25 ✅ 8961 ms (24 iters) ✅ 8800 ms (24 iters) ≈ -1.8% (±10.8%) — / —
finagle-chirper 21 ✅ 6006 ms (33 iters) ✅ 5958 ms (33 iters) ≈ -0.8% (±26.1%) ⚠️ W:3 / ⚠️ W:3
finagle-chirper 25 ✅ 5464 ms (36 iters) ✅ 5489 ms (36 iters) ≈ +0.5% (±24.6%) ⚠️ W:3 / ⚠️ W:4
fj-kmeans 21 ✅ 2721 ms (68 iters) ✅ 2826 ms (66 iters) 🔴 +3.9% — / —
fj-kmeans 25 ✅ 2812 ms (66 iters) ✅ 2795 ms (67 iters) ≈ -0.6% (±2.5%) — / —
future-genetic 21 ✅ 2055 ms (90 iters) ✅ 2109 ms (88 iters) 🔴 +2.6% — / —
future-genetic 25 ✅ 2021 ms (92 iters) ✅ 2110 ms (88 iters) 🔴 +4.4% — / —
naive-bayes 21 ✅ 1251 ms (137 iters) ✅ 1313 ms (131 iters) ≈ +5% (±33.5%) — / —
naive-bayes 25 ✅ 1032 ms (166 iters) ✅ 1013 ms (168 iters) ≈ -1.8% (±31.2%) — / —
reactors 21 ✅ 16767 ms (15 iters) ✅ 15964 ms (15 iters) ≈ -4.8% (±7%) — / —
reactors 25 ✅ 18801 ms (15 iters) ✅ 18428 ms (15 iters) ≈ -2% (±5%) — / —
Internal counter details (ddprof)

ddprof internal counters, latest / dev (✅ = 0, · = unavailable):

Benchmark JDK Dropped rec Dropped jvmti Dropped trace Skipped WC AGCT fail Unwind fail
akka-uct 21 ✅ / ✅ ✅ / ✅ ✅ / 5 1966 / 2025 ✅ / ✅ ✅ / ✅
akka-uct 25 ✅ / ✅ ✅ / ✅ 1 / 3 2400 / 2261 ✅ / ✅ ✅ / ✅
finagle-chirper 21 ✅ / ✅ ✅ / ✅ 7 / 3 8658 / 8467 ✅ / ✅ ✅ / ✅
finagle-chirper 25 ✅ / ✅ ✅ / ✅ ✅ / 1 8288 / 8713 ✅ / ✅ ✅ / ✅
fj-kmeans 21 ✅ / ✅ ✅ / ✅ 1 / 4 1278 / 1251 ✅ / ✅ ✅ / ✅
fj-kmeans 25 ✅ / ✅ ✅ / ✅ 1 / 3 1295 / 1247 ✅ / ✅ ✅ / ✅
future-genetic 21 ✅ / ✅ ✅ / ✅ 1 / 1 3067 / 2997 ✅ / ✅ ✅ / ✅
future-genetic 25 ✅ / ✅ ✅ / ✅ ✅ / 2 2913 / 2848 ✅ / ✅ ✅ / ✅
naive-bayes 21 ✅ / ✅ ✅ / ✅ 5 / 1 3505 / 3571 ✅ / ✅ ✅ / ✅
naive-bayes 25 ✅ / ✅ ✅ / ✅ 3 / 9 3509 / 3461 ✅ / ✅ ✅ / ✅
reactors 21 ✅ / ✅ ✅ / ✅ ✅ / ✅ ✅ / ✅ ✅ / ✅ ✅ / ✅
reactors 25 ✅ / ✅ ✅ / ✅ 1 / 1 1932 / 1816 ✅ / ✅ ✅ / ✅

@kaahos kaahos 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.

Thanks for the work! Overall this looks good to me. I've made some suggestions to make some tests and comments more consistent with this switch.

Comment thread ddprof-lib/src/test/cpp/hotspot_crash_protection_ut.cpp Outdated
Comment thread ddprof-lib/src/test/cpp/hotspot_crash_protection_ut.cpp Outdated
Comment thread ddprof-lib/src/test/cpp/hotspot_crash_protection_ut.cpp Outdated
Comment thread ddprof-lib/src/test/cpp/hotspot_crash_protection_ut.cpp Outdated
Comment thread ddprof-lib/src/test/cpp/hotspot_crash_protection_ut.cpp Outdated
Comment thread ddprof-lib/src/test/cpp/hotspot_crash_protection_ut.cpp Outdated
Comment thread ddprof-lib/src/test/cpp/hotspot_crash_protection_ut.cpp Outdated
Comment thread ddprof-lib/src/test/cpp/hotspot_crash_protection_ut.cpp Outdated
Comment thread ddprof-lib/src/test/cpp/hotspot_crash_protection_ut.cpp Outdated
Comment thread ddprof-lib/src/test/cpp/hotspot_crash_protection_ut.cpp Outdated
Copilot AI review requested due to automatic review settings July 27, 2026 09:54

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings July 27, 2026 10:02

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@dd-octo-sts

dd-octo-sts Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results (commit b4a0284)

Pipeline: https://gitlab.ddbuild.io/DataDog/apm-reliability/benchmarking-platform/-/pipelines/127106698 Commit: b4a0284218a2d682fa7deb11d9dc35d3accc56a3

⚠️ Significant outliers

  • 🔴 future-genetic (JDK 21): runtime +3.6% (2033→2106 ms)
  • 🟢 future-genetic (JDK 25): runtime -3.4% (2075→2005 ms)
Runtime details (per benchmark × JDK)
Benchmark JDK Latest Dev Δ (dev vs latest) Issues L/D
akka-uct 21 ✅ 10199 ms (21 iters) ✅ 10390 ms (21 iters) ≈ +1.9% (±11.4%) — / —
akka-uct 25 ✅ 8878 ms (24 iters) ✅ 8830 ms (24 iters) ≈ -0.5% (±10.3%) — / —
finagle-chirper 21 ✅ 5992 ms (33 iters) ✅ 5986 ms (33 iters) ≈ -0.1% (±25.2%) ⚠️ W:3 / ⚠️ W:3
finagle-chirper 25 ✅ 5505 ms (36 iters) ✅ 5456 ms (36 iters) ≈ -0.9% (±25%) ⚠️ W:3 / ⚠️ W:3
fj-kmeans 21 ✅ 2704 ms (70 iters) ✅ 2749 ms (68 iters) ≈ +1.7% (±2.7%) — / —
fj-kmeans 25 ✅ 2819 ms (66 iters) ✅ 2811 ms (66 iters) ≈ -0.3% (±2.6%) — / —
future-genetic 21 ✅ 2033 ms (92 iters) ✅ 2106 ms (88 iters) 🔴 +3.6% — / —
future-genetic 25 ✅ 2075 ms (89 iters) ✅ 2005 ms (93 iters) 🟢 -3.4% — / —
naive-bayes 21 ✅ 1340 ms (128 iters) ✅ 1273 ms (134 iters) ≈ -5% (±32.2%) — / —
naive-bayes 25 ✅ 1007 ms (169 iters) ✅ 983 ms (173 iters) ≈ -2.4% (±31.3%) — / —
reactors 21 ✅ 16201 ms (15 iters) ✅ 16155 ms (15 iters) ≈ -0.3% (±7.9%) — / —
reactors 25 ✅ 18074 ms (15 iters) ✅ 18767 ms (15 iters) ≈ +3.8% (±5.2%) — / —
Internal counter details (ddprof)

ddprof internal counters, latest / dev (✅ = 0, · = unavailable):

Benchmark JDK Dropped rec Dropped jvmti Dropped trace Skipped WC AGCT fail Unwind fail
akka-uct 21 ✅ / ✅ ✅ / ✅ 1 / 2 1910 / 2009 ✅ / ✅ ✅ / ✅
akka-uct 25 ✅ / ✅ ✅ / ✅ 1 / 3 2492 / 2064 ✅ / ✅ ✅ / ✅
finagle-chirper 21 ✅ / ✅ ✅ / ✅ 6 / 5 8735 / 8297 ✅ / ✅ ✅ / ✅
finagle-chirper 25 ✅ / ✅ ✅ / ✅ 1 / ✅ 8462 / 8466 ✅ / ✅ ✅ / ✅
fj-kmeans 21 ✅ / ✅ ✅ / ✅ 3 / ✅ 1286 / 1280 ✅ / ✅ ✅ / ✅
fj-kmeans 25 ✅ / ✅ ✅ / ✅ 2 / 3 1262 / 1259 ✅ / ✅ ✅ / ✅
future-genetic 21 ✅ / ✅ ✅ / ✅ 2 / 1 3016 / 2973 ✅ / ✅ ✅ / ✅
future-genetic 25 ✅ / ✅ ✅ / ✅ 2 / 6 2886 / 2947 ✅ / ✅ ✅ / ✅
naive-bayes 21 ✅ / ✅ ✅ / ✅ 4 / 4 3496 / 3485 ✅ / ✅ ✅ / ✅
naive-bayes 25 ✅ / ✅ ✅ / ✅ 4 / 1 3482 / 3478 ✅ / ✅ ✅ / ✅
reactors 21 ✅ / ✅ ✅ / ✅ ✅ / ✅ ✅ / ✅ ✅ / ✅ ✅ / ✅
reactors 25 ✅ / ✅ ✅ / ✅ 1 / 1 1817 / 1887 ✅ / ✅ ✅ / ✅

@rkennke

rkennke commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

A couple of things worth addressing before merging:

Performance

sigsetjmp(ctx, 1) in ddprof-lib/src/main/cpp/hotspot/hotspotSupport.cpp (the sigsetjmp(crash_protection_ctx, 1) call in walkVM) saves the signal mask on every call, which typically costs a rt_sigprocmask syscall — unlike plain setjmp(), which is pure userspace register save. walkVM is on the hot path of a sampling profiler and can run very frequently, so this adds a syscall-class cost to every stack walk, not just the fault-recovery path.

Could you run a before/after comparison on a stack-walk-heavy benchmark to quantify the overhead, and add a note in the PR description on the correctness-vs-perf tradeoff (and why it's acceptable)?

Test coverage

None of the updated tests (ddprof-lib/src/test/cpp/faultInjection_ut.cpp, ddprof-lib/src/test/cpp/hotspot_crash_protection_ut.cpp) assert on the actual defect this PR fixes — that the signal mask is restored after a siglongjmp out of a handler. They cover functional recovery (control returns, depth/nesting counters), but not mask restoration. Could you add a regression test that blocks a signal, triggers a fault that siglongjmps out of a handler, and asserts the mask afterward matches what it was before signal delivery?

Nit

The "How to test the change?" section in the PR description is empty — worth filling in given this is a signal-safety fix that regular CI likely won't exercise end-to-end.


🤖 Generated with Claude Code

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.

5 participants