Skip to content

Commit 70085b2

Browse files
jbachorikclaude
andcommitted
Restore error frames for truncated stacks in walkVM
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 84b36c7 commit 70085b2

3 files changed

Lines changed: 34 additions & 32 deletions

File tree

ddprof-lib/src/main/cpp/stackWalker.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,7 @@ __attribute__((no_sanitize("address"))) int StackWalker::walkVM(void* ucontext,
555555
// already used the anchor; disable it
556556
anchor = NULL;
557557
if (sp < prev_sp || sp >= bottom || !aligned(sp)) {
558+
fillFrame(frames[depth++], BCI_ERROR, "break_no_anchor");
558559
break;
559560
}
560561
// we restored from Java frame; clean the prev_native_pc
@@ -579,6 +580,11 @@ __attribute__((no_sanitize("address"))) int StackWalker::walkVM(void* ucontext,
579580
}
580581
}
581582
}
583+
if (vm_thread != NULL && vm_thread->isJavaThread()) {
584+
fillFrame(frames[depth++], BCI_ERROR, "break_no_anchor");
585+
} else {
586+
fillFrame(frames[depth++], BCI_ERROR, "break_no_symbol");
587+
}
582588
break;
583589
}
584590
fillFrame(frames[depth++], frame_bci, (void*)method_name);

ddprof-test/src/test/java/com/datadoghq/profiler/nativethread/NativeThreadTest.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,10 @@
3232
import static org.junit.jupiter.api.Assertions.assertTrue;
3333

3434
/**
35-
* Tests native thread profiling to ensure proper stack unwinding without error frames.
36-
* This test validates Theory 1 (thread entry point detection) by verifying that
37-
* native threads no longer produce break_no_anchor or other error frames.
35+
* Tests native thread profiling to ensure proper stack unwinding.
36+
* Validates that native threads produce usable samples with do_primes() and
37+
* that non-Java thread samples are not misclassified as break_no_anchor
38+
* (which implies a missing JavaFrameAnchor, inapplicable for pure pthreads).
3839
*/
3940
public class NativeThreadTest extends AbstractProfilerTest {
4041

@@ -68,13 +69,13 @@ public void test() {
6869
String stacktrace = stacktraceAccessor.getMember(item);
6970
totalSamples++;
7071

71-
// Primary verification: Assert NO break_no_anchor errors
72-
// Note: no_Java_frame is acceptable for pure native threads that never enter Java
73-
assertFalse(stacktrace.contains("break_no_anchor"),
74-
"Found break_no_anchor error frame in stacktrace: " + stacktrace);
75-
76-
// Secondary verification: Check for expected native frames
7772
if (stacktrace.indexOf("do_primes()") != -1) {
73+
// Native thread sample: must not contain break_no_anchor
74+
// (non-Java threads have no JavaFrameAnchor — that error is inapplicable)
75+
// break_no_symbol is expected when DWARF CFI is incomplete
76+
assertFalse(stacktrace.contains("break_no_anchor"),
77+
"Found break_no_anchor in native thread sample: " + stacktrace);
78+
7879
if (!stacktrace_printed) {
7980
stacktrace_printed = true;
8081
System.out.println("Native thread stack:");

ddprof-test/src/test/java/com/datadoghq/profiler/nativethread/ThreadEntryDetectionTest.java

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,14 @@
2828
import static org.junit.jupiter.api.Assertions.assertTrue;
2929

3030
/**
31-
* Test specifically for thread entry point detection (Theory 1).
31+
* Test for thread entry point detection on native threads.
3232
*
33-
* This test validates that when profiling native threads, the profiler:
34-
* 1. Correctly detects JVM thread entry points (thread_native_entry, JavaThread::, etc.)
35-
* 2. Does NOT produce break_no_anchor or other error frames
36-
* 3. Treats thread entry points as root frames and stops unwinding
37-
*
38-
* The test uses a larger number of threads than the standard test to increase
39-
* the likelihood of catching race conditions in thread startup detection.
33+
* Validates that when profiling native threads the profiler produces usable
34+
* samples and that non-Java thread samples are not misclassified as
35+
* break_no_anchor (which implies a missing JavaFrameAnchor, inapplicable
36+
* for pure pthreads). break_no_symbol is acceptable — it indicates the
37+
* DWARF unwind could not resolve the next frame, which is expected when
38+
* the test library or libc lacks complete CFI.
4039
*/
4140
public class ThreadEntryDetectionTest extends AbstractProfilerTest {
4241

@@ -76,9 +75,10 @@ public void testThreadEntryDetection() throws Exception {
7675
}
7776

7877
/**
79-
* Verifies that no error frames are present in the stacktraces.
80-
* This is the primary validation for Theory 1 - ensuring thread entry
81-
* detection prevents malformed stacktraces.
78+
* Verifies that native thread samples do not contain break_no_anchor.
79+
* break_no_anchor implies a missing JavaFrameAnchor which is inapplicable
80+
* for pure pthreads. break_no_symbol is acceptable — it means the DWARF
81+
* unwind hit an unresolvable PC, expected when CFI is incomplete.
8282
*/
8383
private void assertNoErrorFrames(IItemCollection events) {
8484
int samplesChecked = 0;
@@ -91,21 +91,16 @@ private void assertNoErrorFrames(IItemCollection events) {
9191
String stackTrace = stackTraceAccessor.getMember(sample);
9292
samplesChecked++;
9393

94-
// Critical assertion: NO error frames should be present
95-
assertFalse(stackTrace.contains("break_no_anchor"),
96-
String.format("Found break_no_anchor error in sample %d/%d:\n%s",
97-
samplesChecked, samplesChecked, stackTrace));
98-
99-
// Note: no_Java_frame is acceptable for pure native threads that never enter Java
100-
101-
// Additional checks for other error indicators
102-
assertFalse(stackTrace.contains("BCI_ERROR"),
103-
String.format("Found BCI_ERROR in sample %d/%d:\n%s",
104-
samplesChecked, samplesChecked, stackTrace));
94+
if (stackTrace.contains("do_primes()")) {
95+
// Native thread sample: must not be misclassified as break_no_anchor
96+
assertFalse(stackTrace.contains("break_no_anchor"),
97+
String.format("Found break_no_anchor in native thread sample %d:\n%s",
98+
samplesChecked, stackTrace));
99+
}
105100
}
106101
}
107102

108-
System.out.println("Verified " + samplesChecked + " samples - no error frames found");
103+
System.out.println("Verified " + samplesChecked + " samples");
109104
}
110105

111106
/**

0 commit comments

Comments
 (0)