From a18740f8cccd1188830e37bba955724140112376 Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Fri, 17 Jul 2026 18:04:00 +0000 Subject: [PATCH 01/20] v0 --- ddprof-lib/src/main/cpp/hotspot/vmStructs.cpp | 3 +- ddprof-lib/src/main/cpp/hotspot/vmStructs.h | 20 ++++--------- .../src/main/cpp/hotspot/vmStructs.inline.h | 30 +++++++++++++++++++ 3 files changed, 37 insertions(+), 16 deletions(-) diff --git a/ddprof-lib/src/main/cpp/hotspot/vmStructs.cpp b/ddprof-lib/src/main/cpp/hotspot/vmStructs.cpp index ccbca45089..469138ac86 100644 --- a/ddprof-lib/src/main/cpp/hotspot/vmStructs.cpp +++ b/ddprof-lib/src/main/cpp/hotspot/vmStructs.cpp @@ -863,7 +863,8 @@ VMFlag *VMFlag::find(const char *name, int type_mask) { size_t count = *(size_t*)_flag_count; for (size_t i = 0; i < count; i++) { VMFlag* f = VMFlag::cast(*(const char**)_flags_addr + i * VMFlag::type_size()); - if (f->name() != NULL && strcmp(f->name(), name) == 0) { + const char* fname = f->name(); + if (fname != nullptr && strcmp(fname, name) == 0) { int masked = 0x1 << f->type(); if (masked & type_mask) { return (VMFlag*)f; diff --git a/ddprof-lib/src/main/cpp/hotspot/vmStructs.h b/ddprof-lib/src/main/cpp/hotspot/vmStructs.h index 9ef2586851..139d430a34 100644 --- a/ddprof-lib/src/main/cpp/hotspot/vmStructs.h +++ b/ddprof-lib/src/main/cpp/hotspot/vmStructs.h @@ -449,18 +449,11 @@ class VMStructs { static void checkNativeBinding(jvmtiEnv *jvmti, JNIEnv *jni, jmethodID method, void *address); static const void *findHeapUsageFunc(); - const char* at(int offset) { - const char* ptr = (const char*)this + offset; - assert(crashProtectionActive() || SafeAccess::isReadable(ptr)); - // Poison only the returned pointer; the assert above sees the real ptr. - return INJECT_FAULT_ADDRESS_RARE(ptr); - } + inline const char* at(int offset); + inline const char* at(int offset) const; - const char* at(int offset) const { - const char* ptr = (const char*)this + offset; - assert(crashProtectionActive() || SafeAccess::isReadable(ptr)); - return INJECT_FAULT_ADDRESS_RARE(ptr); - } + template + T load_at_offset(int offset) const; static bool goodPtr(const void* ptr) { return (uintptr_t)ptr >= 0x1000 && ((uintptr_t)ptr & (sizeof(uintptr_t) - 1)) == 0; @@ -1136,10 +1129,7 @@ DECLARE(VMFlag) static VMFlag* find(const char* name); static VMFlag *find(const char* name, std::initializer_list types); - const char* name() { - assert(_flag_name_offset >= 0); - return *(const char**) at(_flag_name_offset); - } + inline const char* name() const; int type(); diff --git a/ddprof-lib/src/main/cpp/hotspot/vmStructs.inline.h b/ddprof-lib/src/main/cpp/hotspot/vmStructs.inline.h index 2b7edb3250..7453280975 100644 --- a/ddprof-lib/src/main/cpp/hotspot/vmStructs.inline.h +++ b/ddprof-lib/src/main/cpp/hotspot/vmStructs.inline.h @@ -10,6 +10,7 @@ #include "hotspot/hotspotSupport.h" #include "hotspot/vmStructs.h" #include "jvmThread.h" +#include "safeAccess.h" #include "threadLocalData.h" inline bool crashProtectionActive() { @@ -28,6 +29,30 @@ inline T* cast_to(const void* ptr) { return reinterpret_cast(const_cast(ptr)); } +inline const char* VMStructs::at(int offset) { + const char* ptr = (const char*)this + offset; + assert(crashProtectionActive() || SafeAccess::isReadable(ptr)); + // Poison only the returned pointer; the assert above sees the real ptr. + return INJECT_FAULT_ADDRESS_RARE(ptr); +} + +inline const char* VMStructs::at(int offset) const { + const char* ptr = (const char*)this + offset; + assert(crashProtectionActive() || SafeAccess::isReadable(ptr)); + return INJECT_FAULT_ADDRESS_RARE(ptr); +} + +template +T VMStructs::load_at_offset(int offset) const { + const char* ptr = at(offset); + if (safe) { + return (T)SafeAccess::loadPtr((void**)ptr, nullptr); + } else { + return *((T*)ptr); + } +} + + VMThread* VMThread::current() { assert(VM::isHotspot()); return VMThread::cast(JVMThread::current()); @@ -188,5 +213,10 @@ u16 VMConstMethod::signatureIndex() const { return *(u16*)at(_constmethod_sig_index_offset); } +// The method is called without protection +inline const char* VMFlag::name() const { + assert(_flag_name_offset >= 0); + return load_at_offset(_flag_name_offset); +} #endif // _HOTSPOT_VMSTRUCTS_INLINE_H From 4db801f38eac983bad7b6791ccb2639866557596 Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Mon, 20 Jul 2026 15:57:19 +0000 Subject: [PATCH 02/20] Fix startup --- ddprof-lib/src/main/cpp/vmEntry.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ddprof-lib/src/main/cpp/vmEntry.cpp b/ddprof-lib/src/main/cpp/vmEntry.cpp index 8eadc643d1..77a39a39e3 100644 --- a/ddprof-lib/src/main/cpp/vmEntry.cpp +++ b/ddprof-lib/src/main/cpp/vmEntry.cpp @@ -328,9 +328,6 @@ bool VM::initShared(JavaVM* vm) { return false; } - // Initialize VMStructs - VMStructs::init(lib); - // Mark thread entry points for all JVMs (critical for correct stack unwinding) lib->mark(isThreadEntry, MARK_THREAD_ENTRY); @@ -563,6 +560,10 @@ void VM::ready(jvmtiEnv *jvmti, JNIEnv *jni) { Profiler::setupSignalHandlers(); if (isHotspot()) { JitWriteProtection jit(true); + CodeCache* lib = openJvmLibrary(); + assert(lib != nullptr); + // Initialize VMStructs + VMStructs::init(lib); VMStructs::ready(); } } From e11fe697f93ea269577715d214857028822423c2 Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Tue, 21 Jul 2026 00:31:35 +0000 Subject: [PATCH 03/20] Fix startup and add test --- ddprof-lib/src/main/cpp/vmEntry.cpp | 29 ++++++++-- .../com/datadoghq/profiler/LibraryLoader.java | 24 +++++--- .../com/datadoghq/profiler/JVMAccessTest.java | 55 +++++++++++++++++++ 3 files changed, 95 insertions(+), 13 deletions(-) diff --git a/ddprof-lib/src/main/cpp/vmEntry.cpp b/ddprof-lib/src/main/cpp/vmEntry.cpp index 77a39a39e3..af793b0230 100644 --- a/ddprof-lib/src/main/cpp/vmEntry.cpp +++ b/ddprof-lib/src/main/cpp/vmEntry.cpp @@ -446,6 +446,15 @@ bool VM::initProfilerBridge(JavaVM *vm, bool attach) { return false; } + // Under Agent_OnLoad (attach == false), this is the first native entry point and + // VM_INIT has not fired yet, so VMStructs would otherwise stay uninitialized until + // VM::VMInit() runs -- but CodeHeap::available()/VMFlag::find() below are used + // synchronously in this function. Get VMStructs (and crash-protection signal + // handlers) ready now; VM::ready() is idempotent, so the later VM::VMInit() + // callback (attach == false) or the direct call from VM::initLibrary() (already + // run before a JNI-triggered attach == true call gets here) is a safe no-op. + ready(jvmti(), jni()); + if (!attach && hotspot_version() == 8 && OS::isLinux()) { // Workaround for JDK-8185348 char *func = (char *)lib->findSymbol( @@ -514,6 +523,8 @@ bool VM::initProfilerBridge(JavaVM *vm, bool attach) { NULL); if (hotspot_version() == 0 || !CodeHeap::available()) { + TEST_LOG("CompiledMethodLoad workaround: hotspot_version=%d CodeHeap::available=%d", + hotspot_version(), CodeHeap::available()); // Workaround for JDK-8173361: avoid CompiledMethodLoad events when possible _jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_LOAD, NULL); @@ -521,6 +532,7 @@ bool VM::initProfilerBridge(JavaVM *vm, bool attach) { // DebugNonSafepoints is automatically enabled with CompiledMethodLoad, // otherwise we set the flag manually VMFlag* f = VMFlag::find("DebugNonSafepoints", {VMFlag::Type::Bool}); + TEST_LOG("DebugNonSafepoints flag %s", f != NULL ? "found" : "not found"); if (f != NULL && f->isDefault()) { f->set(1); } @@ -554,17 +566,22 @@ bool VM::initProfilerBridge(JavaVM *vm, bool attach) { return true; } -// Run late initialization when JVM is ready +// Run late initialization when JVM is ready. May be called more than once (from +// initProfilerBridge() directly, and later from the VMInit JVMTI callback, or from +// initLibrary() followed by a JNI-triggered attach) -- the VMStructs init below only +// ever runs once. void VM::ready(jvmtiEnv *jvmti, JNIEnv *jni) { Profiler::check_JDK_8313796_workaround(); Profiler::setupSignalHandlers(); - if (isHotspot()) { + static bool vmstructs_ready = false; + if (isHotspot() && __sync_bool_compare_and_swap(&vmstructs_ready, false, true)) { JitWriteProtection jit(true); CodeCache* lib = openJvmLibrary(); - assert(lib != nullptr); - // Initialize VMStructs - VMStructs::init(lib); - VMStructs::ready(); + if (lib != nullptr) { + // Initialize VMStructs + VMStructs::init(lib); + VMStructs::ready(); + } } } diff --git a/ddprof-lib/src/main/java/com/datadoghq/profiler/LibraryLoader.java b/ddprof-lib/src/main/java/com/datadoghq/profiler/LibraryLoader.java index f7643e8fbc..b65000ed4e 100644 --- a/ddprof-lib/src/main/java/com/datadoghq/profiler/LibraryLoader.java +++ b/ddprof-lib/src/main/java/com/datadoghq/profiler/LibraryLoader.java @@ -107,13 +107,7 @@ private static Result loadLibrary(final String libraryLocation, String scratchDi if (state.get() == LoadingState.UNAVAILABLE) { return Result.UNAVAILABLE; } - Path libraryPath = libraryLocation != null ? Paths.get(libraryLocation) : null; - if (libraryPath == null) { - OperatingSystem os = OperatingSystem.current(); - String qualifier = (os == OperatingSystem.linux && os.isMusl()) ? "musl" : null; - - libraryPath = libraryFromClasspath(os, Arch.current(), qualifier, Paths.get(scratchDir != null ? scratchDir : System.getProperty("java.io.tmpdir"))); - } + Path libraryPath = libraryLocation != null ? Paths.get(libraryLocation) : resolveLibraryPath(scratchDir); System.load(libraryPath.toAbsolutePath().toString()); return Result.SUCCESS; } catch (Throwable t) { @@ -124,6 +118,22 @@ private static Result loadLibrary(final String libraryLocation, String scratchDi } } + /** + * Resolves the on-disk path of the bundled native library for the current OS/arch, extracting it + * from the classpath if necessary. Package-visible so tests can obtain a real file path (e.g. to + * pass via {@code -agentpath:}) without loading the library into the calling process. + * + * @param scratchDir The working scratch dir where to store the temp library file, or {@code null} + * to use {@code java.io.tmpdir} + * @return The library absolute path + * @throws IOException if the resource is not found on the classpath + */ + static Path resolveLibraryPath(String scratchDir) throws IOException { + OperatingSystem os = OperatingSystem.current(); + String qualifier = (os == OperatingSystem.linux && os.isMusl()) ? "musl" : null; + return libraryFromClasspath(os, Arch.current(), qualifier, Paths.get(scratchDir != null ? scratchDir : System.getProperty("java.io.tmpdir"))); + } + /** * Locates a library on class-path (eg. in a JAR) and creates a publicly accessible temporary copy * of the library which can then be used by the application by its absolute path. diff --git a/ddprof-test/src/test/java/com/datadoghq/profiler/JVMAccessTest.java b/ddprof-test/src/test/java/com/datadoghq/profiler/JVMAccessTest.java index acb4ce3e2a..1335d5016e 100644 --- a/ddprof-test/src/test/java/com/datadoghq/profiler/JVMAccessTest.java +++ b/ddprof-test/src/test/java/com/datadoghq/profiler/JVMAccessTest.java @@ -4,6 +4,7 @@ import org.junit.jupiter.api.Test; import java.lang.management.ManagementFactory; +import java.nio.file.Path; import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @@ -42,6 +43,60 @@ void sanityInitailizationTest() throws Exception { assertFalse(initProfilerFound.get(), "initProfilerBridge found"); } + /** + * Regression test for the Agent_OnLoad (static {@code -agentpath:}) startup path: unlike the + * {@code library}/{@code profiler} targets used elsewhere in this file, which load the native + * library via {@code System.load} ({@code JNI_OnLoad} -> {@code VM::initLibrary}), an + * {@code -agentpath:} JVM argument makes {@code Agent_OnLoad} -> {@code VM::initProfilerBridge} + * the *first* native entry point invoked, with no prior call to {@code VM::ready()}. + * {@code initProfilerBridge()} looks up the {@code DebugNonSafepoints} VM flag via + * {@code VMFlag::find()}, gated on {@code CodeHeap::available()} -- both depend on + * {@code VMStructs} offsets/addresses populated by {@code VMStructs::init()}. If that + * initialization has been deferred to the asynchronous {@code VMInit} callback without also + * covering this call site, {@code CodeHeap::available()} reads {@code false} (its backing + * address is still unset) and the code takes the JDK-8173361 workaround path instead of ever + * calling {@code VMFlag::find()} -- silently disabling {@code CompiledMethodLoad} events and + * skipping the {@code DebugNonSafepoints} detection, even on a JVM where both should have + * succeeded. + */ + @Test + void agentOnLoadVMFlagDetectionTest() throws Exception { + String config = System.getProperty("ddprof_test.config"); + assumeTrue("debug".equals(config)); + + Path agentLib = LibraryLoader.resolveLibraryPath(System.getProperty("java.io.tmpdir")); + + AtomicReference workaroundLine = new AtomicReference<>(null); + AtomicReference flagLine = new AtomicReference<>(null); + + boolean rslt = launch("noop", + Collections.singletonList("-agentpath:" + agentLib.toAbsolutePath()), + null, + l -> { + if (l.contains("[TEST::INFO] CompiledMethodLoad workaround:")) { + workaroundLine.set(l); + return LineConsumerResult.STOP; + } + if (l.contains("[TEST::INFO] DebugNonSafepoints flag")) { + flagLine.set(l); + return LineConsumerResult.STOP; + } + return LineConsumerResult.CONTINUE; + }, + null + ).inTime; + + assertTrue(rslt); + assertNull(workaroundLine.get(), + "VM::initProfilerBridge() took the JDK-8173361 CompiledMethodLoad workaround path (" + + workaroundLine.get() + ") instead of reaching the DebugNonSafepoints lookup -- " + + "CodeHeap::available() was false, meaning VMStructs was not initialized yet during " + + "Agent_OnLoad (startup-ordering regression)"); + assertNotNull(flagLine.get(), "expected DebugNonSafepoints flag lookup log line was not observed"); + assertTrue(flagLine.get().contains("flag found"), + "VMFlag::find(\"DebugNonSafepoints\") returned NULL during Agent_OnLoad: " + flagLine.get()); + } + @Test void jvmVersionTest() throws Exception { String config = System.getProperty("ddprof_test.config"); From 09bc992f0a2173acfa84d79a99a3c4005c082f7f Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Tue, 21 Jul 2026 13:24:18 +0000 Subject: [PATCH 04/20] Fix j9 --- ddprof-lib/src/main/cpp/flightRecorder.cpp | 2 +- ddprof-lib/src/main/cpp/profiler.cpp | 2 +- ddprof-lib/src/main/cpp/vmEntry.cpp | 13 +++++++++---- ddprof-lib/src/main/cpp/vmEntry.h | 7 ++++++- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/ddprof-lib/src/main/cpp/flightRecorder.cpp b/ddprof-lib/src/main/cpp/flightRecorder.cpp index a622baf638..64c9e2d761 100644 --- a/ddprof-lib/src/main/cpp/flightRecorder.cpp +++ b/ddprof-lib/src/main/cpp/flightRecorder.cpp @@ -1204,7 +1204,7 @@ void Recording::writeSettings(Buffer *buf, Arguments &args) { } writeBoolSetting(buf, T_ACTIVE_RECORDING, "debugSymbols", - VMStructs::libjvm()->hasDebugSymbols()); + VM::libjvm()->hasDebugSymbols()); writeBoolSetting(buf, T_ACTIVE_RECORDING, "kernelSymbols", Symbols::haveKernelSymbols()); writeStringSetting(buf, T_ACTIVE_RECORDING, "cpuEngine", diff --git a/ddprof-lib/src/main/cpp/profiler.cpp b/ddprof-lib/src/main/cpp/profiler.cpp index 7bb3c95d4f..dfe319693b 100644 --- a/ddprof-lib/src/main/cpp/profiler.cpp +++ b/ddprof-lib/src/main/cpp/profiler.cpp @@ -1219,7 +1219,7 @@ Error Profiler::checkJvmCapabilities() { } } - if (!VMStructs::libjvm()->hasDebugSymbols() && !VM::isOpenJ9()) { + if (!VM::libjvm()->hasDebugSymbols()) { Log::warn("Install JVM debug symbols to improve profile accuracy"); } diff --git a/ddprof-lib/src/main/cpp/vmEntry.cpp b/ddprof-lib/src/main/cpp/vmEntry.cpp index af793b0230..32d8e8c7fb 100644 --- a/ddprof-lib/src/main/cpp/vmEntry.cpp +++ b/ddprof-lib/src/main/cpp/vmEntry.cpp @@ -58,7 +58,7 @@ jvmtiError(JNICALL *VM::_orig_RedefineClasses)(jvmtiEnv *, jint, jvmtiError(JNICALL *VM::_orig_RetransformClasses)(jvmtiEnv *, jint, const jclass *classes); -void *VM::_libjvm; +CodeCache* VM::_libjvm = nullptr; AsyncGetCallTrace VM::_asyncGetCallTrace; JVM_GetManagement VM::_getManagement; @@ -204,6 +204,10 @@ int JavaVersionAccess::get_hotspot_version(char* prop_value) { } CodeCache* VM::openJvmLibrary() { + if (_libjvm != nullptr) { + return _libjvm; + } + if ((void*)_asyncGetCallTrace == nullptr) { return nullptr; } @@ -213,6 +217,7 @@ CodeCache* VM::openJvmLibrary() { isOpenJ9() ? libraries->findJvmLibrary("libj9vm") : libraries->findLibraryByAddress((const void *)_asyncGetCallTrace); + _libjvm = lib; return lib; } @@ -250,9 +255,9 @@ bool VM::initShared(JavaVM* vm) { prop = NULL; } - _libjvm = getLibraryHandle("libjvm.so"); - _asyncGetCallTrace = (AsyncGetCallTrace)dlsym(_libjvm, "AsyncGetCallTrace"); - _getManagement = (JVM_GetManagement)dlsym(_libjvm, "JVM_GetManagement"); + void *libjvm = getLibraryHandle("libjvm.so"); + _asyncGetCallTrace = (AsyncGetCallTrace)dlsym(libjvm, "AsyncGetCallTrace"); + _getManagement = (JVM_GetManagement)dlsym(libjvm, "JVM_GetManagement"); Libraries *libraries = Libraries::instance(); libraries->updateSymbols(false); diff --git a/ddprof-lib/src/main/cpp/vmEntry.h b/ddprof-lib/src/main/cpp/vmEntry.h index 875e7c39b1..317aeef132 100644 --- a/ddprof-lib/src/main/cpp/vmEntry.h +++ b/ddprof-lib/src/main/cpp/vmEntry.h @@ -148,6 +148,7 @@ class VM { static bool _can_sample_objects; static bool _can_intercept_binding; static bool _is_adaptive_gc_boundary_flag_set; + static CodeCache *_libjvm; // HotSpot JFR async stack-trace extension (optional, JDK 27+). // _request_stack_trace is atomic (RELEASE/ACQUIRE) because canRequestStackTrace() @@ -172,7 +173,11 @@ class VM { static CodeCache* openJvmLibrary(); public: - static void *_libjvm; + static inline CodeCache* libjvm() { + assert(_libjvm != nullptr && "Out of order initialization sequence"); + return _libjvm; + } + static AsyncGetCallTrace _asyncGetCallTrace; static JVM_GetManagement _getManagement; From 1243287d403187875e094054c39d46938fa89248 Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Tue, 21 Jul 2026 15:35:36 +0200 Subject: [PATCH 05/20] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- ddprof-lib/src/main/cpp/hotspot/vmStructs.inline.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ddprof-lib/src/main/cpp/hotspot/vmStructs.inline.h b/ddprof-lib/src/main/cpp/hotspot/vmStructs.inline.h index 7453280975..3bfc4c8dc2 100644 --- a/ddprof-lib/src/main/cpp/hotspot/vmStructs.inline.h +++ b/ddprof-lib/src/main/cpp/hotspot/vmStructs.inline.h @@ -30,10 +30,10 @@ inline T* cast_to(const void* ptr) { } inline const char* VMStructs::at(int offset) { - const char* ptr = (const char*)this + offset; - assert(crashProtectionActive() || SafeAccess::isReadable(ptr)); - // Poison only the returned pointer; the assert above sees the real ptr. - return INJECT_FAULT_ADDRESS_RARE(ptr); + const char* ptr = (const char*)this + offset; + assert(crashProtectionActive() || SafeAccess::isReadable(ptr)); + // Poison only the returned pointer; the assert above sees the real ptr. + return INJECT_FAULT_ADDRESS_RARE(ptr); } inline const char* VMStructs::at(int offset) const { From 0c9562260a1b95aa5bf745b0a58871ccfbbf89ad Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Tue, 21 Jul 2026 16:10:50 +0200 Subject: [PATCH 06/20] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- ddprof-lib/src/main/cpp/vmEntry.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ddprof-lib/src/main/cpp/vmEntry.h b/ddprof-lib/src/main/cpp/vmEntry.h index 317aeef132..75725ef151 100644 --- a/ddprof-lib/src/main/cpp/vmEntry.h +++ b/ddprof-lib/src/main/cpp/vmEntry.h @@ -174,8 +174,9 @@ class VM { public: static inline CodeCache* libjvm() { - assert(_libjvm != nullptr && "Out of order initialization sequence"); - return _libjvm; + CodeCache* lib = __atomic_load_n(&_libjvm, __ATOMIC_ACQUIRE); + assert(lib != nullptr && "Out of order initialization sequence"); + return lib; } static AsyncGetCallTrace _asyncGetCallTrace; From 73674860fd7894beb63b6af3a3a80a3e568c0850 Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Tue, 21 Jul 2026 18:08:38 +0000 Subject: [PATCH 07/20] Fix memory ordering --- ddprof-lib/src/main/cpp/vmEntry.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ddprof-lib/src/main/cpp/vmEntry.cpp b/ddprof-lib/src/main/cpp/vmEntry.cpp index 32d8e8c7fb..a3c3a35b8c 100644 --- a/ddprof-lib/src/main/cpp/vmEntry.cpp +++ b/ddprof-lib/src/main/cpp/vmEntry.cpp @@ -204,8 +204,9 @@ int JavaVersionAccess::get_hotspot_version(char* prop_value) { } CodeCache* VM::openJvmLibrary() { - if (_libjvm != nullptr) { - return _libjvm; + CodeCache* lib = __atomic_load_n(&_libjvm, __ATOMIC_ACQUIRE); + if (lib != nullptr) { + return lib; } if ((void*)_asyncGetCallTrace == nullptr) { @@ -213,11 +214,10 @@ CodeCache* VM::openJvmLibrary() { } Libraries* libraries = Libraries::instance(); - CodeCache *lib = - isOpenJ9() + lib = isOpenJ9() ? libraries->findJvmLibrary("libj9vm") : libraries->findLibraryByAddress((const void *)_asyncGetCallTrace); - _libjvm = lib; + __atomic_store_n(&_libjvm, lib, __ATOMIC_RELEASE); return lib; } From 00316b143fcda689b84f96d74ed5d750a86f5bb7 Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Tue, 21 Jul 2026 20:09:50 +0200 Subject: [PATCH 08/20] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../java/com/datadoghq/profiler/LibraryLoader.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ddprof-lib/src/main/java/com/datadoghq/profiler/LibraryLoader.java b/ddprof-lib/src/main/java/com/datadoghq/profiler/LibraryLoader.java index b65000ed4e..94e9440adf 100644 --- a/ddprof-lib/src/main/java/com/datadoghq/profiler/LibraryLoader.java +++ b/ddprof-lib/src/main/java/com/datadoghq/profiler/LibraryLoader.java @@ -126,12 +126,18 @@ private static Result loadLibrary(final String libraryLocation, String scratchDi * @param scratchDir The working scratch dir where to store the temp library file, or {@code null} * to use {@code java.io.tmpdir} * @return The library absolute path - * @throws IOException if the resource is not found on the classpath + * @throws IOException if an I/O error occurs while extracting the library + * @throws IllegalStateException if the resource is not found on the classpath */ static Path resolveLibraryPath(String scratchDir) throws IOException { OperatingSystem os = OperatingSystem.current(); String qualifier = (os == OperatingSystem.linux && os.isMusl()) ? "musl" : null; - return libraryFromClasspath(os, Arch.current(), qualifier, Paths.get(scratchDir != null ? scratchDir : System.getProperty("java.io.tmpdir"))); + return libraryFromClasspath( + os, + Arch.current(), + qualifier, + Paths.get(scratchDir != null ? scratchDir : System.getProperty("java.io.tmpdir"))) + .toAbsolutePath(); } /** From 3848d40d2788f1930159d90785779eea3e9bb992 Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Tue, 21 Jul 2026 20:10:34 +0200 Subject: [PATCH 09/20] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- ddprof-lib/src/main/cpp/hotspot/vmStructs.inline.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ddprof-lib/src/main/cpp/hotspot/vmStructs.inline.h b/ddprof-lib/src/main/cpp/hotspot/vmStructs.inline.h index 3bfc4c8dc2..3f55e17421 100644 --- a/ddprof-lib/src/main/cpp/hotspot/vmStructs.inline.h +++ b/ddprof-lib/src/main/cpp/hotspot/vmStructs.inline.h @@ -213,7 +213,7 @@ u16 VMConstMethod::signatureIndex() const { return *(u16*)at(_constmethod_sig_index_offset); } -// The method is called without protection +// This method may be called without crash protection, so read the name via SafeAccess. inline const char* VMFlag::name() const { assert(_flag_name_offset >= 0); return load_at_offset(_flag_name_offset); From 94a667cf897bc11eb70546af83a6db3ef8734d79 Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Tue, 21 Jul 2026 20:21:28 +0200 Subject: [PATCH 10/20] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../src/test/java/com/datadoghq/profiler/JVMAccessTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ddprof-test/src/test/java/com/datadoghq/profiler/JVMAccessTest.java b/ddprof-test/src/test/java/com/datadoghq/profiler/JVMAccessTest.java index 1335d5016e..9acf73d203 100644 --- a/ddprof-test/src/test/java/com/datadoghq/profiler/JVMAccessTest.java +++ b/ddprof-test/src/test/java/com/datadoghq/profiler/JVMAccessTest.java @@ -79,7 +79,7 @@ void agentOnLoadVMFlagDetectionTest() throws Exception { } if (l.contains("[TEST::INFO] DebugNonSafepoints flag")) { flagLine.set(l); - return LineConsumerResult.STOP; + return LineConsumerResult.CONTINUE; } return LineConsumerResult.CONTINUE; }, From 9d3fd613ba67c68c147c8132c619e7f2ba28651c Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Tue, 21 Jul 2026 18:41:27 +0000 Subject: [PATCH 11/20] More memory ordering --- ddprof-lib/src/main/cpp/vmEntry.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ddprof-lib/src/main/cpp/vmEntry.cpp b/ddprof-lib/src/main/cpp/vmEntry.cpp index a3c3a35b8c..2c1c360453 100644 --- a/ddprof-lib/src/main/cpp/vmEntry.cpp +++ b/ddprof-lib/src/main/cpp/vmEntry.cpp @@ -579,7 +579,7 @@ void VM::ready(jvmtiEnv *jvmti, JNIEnv *jni) { Profiler::check_JDK_8313796_workaround(); Profiler::setupSignalHandlers(); static bool vmstructs_ready = false; - if (isHotspot() && __sync_bool_compare_and_swap(&vmstructs_ready, false, true)) { + if (isHotspot() && __sync_bool_compare_and_swap(&vmstructs_ready, false, true, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)) { JitWriteProtection jit(true); CodeCache* lib = openJvmLibrary(); if (lib != nullptr) { From 6c9f113f09972dc260de6acbdc6ccefc2842ea73 Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Tue, 21 Jul 2026 21:05:45 +0200 Subject: [PATCH 12/20] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- ddprof-lib/src/main/cpp/vmEntry.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ddprof-lib/src/main/cpp/vmEntry.cpp b/ddprof-lib/src/main/cpp/vmEntry.cpp index 2c1c360453..e498455402 100644 --- a/ddprof-lib/src/main/cpp/vmEntry.cpp +++ b/ddprof-lib/src/main/cpp/vmEntry.cpp @@ -579,7 +579,10 @@ void VM::ready(jvmtiEnv *jvmti, JNIEnv *jni) { Profiler::check_JDK_8313796_workaround(); Profiler::setupSignalHandlers(); static bool vmstructs_ready = false; - if (isHotspot() && __sync_bool_compare_and_swap(&vmstructs_ready, false, true, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)) { + bool expected = false; + if (isHotspot() && + __atomic_compare_exchange_n(&vmstructs_ready, &expected, true, false, + __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)) { JitWriteProtection jit(true); CodeCache* lib = openJvmLibrary(); if (lib != nullptr) { From 8da863ca7072b3c62355e2fb0cf47f0cba5ed983 Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Tue, 21 Jul 2026 20:21:26 +0000 Subject: [PATCH 13/20] Cache flag name --- ddprof-lib/src/main/cpp/hotspot/vmStructs.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ddprof-lib/src/main/cpp/hotspot/vmStructs.cpp b/ddprof-lib/src/main/cpp/hotspot/vmStructs.cpp index 469138ac86..1a68238c32 100644 --- a/ddprof-lib/src/main/cpp/hotspot/vmStructs.cpp +++ b/ddprof-lib/src/main/cpp/hotspot/vmStructs.cpp @@ -842,7 +842,8 @@ VMFlag* VMFlag::find(const char* name) { for (size_t i = 0; i < count; i++) { VMFlag* f = VMFlag::cast(*(const char**)_flags_addr + i * VMFlag::type_size()); - if (f->name() != NULL && strcmp(f->name(), name) == 0 && f->addr() != NULL) { + const char* fname = f->name(); + if (fname != nullptr && strcmp(fname, name) == 0 && f->addr() != nullptr) { return f; } } From f8cdcf6025c7edcc5b29e933bcbe31ecbc2267de Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Tue, 21 Jul 2026 21:34:08 +0000 Subject: [PATCH 14/20] Fix --- ddprof-lib/src/main/cpp/libraries.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ddprof-lib/src/main/cpp/libraries.cpp b/ddprof-lib/src/main/cpp/libraries.cpp index a0c8e83ca2..06263812a3 100644 --- a/ddprof-lib/src/main/cpp/libraries.cpp +++ b/ddprof-lib/src/main/cpp/libraries.cpp @@ -1,7 +1,6 @@ #include "codeCache.h" #include "common.h" #include "findLibraryImpl.h" -#include "hotspot/vmStructs.h" #include "libraries.h" #include "libraryPatcher.h" #include "log.h" @@ -191,7 +190,7 @@ const void *Libraries::resolveSymbol(const char *name) { } CodeCache *Libraries::findJvmLibrary(const char *j9_lib_name) { - return VM::isOpenJ9() ? findLibraryByName(j9_lib_name) : VMStructs::libjvm(); + return VM::isOpenJ9() ? findLibraryByName(j9_lib_name) : VM::libjvm(); } CodeCache *Libraries::findLibraryByName(const char *lib_name) { From fd7237d67e09ae240b24182d08bb786e872c187f Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Wed, 22 Jul 2026 00:04:38 +0200 Subject: [PATCH 15/20] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- ddprof-lib/src/main/cpp/hotspot/vmStructs.inline.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ddprof-lib/src/main/cpp/hotspot/vmStructs.inline.h b/ddprof-lib/src/main/cpp/hotspot/vmStructs.inline.h index 3f55e17421..5de4c94cad 100644 --- a/ddprof-lib/src/main/cpp/hotspot/vmStructs.inline.h +++ b/ddprof-lib/src/main/cpp/hotspot/vmStructs.inline.h @@ -44,12 +44,13 @@ inline const char* VMStructs::at(int offset) const { template T VMStructs::load_at_offset(int offset) const { - const char* ptr = at(offset); + const char* raw = (const char*)this + offset; if (safe) { - return (T)SafeAccess::loadPtr((void**)ptr, nullptr); - } else { - return *((T*)ptr); + // SafeAccess loads must work even when crash protection isn't active; avoid at() asserts. + return (T)SafeAccess::loadPtr((void**)INJECT_FAULT_ADDRESS_RARE(raw), nullptr); } + assert(crashProtectionActive() || SafeAccess::isReadable(raw)); + return *((T*)INJECT_FAULT_ADDRESS_RARE(raw)); } From d258efa30b7833be8b818467c05f489454a8ee9f Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Tue, 21 Jul 2026 22:18:05 +0000 Subject: [PATCH 16/20] Copyright headers --- ddprof-lib/src/main/cpp/libraries.cpp | 5 +++++ .../src/main/java/com/datadoghq/profiler/LibraryLoader.java | 4 ++++ .../src/test/java/com/datadoghq/profiler/JVMAccessTest.java | 5 +++++ 3 files changed, 14 insertions(+) diff --git a/ddprof-lib/src/main/cpp/libraries.cpp b/ddprof-lib/src/main/cpp/libraries.cpp index 06263812a3..66382e9d1b 100644 --- a/ddprof-lib/src/main/cpp/libraries.cpp +++ b/ddprof-lib/src/main/cpp/libraries.cpp @@ -1,3 +1,8 @@ +/* + * Copyright 2026, Datadog, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + #include "codeCache.h" #include "common.h" #include "findLibraryImpl.h" diff --git a/ddprof-lib/src/main/java/com/datadoghq/profiler/LibraryLoader.java b/ddprof-lib/src/main/java/com/datadoghq/profiler/LibraryLoader.java index 94e9440adf..c0003f03b4 100644 --- a/ddprof-lib/src/main/java/com/datadoghq/profiler/LibraryLoader.java +++ b/ddprof-lib/src/main/java/com/datadoghq/profiler/LibraryLoader.java @@ -1,3 +1,7 @@ +/* + * Copyright 2026, Datadog, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ package com.datadoghq.profiler; import java.io.IOException; diff --git a/ddprof-test/src/test/java/com/datadoghq/profiler/JVMAccessTest.java b/ddprof-test/src/test/java/com/datadoghq/profiler/JVMAccessTest.java index 9acf73d203..f6b983f603 100644 --- a/ddprof-test/src/test/java/com/datadoghq/profiler/JVMAccessTest.java +++ b/ddprof-test/src/test/java/com/datadoghq/profiler/JVMAccessTest.java @@ -1,3 +1,8 @@ +/* + * Copyright 2026, Datadog, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + package com.datadoghq.profiler; import org.junit.jupiter.api.BeforeAll; From 040565b32d28423098479b5c8e39a45c0a9d6e3d Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Thu, 23 Jul 2026 13:15:54 +0000 Subject: [PATCH 17/20] Fix --- ddprof-lib/src/main/cpp/vmEntry.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/ddprof-lib/src/main/cpp/vmEntry.cpp b/ddprof-lib/src/main/cpp/vmEntry.cpp index e498455402..d7d5cb8d32 100644 --- a/ddprof-lib/src/main/cpp/vmEntry.cpp +++ b/ddprof-lib/src/main/cpp/vmEntry.cpp @@ -576,20 +576,23 @@ bool VM::initProfilerBridge(JavaVM *vm, bool attach) { // initLibrary() followed by a JNI-triggered attach) -- the VMStructs init below only // ever runs once. void VM::ready(jvmtiEnv *jvmti, JNIEnv *jni) { - Profiler::check_JDK_8313796_workaround(); - Profiler::setupSignalHandlers(); + if (!VM::isHotspot()) { + return; + } + // Hotspot specific static bool vmstructs_ready = false; - bool expected = false; - if (isHotspot() && - __atomic_compare_exchange_n(&vmstructs_ready, &expected, true, false, - __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)) { + static SpinLock lock; + ExclusiveLockGuard guard(&lock); + if (!vmstructs_ready) { + Profiler::check_JDK_8313796_workaround(); + Profiler::setupSignalHandlers(); JitWriteProtection jit(true); CodeCache* lib = openJvmLibrary(); - if (lib != nullptr) { - // Initialize VMStructs - VMStructs::init(lib); - VMStructs::ready(); - } + assert(lib != nullptr && "JVM library must have been loaded"); + // Initialize VMStructs + VMStructs::init(lib); + VMStructs::ready(); + vmstructs_ready = true; } } From 4fe08081e53605562989b5197aa9ce4e33725eee Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Thu, 23 Jul 2026 14:26:33 +0000 Subject: [PATCH 18/20] Fix --- ddprof-lib/src/main/cpp/vmEntry.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/ddprof-lib/src/main/cpp/vmEntry.cpp b/ddprof-lib/src/main/cpp/vmEntry.cpp index d7d5cb8d32..01cc1d140a 100644 --- a/ddprof-lib/src/main/cpp/vmEntry.cpp +++ b/ddprof-lib/src/main/cpp/vmEntry.cpp @@ -576,9 +576,6 @@ bool VM::initProfilerBridge(JavaVM *vm, bool attach) { // initLibrary() followed by a JNI-triggered attach) -- the VMStructs init below only // ever runs once. void VM::ready(jvmtiEnv *jvmti, JNIEnv *jni) { - if (!VM::isHotspot()) { - return; - } // Hotspot specific static bool vmstructs_ready = false; static SpinLock lock; @@ -586,12 +583,14 @@ void VM::ready(jvmtiEnv *jvmti, JNIEnv *jni) { if (!vmstructs_ready) { Profiler::check_JDK_8313796_workaround(); Profiler::setupSignalHandlers(); - JitWriteProtection jit(true); - CodeCache* lib = openJvmLibrary(); - assert(lib != nullptr && "JVM library must have been loaded"); - // Initialize VMStructs - VMStructs::init(lib); - VMStructs::ready(); + if (VM::isHotspot()) { + JitWriteProtection jit(true); + CodeCache* lib = openJvmLibrary(); + assert(lib != nullptr && "JVM library must have been loaded"); + // Initialize VMStructs + VMStructs::init(lib); + VMStructs::ready(); + } vmstructs_ready = true; } } From d01cffe24d7b647443f5243b72ffc89ba6fddcd1 Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Thu, 23 Jul 2026 17:35:30 +0000 Subject: [PATCH 19/20] Fix --- ddprof-lib/src/main/cpp/hotspot/vmStructs.cpp | 4 +--- ddprof-lib/src/main/cpp/vmEntry.cpp | 10 +++++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/ddprof-lib/src/main/cpp/hotspot/vmStructs.cpp b/ddprof-lib/src/main/cpp/hotspot/vmStructs.cpp index 1a68238c32..73d7e2aec6 100644 --- a/ddprof-lib/src/main/cpp/hotspot/vmStructs.cpp +++ b/ddprof-lib/src/main/cpp/hotspot/vmStructs.cpp @@ -342,9 +342,7 @@ void VMStructs::initOffsets() { } void VMStructs::resolveOffsets() { - if (VM::isOpenJ9() || VM::isZing()) { - return; - } + assert(VM::isHotspot() && "Should not reach here"); if (_klass_offset_addr != NULL) { _klass = (jfieldID)(uintptr_t)(*(int*)_klass_offset_addr << 2 | 2); diff --git a/ddprof-lib/src/main/cpp/vmEntry.cpp b/ddprof-lib/src/main/cpp/vmEntry.cpp index 01cc1d140a..d93740fbe3 100644 --- a/ddprof-lib/src/main/cpp/vmEntry.cpp +++ b/ddprof-lib/src/main/cpp/vmEntry.cpp @@ -577,10 +577,10 @@ bool VM::initProfilerBridge(JavaVM *vm, bool attach) { // ever runs once. void VM::ready(jvmtiEnv *jvmti, JNIEnv *jni) { // Hotspot specific - static bool vmstructs_ready = false; + static bool vmstructs_init = false; static SpinLock lock; ExclusiveLockGuard guard(&lock); - if (!vmstructs_ready) { + if (!vmstructs_init) { Profiler::check_JDK_8313796_workaround(); Profiler::setupSignalHandlers(); if (VM::isHotspot()) { @@ -591,7 +591,11 @@ void VM::ready(jvmtiEnv *jvmti, JNIEnv *jni) { VMStructs::init(lib); VMStructs::ready(); } - vmstructs_ready = true; + vmstructs_init = true; + } else { + if (VM::isHotspot()) { + VMStructs::ready(); + } } } From a5ec8f8b80b9668d00378e64ba4640cade2ffcb2 Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Thu, 23 Jul 2026 18:20:37 +0000 Subject: [PATCH 20/20] Fix --- ddprof-lib/src/main/cpp/vmEntry.cpp | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/ddprof-lib/src/main/cpp/vmEntry.cpp b/ddprof-lib/src/main/cpp/vmEntry.cpp index d93740fbe3..670c54af6c 100644 --- a/ddprof-lib/src/main/cpp/vmEntry.cpp +++ b/ddprof-lib/src/main/cpp/vmEntry.cpp @@ -577,25 +577,21 @@ bool VM::initProfilerBridge(JavaVM *vm, bool attach) { // ever runs once. void VM::ready(jvmtiEnv *jvmti, JNIEnv *jni) { // Hotspot specific - static bool vmstructs_init = false; + static bool init_signal = false; static SpinLock lock; ExclusiveLockGuard guard(&lock); - if (!vmstructs_init) { + if (!init_signal) { Profiler::check_JDK_8313796_workaround(); Profiler::setupSignalHandlers(); - if (VM::isHotspot()) { - JitWriteProtection jit(true); - CodeCache* lib = openJvmLibrary(); - assert(lib != nullptr && "JVM library must have been loaded"); - // Initialize VMStructs - VMStructs::init(lib); - VMStructs::ready(); - } - vmstructs_init = true; - } else { - if (VM::isHotspot()) { - VMStructs::ready(); - } + init_signal = true; + } + if (VM::isHotspot()) { + JitWriteProtection jit(true); + CodeCache* lib = openJvmLibrary(); + assert(lib != nullptr && "JVM library must have been loaded"); + // Initialize VMStructs + VMStructs::init(lib); + VMStructs::ready(); } }