From 59e54f2a90a15aad3bcc070ff7931d039202a5fa Mon Sep 17 00:00:00 2001 From: Andrei Pangin Date: Wed, 18 Dec 2024 03:37:20 +0000 Subject: [PATCH 1/5] Fixed DWARF unwinding of nativemem stacks on ARM64 (cherry picked from commit c578c17) --- ddprof-lib/src/main/cpp/arch.h | 70 ++++++++++-- ddprof-lib/src/main/cpp/stackWalker.cpp | 143 +++++++++++------------- 2 files changed, 124 insertions(+), 89 deletions(-) diff --git a/ddprof-lib/src/main/cpp/arch.h b/ddprof-lib/src/main/cpp/arch.h index 192273a78..4ba8dc85f 100644 --- a/ddprof-lib/src/main/cpp/arch.h +++ b/ddprof-lib/src/main/cpp/arch.h @@ -77,6 +77,9 @@ const int PERF_REG_PC = 8; // PERF_REG_X86_IP #define flushCache(addr) \ asm volatile("mfence; clflush (%0); mfence" : : "r"(addr) : "memory") +#define callerFP() __builtin_frame_address(1) +#define callerSP() ((void**)__builtin_frame_address(0) + 2) + #elif defined(__arm__) || defined(__thumb__) typedef unsigned int instruction_t; @@ -97,6 +100,9 @@ const int PERF_REG_PC = 15; // PERF_REG_ARM_PC __builtin___clear_cache((char *)(addr), \ (char *)(addr) + sizeof(instruction_t)) +#define callerFP() __builtin_frame_address(1) +#define callerSP() __builtin_frame_address(1) + #elif defined(__aarch64__) typedef unsigned int instruction_t; @@ -116,6 +122,9 @@ const int PERF_REG_PC = 32; // PERF_REG_ARM64_PC __builtin___clear_cache((char *)(addr), \ (char *)(addr) + sizeof(instruction_t)) +#define callerFP() __builtin_frame_address(1) +#define callerSP() __builtin_frame_address(1) + #elif defined(__PPC64__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) typedef unsigned int instruction_t; @@ -132,17 +141,56 @@ const int PLT_HEADER_SIZE = 24; const int PLT_ENTRY_SIZE = 24; const int PERF_REG_PC = 32; // PERF_REG_POWERPC_NIP -#define spinPause() \ - asm volatile("yield") // does nothing, but using or 1,1,1 would lead to other - // problems -#define rmb() \ - asm volatile("sync" \ - : \ - : \ - : "memory") // lwsync would do but better safe than sorry -#define flushCache(addr) \ - __builtin___clear_cache((char *)(addr), \ - (char *)(addr) + sizeof(instruction_t)) +#define spinPause() asm volatile("yield") // does nothing, but using or 1,1,1 would lead to other problems +#define rmb() asm volatile ("sync" : : : "memory") // lwsync would do but better safe than sorry +#define flushCache(addr) __builtin___clear_cache((char*)(addr), (char*)(addr) + sizeof(instruction_t)) + +#define callerFP() __builtin_frame_address(1) +#define callerSP() __builtin_frame_address(0) + +#elif defined(__riscv) && (__riscv_xlen == 64) + +typedef unsigned int instruction_t; +#if defined(__riscv_compressed) +const instruction_t BREAKPOINT = 0x9002; // EBREAK (compressed form) +#else +const instruction_t BREAKPOINT = 0x00100073; // EBREAK +#endif +const int BREAKPOINT_OFFSET = 0; + +const int SYSCALL_SIZE = sizeof(instruction_t); +const int FRAME_PC_SLOT = 1; // return address is at -1 from FP +const int PROBE_SP_LIMIT = 0; +const int PLT_HEADER_SIZE = 24; // Best guess from examining readelf +const int PLT_ENTRY_SIZE = 24; // ...same... +const int PERF_REG_PC = 0; // PERF_REG_RISCV_PC + +#define spinPause() // No architecture support +#define rmb() asm volatile ("fence" : : : "memory") +#define flushCache(addr) __builtin___clear_cache((char*)(addr), (char*)(addr) + sizeof(instruction_t)) + +#define callerFP() __builtin_frame_address(1) +#define callerSP() __builtin_frame_address(0) + +#elif defined(__loongarch_lp64) + +typedef unsigned int instruction_t; +const instruction_t BREAKPOINT = 0x002a0005; // EBREAK +const int BREAKPOINT_OFFSET = 0; + +const int SYSCALL_SIZE = sizeof(instruction_t); +const int FRAME_PC_SLOT = 1; +const int PROBE_SP_LIMIT = 0; +const int PLT_HEADER_SIZE = 32; +const int PLT_ENTRY_SIZE = 16; +const int PERF_REG_PC = 0; // PERF_REG_LOONGARCH_PC + +#define spinPause() asm volatile("ibar 0x0") +#define rmb() asm volatile("dbar 0x0" : : : "memory") +#define flushCache(addr) __builtin___clear_cache((char*)(addr), (char*)(addr) + sizeof(instruction_t)) + +#define callerFP() __builtin_frame_address(1) +#define callerSP() __builtin_frame_address(0) #else diff --git a/ddprof-lib/src/main/cpp/stackWalker.cpp b/ddprof-lib/src/main/cpp/stackWalker.cpp index 4c668381c..e91d76a69 100644 --- a/ddprof-lib/src/main/cpp/stackWalker.cpp +++ b/ddprof-lib/src/main/cpp/stackWalker.cpp @@ -1,17 +1,6 @@ /* - * Copyright 2021 Andrei Pangin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright The async-profiler authors + * SPDX-License-Identifier: Apache-2.0 */ #include "stackWalker.h" @@ -96,17 +85,16 @@ int StackWalker::walkFP(void *ucontext, const void **callchain, int max_depth, uintptr_t sp; uintptr_t bottom = (uintptr_t)&sp + MAX_WALK_SIZE; - StackFrame frame(ucontext); - if (ucontext == NULL) { - pc = __builtin_return_address(0); - fp = (uintptr_t)__builtin_frame_address(1); - sp = (uintptr_t)__builtin_frame_address(0); - } else { StackFrame frame(ucontext); - pc = (const void *)frame.pc(); - fp = frame.fp(); - sp = frame.sp(); - } + if (ucontext == NULL) { + pc = callerPC(); + fp = (uintptr_t)callerFP(); + sp = (uintptr_t)callerSP(); + } else { + pc = (const void*)frame.pc(); + fp = frame.fp(); + sp = frame.sp(); + } int depth = 0; @@ -149,24 +137,23 @@ int StackWalker::walkFP(void *ucontext, const void **callchain, int max_depth, return depth; } -int StackWalker::walkDwarf(void *ucontext, const void **callchain, - int max_depth, StackContext *java_ctx, - bool *truncated) { - const void *pc; - uintptr_t fp; - uintptr_t sp; - uintptr_t prev_sp; - uintptr_t bottom = (uintptr_t)&sp + MAX_WALK_SIZE; - StackFrame frame(ucontext); - if (ucontext == NULL) { - pc = __builtin_return_address(0); - fp = (uintptr_t)__builtin_frame_address(1); - sp = (uintptr_t)__builtin_frame_address(0); - } else { - pc = (const void *)frame.pc(); - fp = frame.fp(); - sp = frame.sp(); - } +int StackWalker::walkDwarf(void* ucontext, const void** callchain, int max_depth, StackContext* java_ctx, bool* truncated) { + const void* pc; + uintptr_t fp; + uintptr_t sp; + uintptr_t prev_sp; + uintptr_t bottom = (uintptr_t)&sp + MAX_WALK_SIZE; + + StackFrame frame(ucontext); + if (ucontext == NULL) { + pc = callerPC(); + fp = (uintptr_t)callerFP(); + sp = (uintptr_t)callerSP(); + } else { + pc = (const void*)frame.pc(); + fp = frame.fp(); + sp = frame.sp(); + } int depth = 0; Libraries *libraries = Libraries::instance(); @@ -245,6 +232,43 @@ int StackWalker::walkDwarf(void *ucontext, const void **callchain, return depth; } +int StackWalker::walkVM(void* ucontext, ASGCT_CallFrame* frames, int max_depth, JavaFrameAnchor* anchor, bool *truncated) { + uintptr_t sp = anchor->lastJavaSP(); + if (sp == 0) { + *truncated = true; + return 0; + } + + uintptr_t fp = anchor->lastJavaFP(); + if (fp == 0) { + fp = sp; + } + + const void* pc = anchor->lastJavaPC(); + if (pc == NULL) { + pc = ((const void**)sp)[-1]; + } + + return walkVM(ucontext, frames, max_depth, VM_BASIC, pc, sp, fp, truncated); +} + + +#ifdef __aarch64__ +// we are seeing false alarms on aarch64 GHA runners due to 'heap-use-after-free' +__attribute__((no_sanitize("address"))) +#endif +int StackWalker::walkVM(void* ucontext, ASGCT_CallFrame* frames, int max_depth, StackDetail detail, bool *truncated) { + if (ucontext == NULL) { + const void* pc = callerPC(); + uintptr_t sp = (uintptr_t)callerSP(); + uintptr_t fp = (uintptr_t)callerFP(); + return walkVM(ucontext, frames, max_depth, detail, pc, sp, fp, truncated); + } else { + StackFrame frame(ucontext); + return walkVM(ucontext, frames, max_depth, detail, (const void*)frame.pc(), frame.sp(), frame.fp(), truncated); + } +} + int StackWalker::walkVM(void* ucontext, ASGCT_CallFrame* frames, int max_depth, StackDetail detail, const void* pc, uintptr_t sp, uintptr_t fp, bool *truncated) { // extract StackFrame from uncontext @@ -621,43 +645,6 @@ int StackWalker::walkVM(void* ucontext, ASGCT_CallFrame* frames, int max_depth, return depth; } -int StackWalker::walkVM(void* ucontext, ASGCT_CallFrame* frames, int max_depth, JavaFrameAnchor* anchor, bool *truncated) { - uintptr_t sp = anchor->lastJavaSP(); - if (sp == 0) { - *truncated = true; - return 0; - } - - uintptr_t fp = anchor->lastJavaFP(); - if (fp == 0) { - fp = sp; - } - - const void* pc = anchor->lastJavaPC(); - if (pc == NULL) { - pc = ((const void**)sp)[-1]; - } - - return walkVM(ucontext, frames, max_depth, VM_BASIC, pc, sp, fp, truncated); -} - - -#ifdef __aarch64__ -// we are seeing false alarms on aarch64 GHA runners due to 'heap-use-after-free' -__attribute__((no_sanitize("address"))) -#endif -int StackWalker::walkVM(void* ucontext, ASGCT_CallFrame* frames, int max_depth, StackDetail detail, bool *truncated) { - if (ucontext == NULL) { - const void* pc = __builtin_return_address(0); - uintptr_t sp = (uintptr_t)__builtin_frame_address(0) + LINKED_FRAME_SIZE; - uintptr_t fp = (uintptr_t)__builtin_frame_address(1); - return walkVM(ucontext, frames, max_depth, detail, pc, sp, fp, truncated); - } else { - StackFrame frame(ucontext); - return walkVM(ucontext, frames, max_depth, detail, (const void*)frame.pc(), frame.sp(), frame.fp(), truncated); - } -} - void StackWalker::checkFault(ProfiledThread* thrd) { VMThread *vm_thread = VMThread::current(); if (vm_thread != NULL && sameStack(vm_thread->exception(), &vm_thread)) { From e526924a27083f2b6f004bddf850f3a816e85d0f Mon Sep 17 00:00:00 2001 From: Andrei Pangin Date: Fri, 19 Apr 2024 02:11:18 +0100 Subject: [PATCH 2/5] #914: Do not set DebugNonSafepoints, if it is already set in the Command Line (cherry picked from commit ac514e5) --- ddprof-lib/src/main/cpp/vmEntry.cpp | 26 +++++------------- ddprof-lib/src/main/cpp/vmStructs.cpp | 38 ++++++++++++++------------- ddprof-lib/src/main/cpp/vmStructs.h | 36 ++++++++++++------------- 3 files changed, 45 insertions(+), 55 deletions(-) diff --git a/ddprof-lib/src/main/cpp/vmEntry.cpp b/ddprof-lib/src/main/cpp/vmEntry.cpp index 6fadd6a63..1b07c1435 100644 --- a/ddprof-lib/src/main/cpp/vmEntry.cpp +++ b/ddprof-lib/src/main/cpp/vmEntry.cpp @@ -1,18 +1,6 @@ /* - * Copyright 2016 Andrei Pangin - * Copyright 2021, 2024 Datadog, Inc - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright The async-profiler authors + * SPDX-License-Identifier: Apache-2.0 */ #include "common.h" @@ -383,9 +371,9 @@ bool VM::initProfilerBridge(JavaVM *vm, bool attach) { } else { // DebugNonSafepoints is automatically enabled with CompiledMethodLoad, // otherwise we set the flag manually - char *flag_addr = (char *)JVMFlag::find("DebugNonSafepoints", {JVMFlag::Type::Bool}); - if (flag_addr != NULL) { - *flag_addr = 1; + JVMFlag* f = JVMFlag::find("DebugNonSafepoints", {JVMFlag::Type::Bool}); + if (f != NULL && f->origin() == 0) { + f->set(1); } } @@ -393,8 +381,8 @@ bool VM::initProfilerBridge(JavaVM *vm, bool attach) { // profiler to avoid the risk of crashing flag was made obsolete (inert) in 15 // (see JDK-8228991) and removed in 16 (see JDK-8231560) if (hotspot_version() < 15) { - char *flag_addr = (char *)JVMFlag::find("UseAdaptiveGCBoundary", {JVMFlag::Type::Bool}); - _is_adaptive_gc_boundary_flag_set = flag_addr != NULL && *flag_addr == 1; + JVMFlag *f = JVMFlag::find("UseAdaptiveGCBoundary", {JVMFlag::Type::Bool}); + _is_adaptive_gc_boundary_flag_set = f != NULL && f->get(); } // Make sure we reload method IDs upon class retransformation diff --git a/ddprof-lib/src/main/cpp/vmStructs.cpp b/ddprof-lib/src/main/cpp/vmStructs.cpp index 815da2212..0a8cfc91d 100644 --- a/ddprof-lib/src/main/cpp/vmStructs.cpp +++ b/ddprof-lib/src/main/cpp/vmStructs.cpp @@ -1,18 +1,6 @@ /* - * Copyright 2017 Andrei Pangin - * Copyright 2022, 2023 Datadog, Inc - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright The async-profiler authors + * SPDX-License-Identifier: Apache-2.0 */ #include "vmStructs.h" @@ -89,6 +77,7 @@ int VMStructs::_vs_high_offset = -1; int VMStructs::_flag_type_offset = -1; int VMStructs::_flag_name_offset = -1; int VMStructs::_flag_addr_offset = -1; +int VMStructs::_flag_origin_offset = -1; const char *VMStructs::_flags_addr = NULL; int VMStructs::_flag_count = 0; int VMStructs::_flag_size = 0; @@ -338,7 +327,9 @@ void VMStructs::initOffsets() { _flag_name_offset = *(int *)(entry + offset_offset); } else if (strcmp(field, "_addr") == 0 || strcmp(field, "addr") == 0) { _flag_addr_offset = *(int *)(entry + offset_offset); - } else if (strcmp(field, "flags") == 0) { + } else if (strcmp(field, "_flags") == 0 || strcmp(field, "origin") == 0) { + _flag_origin_offset = *(int*)(entry + offset_offset); + } else if (strcmp(field, "flags") == 0) { _flags_addr = **(char ***)(entry + address_offset); } else if (strcmp(field, "numFlags") == 0) { _flag_count = **(int **)(entry + address_offset); @@ -402,6 +393,17 @@ void VMStructs::resolveOffsets() { _klass = (jfieldID)(uintptr_t)(*_klass_offset_addr << 2 | 2); } + JVMFlag* ccp = JVMFlag::find("UseCompressedClassPointers"); + if (ccp != NULL && ccp->get() && _narrow_klass_base_addr != NULL && _narrow_klass_shift_addr != NULL) { + _narrow_klass_base = *_narrow_klass_base_addr; + _narrow_klass_shift = *_narrow_klass_shift_addr; + } + + JVMFlag* coh = JVMFlag::find("UseCompactObjectHeaders"); + if (coh != NULL && coh->get()) { + _compact_object_headers = true; + } + _has_class_names = _klass_name_offset >= 0 && (_symbol_length_offset >= 0 || _symbol_length_and_refcount_offset >= 0) && @@ -840,12 +842,12 @@ CodeHeap::findNMethod(char *heap, const void *pc) { return *block ? align(block + sizeof(uintptr_t)) : NULL; } -void *JVMFlag::find(const char *name) { +JVMFlag *JVMFlag::find(const char *name) { if (_flags_addr != NULL && _flag_size > 0) { for (int i = 0; i < _flag_count; i++) { JVMFlag *f = (JVMFlag *)(_flags_addr + i * _flag_size); - if (f->name() != NULL && strcmp(f->name(), name) == 0) { - return f->addr(); + if (f->name() != NULL && strcmp(f->name(), name) == 0&& f->addr() != NULL) { + return f; } } } diff --git a/ddprof-lib/src/main/cpp/vmStructs.h b/ddprof-lib/src/main/cpp/vmStructs.h index 930678890..54c2d1650 100644 --- a/ddprof-lib/src/main/cpp/vmStructs.h +++ b/ddprof-lib/src/main/cpp/vmStructs.h @@ -1,18 +1,6 @@ /* - * Copyright 2017 Andrei Pangin - * Copyright 2022, 2023 Datadog, Inc - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright The async-profiler authors + * SPDX-License-Identifier: Apache-2.0 */ #ifndef _VMSTRUCTS_H @@ -98,7 +86,8 @@ class VMStructs { static int _flag_type_offset; static int _flag_name_offset; static int _flag_addr_offset; - static const char *_flags_addr; + static int _flag_origin_offset; + static const char *_flags_addr; static int _flag_count; static int _flag_size; static char *_code_heap[3]; @@ -558,7 +547,7 @@ class CodeHeap : VMStructs { class JVMFlag : VMStructs { private: - static void *find(const char *name, int type_mask); + static JVMFlag* find(const char *name, int type_mask); public: enum Type { Bool = 0, @@ -574,13 +563,24 @@ class JVMFlag : VMStructs { Unknown = -1 }; - static void *find(const char *name); - static void *find(const char *name, std::initializer_list types); + static JVMFlag* find(const char *name); + static JVMFlag *find(const char *name, std::initializer_list types); const char *name() { return *(const char **)at(_flag_name_offset); } int type(); void *addr() { return *(void **)at(_flag_addr_offset); } + char origin() { + return _flag_origin_offset >= 0 ? (*(char*) at(_flag_origin_offset)) & 15 : 0; + } + + char get() { + return *addr(); + } + + void set(char value) { + *addr() = value; + } }; class HeapUsage : VMStructs { From aa4ee327b68fe3f4b92d8b9d615fea88eae38532 Mon Sep 17 00:00:00 2001 From: Jaroslav Bachorik Date: Fri, 11 Apr 2025 13:44:45 +0200 Subject: [PATCH 3/5] Do not set DebugNonSafepoints, if it is already set in the Command Line (follow-up merge) --- ddprof-lib/src/main/cpp/javaApi.cpp | 54 ++++++++++++++++++--------- ddprof-lib/src/main/cpp/vmStructs.cpp | 17 ++------- ddprof-lib/src/main/cpp/vmStructs.h | 4 +- 3 files changed, 41 insertions(+), 34 deletions(-) diff --git a/ddprof-lib/src/main/cpp/javaApi.cpp b/ddprof-lib/src/main/cpp/javaApi.cpp index 365d1dc45..34e6a3001 100644 --- a/ddprof-lib/src/main/cpp/javaApi.cpp +++ b/ddprof-lib/src/main/cpp/javaApi.cpp @@ -312,9 +312,12 @@ Java_com_datadoghq_profiler_JVMAccess_findStringJVMFlag0(JNIEnv *env, jobject unused, jstring flagName) { JniString flag_str(env, flagName); - char** value = static_cast(JVMFlag::find(flag_str.c_str(), {JVMFlag::Type::String})); - if (value != NULL && *value != NULL) { - return env->NewStringUTF(*value); + JVMFlag *f = JVMFlag::find(flag_str.c_str(), {JVMFlag::Type::String}); + if (f) { + char** value = static_cast(f->addr()); + if (value != NULL && *value != NULL) { + return env->NewStringUTF(*value); + } } return NULL; } @@ -326,9 +329,12 @@ Java_com_datadoghq_profiler_JVMAccess_setStringJVMFlag0(JNIEnv *env, jstring flagValue) { JniString flag_str(env, flagName); JniString value_str(env, flagValue); - char** value = static_cast(JVMFlag::find(flag_str.c_str(), {JVMFlag::Type::String})); - if (value != NULL) { - *value = strdup(value_str.c_str()); + JVMFlag *f = JVMFlag::find(flag_str.c_str(), {JVMFlag::Type::String}); + if (f) { + char** value = static_cast(f->addr()); + if (value != NULL) { + *value = strdup(value_str.c_str()); + } } } @@ -337,9 +343,12 @@ Java_com_datadoghq_profiler_JVMAccess_findBooleanJVMFlag0(JNIEnv *env, jobject unused, jstring flagName) { JniString flag_str(env, flagName); - char* value = static_cast(JVMFlag::find(flag_str.c_str(), {JVMFlag::Type::Bool})); - if (value != NULL) { - return ((*value) & 0xff) == 1; + JVMFlag *f = JVMFlag::find(flag_str.c_str(), {JVMFlag::Type::Bool}); + if (f) { + char* value = static_cast(f->addr()); + if (value != NULL) { + return ((*value) & 0xff) == 1; + } } return false; } @@ -350,9 +359,12 @@ Java_com_datadoghq_profiler_JVMAccess_setBooleanJVMFlag0(JNIEnv *env, jstring flagName, jboolean flagValue) { JniString flag_str(env, flagName); - char* value = static_cast(JVMFlag::find(flag_str.c_str(), {JVMFlag::Type::Bool})); - if (value != NULL) { - *value = flagValue ? 1 : 0; + JVMFlag *f = JVMFlag::find(flag_str.c_str(), {JVMFlag::Type::Bool}); + if (f) { + char* value = static_cast(f->addr()); + if (value != NULL) { + *value = flagValue ? 1 : 0; + } } } @@ -361,9 +373,12 @@ Java_com_datadoghq_profiler_JVMAccess_findIntJVMFlag0(JNIEnv *env, jobject unused, jstring flagName) { JniString flag_str(env, flagName); - long* value = static_cast(JVMFlag::find(flag_str.c_str(), {JVMFlag::Type::Int, JVMFlag::Type::Uint, JVMFlag::Type::Intx, JVMFlag::Type::Uintx, JVMFlag::Type::Uint64_t, JVMFlag::Type::Size_t})); - if (value != NULL) { - return *value; + JVMFlag *f = JVMFlag::find(flag_str.c_str(), {JVMFlag::Type::Int, JVMFlag::Type::Uint, JVMFlag::Type::Intx, JVMFlag::Type::Uintx, JVMFlag::Type::Uint64_t, JVMFlag::Type::Size_t}); + if (f) { + long* value = static_cast(f->addr()); + if (value != NULL) { + return *value; + } } return 0; } @@ -373,9 +388,12 @@ Java_com_datadoghq_profiler_JVMAccess_findFloatJVMFlag0(JNIEnv *env, jobject unused, jstring flagName) { JniString flag_str(env, flagName); - double* value = static_cast(JVMFlag::find(flag_str.c_str(),{ JVMFlag::Type::Double})); - if (value != NULL) { - return *value; + JVMFlag *f = JVMFlag::find(flag_str.c_str(),{ JVMFlag::Type::Double}); + if (f) { + double* value = static_cast(f->addr()); + if (value != NULL) { + return *value; + } } return 0.0; } diff --git a/ddprof-lib/src/main/cpp/vmStructs.cpp b/ddprof-lib/src/main/cpp/vmStructs.cpp index 0a8cfc91d..b238fb949 100644 --- a/ddprof-lib/src/main/cpp/vmStructs.cpp +++ b/ddprof-lib/src/main/cpp/vmStructs.cpp @@ -393,17 +393,6 @@ void VMStructs::resolveOffsets() { _klass = (jfieldID)(uintptr_t)(*_klass_offset_addr << 2 | 2); } - JVMFlag* ccp = JVMFlag::find("UseCompressedClassPointers"); - if (ccp != NULL && ccp->get() && _narrow_klass_base_addr != NULL && _narrow_klass_shift_addr != NULL) { - _narrow_klass_base = *_narrow_klass_base_addr; - _narrow_klass_shift = *_narrow_klass_shift_addr; - } - - JVMFlag* coh = JVMFlag::find("UseCompactObjectHeaders"); - if (coh != NULL && coh->get()) { - _compact_object_headers = true; - } - _has_class_names = _klass_name_offset >= 0 && (_symbol_length_offset >= 0 || _symbol_length_and_refcount_offset >= 0) && @@ -854,7 +843,7 @@ JVMFlag *JVMFlag::find(const char *name) { return NULL; } -void *JVMFlag::find(const char *name, std::initializer_list types) { +JVMFlag *JVMFlag::find(const char *name, std::initializer_list types) { int mask = 0; for (int type : types) { mask |= 0x1 << type; @@ -896,14 +885,14 @@ int JVMFlag::type() { } } -void *JVMFlag::find(const char *name, int type_mask) { +JVMFlag *JVMFlag::find(const char *name, int type_mask) { if (_flags_addr != NULL && _flag_size > 0) { for (int i = 0; i < _flag_count; i++) { JVMFlag *f = (JVMFlag *)(_flags_addr + i * _flag_size); if (f->name() != NULL && strcmp(f->name(), name) == 0) { int masked = 0x1 << f->type(); if (masked & type_mask) { - return f->addr(); + return (JVMFlag*)f; } } } diff --git a/ddprof-lib/src/main/cpp/vmStructs.h b/ddprof-lib/src/main/cpp/vmStructs.h index 54c2d1650..0a32e83cf 100644 --- a/ddprof-lib/src/main/cpp/vmStructs.h +++ b/ddprof-lib/src/main/cpp/vmStructs.h @@ -575,11 +575,11 @@ class JVMFlag : VMStructs { } char get() { - return *addr(); + return *((char*)addr()); } void set(char value) { - *addr() = value; + *((char*)addr()) = value; } }; From 0d99dd0d2d20f9272e843cda5658525648f422dd Mon Sep 17 00:00:00 2001 From: Andrei Pangin Date: Tue, 31 Dec 2024 19:15:51 +0000 Subject: [PATCH 4/5] Better allocation stack traces on non-HotSpot JVMs (cherry picked from commit 87c5436) --- ddprof-lib/src/main/cpp/flightRecorder.cpp | 3 +- ddprof-lib/src/main/cpp/vmEntry.cpp | 39 ++++++++++++++++++++-- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/ddprof-lib/src/main/cpp/flightRecorder.cpp b/ddprof-lib/src/main/cpp/flightRecorder.cpp index a4cdcebc7..0b1e49348 100644 --- a/ddprof-lib/src/main/cpp/flightRecorder.cpp +++ b/ddprof-lib/src/main/cpp/flightRecorder.cpp @@ -1,7 +1,6 @@ /* * Copyright The async-profiler authors * SPDX-License-Identifier: Apache-2.0 - * Copyright 2021, 2025 Datadog, Inc */ #include @@ -754,7 +753,7 @@ void Recording::writeSettings(Buffer *buf, Arguments &args) { Profiler::instance()->cpuEngine()->name()); writeStringSetting(buf, T_ACTIVE_RECORDING, "wallEngine", Profiler::instance()->wallEngine()->name()); - writeStringSetting(buf, T_ACTIVE_RECORDING, "cstack", + writeStringSetting(buf, T_ACTIVE_RECORDING, "cstack", Profiler::instance()->cstack()); flushIfNeeded(buf); } diff --git a/ddprof-lib/src/main/cpp/vmEntry.cpp b/ddprof-lib/src/main/cpp/vmEntry.cpp index 1b07c1435..54f422192 100644 --- a/ddprof-lib/src/main/cpp/vmEntry.cpp +++ b/ddprof-lib/src/main/cpp/vmEntry.cpp @@ -51,6 +51,19 @@ JVM_GetManagement VM::_getManagement; static void wakeupHandler(int signo) { // Dummy handler for interrupting syscalls + +static bool isVmRuntimeEntry(const char* blob_name) { + return strcmp(blob_name, "_ZNK12MemAllocator8allocateEv") == 0 + || strncmp(blob_name, "_Z22post_allocation_notify", 26) == 0 + || strncmp(blob_name, "_ZN11OptoRuntime", 16) == 0 + || strncmp(blob_name, "_ZN8Runtime1", 12) == 0 + || strncmp(blob_name, "_ZN13SharedRuntime", 18) == 0 + || strncmp(blob_name, "_ZN18InterpreterRuntime", 23) == 0; +} + +static bool isZingRuntimeEntry(const char* blob_name) { + return strncmp(blob_name, "_ZN14DolphinRuntime", 19) == 0 + || strncmp(blob_name, "_ZN37JvmtiSampledObjectAllocEventCollector", 42) == 0; } static bool isZeroInterpreterMethod(const char *blob_name) { @@ -75,8 +88,28 @@ static bool isOpenJ9JitStub(const char *blob_name) { return false; } -static void *resolveMethodId(void **mid) { - return mid == NULL || *mid < (void *)4096 ? NULL : *mid; +static bool isOpenJ9Resolve(const char* blob_name) { + return strncmp(blob_name, "resolve", 7) == 0; +} + +static bool isOpenJ9JitAlloc(const char* blob_name) { + return strncmp(blob_name, "old_", 4) == 0; +} + +static bool isOpenJ9GcAlloc(const char* blob_name) { + return strncmp(blob_name, "J9Allocate", 10) == 0; +} + +static bool isOpenJ9JvmtiAlloc(const char* blob_name) { + return strcmp(blob_name, "jvmtiHookSampledObjectAlloc") == 0; +} + +static bool isCompilerEntry(const char* blob_name) { + return strncmp(blob_name, "_ZN13CompileBroker25invoke_compiler_on_method", 45) == 0; +} + +static void* resolveMethodId(void** mid) { + return mid == NULL || *mid < (void*)4096 ? NULL : *mid; } static void resolveMethodIdEnd() {} @@ -372,7 +405,7 @@ bool VM::initProfilerBridge(JavaVM *vm, bool attach) { // DebugNonSafepoints is automatically enabled with CompiledMethodLoad, // otherwise we set the flag manually JVMFlag* f = JVMFlag::find("DebugNonSafepoints", {JVMFlag::Type::Bool}); - if (f != NULL && f->origin() == 0) { + if (f != NULL && f->isDefault()) { f->set(1); } } From e343b13f7d8480cbd793b6086a9e62984499c100 Mon Sep 17 00:00:00 2001 From: Jaroslav Bachorik Date: Fri, 11 Apr 2025 17:13:13 +0200 Subject: [PATCH 5/5] Adjust for missing functionality for downports --- ddprof-lib/src/main/cpp/codeCache.cpp | 9 ------- ddprof-lib/src/main/cpp/codeCache.h | 30 +++++++++++++++++++++-- ddprof-lib/src/main/cpp/vmEntry.cpp | 35 +++++++++++++++++++++------ ddprof-lib/src/main/cpp/vmStructs.h | 17 +++++++++++++ 4 files changed, 72 insertions(+), 19 deletions(-) diff --git a/ddprof-lib/src/main/cpp/codeCache.cpp b/ddprof-lib/src/main/cpp/codeCache.cpp index 985bc5c21..408d92f2a 100644 --- a/ddprof-lib/src/main/cpp/codeCache.cpp +++ b/ddprof-lib/src/main/cpp/codeCache.cpp @@ -162,15 +162,6 @@ void CodeCache::sort() { _max_address = _blobs[_count - 1]._end; } -void CodeCache::mark(NamePredicate predicate) { - for (int i = 0; i < _count; i++) { - const char *blob_name = _blobs[i]._name; - if (blob_name != NULL && predicate(blob_name)) { - NativeFunc::mark(blob_name); - } - } -} - CodeBlob *CodeCache::findBlob(const char *name) { for (int i = 0; i < _count; i++) { const char *blob_name = _blobs[i]._name; diff --git a/ddprof-lib/src/main/cpp/codeCache.h b/ddprof-lib/src/main/cpp/codeCache.h index 677ff23d4..2e595f767 100644 --- a/ddprof-lib/src/main/cpp/codeCache.h +++ b/ddprof-lib/src/main/cpp/codeCache.h @@ -38,6 +38,13 @@ enum ImportType { NUM_IMPORT_TYPES }; +enum Mark { + MARK_VM_RUNTIME = 1, + MARK_INTERPRETER = 2, + MARK_COMPILER_ENTRY = 3, + MARK_ASYNC_PROFILER = 4, // async-profiler internals such as native hooks. +}; + class NativeFunc { private: short _lib_index; @@ -63,7 +70,13 @@ class NativeFunc { static bool isMarked(const char *name) { return from(name)->_mark != 0; } - static void mark(const char *name) { from(name)->_mark = 1; } + static char mark(const char* name) { + return from(name)->_mark; + } + + static void mark(const char* name, char value) { + from(name)->_mark = value; + } }; class CodeBlob { @@ -154,7 +167,20 @@ class CodeCache { bool update_bounds = false); void updateBounds(const void *start, const void *end); void sort(); - void mark(NamePredicate predicate); + template + inline void mark(NamePredicate predicate, char value) { + for (int i = 0; i < _count; i++) { + const char* blob_name = _blobs[i]._name; + if (blob_name != NULL && predicate(blob_name)) { + NativeFunc::mark(blob_name, value); + } + } + + if (value == MARK_VM_RUNTIME && _name != NULL) { + // In case a library has no debug symbols + NativeFunc::mark(_name, value); + } + } void addImport(void **entry, const char *name); void **findImport(ImportId id); diff --git a/ddprof-lib/src/main/cpp/vmEntry.cpp b/ddprof-lib/src/main/cpp/vmEntry.cpp index 54f422192..b2e7eae65 100644 --- a/ddprof-lib/src/main/cpp/vmEntry.cpp +++ b/ddprof-lib/src/main/cpp/vmEntry.cpp @@ -51,6 +51,7 @@ JVM_GetManagement VM::_getManagement; static void wakeupHandler(int signo) { // Dummy handler for interrupting syscalls +} static bool isVmRuntimeEntry(const char* blob_name) { return strcmp(blob_name, "_ZNK12MemAllocator8allocateEv") == 0 @@ -302,14 +303,32 @@ bool VM::initShared(JavaVM* vm) { } VMStructs::init(lib); - if (is_zero_vm) { - lib->mark(isZeroInterpreterMethod); - } else if (isOpenJ9()) { - lib->mark(isOpenJ9InterpreterMethod); - CodeCache *libjit = libraries->findJvmLibrary("libj9jit"); - if (libjit != NULL) { - libjit->mark(isOpenJ9JitStub); - } + if (isOpenJ9()) { + Libraries* libraries = Libraries::instance(); + lib->mark(isOpenJ9InterpreterMethod, MARK_INTERPRETER); + lib->mark(isOpenJ9Resolve, MARK_VM_RUNTIME); + CodeCache* libjit = libraries->findJvmLibrary("libj9jit"); + if (libjit != NULL) { + libjit->mark(isOpenJ9JitStub, MARK_INTERPRETER); + libjit->mark(isOpenJ9JitAlloc, MARK_VM_RUNTIME); + } + CodeCache* libgc = libraries->findJvmLibrary("libj9gc"); + if (libgc != NULL) { + libgc->mark(isOpenJ9GcAlloc, MARK_VM_RUNTIME); + } + CodeCache* libjvmti = libraries->findJvmLibrary("libj9jvmti"); + if (libjvmti != NULL) { + libjvmti->mark(isOpenJ9JvmtiAlloc, MARK_VM_RUNTIME); + } + } else { + lib->mark(isVmRuntimeEntry, MARK_VM_RUNTIME); + if (isZing()) { + lib->mark(isZingRuntimeEntry, MARK_VM_RUNTIME); + } else if (is_zero_vm) { + lib->mark(isZeroInterpreterMethod, MARK_INTERPRETER); + } else { + lib->mark(isCompilerEntry, MARK_COMPILER_ENTRY); + } } return true; } diff --git a/ddprof-lib/src/main/cpp/vmStructs.h b/ddprof-lib/src/main/cpp/vmStructs.h index 0a32e83cf..9cd3fd2bc 100644 --- a/ddprof-lib/src/main/cpp/vmStructs.h +++ b/ddprof-lib/src/main/cpp/vmStructs.h @@ -547,6 +547,12 @@ class CodeHeap : VMStructs { class JVMFlag : VMStructs { private: + enum { + ORIGIN_DEFAULT = 0, + ORIGIN_MASK = 15, + SET_ON_CMDLINE = 1 << 17 + }; + static JVMFlag* find(const char *name, int type_mask); public: enum Type { @@ -570,10 +576,21 @@ class JVMFlag : VMStructs { int type(); void *addr() { return *(void **)at(_flag_addr_offset); } + char origin() { return _flag_origin_offset >= 0 ? (*(char*) at(_flag_origin_offset)) & 15 : 0; } + bool isDefault() { + return _flag_origin_offset < 0 || (*(int*) at(_flag_origin_offset) & ORIGIN_MASK) == ORIGIN_DEFAULT; + } + + void setCmdline() { + if (_flag_origin_offset >= 0) { + *(int*) at(_flag_origin_offset) |= SET_ON_CMDLINE; + } + } + char get() { return *((char*)addr()); }