diff --git a/ddprof-lib/src/main/cpp/stackWalker.cpp b/ddprof-lib/src/main/cpp/stackWalker.cpp index ab318da2..ea4a7142 100644 --- a/ddprof-lib/src/main/cpp/stackWalker.cpp +++ b/ddprof-lib/src/main/cpp/stackWalker.cpp @@ -11,6 +11,7 @@ #include "stackFrame.h" #include "symbols.h" #include "vmStructs.h" +#include "vmStructs_dd.h" const uintptr_t SAME_STACK_DISTANCE = 8192; @@ -370,6 +371,8 @@ __attribute__((no_sanitize("address"))) int StackWalker::walkVM(void* ucontext, if (is_plausible_interpreter_frame) { VMMethod* method = ((VMMethod**)fp)[InterpreterFrame::method_offset]; +// assert(ddprof::BootstrapClassLoader::loaded_by(method)); + jmethodID method_id = getMethodId(method); if (method_id != NULL) { const char* bytecode_start = method->bytecode(); @@ -386,6 +389,7 @@ __attribute__((no_sanitize("address"))) int StackWalker::walkVM(void* ucontext, if (depth == 0) { VMMethod* method = (VMMethod*)frame.method(); + // assert(ddprof::BootstrapClassLoader::loaded_by(method)); jmethodID method_id = getMethodId(method); if (method_id != NULL) { fillFrame(frames[depth++], FRAME_INTERPRETED, 0, method_id); diff --git a/ddprof-lib/src/main/cpp/vmStructs.cpp b/ddprof-lib/src/main/cpp/vmStructs.cpp index e7f3b66d..fa2d3834 100644 --- a/ddprof-lib/src/main/cpp/vmStructs.cpp +++ b/ddprof-lib/src/main/cpp/vmStructs.cpp @@ -686,6 +686,16 @@ jmethodID VMMethod::id() { return NULL; } +VMKlass* VMMethod::method_holder() { + // We may find a bogus NMethod during stack walking, it does not always point to a valid VMMethod + const char* const_method = (const char*) SafeAccess::load((void**) at(_method_constmethod_offset)); + assert(goodPtr(const_method)); + const char* cpool = (const char*) SafeAccess::load((void**)(const_method + _constmethod_constants_offset)); + assert(goodPtr(cpool)); + return *(VMKlass**)(cpool + _pool_holder_offset); +} + + jmethodID VMMethod::validatedId() { jmethodID method_id = id(); if (!_can_dereference_jmethod_id || (goodPtr(method_id) && *(VMMethod**)method_id == this)) { diff --git a/ddprof-lib/src/main/cpp/vmStructs.h b/ddprof-lib/src/main/cpp/vmStructs.h index 1afd9e52..c07f83c8 100644 --- a/ddprof-lib/src/main/cpp/vmStructs.h +++ b/ddprof-lib/src/main/cpp/vmStructs.h @@ -434,6 +434,8 @@ class VMMethod : VMStructs { NMethod* code() { return *(NMethod**) at(_method_code_offset); } + + VMKlass* method_holder(); }; class NMethod : VMStructs { diff --git a/ddprof-lib/src/main/cpp/vmStructs_dd.cpp b/ddprof-lib/src/main/cpp/vmStructs_dd.cpp index c065e211..63768c6a 100644 --- a/ddprof-lib/src/main/cpp/vmStructs_dd.cpp +++ b/ddprof-lib/src/main/cpp/vmStructs_dd.cpp @@ -36,6 +36,7 @@ namespace ddprof { VMStructs_::MemoryUsageFunc VMStructs_::_memory_usage_func = NULL; VMStructs_::GCHeapSummaryFunc VMStructs_::_gc_heap_summary_func = NULL; VMStructs_::IsValidMethodFunc VMStructs_::_is_valid_method_func = NULL; + VMKlass** BootstrapClassLoader::_object_klass_addr = nullptr; // Run at agent load time @@ -66,6 +67,13 @@ namespace ddprof { break; } + if (strcmp(type, "vmClasses") == 0) { + // java/lang/Object must be loaded by bootstrap class loader, we use it to locate + // its CLD + if (strcmp(field, "_klasses[static_cast(vmClassID::Object_klass_knum)]") == 0) { + BootstrapClassLoader::set_object_klass_location(*(::VMKlass***)(entry + address_offset)); + } + } if (strcmp(type, "OSThread") == 0) { if (strcmp(field, "_state") == 0) { TEST_LOG("Setting _osthread_state_offset value"); @@ -395,4 +403,4 @@ namespace ddprof { jvmti->Deallocate((unsigned char *)method_sig); jvmti->Deallocate((unsigned char *)method_name); } -} \ No newline at end of file +} diff --git a/ddprof-lib/src/main/cpp/vmStructs_dd.h b/ddprof-lib/src/main/cpp/vmStructs_dd.h index dbec8404..37025368 100644 --- a/ddprof-lib/src/main/cpp/vmStructs_dd.h +++ b/ddprof-lib/src/main/cpp/vmStructs_dd.h @@ -118,7 +118,6 @@ namespace ddprof { // Copied from JDK's globalDefinitions.hpp 'JavaThreadState' enum enum JVMJavaThreadState { _thread_uninitialized = 0, // should never happen (missing initialization) - _thread_new = 2, // just starting up, i.e., in process of being initialized _thread_new_trans = 3, // corresponding transition state (not used, included for completeness) _thread_in_native = 4, // running in native code _thread_in_native_trans = 5, // corresponding transition state @@ -189,6 +188,33 @@ namespace ddprof { } }; + // Bootstrape class loader is immortal. Therefore, classes/methods + // that are loaded by it, are safe to walk. + class BootstrapClassLoader : public ::VMStructs { + private: + // java.lang.Object must be loaded by bootstrap class loader. + // _object_klass points to java/lang/Object instanceKlass stored in vmClasses, + // and we use it to figure out if classes/methods are loaded by bootstrap classloader + static ::VMKlass** _object_klass_addr; + + public: + static void set_object_klass_location(::VMKlass** addr) { + _object_klass_addr = addr; + } + + // If a Method is loaded by BootstrapClassLoader + static bool loaded_by(::VMMethod* method) { + return loaded_by(method->method_holder()); + } + + static bool loaded_by(::VMKlass* klass) { + VMKlass* object_klass = *_object_klass_addr; + assert(object_klass != nullptr); + assert(klass != nullptr); + return object_klass->classLoaderData() == klass->classLoaderData(); + } + }; + class JVMFlag : public VMStructs { private: enum {