diff --git a/ddprof-lib/src/main/cpp/itimer.cpp b/ddprof-lib/src/main/cpp/itimer.cpp index 6cd2c75d6..574a4fe03 100644 --- a/ddprof-lib/src/main/cpp/itimer.cpp +++ b/ddprof-lib/src/main/cpp/itimer.cpp @@ -20,7 +20,6 @@ #include "os.h" #include "profiler.h" #include "stackWalker.h" -#include "context.h" #include "thread.h" #include "vmStructs.h" @@ -30,7 +29,10 @@ CStack ITimer::_cstack; void ITimer::signalHandler(int signo, siginfo_t* siginfo, void* ucontext) { if (!_enabled) return; - + AsyncSampleMutex mutex; + if (!mutex.acquired()) { + return; + } int tid = 0; ProfiledThread* current = ProfiledThread::current(); if (current != NULL) { diff --git a/ddprof-lib/src/main/cpp/perfEvents_linux.cpp b/ddprof-lib/src/main/cpp/perfEvents_linux.cpp index 9e35420f6..8292b869a 100644 --- a/ddprof-lib/src/main/cpp/perfEvents_linux.cpp +++ b/ddprof-lib/src/main/cpp/perfEvents_linux.cpp @@ -702,6 +702,10 @@ void PerfEvents::signalHandler(int signo, siginfo_t* siginfo, void* ucontext) { // Looks like an external signal; don't treat as a profiling event return; } + AsyncSampleMutex mutex; + if (!mutex.acquired()) { + return; + } ProfiledThread* current = ProfiledThread::current(); if (current != NULL) { diff --git a/ddprof-lib/src/main/cpp/profiler.cpp b/ddprof-lib/src/main/cpp/profiler.cpp index 39202f21c..5637c3f23 100644 --- a/ddprof-lib/src/main/cpp/profiler.cpp +++ b/ddprof-lib/src/main/cpp/profiler.cpp @@ -341,6 +341,58 @@ int Profiler::convertNativeTrace(int native_frames, const void** callchain, ASGC return depth; } +NOINLINE void Profiler::getJavaTraceAsyncRetryPopStub(void* ucontext, ASGCT_CallTrace* trace, int max_depth, CodeBlob* stub, StackFrame& frame) { + if (!(_safe_mode & POP_STUB) && frame.popStub((instruction_t*)stub->_start, stub->_name) + && isAddressInCode(frame.pc() -= ADJUST_RET)) { + VM::_asyncGetCallTrace(trace, max_depth, ucontext); + } +} + +NOINLINE void Profiler::getJavaTraceAsyncRetryPopMethod(void* ucontext, ASGCT_CallTrace* trace, int max_depth, CodeBlob* stub, StackFrame& frame, NMethod* nmethod) { + if (!(_safe_mode & POP_METHOD) && frame.popMethod((instruction_t*)nmethod->entry()) + && isAddressInCode(frame.pc() -= ADJUST_RET)) { + VM::_asyncGetCallTrace(trace, max_depth, ucontext); + } +} + +NOINLINE void Profiler::getJavaTraceAsyncRetryMakeFrameWalkable(void* ucontext, ASGCT_CallTrace* trace, int max_depth, VMThread* vm_thread) { + uintptr_t& sp = vm_thread->lastJavaSP(); + uintptr_t& pc = vm_thread->lastJavaPC(); + if (sp != 0 && pc == 0) { + // We have the last Java frame anchor, but it is not marked as walkable. + // Make it walkable here + pc = ((uintptr_t*)sp)[-1]; + + NMethod* m = CodeHeap::findNMethod((const void*)pc); + if (m != NULL) { + // AGCT fails if the last Java frame is a Runtime Stub with an invalid _frame_complete_offset. + // In this case we patch _frame_complete_offset manually + if (!m->isNMethod() && m->frameSize() > 0 && m->frameCompleteOffset() == -1) { + m->setFrameCompleteOffset(0); + } + VM::_asyncGetCallTrace(trace, max_depth, ucontext); + } else if (findLibraryByAddress((const void*)pc) != NULL) { + VM::_asyncGetCallTrace(trace, max_depth, ucontext); + } + + pc = 0; + } +} + +NOINLINE void Profiler::getJavaTraceAsyncRetryInvalidRuntimeStubFrameCompleteOffset(void* ucontext, ASGCT_CallTrace* trace, int max_depth, VMThread* vm_thread) { + uintptr_t& sp = vm_thread->lastJavaSP(); + uintptr_t& pc = vm_thread->lastJavaPC(); + if (sp != 0 && pc != 0) { + // Similar to the above: last Java frame is set, + // but points to a Runtime Stub with an invalid _frame_complete_offset + NMethod* m = CodeHeap::findNMethod((const void*)pc); + if (m != NULL && !m->isNMethod() && m->frameSize() > 0 && m->frameCompleteOffset() == -1) { + m->setFrameCompleteOffset(0); + VM::_asyncGetCallTrace(trace, max_depth, ucontext); + } + } +} + int Profiler::getJavaTraceAsync(void* ucontext, ASGCT_CallFrame* frames, int max_depth, StackContext* java_ctx, bool *truncated) { // Workaround for JDK-8132510: it's not safe to call GetEnv() inside a signal handler // since JDK 9, so we do it only for threads already registered in ThreadLocalStorage @@ -436,10 +488,7 @@ int Profiler::getJavaTraceAsync(void* ucontext, ASGCT_CallFrame* frames, int max if (_cstack != CSTACK_NO) { max_depth -= makeFrame(trace.frames++, BCI_NATIVE_FRAME, stub->_name); } - if (!(_safe_mode & POP_STUB) && frame.popStub((instruction_t*)stub->_start, stub->_name) - && isAddressInCode(frame.pc() -= ADJUST_RET)) { - VM::_asyncGetCallTrace(&trace, max_depth, ucontext); - } + getJavaTraceAsyncRetryPopStub(ucontext, &trace, max_depth, stub, frame); } else if (VMStructs::hasMethodStructs()) { NMethod* nmethod = CodeHeap::findNMethod((const void*)frame.pc()); if (nmethod != NULL && nmethod->isNMethod() && nmethod->isAlive()) { @@ -449,55 +498,19 @@ int Profiler::getJavaTraceAsync(void* ucontext, ASGCT_CallFrame* frames, int max if (method_id != NULL) { max_depth -= makeFrame(trace.frames++, 0, method_id); } - if (!(_safe_mode & POP_METHOD) && frame.popMethod((instruction_t*)nmethod->entry()) - && isAddressInCode(frame.pc() -= ADJUST_RET)) { - VM::_asyncGetCallTrace(&trace, max_depth, ucontext); - } + getJavaTraceAsyncRetryPopMethod(ucontext, &trace, max_depth, stub, frame, nmethod); } } else if (nmethod != NULL) { if (_cstack != CSTACK_NO) { max_depth -= makeFrame(trace.frames++, BCI_NATIVE_FRAME, nmethod->name()); } - if (!(_safe_mode & POP_STUB) && frame.popStub(NULL, nmethod->name()) - && isAddressInCode(frame.pc() -= ADJUST_RET)) { - VM::_asyncGetCallTrace(&trace, max_depth, ucontext); - } + getJavaTraceAsyncRetryPopStub(ucontext, &trace, max_depth, stub, frame); } } } else if (trace.num_frames == ticks_unknown_not_Java && !(_safe_mode & LAST_JAVA_PC)) { - uintptr_t& sp = vm_thread->lastJavaSP(); - uintptr_t& pc = vm_thread->lastJavaPC(); - if (sp != 0 && pc == 0) { - // We have the last Java frame anchor, but it is not marked as walkable. - // Make it walkable here - pc = ((uintptr_t*)sp)[-1]; - - NMethod* m = CodeHeap::findNMethod((const void*)pc); - if (m != NULL) { - // AGCT fails if the last Java frame is a Runtime Stub with an invalid _frame_complete_offset. - // In this case we patch _frame_complete_offset manually - if (!m->isNMethod() && m->frameSize() > 0 && m->frameCompleteOffset() == -1) { - m->setFrameCompleteOffset(0); - } - VM::_asyncGetCallTrace(&trace, max_depth, ucontext); - } else if (findLibraryByAddress((const void*)pc) != NULL) { - VM::_asyncGetCallTrace(&trace, max_depth, ucontext); - } - - pc = 0; - } + getJavaTraceAsyncRetryMakeFrameWalkable(ucontext, &trace, max_depth, vm_thread); } else if (trace.num_frames == ticks_not_walkable_not_Java && !(_safe_mode & LAST_JAVA_PC)) { - uintptr_t& sp = vm_thread->lastJavaSP(); - uintptr_t& pc = vm_thread->lastJavaPC(); - if (sp != 0 && pc != 0) { - // Similar to the above: last Java frame is set, - // but points to a Runtime Stub with an invalid _frame_complete_offset - NMethod* m = CodeHeap::findNMethod((const void*)pc); - if (m != NULL && !m->isNMethod() && m->frameSize() > 0 && m->frameCompleteOffset() == -1) { - m->setFrameCompleteOffset(0); - VM::_asyncGetCallTrace(&trace, max_depth, ucontext); - } - } + getJavaTraceAsyncRetryInvalidRuntimeStubFrameCompleteOffset(ucontext, &trace, max_depth, vm_thread); } else if (trace.num_frames == ticks_GC_active && !(_safe_mode & GC_TRACES)) { if (vm_thread->lastJavaSP() == 0) { // Do not add 'GC_active' for threads with no Java frames, e.g. Compiler threads diff --git a/ddprof-lib/src/main/cpp/profiler.h b/ddprof-lib/src/main/cpp/profiler.h index 6be15aa7b..5b4f39ab1 100644 --- a/ddprof-lib/src/main/cpp/profiler.h +++ b/ddprof-lib/src/main/cpp/profiler.h @@ -36,6 +36,8 @@ #include "vmEntry.h" #include "objectSampler.h" #include "thread.h" +#include "vmStructs.h" +#include "stackFrame.h" // avoid linking against newer symbols here for wide compatibility #ifdef __GLIBC__ @@ -45,6 +47,12 @@ #endif #endif +#ifdef __clang__ +# define NOINLINE __attribute__((noinline)) +#else +# define NOINLINE __attribute__((noinline,noclone)) +#endif + const int MAX_NATIVE_FRAMES = 128; const int RESERVED_FRAMES = 4; @@ -58,6 +66,32 @@ struct CallTraceBuffer { ASGCT_CallFrame _asgct_frames[1]; }; +// controls access to AGCT +class AsyncSampleMutex { +private: + bool _acquired; + bool try_set(bool flag) { + ProfiledThread* current = ProfiledThread::current(); + if (current != NULL) { + bool was_set = current->is_unwinding_Java(); + current->set_unwinding_Java(flag); + return !was_set; + } + return false; + } +public: + AsyncSampleMutex() { + _acquired = try_set(true); + } + AsyncSampleMutex(AsyncSampleMutex& other) = delete; + ~AsyncSampleMutex() { + try_set(false); + } + bool acquired() { + return _acquired; + } +}; + class FrameName; class NMethod; @@ -157,6 +191,10 @@ class Profiler { Engine* selectWallEngine(Arguments& args); Engine* selectAllocEngine(Arguments& args); Error checkJvmCapabilities(); + NOINLINE void getJavaTraceAsyncRetryPopStub(void* ucontext, ASGCT_CallTrace* trace, int max_depth, CodeBlob* stub, StackFrame& frame); + NOINLINE void getJavaTraceAsyncRetryPopMethod(void* ucontext, ASGCT_CallTrace* trace, int max_depth, CodeBlob* stub, StackFrame& frame, NMethod* nmethod); + NOINLINE void getJavaTraceAsyncRetryMakeFrameWalkable(void* ucontext, ASGCT_CallTrace* trace, int max_depth, VMThread* vm_thread); + NOINLINE void getJavaTraceAsyncRetryInvalidRuntimeStubFrameCompleteOffset(void* ucontext, ASGCT_CallTrace* trace, int max_depth, VMThread* vm_thread); void lockAll(); void unlockAll(); diff --git a/ddprof-lib/src/main/cpp/thread.h b/ddprof-lib/src/main/cpp/thread.h index e2aa23e03..f2b5b714f 100644 --- a/ddprof-lib/src/main/cpp/thread.h +++ b/ddprof-lib/src/main/cpp/thread.h @@ -30,6 +30,7 @@ class ProfiledThread { u64 _wall_epoch; u64 _skipped_samples; u64 _context_key; + bool _unwinding_java; ProfiledThread(int buffer_pos, int tid) : _buffer_pos(buffer_pos), @@ -37,7 +38,8 @@ class ProfiledThread { _cpu_epoch(0), _wall_epoch(0), _skipped_samples(0), - _context_key(0){}; + _context_key(0), + _unwinding_java(false){}; void releaseFromBuffer(); public: @@ -55,6 +57,12 @@ class ProfiledThread { static ProfiledThread* current(); static int currentTid(); + bool is_unwinding_Java() { + return _unwinding_java; + } + void set_unwinding_Java(bool flag) { + _unwinding_java = flag; + } inline int tid() { return _tid; diff --git a/ddprof-lib/src/main/cpp/wallClock.cpp b/ddprof-lib/src/main/cpp/wallClock.cpp index c72c766b1..a75064d69 100644 --- a/ddprof-lib/src/main/cpp/wallClock.cpp +++ b/ddprof-lib/src/main/cpp/wallClock.cpp @@ -57,6 +57,10 @@ void WallClock::sharedSignalHandler(int signo, siginfo_t* siginfo, void* ucontex } void WallClock::signalHandler(int signo, siginfo_t* siginfo, void* ucontext, u64 last_sample) { + AsyncSampleMutex mutex; + if (!mutex.acquired()) { + return; + } ProfiledThread* current = ProfiledThread::current(); int tid = current != NULL ? current->tid() : OS::threadId(); Shims::instance().setSighandlerTid(tid);