From 150934c0271ac71cadf0802b94b9c5c1c1727b18 Mon Sep 17 00:00:00 2001 From: Richard Startin Date: Fri, 19 Jan 2024 10:33:04 +0000 Subject: [PATCH] terminate unwinding at call_stub --- ddprof-lib/src/main/cpp/profiler.cpp | 2 +- ddprof-lib/src/main/cpp/stackWalker.cpp | 6 ++- ddprof-lib/src/main/cpp/stackWalker.h | 2 +- .../profiler/cpu/VMStructsBasedCpuTest.java | 53 +++++++++++++++++++ 4 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 ddprof-test/src/test/java/com/datadoghq/profiler/cpu/VMStructsBasedCpuTest.java diff --git a/ddprof-lib/src/main/cpp/profiler.cpp b/ddprof-lib/src/main/cpp/profiler.cpp index dd240d30..cd335297 100644 --- a/ddprof-lib/src/main/cpp/profiler.cpp +++ b/ddprof-lib/src/main/cpp/profiler.cpp @@ -690,7 +690,7 @@ void Profiler::recordSample(void* ucontext, u64 counter, int tid, jint event_typ ASGCT_CallFrame *native_stop = frames + num_frames; num_frames += getNativeTrace(ucontext, native_stop, event_type, tid, &java_ctx, &truncated); if (_cstack == CSTACK_VM) { - num_frames += StackWalker::walkVM(ucontext, frames + num_frames, _max_stack_depth); + num_frames += StackWalker::walkVM(ucontext, frames + num_frames, _max_stack_depth, _call_stub_begin, _call_stub_end); } else if (event_type == BCI_CPU || event_type == BCI_WALL) { int java_frames = 0; { diff --git a/ddprof-lib/src/main/cpp/stackWalker.cpp b/ddprof-lib/src/main/cpp/stackWalker.cpp index 4b33bbc6..21db0a80 100644 --- a/ddprof-lib/src/main/cpp/stackWalker.cpp +++ b/ddprof-lib/src/main/cpp/stackWalker.cpp @@ -217,7 +217,8 @@ int StackWalker::walkDwarf(void* ucontext, const void** callchain, int max_depth return depth; } -int StackWalker::walkVM(void* ucontext, ASGCT_CallFrame* frames, int max_depth) { +int StackWalker::walkVM(void* ucontext, ASGCT_CallFrame* frames, int max_depth, + const void* _termination_frame_begin, const void* _termination_frame_end) { const void* pc; uintptr_t fp; uintptr_t sp; @@ -257,6 +258,9 @@ int StackWalker::walkVM(void* ucontext, ASGCT_CallFrame* frames, int max_depth) // Walk until the bottom of the stack or until the first Java frame while (depth < max_depth) { + if (pc >= _termination_frame_begin && pc < _termination_frame_end) { + break; + } if (CodeHeap::contains(pc)) { NMethod* nm = CodeHeap::findNMethod(pc); if (nm == NULL) { diff --git a/ddprof-lib/src/main/cpp/stackWalker.h b/ddprof-lib/src/main/cpp/stackWalker.h index c8bf7c9d..d0ca5f5e 100644 --- a/ddprof-lib/src/main/cpp/stackWalker.h +++ b/ddprof-lib/src/main/cpp/stackWalker.h @@ -37,7 +37,7 @@ class StackWalker { public: static int walkFP(void* ucontext, const void** callchain, int max_depth, StackContext* java_ctx, bool *truncated); static int walkDwarf(void* ucontext, const void** callchain, int max_depth, StackContext* java_ctx, bool *truncated); - static int walkVM(void* ucontext, ASGCT_CallFrame* frames, int max_depth); + static int walkVM(void* ucontext, ASGCT_CallFrame* frames, int max_depth, const void* _termination_frame_begin, const void* _termination_frame_end); static void checkFault(); }; diff --git a/ddprof-test/src/test/java/com/datadoghq/profiler/cpu/VMStructsBasedCpuTest.java b/ddprof-test/src/test/java/com/datadoghq/profiler/cpu/VMStructsBasedCpuTest.java new file mode 100644 index 00000000..4ec21551 --- /dev/null +++ b/ddprof-test/src/test/java/com/datadoghq/profiler/cpu/VMStructsBasedCpuTest.java @@ -0,0 +1,53 @@ +package com.datadoghq.profiler.cpu; + +import com.datadoghq.profiler.AbstractProfilerTest; +import com.datadoghq.profiler.Platform; +import org.junit.jupiter.api.Test; +import org.openjdk.jmc.common.item.IItem; +import org.openjdk.jmc.common.item.IItemCollection; +import org.openjdk.jmc.common.item.IItemIterable; +import org.openjdk.jmc.common.item.IMemberAccessor; +import org.openjdk.jmc.flightrecorder.jdk.JdkAttributes; + +import java.util.concurrent.ExecutionException; + +import static org.junit.jupiter.api.Assertions.assertFalse; + + +public class VMStructsBasedCpuTest extends AbstractProfilerTest { + + private ProfiledCode profiledCode; + + @Override + protected void before() { + profiledCode = new ProfiledCode(profiler); + } + + @Test + public void test() throws ExecutionException, InterruptedException { + for (int i = 0, id = 1; i < 100; i++, id += 3) { + profiledCode.method1(id); + } + stopProfiler(); + IItemCollection events = verifyEvents("datadog.ExecutionSample"); + for (IItemIterable it : events) { + IMemberAccessor stackTraceAccessor = JdkAttributes.STACK_TRACE_STRING.getAccessor(it.getType()); + for (IItem item : it) { + String stacktrace = stackTraceAccessor.getMember(item); + assertFalse(stacktrace.contains("JavaCalls::call_virtual()"), + "JavaCalls::call_virtual() (above call_stub) found in stack trace"); + assertFalse(stacktrace.contains("call_stub()"), + "call_stub() (sentinel value used to halt unwinding) found in stack trace"); + } + } + } + + @Override + protected String getProfilerCommand() { + return "cpu=10ms" + (vmstructsUnwinderSupported() ? ",cstack=vm" : ""); + } + + private boolean vmstructsUnwinderSupported() { + return !Platform.isJ9() && !Platform.isZing(); + } +}