From 4feff306bb653f01c78037b12f7db098274ee27f Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Fri, 10 Oct 2025 14:24:31 +0200 Subject: [PATCH 1/4] [PROF-12714] Add CriticalNative support --- ddprof-lib/src/main/cpp/javaApi.cpp | 104 ++++++++++++++---- .../com/datadoghq/profiler/JavaProfiler.java | 8 +- 2 files changed, 85 insertions(+), 27 deletions(-) diff --git a/ddprof-lib/src/main/cpp/javaApi.cpp b/ddprof-lib/src/main/cpp/javaApi.cpp index 1ff94c8b..d9a6ec8b 100644 --- a/ddprof-lib/src/main/cpp/javaApi.cpp +++ b/ddprof-lib/src/main/cpp/javaApi.cpp @@ -68,6 +68,11 @@ Java_com_datadoghq_profiler_JavaProfiler_init0(JNIEnv *env, jclass unused) { return VM::initProfilerBridge(nullptr, true); } +extern "C" JNIEXPORT jboolean JNICALL +JavaCritical_com_datadoghq_profiler_JavaProfiler_init0() { + return VM::initProfilerBridge(nullptr, true); +} + extern "C" DLLEXPORT void JNICALL Java_com_datadoghq_profiler_JavaProfiler_stop0(JNIEnv *env, jobject unused) { Error error = Profiler::instance()->stop(); @@ -78,10 +83,16 @@ Java_com_datadoghq_profiler_JavaProfiler_stop0(JNIEnv *env, jobject unused) { } extern "C" DLLEXPORT jint JNICALL -Java_com_datadoghq_profiler_JavaProfiler_getTid0(JNIEnv *env, jobject unused) { +Java_com_datadoghq_profiler_JavaProfiler_getTid0(JNIEnv *env, jclass unused) { + return OS::threadId(); +} + +extern "C" DLLEXPORT jint JNICALL +JavaCritical_com_datadoghq_profiler_JavaProfiler_getTid0() { return OS::threadId(); } + extern "C" DLLEXPORT jstring JNICALL Java_com_datadoghq_profiler_JavaProfiler_execute0(JNIEnv *env, jobject unused, jstring command) { @@ -113,7 +124,7 @@ Java_com_datadoghq_profiler_JavaProfiler_execute0(JNIEnv *env, jobject unused, extern "C" DLLEXPORT jstring JNICALL Java_com_datadoghq_profiler_JavaProfiler_getStatus0(JNIEnv* env, - jobject unused) { + jclass unused) { char msg[2048]; int ret = Profiler::instance()->status((char*)msg, sizeof(msg) - 1); return env->NewStringUTF(msg); @@ -121,14 +132,18 @@ Java_com_datadoghq_profiler_JavaProfiler_getStatus0(JNIEnv* env, extern "C" DLLEXPORT jlong JNICALL Java_com_datadoghq_profiler_JavaProfiler_getSamples(JNIEnv *env, - jobject unused) { + jclass unused) { + return (jlong)Profiler::instance()->total_samples(); +} + +extern "C" DLLEXPORT jlong JNICALL +JavaCritical_com_datadoghq_profiler_JavaProfiler_getSamples() { return (jlong)Profiler::instance()->total_samples(); } // some duplication between add and remove, though we want to avoid having an extra branch in the hot path extern "C" DLLEXPORT void JNICALL -Java_com_datadoghq_profiler_JavaProfiler_filterThreadAdd0(JNIEnv *env, - jobject unused) { +JavaCritical_com_datadoghq_profiler_JavaProfiler_filterThreadAdd0() { ProfiledThread *current = ProfiledThread::current(); if (unlikely(current == nullptr)) { assert(false); @@ -158,8 +173,13 @@ Java_com_datadoghq_profiler_JavaProfiler_filterThreadAdd0(JNIEnv *env, } extern "C" DLLEXPORT void JNICALL -Java_com_datadoghq_profiler_JavaProfiler_filterThreadRemove0(JNIEnv *env, - jobject unused) { +Java_com_datadoghq_profiler_JavaProfiler_filterThreadAdd0(JNIEnv *env, + jclass unused) { + JavaCritical_com_datadoghq_profiler_JavaProfiler_filterThreadAdd0(); +} + +extern "C" DLLEXPORT void JNICALL +JavaCritical_com_datadoghq_profiler_JavaProfiler_filterThreadRemove0() { ProfiledThread *current = ProfiledThread::current(); if (unlikely(current == nullptr)) { assert(false); @@ -182,11 +202,15 @@ Java_com_datadoghq_profiler_JavaProfiler_filterThreadRemove0(JNIEnv *env, thread_filter->remove(slot_id); } +extern "C" DLLEXPORT void JNICALL +Java_com_datadoghq_profiler_JavaProfiler_filterThreadRemove0(JNIEnv *env, + jclass unused) { + JavaCritical_com_datadoghq_profiler_JavaProfiler_filterThreadRemove0(); +} + // Backward compatibility for existing code extern "C" DLLEXPORT void JNICALL -Java_com_datadoghq_profiler_JavaProfiler_filterThread0(JNIEnv *env, - jobject unused, - jboolean enable) { +JavaCritical_com_datadoghq_profiler_JavaProfiler_filterThread0(jboolean enable) { ProfiledThread *current = ProfiledThread::current(); if (unlikely(current == nullptr)) { assert(false); @@ -225,9 +249,16 @@ Java_com_datadoghq_profiler_JavaProfiler_filterThread0(JNIEnv *env, } } +extern "C" DLLEXPORT void JNICALL +Java_com_datadoghq_profiler_JavaProfiler_filterThread0(JNIEnv *env, + jclass unused, + jboolean enable) { + JavaCritical_com_datadoghq_profiler_JavaProfiler_filterThread0(enable); +} + extern "C" DLLEXPORT jobject JNICALL Java_com_datadoghq_profiler_JavaProfiler_getContextPage0(JNIEnv *env, - jobject unused, + jclass unused, jint tid) { ContextPage page = Contexts::getPage((int)tid); if (page.storage == 0) { @@ -238,21 +269,32 @@ Java_com_datadoghq_profiler_JavaProfiler_getContextPage0(JNIEnv *env, extern "C" DLLEXPORT jlong JNICALL Java_com_datadoghq_profiler_JavaProfiler_getContextPageOffset0(JNIEnv *env, - jobject unused, + jclass unused, jint tid) { ContextPage page = Contexts::getPage((int)tid); return (jlong)page.storage; } +extern "C" DLLEXPORT jlong JNICALL +JavaCritical_com_datadoghq_profiler_JavaProfiler_getContextPageOffset0(jint tid) { + ContextPage page = Contexts::getPage((int)tid); + return (jlong)page.storage; +} + extern "C" DLLEXPORT jint JNICALL Java_com_datadoghq_profiler_JavaProfiler_getMaxContextPages0(JNIEnv *env, - jobject unused) { + jclass unused) { + return (jint)Contexts::getMaxPages(); +} + +extern "C" DLLEXPORT jint JNICALL +JavaCritical_com_datadoghq_profiler_JavaProfiler_getMaxContextPages0() { return (jint)Contexts::getMaxPages(); } extern "C" DLLEXPORT jboolean JNICALL Java_com_datadoghq_profiler_JavaProfiler_recordTrace0( - JNIEnv *env, jobject unused, jlong rootSpanId, jstring endpoint, + JNIEnv *env, jclass unused, jlong rootSpanId, jstring endpoint, jstring operation, jint sizeLimit) { JniString endpoint_str(env, endpoint); u32 endpointLabel = Profiler::instance()->stringLabelMap()->bounded_lookup( @@ -274,7 +316,7 @@ Java_com_datadoghq_profiler_JavaProfiler_recordTrace0( extern "C" DLLEXPORT jint JNICALL Java_com_datadoghq_profiler_JavaProfiler_registerConstant0(JNIEnv *env, - jobject unused, + jclass unused, jstring value) { JniString value_str(env, value); u32 encoding = Profiler::instance()->contextValueMap()->bounded_lookup( @@ -283,7 +325,7 @@ Java_com_datadoghq_profiler_JavaProfiler_registerConstant0(JNIEnv *env, } extern "C" DLLEXPORT void JNICALL -Java_com_datadoghq_profiler_JavaProfiler_dump0(JNIEnv *env, jobject unused, +Java_com_datadoghq_profiler_JavaProfiler_dump0(JNIEnv *env, jclass unused, jstring path) { JniString path_str(env, path); Profiler::instance()->dump(path_str.c_str(), path_str.length()); @@ -291,7 +333,7 @@ Java_com_datadoghq_profiler_JavaProfiler_dump0(JNIEnv *env, jobject unused, extern "C" DLLEXPORT jobject JNICALL Java_com_datadoghq_profiler_JavaProfiler_getDebugCounters0(JNIEnv *env, - jobject unused) { + jclass unused) { #ifdef COUNTERS return env->NewDirectByteBuffer((void *)Counters::getCounters(), (jlong)Counters::size()); @@ -302,7 +344,7 @@ Java_com_datadoghq_profiler_JavaProfiler_getDebugCounters0(JNIEnv *env, extern "C" DLLEXPORT jobjectArray JNICALL Java_com_datadoghq_profiler_JavaProfiler_describeDebugCounters0( - JNIEnv *env, jobject unused) { + JNIEnv *env, jclass unused) { #ifdef COUNTERS std::vector counter_names = Counters::describeCounters(); jobjectArray array = (jobjectArray)env->NewObjectArray( @@ -320,7 +362,7 @@ Java_com_datadoghq_profiler_JavaProfiler_describeDebugCounters0( extern "C" DLLEXPORT void JNICALL Java_com_datadoghq_profiler_JavaProfiler_recordSettingEvent0( - JNIEnv *env, jobject unused, jstring name, jstring value, jstring unit) { + JNIEnv *env, jclass unused, jstring name, jstring value, jstring unit) { int tid = ProfiledThread::currentTid(); if (tid < 0) { return; @@ -343,7 +385,7 @@ static int dictionarizeClassName(JNIEnv* env, jstring className) { extern "C" DLLEXPORT void JNICALL Java_com_datadoghq_profiler_JavaProfiler_recordQueueEnd0( - JNIEnv *env, jobject unused, jlong startTime, jlong endTime, jstring task, + JNIEnv *env, jclass unused, jlong startTime, jlong endTime, jstring task, jstring scheduler, jthread origin, jstring queueType, jint queueLength) { int tid = ProfiledThread::currentTid(); if (tid < 0) { @@ -379,23 +421,39 @@ Java_com_datadoghq_profiler_JavaProfiler_recordQueueEnd0( extern "C" DLLEXPORT jlong JNICALL Java_com_datadoghq_profiler_JavaProfiler_currentTicks0(JNIEnv *env, - jobject unused) { + jclass unused) { + return TSC::ticks(); +} + +extern "C" DLLEXPORT jlong JNICALL +JavaCritical_com_datadoghq_profiler_JavaProfiler_currentTicks0() { return TSC::ticks(); } extern "C" DLLEXPORT jlong JNICALL Java_com_datadoghq_profiler_JavaProfiler_tscFrequency0(JNIEnv *env, - jobject unused) { + jclass unused) { + return TSC::frequency(); +} + +extern "C" DLLEXPORT jlong JNICALL +JavaCritical_com_datadoghq_profiler_JavaProfiler_tscFrequency0() { return TSC::frequency(); } extern "C" DLLEXPORT void JNICALL Java_com_datadoghq_profiler_JavaProfiler_mallocArenaMax0(JNIEnv *env, - jobject unused, + jclass unused, jint maxArenas) { ddprof::OS::mallocArenaMax(maxArenas); } +extern "C" DLLEXPORT void JNICALL +JavaCritical_com_datadoghq_profiler_JavaProfiler_mallocArenaMax0(jint maxArenas) { + ddprof::OS::mallocArenaMax(maxArenas); +} + + extern "C" DLLEXPORT jstring JNICALL Java_com_datadoghq_profiler_JVMAccess_findStringJVMFlag0(JNIEnv *env, jobject unused, diff --git a/ddprof-lib/src/main/java/com/datadoghq/profiler/JavaProfiler.java b/ddprof-lib/src/main/java/com/datadoghq/profiler/JavaProfiler.java index fbc96555..3bcafa5a 100644 --- a/ddprof-lib/src/main/java/com/datadoghq/profiler/JavaProfiler.java +++ b/ddprof-lib/src/main/java/com/datadoghq/profiler/JavaProfiler.java @@ -154,7 +154,7 @@ public void stop() throws IllegalStateException { * * @return Number of samples */ - public native long getSamples(); + public static native long getSamples(); /** * Get profiler agent version, e.g. "1.0" @@ -470,10 +470,10 @@ public Map getDebugCounters() { private native void stop0() throws IllegalStateException; private native String execute0(String command) throws IllegalArgumentException, IllegalStateException, IOException; - private native void filterThreadAdd0(); - private native void filterThreadRemove0(); + private static native void filterThreadAdd0(); + private static native void filterThreadRemove0(); // Backward compatibility for existing code - private native void filterThread0(boolean enable); + private static native void filterThread0(boolean enable); private static native int getTid0(); private static native ByteBuffer getContextPage0(int tid); From 6cfd40f3ff588d5c317ae625ce7b63b855d3a750 Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Fri, 24 Oct 2025 13:06:58 -0400 Subject: [PATCH 2/4] Disable CriticalJNINatives on aarch64 --- ddprof-lib/src/main/cpp/vmStructs_dd.cpp | 11 +++++++++++ ddprof-lib/src/main/cpp/vmStructs_dd.h | 1 + 2 files changed, 12 insertions(+) diff --git a/ddprof-lib/src/main/cpp/vmStructs_dd.cpp b/ddprof-lib/src/main/cpp/vmStructs_dd.cpp index 3ac44fb0..e2089a2d 100644 --- a/ddprof-lib/src/main/cpp/vmStructs_dd.cpp +++ b/ddprof-lib/src/main/cpp/vmStructs_dd.cpp @@ -44,6 +44,7 @@ namespace ddprof { initOffsets(); initJvmFunctions(); initUnsafeFunctions(); + initCriticalJNINatives(); } void VMStructs_::initOffsets() { @@ -98,6 +99,16 @@ namespace ddprof { } } + void VMStructs_::initCriticalJNINatives() { +#ifdef __aarch64__ + // aarch64 does not support CriticalJNINatives + JVMFlag* flag = JVMFlag::find("CriticalJNINatives", {JVMFlag::Type::Bool}); + if (flag != nullptr && flag->get()) { + flag->set(0); + } +#endif // __aarch64__ + } + const void *VMStructs_::findHeapUsageFunc() { if (VM::hotspot_version() < 17) { // For JDK 11 it is really unreliable to find the memory_usage function - diff --git a/ddprof-lib/src/main/cpp/vmStructs_dd.h b/ddprof-lib/src/main/cpp/vmStructs_dd.h index c4547140..dbec8404 100644 --- a/ddprof-lib/src/main/cpp/vmStructs_dd.h +++ b/ddprof-lib/src/main/cpp/vmStructs_dd.h @@ -52,6 +52,7 @@ namespace ddprof { static void initOffsets(); static void initJvmFunctions(); static void initUnsafeFunctions(); + static void initCriticalJNINatives(); static void checkNativeBinding(jvmtiEnv *jvmti, JNIEnv *jni, jmethodID method, void *address); From 19b54dbc0b61e03508ca93ad61ef3a40d7fbee65 Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Wed, 29 Oct 2025 13:55:34 -0400 Subject: [PATCH 3/4] Remove none performance critical variants and add comments --- ddprof-lib/src/main/cpp/javaApi.cpp | 63 +++++++++-------------------- 1 file changed, 19 insertions(+), 44 deletions(-) diff --git a/ddprof-lib/src/main/cpp/javaApi.cpp b/ddprof-lib/src/main/cpp/javaApi.cpp index d9a6ec8b..c1124703 100644 --- a/ddprof-lib/src/main/cpp/javaApi.cpp +++ b/ddprof-lib/src/main/cpp/javaApi.cpp @@ -68,11 +68,6 @@ Java_com_datadoghq_profiler_JavaProfiler_init0(JNIEnv *env, jclass unused) { return VM::initProfilerBridge(nullptr, true); } -extern "C" JNIEXPORT jboolean JNICALL -JavaCritical_com_datadoghq_profiler_JavaProfiler_init0() { - return VM::initProfilerBridge(nullptr, true); -} - extern "C" DLLEXPORT void JNICALL Java_com_datadoghq_profiler_JavaProfiler_stop0(JNIEnv *env, jobject unused) { Error error = Profiler::instance()->stop(); @@ -87,12 +82,6 @@ Java_com_datadoghq_profiler_JavaProfiler_getTid0(JNIEnv *env, jclass unused) { return OS::threadId(); } -extern "C" DLLEXPORT jint JNICALL -JavaCritical_com_datadoghq_profiler_JavaProfiler_getTid0() { - return OS::threadId(); -} - - extern "C" DLLEXPORT jstring JNICALL Java_com_datadoghq_profiler_JavaProfiler_execute0(JNIEnv *env, jobject unused, jstring command) { @@ -136,12 +125,12 @@ Java_com_datadoghq_profiler_JavaProfiler_getSamples(JNIEnv *env, return (jlong)Profiler::instance()->total_samples(); } -extern "C" DLLEXPORT jlong JNICALL -JavaCritical_com_datadoghq_profiler_JavaProfiler_getSamples() { - return (jlong)Profiler::instance()->total_samples(); -} - // some duplication between add and remove, though we want to avoid having an extra branch in the hot path + +// JavaCritical is faster JNI, but more restrictive - parameters and return value have to be +// primitives or arrays of primitive types. +// We direct corresponding JNI calls to JavaCritical to make sure the parameters/return value +// still compatible in the event of signature changes in the future. extern "C" DLLEXPORT void JNICALL JavaCritical_com_datadoghq_profiler_JavaProfiler_filterThreadAdd0() { ProfiledThread *current = ProfiledThread::current(); @@ -172,12 +161,6 @@ JavaCritical_com_datadoghq_profiler_JavaProfiler_filterThreadAdd0() { thread_filter->add(tid, slot_id); } -extern "C" DLLEXPORT void JNICALL -Java_com_datadoghq_profiler_JavaProfiler_filterThreadAdd0(JNIEnv *env, - jclass unused) { - JavaCritical_com_datadoghq_profiler_JavaProfiler_filterThreadAdd0(); -} - extern "C" DLLEXPORT void JNICALL JavaCritical_com_datadoghq_profiler_JavaProfiler_filterThreadRemove0() { ProfiledThread *current = ProfiledThread::current(); @@ -193,7 +176,7 @@ JavaCritical_com_datadoghq_profiler_JavaProfiler_filterThreadRemove0() { if (unlikely(!thread_filter->enabled())) { return; } - + int slot_id = current->filterSlotId(); if (unlikely(slot_id == -1)) { // Thread doesn't have a slot ID yet - nothing to remove @@ -202,6 +185,13 @@ JavaCritical_com_datadoghq_profiler_JavaProfiler_filterThreadRemove0() { thread_filter->remove(slot_id); } + +extern "C" DLLEXPORT void JNICALL +Java_com_datadoghq_profiler_JavaProfiler_filterThreadAdd0(JNIEnv *env, + jclass unused) { + JavaCritical_com_datadoghq_profiler_JavaProfiler_filterThreadAdd0(); +} + extern "C" DLLEXPORT void JNICALL Java_com_datadoghq_profiler_JavaProfiler_filterThreadRemove0(JNIEnv *env, jclass unused) { @@ -210,7 +200,9 @@ Java_com_datadoghq_profiler_JavaProfiler_filterThreadRemove0(JNIEnv *env, // Backward compatibility for existing code extern "C" DLLEXPORT void JNICALL -JavaCritical_com_datadoghq_profiler_JavaProfiler_filterThread0(jboolean enable) { +Java_com_datadoghq_profiler_JavaProfiler_filterThread0(JNIEnv *env, + jclass unused, + jboolean enable) { ProfiledThread *current = ProfiledThread::current(); if (unlikely(current == nullptr)) { assert(false); @@ -224,7 +216,7 @@ JavaCritical_com_datadoghq_profiler_JavaProfiler_filterThread0(jboolean enable) if (unlikely(!thread_filter->enabled())) { return; } - + int slot_id = current->filterSlotId(); if (unlikely(slot_id == -1)) { if (enable) { @@ -237,11 +229,11 @@ JavaCritical_com_datadoghq_profiler_JavaProfiler_filterThread0(jboolean enable) return; } } - + if (unlikely(slot_id == -1)) { return; // Failed to register thread } - + if (enable) { thread_filter->add(tid, slot_id); } else { @@ -249,13 +241,6 @@ JavaCritical_com_datadoghq_profiler_JavaProfiler_filterThread0(jboolean enable) } } -extern "C" DLLEXPORT void JNICALL -Java_com_datadoghq_profiler_JavaProfiler_filterThread0(JNIEnv *env, - jclass unused, - jboolean enable) { - JavaCritical_com_datadoghq_profiler_JavaProfiler_filterThread0(enable); -} - extern "C" DLLEXPORT jobject JNICALL Java_com_datadoghq_profiler_JavaProfiler_getContextPage0(JNIEnv *env, jclass unused, @@ -275,22 +260,12 @@ Java_com_datadoghq_profiler_JavaProfiler_getContextPageOffset0(JNIEnv *env, return (jlong)page.storage; } -extern "C" DLLEXPORT jlong JNICALL -JavaCritical_com_datadoghq_profiler_JavaProfiler_getContextPageOffset0(jint tid) { - ContextPage page = Contexts::getPage((int)tid); - return (jlong)page.storage; -} - extern "C" DLLEXPORT jint JNICALL Java_com_datadoghq_profiler_JavaProfiler_getMaxContextPages0(JNIEnv *env, jclass unused) { return (jint)Contexts::getMaxPages(); } -extern "C" DLLEXPORT jint JNICALL -JavaCritical_com_datadoghq_profiler_JavaProfiler_getMaxContextPages0() { - return (jint)Contexts::getMaxPages(); -} extern "C" DLLEXPORT jboolean JNICALL Java_com_datadoghq_profiler_JavaProfiler_recordTrace0( From 18c788e02026d8c7828f434da178406726dea502 Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Wed, 29 Oct 2025 14:25:38 -0400 Subject: [PATCH 4/4] Remove more none performance critical CriticalJNINatives methods --- ddprof-lib/src/main/cpp/javaApi.cpp | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/ddprof-lib/src/main/cpp/javaApi.cpp b/ddprof-lib/src/main/cpp/javaApi.cpp index c1124703..2a8b718f 100644 --- a/ddprof-lib/src/main/cpp/javaApi.cpp +++ b/ddprof-lib/src/main/cpp/javaApi.cpp @@ -400,21 +400,12 @@ Java_com_datadoghq_profiler_JavaProfiler_currentTicks0(JNIEnv *env, return TSC::ticks(); } -extern "C" DLLEXPORT jlong JNICALL -JavaCritical_com_datadoghq_profiler_JavaProfiler_currentTicks0() { - return TSC::ticks(); -} - extern "C" DLLEXPORT jlong JNICALL Java_com_datadoghq_profiler_JavaProfiler_tscFrequency0(JNIEnv *env, jclass unused) { return TSC::frequency(); } -extern "C" DLLEXPORT jlong JNICALL -JavaCritical_com_datadoghq_profiler_JavaProfiler_tscFrequency0() { - return TSC::frequency(); -} extern "C" DLLEXPORT void JNICALL Java_com_datadoghq_profiler_JavaProfiler_mallocArenaMax0(JNIEnv *env, @@ -423,12 +414,6 @@ Java_com_datadoghq_profiler_JavaProfiler_mallocArenaMax0(JNIEnv *env, ddprof::OS::mallocArenaMax(maxArenas); } -extern "C" DLLEXPORT void JNICALL -JavaCritical_com_datadoghq_profiler_JavaProfiler_mallocArenaMax0(jint maxArenas) { - ddprof::OS::mallocArenaMax(maxArenas); -} - - extern "C" DLLEXPORT jstring JNICALL Java_com_datadoghq_profiler_JVMAccess_findStringJVMFlag0(JNIEnv *env, jobject unused,