From 4bfccf5bd4aec507960351ca755fe7c2dbc6e77c Mon Sep 17 00:00:00 2001 From: "zhengyu.gu" Date: Tue, 10 Jun 2025 14:59:05 -0400 Subject: [PATCH 01/11] v0 --- ddprof-lib/src/main/cpp/threadFilter.cpp | 16 +++++++++++++++- ddprof-lib/src/main/cpp/threadFilter.h | 2 ++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/ddprof-lib/src/main/cpp/threadFilter.cpp b/ddprof-lib/src/main/cpp/threadFilter.cpp index 034aabf9be..2f61ebf61e 100644 --- a/ddprof-lib/src/main/cpp/threadFilter.cpp +++ b/ddprof-lib/src/main/cpp/threadFilter.cpp @@ -17,6 +17,7 @@ #include "threadFilter.h" #include "counters.h" #include "os.h" +#include #include #include @@ -85,12 +86,21 @@ void ThreadFilter::clear() { _size = 0; } +int ThreadFilter::hashThreadId(int thread_id) { + u16 lower16 = (u16)(thread_id & 0xffff); + lower16 = ((lower16 & 0x00ff) << 8) | ((lower16 & 0xff00) >> 8); + int tid = (thread_id & ~0xffff) | lower16; + return tid; +} + bool ThreadFilter::accept(int thread_id) { + thread_id = hashThreadId(thread_id); u64 *b = bitmap(thread_id); return b != NULL && (word(b, thread_id) & (1ULL << (thread_id & 0x3f))); } void ThreadFilter::add(int thread_id) { + thread_id = hashThreadId(thread_id); u64 *b = bitmap(thread_id); if (b == NULL) { b = (u64 *)OS::safeAlloc(BITMAP_SIZE); @@ -111,6 +121,7 @@ void ThreadFilter::add(int thread_id) { } void ThreadFilter::remove(int thread_id) { + thread_id = hashThreadId(thread_id); u64 *b = bitmap(thread_id); if (b == NULL) { return; @@ -132,7 +143,10 @@ void ThreadFilter::collect(std::vector &v) { // order here u64 word = __atomic_load_n(&b[j], __ATOMIC_ACQUIRE); while (word != 0) { - v.push_back(start_id + j * 64 + __builtin_ctzl(word)); + int tid = start_id + j * 64 + __builtin_ctzl(word); + // restore thread id; + tid = hashThreadId(tid); + v.push_back(tid); word &= (word - 1); } } diff --git a/ddprof-lib/src/main/cpp/threadFilter.h b/ddprof-lib/src/main/cpp/threadFilter.h index cec7e70481..40a44fc944 100644 --- a/ddprof-lib/src/main/cpp/threadFilter.h +++ b/ddprof-lib/src/main/cpp/threadFilter.h @@ -45,6 +45,8 @@ class ThreadFilter { __ATOMIC_ACQUIRE); } + static int hashThreadId(int thread_id); + u64 &word(u64 *bitmap, int thread_id) { // todo: add thread safe APIs return bitmap[((u32)thread_id % BITMAP_CAPACITY) >> 6]; From a9eda77181173cfd1009e942df81cc9a06503ceb Mon Sep 17 00:00:00 2001 From: "zhengyu.gu" Date: Tue, 10 Jun 2025 15:14:20 -0400 Subject: [PATCH 02/11] Remove unnecessary import --- ddprof-lib/src/main/cpp/threadFilter.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/ddprof-lib/src/main/cpp/threadFilter.cpp b/ddprof-lib/src/main/cpp/threadFilter.cpp index 2f61ebf61e..4504bee92c 100644 --- a/ddprof-lib/src/main/cpp/threadFilter.cpp +++ b/ddprof-lib/src/main/cpp/threadFilter.cpp @@ -17,7 +17,6 @@ #include "threadFilter.h" #include "counters.h" #include "os.h" -#include #include #include From cc957930b34f8358cb4088310f7f56998cb04bd6 Mon Sep 17 00:00:00 2001 From: "zhengyu.gu" Date: Tue, 10 Jun 2025 21:55:30 -0400 Subject: [PATCH 03/11] Rename --- ddprof-lib/src/main/cpp/threadFilter.cpp | 12 ++++++------ ddprof-lib/src/main/cpp/threadFilter.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ddprof-lib/src/main/cpp/threadFilter.cpp b/ddprof-lib/src/main/cpp/threadFilter.cpp index 4504bee92c..2e4b09c408 100644 --- a/ddprof-lib/src/main/cpp/threadFilter.cpp +++ b/ddprof-lib/src/main/cpp/threadFilter.cpp @@ -85,7 +85,7 @@ void ThreadFilter::clear() { _size = 0; } -int ThreadFilter::hashThreadId(int thread_id) { +int ThreadFilter::mapThreadId(int thread_id) { u16 lower16 = (u16)(thread_id & 0xffff); lower16 = ((lower16 & 0x00ff) << 8) | ((lower16 & 0xff00) >> 8); int tid = (thread_id & ~0xffff) | lower16; @@ -93,13 +93,13 @@ int ThreadFilter::hashThreadId(int thread_id) { } bool ThreadFilter::accept(int thread_id) { - thread_id = hashThreadId(thread_id); + thread_id = mapThreadId(thread_id); u64 *b = bitmap(thread_id); return b != NULL && (word(b, thread_id) & (1ULL << (thread_id & 0x3f))); } void ThreadFilter::add(int thread_id) { - thread_id = hashThreadId(thread_id); + thread_id = mapThreadId(thread_id); u64 *b = bitmap(thread_id); if (b == NULL) { b = (u64 *)OS::safeAlloc(BITMAP_SIZE); @@ -120,7 +120,7 @@ void ThreadFilter::add(int thread_id) { } void ThreadFilter::remove(int thread_id) { - thread_id = hashThreadId(thread_id); + thread_id = mapThreadId(thread_id); u64 *b = bitmap(thread_id); if (b == NULL) { return; @@ -143,8 +143,8 @@ void ThreadFilter::collect(std::vector &v) { u64 word = __atomic_load_n(&b[j], __ATOMIC_ACQUIRE); while (word != 0) { int tid = start_id + j * 64 + __builtin_ctzl(word); - // restore thread id; - tid = hashThreadId(tid); + // restore thread id + tid = mapThreadId(tid); v.push_back(tid); word &= (word - 1); } diff --git a/ddprof-lib/src/main/cpp/threadFilter.h b/ddprof-lib/src/main/cpp/threadFilter.h index 40a44fc944..76b7419b5b 100644 --- a/ddprof-lib/src/main/cpp/threadFilter.h +++ b/ddprof-lib/src/main/cpp/threadFilter.h @@ -45,7 +45,7 @@ class ThreadFilter { __ATOMIC_ACQUIRE); } - static int hashThreadId(int thread_id); + static int mapThreadId(int thread_id); u64 &word(u64 *bitmap, int thread_id) { // todo: add thread safe APIs From bb2a1168e8870061f41a06ce8edb21106162dedd Mon Sep 17 00:00:00 2001 From: "zhengyu.gu" Date: Wed, 11 Jun 2025 10:57:03 -0400 Subject: [PATCH 04/11] Added assertion for potential mapped thread id overflow --- ddprof-lib/src/main/cpp/threadFilter.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ddprof-lib/src/main/cpp/threadFilter.cpp b/ddprof-lib/src/main/cpp/threadFilter.cpp index 2e4b09c408..11a97cb877 100644 --- a/ddprof-lib/src/main/cpp/threadFilter.cpp +++ b/ddprof-lib/src/main/cpp/threadFilter.cpp @@ -86,6 +86,8 @@ void ThreadFilter::clear() { } int ThreadFilter::mapThreadId(int thread_id) { + // We want to map the thread_id inside the same bitmap + static_assert(BITMAP_SIZE >= (u16)0xffff, "Potential verflow"); u16 lower16 = (u16)(thread_id & 0xffff); lower16 = ((lower16 & 0x00ff) << 8) | ((lower16 & 0xff00) >> 8); int tid = (thread_id & ~0xffff) | lower16; From a3d2477d439f2c865e29fe46a956a7ad4a283d43 Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Thu, 12 Jun 2025 15:52:26 -0400 Subject: [PATCH 05/11] Reverse lower 16 bits, instead of swapping lower 2 bytes --- ddprof-lib/src/main/cpp/reverse_bits.h | 23 +++++++++++++++++++++++ ddprof-lib/src/main/cpp/threadFilter.cpp | 3 ++- 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 ddprof-lib/src/main/cpp/reverse_bits.h diff --git a/ddprof-lib/src/main/cpp/reverse_bits.h b/ddprof-lib/src/main/cpp/reverse_bits.h new file mode 100644 index 0000000000..81e46e19ad --- /dev/null +++ b/ddprof-lib/src/main/cpp/reverse_bits.h @@ -0,0 +1,23 @@ +// +// Borrow the implementation from openjdk +// https://github.com/openjdk/jdk/blob/master/src/hotspot/share/utilities/reverse_bits.hpp +// + +#ifndef REVERSE_BITS_H +#define REVERSE_BITS_H +#include "arch_dd.h" +#include + +static constexpr u32 rep_5555 = static_cast(UINT64_C(0x5555555555555555)); +static constexpr u32 rep_3333 = static_cast(UINT64_C(0x3333333333333333)); +static constexpr u32 rep_0F0F = static_cast(UINT64_C(0x0F0F0F0F0F0F0F0F)); + +inline u16 reverse16(u16 v) { + u32 x = static_cast(v); + x = ((x & rep_5555) << 1) | ((x >> 1) & rep_5555); + x = ((x & rep_3333) << 2) | ((x >> 2) & rep_3333); + x = ((x & rep_0F0F) << 4) | ((x >> 4) & rep_0F0F); + return __builtin_bswap16(static_cast(x)); +} + +#endif //REVERSE_BITS_H diff --git a/ddprof-lib/src/main/cpp/threadFilter.cpp b/ddprof-lib/src/main/cpp/threadFilter.cpp index 11a97cb877..e533ac0d76 100644 --- a/ddprof-lib/src/main/cpp/threadFilter.cpp +++ b/ddprof-lib/src/main/cpp/threadFilter.cpp @@ -17,6 +17,7 @@ #include "threadFilter.h" #include "counters.h" #include "os.h" +#include "reverse_bits.h" #include #include @@ -89,7 +90,7 @@ int ThreadFilter::mapThreadId(int thread_id) { // We want to map the thread_id inside the same bitmap static_assert(BITMAP_SIZE >= (u16)0xffff, "Potential verflow"); u16 lower16 = (u16)(thread_id & 0xffff); - lower16 = ((lower16 & 0x00ff) << 8) | ((lower16 & 0xff00) >> 8); + lower16 = reverse16(lower16); int tid = (thread_id & ~0xffff) | lower16; return tid; } From 0b128b57ec177020c18beb51f6f5caef93519463 Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Mon, 16 Jun 2025 10:32:37 -0400 Subject: [PATCH 06/11] jbachorik's comment --- ddprof-lib/src/main/cpp/threadFilter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/ddprof-lib/src/main/cpp/threadFilter.cpp b/ddprof-lib/src/main/cpp/threadFilter.cpp index e533ac0d76..23ec9a620f 100644 --- a/ddprof-lib/src/main/cpp/threadFilter.cpp +++ b/ddprof-lib/src/main/cpp/threadFilter.cpp @@ -86,6 +86,7 @@ void ThreadFilter::clear() { _size = 0; } +// The mapping has to be reversible: f(f(x)) == x int ThreadFilter::mapThreadId(int thread_id) { // We want to map the thread_id inside the same bitmap static_assert(BITMAP_SIZE >= (u16)0xffff, "Potential verflow"); From e963b3ae1672cd1b65a88a3ccd66389fb6e83ec3 Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Mon, 16 Jun 2025 14:51:48 -0400 Subject: [PATCH 07/11] Active/deactive thread context via Unsafe --- ddprof-lib/src/main/cpp/javaApi.cpp | 21 ++++++ ddprof-lib/src/main/cpp/threadFilter.cpp | 34 ++++++--- ddprof-lib/src/main/cpp/threadFilter.h | 11 ++- .../com/datadoghq/profiler/ActiveBitmap.java | 74 +++++++++++++++++++ .../com/datadoghq/profiler/JavaProfiler.java | 35 +++++---- 5 files changed, 150 insertions(+), 25 deletions(-) create mode 100644 ddprof-lib/src/main/java/com/datadoghq/profiler/ActiveBitmap.java diff --git a/ddprof-lib/src/main/cpp/javaApi.cpp b/ddprof-lib/src/main/cpp/javaApi.cpp index 7d45e4227b..32c94a7854 100644 --- a/ddprof-lib/src/main/cpp/javaApi.cpp +++ b/ddprof-lib/src/main/cpp/javaApi.cpp @@ -406,3 +406,24 @@ Java_com_datadoghq_profiler_JVMAccess_healthCheck0(JNIEnv *env, jobject unused) { return true; } + +extern "C" DLLEXPORT jlong JNICALL +Java_com_datadoghq_profiler_ActiveBitmap_bitmapAddressFor0(JNIEnv *env, + jclass unused, + jint tid) { + u64* bitmap = Profiler::instance()->threadFilter()->bitmapAddressFor((int)tid); + return (jlong)bitmap; +} + +extern "C" DLLEXPORT jboolean JNICALL +Java_com_datadoghq_profiler_ActiveBitmap_isActive0(JNIEnv *env, + jclass unused, + jint tid) { + return Profiler::instance()->threadFilter()->accept((int)tid) ? JNI_TRUE : JNI_FALSE; +} + +extern "C" DLLEXPORT jlong JNICALL +Java_com_datadoghq_profiler_ActiveBitmap_getActiveCountAddr0(JNIEnv *env, + jclass unused) { + return (jlong)Profiler::instance()->threadFilter()->addressOfSize(); +} \ No newline at end of file diff --git a/ddprof-lib/src/main/cpp/threadFilter.cpp b/ddprof-lib/src/main/cpp/threadFilter.cpp index 23ec9a620f..2248444c58 100644 --- a/ddprof-lib/src/main/cpp/threadFilter.cpp +++ b/ddprof-lib/src/main/cpp/threadFilter.cpp @@ -18,6 +18,7 @@ #include "counters.h" #include "os.h" #include "reverse_bits.h" +#include #include #include @@ -96,19 +97,14 @@ int ThreadFilter::mapThreadId(int thread_id) { return tid; } -bool ThreadFilter::accept(int thread_id) { - thread_id = mapThreadId(thread_id); - u64 *b = bitmap(thread_id); - return b != NULL && (word(b, thread_id) & (1ULL << (thread_id & 0x3f))); -} - -void ThreadFilter::add(int thread_id) { - thread_id = mapThreadId(thread_id); - u64 *b = bitmap(thread_id); +// Get bitmap that contains the thread id, create one if it does not exist +u64* ThreadFilter::getBitmapFor(int thread_id) { + int index = static_cast(thread_id) / BITMAP_CAPACITY; + u64* b = _bitmap[index]; if (b == NULL) { b = (u64 *)OS::safeAlloc(BITMAP_SIZE); u64 *oldb = __sync_val_compare_and_swap( - &_bitmap[(u32)thread_id / BITMAP_CAPACITY], NULL, b); + &_bitmap[index], NULL, b); if (oldb != NULL) { OS::safeFree(b, BITMAP_SIZE); b = oldb; @@ -116,7 +112,25 @@ void ThreadFilter::add(int thread_id) { trackPage(); } } + return b; +} +u64* ThreadFilter::bitmapAddressFor(int thread_id) { + u64* bitmap = getBitmapFor(thread_id); + thread_id = mapThreadId(thread_id); + return wordAddress(bitmap, thread_id); +} + +bool ThreadFilter::accept(int thread_id) { + thread_id = mapThreadId(thread_id); + u64 *b = bitmap(thread_id); + return b != NULL && (word(b, thread_id) & (1ULL << (thread_id & 0x3f))); +} + +void ThreadFilter::add(int thread_id) { + thread_id = mapThreadId(thread_id); + u64 *b = getBitmapFor(thread_id); + assert (b != NULL); u64 bit = 1ULL << (thread_id & 0x3f); if (!(__sync_fetch_and_or(&word(b, thread_id), bit) & bit)) { atomicInc(_size); diff --git a/ddprof-lib/src/main/cpp/threadFilter.h b/ddprof-lib/src/main/cpp/threadFilter.h index 76b7419b5b..8db82f9fbf 100644 --- a/ddprof-lib/src/main/cpp/threadFilter.h +++ b/ddprof-lib/src/main/cpp/threadFilter.h @@ -52,14 +52,20 @@ class ThreadFilter { return bitmap[((u32)thread_id % BITMAP_CAPACITY) >> 6]; } + u64* const wordAddress(u64 *bitmap, int thread_id) const { + return &bitmap[((u32)thread_id % BITMAP_CAPACITY) >> 6]; + } + + u64* getBitmapFor(int thread_id); public: ThreadFilter(); ThreadFilter(ThreadFilter &threadFilter) = delete; ~ThreadFilter(); - bool enabled() { return _enabled; } + bool enabled() const { return _enabled; } - int size() { return _size; } + int size() const { return _size; } + const volatile int* addressOfSize() const { return &_size; } void init(const char *filter); void clear(); @@ -67,6 +73,7 @@ class ThreadFilter { bool accept(int thread_id); void add(int thread_id); void remove(int thread_id); + u64* bitmapAddressFor(int thread_id); void collect(std::vector &v); }; diff --git a/ddprof-lib/src/main/java/com/datadoghq/profiler/ActiveBitmap.java b/ddprof-lib/src/main/java/com/datadoghq/profiler/ActiveBitmap.java new file mode 100644 index 0000000000..021298a83f --- /dev/null +++ b/ddprof-lib/src/main/java/com/datadoghq/profiler/ActiveBitmap.java @@ -0,0 +1,74 @@ +package com.datadoghq.profiler; + +import sun.misc.Unsafe; +import java.lang.reflect.Field; + + +class ActiveBitmap { + private static final Unsafe UNSAFE = JavaProfiler.UNSAFE; + + // Address to size field of ThreadFilter in native + private static long activeCountAddr; + + private static final ThreadLocal Address = new ThreadLocal() { + @Override protected Long initialValue() { + return -1L; + } + }; + + public static void initialize() { + activeCountAddr = getActiveCountAddr0(); + } + + // On native side, we reverse lower 16 bits of thread id when maps to bitmap bit. + // So the active bit position of the specific thread id maps to the reverse order + // of the second lowest byte of thread id. + static long getBitmask(int tid) { + int tmp = (tid >> 8) & 0xff ; + int bits = 0; + for (int index = 0; index < 7 ; index++) { + if ((tmp & 0x01) == 0x01) { + bits |= 0x01; + } + tmp >>= 1; + bits <<= 1; + } + return 1L << (bits & 0x3f); + } + + static void setActive(int tid, boolean active) { + long addr = Address.get(); + if (addr == -1) { + addr = bitmapAddressFor0(tid); + Address.set(addr); + } + long bitmask = getBitmask(tid); + long value = UNSAFE.getLong(addr); + long newVal; + if (active) { + newVal = value | bitmask; + } else { + newVal = value & ~bitmask; + } + while (!UNSAFE.compareAndSwapLong(null, addr, value, newVal)) { + value = UNSAFE.getLong(addr); + newVal = active ? (value | bitmask) : (value & ~bitmask); + } + int delta = active ? 1 : -1; + assert activeCountAddr != 0; + UNSAFE.getAndAddInt(null, activeCountAddr, delta); + + assert isActive0(tid) == active; + } + + + + // Address of bitmap word that contains the active bit of this thread Id + private static native long bitmapAddressFor0(int tid); + + private static native long getActiveCountAddr0(); + + // For validation + private static native boolean isActive0(int tid); +} + 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 4436273c61..9ce7a79938 100644 --- a/ddprof-lib/src/main/java/com/datadoghq/profiler/JavaProfiler.java +++ b/ddprof-lib/src/main/java/com/datadoghq/profiler/JavaProfiler.java @@ -34,17 +34,17 @@ * libjavaProfiler.so. */ public final class JavaProfiler { - private static final Unsafe UNSAFE; + static final Unsafe UNSAFE; + static final boolean isJDK8; static { Unsafe unsafe = null; String version = System.getProperty("java.version"); - if (version.startsWith("1.8")) { - try { - Field f = Unsafe.class.getDeclaredField("theUnsafe"); - f.setAccessible(true); - unsafe = (Unsafe) f.get(null); - } catch (Exception ignore) { } - } + isJDK8 = version.startsWith("1.8"); + try { + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + unsafe = (Unsafe) f.get(null); + } catch (Exception ignore) { } UNSAFE = unsafe; } @@ -108,6 +108,7 @@ public static synchronized JavaProfiler getInstance(String libLocation, String s throw new IOException("Failed to load Datadog Java profiler library", result.error); } init0(); + ActiveBitmap.initialize(); profiler.initializeContextStorage(); instance = profiler; @@ -208,7 +209,11 @@ public boolean recordTraceRoot(long rootSpanId, String endpoint, int sizeLimit) * 'filter' option must be enabled to use this method. */ public void addThread() { - filterThread0(true); + if (UNSAFE != null) { + ActiveBitmap.setActive(TID.get(), true); + } else { + filterThread0(true); + } } /** @@ -216,7 +221,11 @@ public void addThread() { * 'filter' option must be enabled to use this method. */ public void removeThread() { - filterThread0(false); + if (UNSAFE != null) { + ActiveBitmap.setActive(TID.get(), false); + } else { + filterThread0(false); + } } @@ -229,7 +238,7 @@ public void removeThread() { */ public void setContext(long spanId, long rootSpanId) { int tid = TID.get(); - if (UNSAFE != null) { + if (isJDK8) { setContextJDK8(tid, spanId, rootSpanId); } else { setContextByteBuffer(tid, spanId, rootSpanId); @@ -304,7 +313,7 @@ public void clearContext() { */ public void setContextValue(int offset, int value) { int tid = TID.get(); - if (UNSAFE != null) { + if (isJDK8) { setContextJDK8(tid, offset, value); } else { setContextByteBuffer(tid, offset, value); @@ -335,7 +344,7 @@ public void setContextByteBuffer(int tid, int offset, int value) { void copyTags(int[] snapshot) { int tid = TID.get(); - if (UNSAFE != null) { + if (isJDK8) { copyTagsJDK8(tid, snapshot); } else { copyTagsByteBuffer(tid, snapshot); From e12c3a4750be71883cd624c981ecd72d66bb5ca2 Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Tue, 17 Jun 2025 09:38:22 -0400 Subject: [PATCH 08/11] v1 --- ddprof-lib/src/main/cpp/threadFilter.cpp | 3 +- .../com/datadoghq/profiler/ActiveBitmaps.java | 79 +++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 ddprof-lib/src/main/java/com/datadoghq/profiler/ActiveBitmaps.java diff --git a/ddprof-lib/src/main/cpp/threadFilter.cpp b/ddprof-lib/src/main/cpp/threadFilter.cpp index 2248444c58..1c1e18432e 100644 --- a/ddprof-lib/src/main/cpp/threadFilter.cpp +++ b/ddprof-lib/src/main/cpp/threadFilter.cpp @@ -99,7 +99,8 @@ int ThreadFilter::mapThreadId(int thread_id) { // Get bitmap that contains the thread id, create one if it does not exist u64* ThreadFilter::getBitmapFor(int thread_id) { - int index = static_cast(thread_id) / BITMAP_CAPACITY; + int index = thread_id / BITMAP_CAPACITY; + assert(index >= 0 && index < (int)_max_bitmaps); u64* b = _bitmap[index]; if (b == NULL) { b = (u64 *)OS::safeAlloc(BITMAP_SIZE); diff --git a/ddprof-lib/src/main/java/com/datadoghq/profiler/ActiveBitmaps.java b/ddprof-lib/src/main/java/com/datadoghq/profiler/ActiveBitmaps.java new file mode 100644 index 0000000000..5c35c46729 --- /dev/null +++ b/ddprof-lib/src/main/java/com/datadoghq/profiler/ActiveBitmaps.java @@ -0,0 +1,79 @@ +package com.datadoghq.profiler; + +import sun.misc.Unsafe; +import java.lang.reflect.Field; + + +class ActiveBitmaps { + private static final Unsafe UNSAFE; + static { + Unsafe unsafe = null; + try { + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + unsafe = (Unsafe) f.get(null); + } catch (Exception ignore) { } + UNSAFE = unsafe; + } + + private static long activeCountAddr = 0; + + private static final ThreadLocal Address = new ThreadLocal() { + @Override protected Long initialValue() { + return -1L; + } + }; + + public static void initialize() { + activeCountAddr = getActiveCountAddr0(); + } + + static long getBitmask(int tid) { + int tmp = (tid >> 8) & 0xff ; + int bits = 0; + for (int index = 0; index < 7 ; index++) { + if ((tmp & 0x01) == 0x01) { + bits |= 0x01; + } + tmp >>= 1; + bits <<= 1; + } + return 1L << (bits & 0x3f); + } + + static void setActive(int tid, boolean active) { + long addr = Address.get(); + if (addr == -1) { + addr = bitmapAddressFor0(tid); + Address.set(addr); + } + long bitmask = getBitmask(tid); + long value = UNSAFE.getLong(addr); + long newVal; + if (active) { + newVal = value | bitmask; + } else { + newVal = value & ~bitmask; + } + while (!UNSAFE.compareAndSwapLong(null, addr, value, newVal)) { + value = UNSAFE.getLong(addr); + newVal = active ? (value | bitmask) : (value & ~bitmask); + } + int delta = active ? 1 : -1; + assert activeCountAddr != 0; + UNSAFE.getAndAddInt(null, activeCountAddr, delta); + if (isActive0(tid) != active) { + throw new RuntimeException("SetActive failed"); + } + + assert isActive0(tid) == active; + } + + // For verification + private static native boolean isActive0(int tid); + + // Setup bitmap and active count locations + private static native long bitmapAddressFor0(int tid); + private static native long getActiveCountAddr0(); +} + From 7737a940983e9beb4fa8ed10d2bf977658151788 Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Tue, 17 Jun 2025 13:59:06 -0400 Subject: [PATCH 09/11] v2 --- ddprof-lib/src/main/cpp/javaApi.cpp | 12 ++- ddprof-lib/src/main/cpp/threadFilter.cpp | 18 ++++- ddprof-lib/src/main/cpp/threadFilter.h | 1 + .../com/datadoghq/profiler/ActiveBitmap.java | 19 ++--- .../com/datadoghq/profiler/ActiveBitmaps.java | 79 ------------------- 5 files changed, 34 insertions(+), 95 deletions(-) delete mode 100644 ddprof-lib/src/main/java/com/datadoghq/profiler/ActiveBitmaps.java diff --git a/ddprof-lib/src/main/cpp/javaApi.cpp b/ddprof-lib/src/main/cpp/javaApi.cpp index 32c94a7854..0bbb6ab8b2 100644 --- a/ddprof-lib/src/main/cpp/javaApi.cpp +++ b/ddprof-lib/src/main/cpp/javaApi.cpp @@ -409,8 +409,8 @@ Java_com_datadoghq_profiler_JVMAccess_healthCheck0(JNIEnv *env, extern "C" DLLEXPORT jlong JNICALL Java_com_datadoghq_profiler_ActiveBitmap_bitmapAddressFor0(JNIEnv *env, - jclass unused, - jint tid) { + jclass unused, + jint tid) { u64* bitmap = Profiler::instance()->threadFilter()->bitmapAddressFor((int)tid); return (jlong)bitmap; } @@ -426,4 +426,12 @@ extern "C" DLLEXPORT jlong JNICALL Java_com_datadoghq_profiler_ActiveBitmap_getActiveCountAddr0(JNIEnv *env, jclass unused) { return (jlong)Profiler::instance()->threadFilter()->addressOfSize(); +} + +extern "C" DLLEXPORT jlong JNICALL +Java_com_datadoghq_profiler_ActiveBitmap_getBitmapValue0(JNIEnv *env, + jclass unused, + jint tid, + jlong v) { + return (jlong)Profiler::instance()->threadFilter()->getBitmapValue((int)tid, (long)v); } \ No newline at end of file diff --git a/ddprof-lib/src/main/cpp/threadFilter.cpp b/ddprof-lib/src/main/cpp/threadFilter.cpp index 1c1e18432e..9e0d392a63 100644 --- a/ddprof-lib/src/main/cpp/threadFilter.cpp +++ b/ddprof-lib/src/main/cpp/threadFilter.cpp @@ -21,6 +21,7 @@ #include #include #include +#include void trackPage() { Counters::increment(THREAD_FILTER_PAGES, 1); @@ -117,11 +118,21 @@ u64* ThreadFilter::getBitmapFor(int thread_id) { } u64* ThreadFilter::bitmapAddressFor(int thread_id) { - u64* bitmap = getBitmapFor(thread_id); + u64* b = getBitmapFor(thread_id); thread_id = mapThreadId(thread_id); - return wordAddress(bitmap, thread_id); + assert(b == bitmap(thread_id)); + return wordAddress(b, thread_id); } +u64 ThreadFilter::getBitmapValue(int thread_id, long v) { + thread_id = mapThreadId(thread_id); + u64 *b = bitmap(thread_id); + if (b == NULL) return 0; + assert((long)word(b, thread_id) == v); + return word(b, thread_id); +} + + bool ThreadFilter::accept(int thread_id) { thread_id = mapThreadId(thread_id); u64 *b = bitmap(thread_id); @@ -129,9 +140,10 @@ bool ThreadFilter::accept(int thread_id) { } void ThreadFilter::add(int thread_id) { - thread_id = mapThreadId(thread_id); u64 *b = getBitmapFor(thread_id); assert (b != NULL); + thread_id = mapThreadId(thread_id); + assert(b == bitmap(thread_id)); u64 bit = 1ULL << (thread_id & 0x3f); if (!(__sync_fetch_and_or(&word(b, thread_id), bit) & bit)) { atomicInc(_size); diff --git a/ddprof-lib/src/main/cpp/threadFilter.h b/ddprof-lib/src/main/cpp/threadFilter.h index 8db82f9fbf..0be5236faa 100644 --- a/ddprof-lib/src/main/cpp/threadFilter.h +++ b/ddprof-lib/src/main/cpp/threadFilter.h @@ -74,6 +74,7 @@ class ThreadFilter { void add(int thread_id); void remove(int thread_id); u64* bitmapAddressFor(int thread_id); + u64 getBitmapValue(int thread_id, long v); void collect(std::vector &v); }; diff --git a/ddprof-lib/src/main/java/com/datadoghq/profiler/ActiveBitmap.java b/ddprof-lib/src/main/java/com/datadoghq/profiler/ActiveBitmap.java index 021298a83f..7e16015952 100644 --- a/ddprof-lib/src/main/java/com/datadoghq/profiler/ActiveBitmap.java +++ b/ddprof-lib/src/main/java/com/datadoghq/profiler/ActiveBitmap.java @@ -26,12 +26,11 @@ public static void initialize() { static long getBitmask(int tid) { int tmp = (tid >> 8) & 0xff ; int bits = 0; - for (int index = 0; index < 7 ; index++) { + for (int index = 0; index <= 7; index++) { if ((tmp & 0x01) == 0x01) { - bits |= 0x01; + bits |= 1 << (7 - index); } - tmp >>= 1; - bits <<= 1; + tmp >>>= 1; } return 1L << (bits & 0x3f); } @@ -44,12 +43,8 @@ static void setActive(int tid, boolean active) { } long bitmask = getBitmask(tid); long value = UNSAFE.getLong(addr); - long newVal; - if (active) { - newVal = value | bitmask; - } else { - newVal = value & ~bitmask; - } + long newVal = active ? (value | bitmask) : (value & ~bitmask); + while (!UNSAFE.compareAndSwapLong(null, addr, value, newVal)) { value = UNSAFE.getLong(addr); newVal = active ? (value | bitmask) : (value & ~bitmask); @@ -57,12 +52,14 @@ static void setActive(int tid, boolean active) { int delta = active ? 1 : -1; assert activeCountAddr != 0; UNSAFE.getAndAddInt(null, activeCountAddr, delta); + if (isActive0(tid) != active) { + throw new RuntimeException("SetActive Failed"); + } assert isActive0(tid) == active; } - // Address of bitmap word that contains the active bit of this thread Id private static native long bitmapAddressFor0(int tid); diff --git a/ddprof-lib/src/main/java/com/datadoghq/profiler/ActiveBitmaps.java b/ddprof-lib/src/main/java/com/datadoghq/profiler/ActiveBitmaps.java deleted file mode 100644 index 5c35c46729..0000000000 --- a/ddprof-lib/src/main/java/com/datadoghq/profiler/ActiveBitmaps.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.datadoghq.profiler; - -import sun.misc.Unsafe; -import java.lang.reflect.Field; - - -class ActiveBitmaps { - private static final Unsafe UNSAFE; - static { - Unsafe unsafe = null; - try { - Field f = Unsafe.class.getDeclaredField("theUnsafe"); - f.setAccessible(true); - unsafe = (Unsafe) f.get(null); - } catch (Exception ignore) { } - UNSAFE = unsafe; - } - - private static long activeCountAddr = 0; - - private static final ThreadLocal Address = new ThreadLocal() { - @Override protected Long initialValue() { - return -1L; - } - }; - - public static void initialize() { - activeCountAddr = getActiveCountAddr0(); - } - - static long getBitmask(int tid) { - int tmp = (tid >> 8) & 0xff ; - int bits = 0; - for (int index = 0; index < 7 ; index++) { - if ((tmp & 0x01) == 0x01) { - bits |= 0x01; - } - tmp >>= 1; - bits <<= 1; - } - return 1L << (bits & 0x3f); - } - - static void setActive(int tid, boolean active) { - long addr = Address.get(); - if (addr == -1) { - addr = bitmapAddressFor0(tid); - Address.set(addr); - } - long bitmask = getBitmask(tid); - long value = UNSAFE.getLong(addr); - long newVal; - if (active) { - newVal = value | bitmask; - } else { - newVal = value & ~bitmask; - } - while (!UNSAFE.compareAndSwapLong(null, addr, value, newVal)) { - value = UNSAFE.getLong(addr); - newVal = active ? (value | bitmask) : (value & ~bitmask); - } - int delta = active ? 1 : -1; - assert activeCountAddr != 0; - UNSAFE.getAndAddInt(null, activeCountAddr, delta); - if (isActive0(tid) != active) { - throw new RuntimeException("SetActive failed"); - } - - assert isActive0(tid) == active; - } - - // For verification - private static native boolean isActive0(int tid); - - // Setup bitmap and active count locations - private static native long bitmapAddressFor0(int tid); - private static native long getActiveCountAddr0(); -} - From 816e3a9e4270abe0ac5861e4ffae58305f5f252d Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Tue, 17 Jun 2025 14:45:17 -0400 Subject: [PATCH 10/11] v3 --- ddprof-lib/src/main/cpp/javaApi.cpp | 8 -------- ddprof-lib/src/main/cpp/threadFilter.cpp | 9 --------- ddprof-lib/src/main/cpp/threadFilter.h | 1 - 3 files changed, 18 deletions(-) diff --git a/ddprof-lib/src/main/cpp/javaApi.cpp b/ddprof-lib/src/main/cpp/javaApi.cpp index 0bbb6ab8b2..017f23c454 100644 --- a/ddprof-lib/src/main/cpp/javaApi.cpp +++ b/ddprof-lib/src/main/cpp/javaApi.cpp @@ -427,11 +427,3 @@ Java_com_datadoghq_profiler_ActiveBitmap_getActiveCountAddr0(JNIEnv *env, jclass unused) { return (jlong)Profiler::instance()->threadFilter()->addressOfSize(); } - -extern "C" DLLEXPORT jlong JNICALL -Java_com_datadoghq_profiler_ActiveBitmap_getBitmapValue0(JNIEnv *env, - jclass unused, - jint tid, - jlong v) { - return (jlong)Profiler::instance()->threadFilter()->getBitmapValue((int)tid, (long)v); -} \ No newline at end of file diff --git a/ddprof-lib/src/main/cpp/threadFilter.cpp b/ddprof-lib/src/main/cpp/threadFilter.cpp index 9e0d392a63..c2009772a4 100644 --- a/ddprof-lib/src/main/cpp/threadFilter.cpp +++ b/ddprof-lib/src/main/cpp/threadFilter.cpp @@ -124,15 +124,6 @@ u64* ThreadFilter::bitmapAddressFor(int thread_id) { return wordAddress(b, thread_id); } -u64 ThreadFilter::getBitmapValue(int thread_id, long v) { - thread_id = mapThreadId(thread_id); - u64 *b = bitmap(thread_id); - if (b == NULL) return 0; - assert((long)word(b, thread_id) == v); - return word(b, thread_id); -} - - bool ThreadFilter::accept(int thread_id) { thread_id = mapThreadId(thread_id); u64 *b = bitmap(thread_id); diff --git a/ddprof-lib/src/main/cpp/threadFilter.h b/ddprof-lib/src/main/cpp/threadFilter.h index 0be5236faa..8db82f9fbf 100644 --- a/ddprof-lib/src/main/cpp/threadFilter.h +++ b/ddprof-lib/src/main/cpp/threadFilter.h @@ -74,7 +74,6 @@ class ThreadFilter { void add(int thread_id); void remove(int thread_id); u64* bitmapAddressFor(int thread_id); - u64 getBitmapValue(int thread_id, long v); void collect(std::vector &v); }; From a56dd798854aa349fcc4c534fae38b81ac9fca7a Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Tue, 17 Jun 2025 15:16:24 -0400 Subject: [PATCH 11/11] v6 --- .../src/main/java/com/datadoghq/profiler/JavaProfiler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 9ce7a79938..851c4baaee 100644 --- a/ddprof-lib/src/main/java/com/datadoghq/profiler/JavaProfiler.java +++ b/ddprof-lib/src/main/java/com/datadoghq/profiler/JavaProfiler.java @@ -129,7 +129,7 @@ private void initializeContextStorage() { if (this.contextStorage == null) { int maxPages = getMaxContextPages0(); if (maxPages > 0) { - if (UNSAFE != null) { + if (isJDK8) { contextBaseOffsets = new long[maxPages]; // be sure to choose an illegal address as a sentinel value Arrays.fill(contextBaseOffsets, Long.MIN_VALUE);