mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-21 03:24:38 +02:00
8022335: Native stack walk while generating hs_err does not work on Windows x64
Use WinDbg API StackWalk64() Reviewed-by: zgu, dholmes
This commit is contained in:
parent
34d47bd672
commit
1c2a7eea85
11 changed files with 275 additions and 7 deletions
|
@ -32,7 +32,11 @@ WindowsDecoder::WindowsDecoder() {
|
|||
_can_decode_in_vm = false;
|
||||
_pfnSymGetSymFromAddr64 = NULL;
|
||||
_pfnUndecorateSymbolName = NULL;
|
||||
|
||||
#ifdef AMD64
|
||||
_pfnStackWalk64 = NULL;
|
||||
_pfnSymFunctionTableAccess64 = NULL;
|
||||
_pfnSymGetModuleBase64 = NULL;
|
||||
#endif
|
||||
_decoder_status = no_error;
|
||||
initialize();
|
||||
}
|
||||
|
@ -53,14 +57,24 @@ void WindowsDecoder::initialize() {
|
|||
_pfnUndecorateSymbolName = (pfn_UndecorateSymbolName)::GetProcAddress(handle, "UnDecorateSymbolName");
|
||||
|
||||
if (_pfnSymSetOptions == NULL || _pfnSymInitialize == NULL || _pfnSymGetSymFromAddr64 == NULL) {
|
||||
_pfnSymGetSymFromAddr64 = NULL;
|
||||
_pfnUndecorateSymbolName = NULL;
|
||||
::FreeLibrary(handle);
|
||||
_dbghelp_handle = NULL;
|
||||
uninitialize();
|
||||
_decoder_status = helper_func_error;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef AMD64
|
||||
_pfnStackWalk64 = (pfn_StackWalk64)::GetProcAddress(handle, "StackWalk64");
|
||||
_pfnSymFunctionTableAccess64 = (pfn_SymFunctionTableAccess64)::GetProcAddress(handle, "SymFunctionTableAccess64");
|
||||
_pfnSymGetModuleBase64 = (pfn_SymGetModuleBase64)::GetProcAddress(handle, "SymGetModuleBase64");
|
||||
if (_pfnStackWalk64 == NULL || _pfnSymFunctionTableAccess64 == NULL || _pfnSymGetModuleBase64 == NULL) {
|
||||
// We can't call StackWalk64 to walk the stack, but we are still
|
||||
// able to decode the symbols. Let's limp on.
|
||||
_pfnStackWalk64 = NULL;
|
||||
_pfnSymFunctionTableAccess64 = NULL;
|
||||
_pfnSymGetModuleBase64 = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
HANDLE hProcess = ::GetCurrentProcess();
|
||||
_pfnSymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_EXACT_SYMBOLS);
|
||||
if (!_pfnSymInitialize(hProcess, NULL, TRUE)) {
|
||||
|
@ -156,6 +170,11 @@ void WindowsDecoder::initialize() {
|
|||
void WindowsDecoder::uninitialize() {
|
||||
_pfnSymGetSymFromAddr64 = NULL;
|
||||
_pfnUndecorateSymbolName = NULL;
|
||||
#ifdef AMD64
|
||||
_pfnStackWalk64 = NULL;
|
||||
_pfnSymFunctionTableAccess64 = NULL;
|
||||
_pfnSymGetModuleBase64 = NULL;
|
||||
#endif
|
||||
if (_dbghelp_handle != NULL) {
|
||||
::FreeLibrary(_dbghelp_handle);
|
||||
}
|
||||
|
@ -195,3 +214,65 @@ bool WindowsDecoder::demangle(const char* symbol, char *buf, int buflen) {
|
|||
_pfnUndecorateSymbolName(symbol, buf, buflen, UNDNAME_COMPLETE);
|
||||
}
|
||||
|
||||
#ifdef AMD64
|
||||
BOOL WindowsDbgHelp::StackWalk64(DWORD MachineType,
|
||||
HANDLE hProcess,
|
||||
HANDLE hThread,
|
||||
LPSTACKFRAME64 StackFrame,
|
||||
PVOID ContextRecord,
|
||||
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
|
||||
PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
|
||||
PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
|
||||
PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress) {
|
||||
DecoderLocker locker;
|
||||
WindowsDecoder* wd = (WindowsDecoder*)locker.decoder();
|
||||
|
||||
if (!wd->has_error() && wd->_pfnStackWalk64) {
|
||||
return wd->_pfnStackWalk64(MachineType,
|
||||
hProcess,
|
||||
hThread,
|
||||
StackFrame,
|
||||
ContextRecord,
|
||||
ReadMemoryRoutine,
|
||||
FunctionTableAccessRoutine,
|
||||
GetModuleBaseRoutine,
|
||||
TranslateAddress);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
PVOID WindowsDbgHelp::SymFunctionTableAccess64(HANDLE hProcess, DWORD64 AddrBase) {
|
||||
DecoderLocker locker;
|
||||
WindowsDecoder* wd = (WindowsDecoder*)locker.decoder();
|
||||
|
||||
if (!wd->has_error() && wd->_pfnSymFunctionTableAccess64) {
|
||||
return wd->_pfnSymFunctionTableAccess64(hProcess, AddrBase);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
pfn_SymFunctionTableAccess64 WindowsDbgHelp::pfnSymFunctionTableAccess64() {
|
||||
DecoderLocker locker;
|
||||
WindowsDecoder* wd = (WindowsDecoder*)locker.decoder();
|
||||
|
||||
if (!wd->has_error()) {
|
||||
return wd->_pfnSymFunctionTableAccess64;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
pfn_SymGetModuleBase64 WindowsDbgHelp::pfnSymGetModuleBase64() {
|
||||
DecoderLocker locker;
|
||||
WindowsDecoder* wd = (WindowsDecoder*)locker.decoder();
|
||||
|
||||
if (!wd->has_error()) {
|
||||
return wd->_pfnSymGetModuleBase64;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // AMD64
|
||||
|
|
|
@ -38,6 +38,20 @@ typedef DWORD (WINAPI *pfn_UndecorateSymbolName)(const char*, char*, DWORD, DWOR
|
|||
typedef BOOL (WINAPI *pfn_SymSetSearchPath)(HANDLE, PCTSTR);
|
||||
typedef BOOL (WINAPI *pfn_SymGetSearchPath)(HANDLE, PTSTR, int);
|
||||
|
||||
#ifdef AMD64
|
||||
typedef BOOL (WINAPI *pfn_StackWalk64)(DWORD MachineType,
|
||||
HANDLE hProcess,
|
||||
HANDLE hThread,
|
||||
LPSTACKFRAME64 StackFrame,
|
||||
PVOID ContextRecord,
|
||||
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
|
||||
PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
|
||||
PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
|
||||
PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress);
|
||||
typedef PVOID (WINAPI *pfn_SymFunctionTableAccess64)(HANDLE hProcess, DWORD64 AddrBase);
|
||||
typedef DWORD64 (WINAPI *pfn_SymGetModuleBase64)(HANDLE hProcess, DWORD64 dwAddr);
|
||||
#endif
|
||||
|
||||
class WindowsDecoder : public AbstractDecoder {
|
||||
|
||||
public:
|
||||
|
@ -61,7 +75,34 @@ private:
|
|||
bool _can_decode_in_vm;
|
||||
pfn_SymGetSymFromAddr64 _pfnSymGetSymFromAddr64;
|
||||
pfn_UndecorateSymbolName _pfnUndecorateSymbolName;
|
||||
#ifdef AMD64
|
||||
pfn_StackWalk64 _pfnStackWalk64;
|
||||
pfn_SymFunctionTableAccess64 _pfnSymFunctionTableAccess64;
|
||||
pfn_SymGetModuleBase64 _pfnSymGetModuleBase64;
|
||||
|
||||
friend class WindowsDbgHelp;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef AMD64
|
||||
// TODO: refactor and move the handling of dbghelp.dll outside of Decoder
|
||||
class WindowsDbgHelp : public Decoder {
|
||||
public:
|
||||
static BOOL StackWalk64(DWORD MachineType,
|
||||
HANDLE hProcess,
|
||||
HANDLE hThread,
|
||||
LPSTACKFRAME64 StackFrame,
|
||||
PVOID ContextRecord,
|
||||
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
|
||||
PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
|
||||
PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
|
||||
PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress);
|
||||
static PVOID SymFunctionTableAccess64(HANDLE hProcess, DWORD64 AddrBase);
|
||||
|
||||
static pfn_SymFunctionTableAccess64 pfnSymFunctionTableAccess64();
|
||||
static pfn_SymGetModuleBase64 pfnSymGetModuleBase64();
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // OS_WINDOWS_VM_DECODER_WINDOWS_HPP
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "classfile/vmSymbols.hpp"
|
||||
#include "code/icBuffer.hpp"
|
||||
#include "code/vtableStubs.hpp"
|
||||
#include "decoder_windows.hpp"
|
||||
#include "interpreter/interpreter.hpp"
|
||||
#include "jvm_windows.h"
|
||||
#include "memory/allocation.inline.hpp"
|
||||
|
@ -327,6 +328,94 @@ add_ptr_func_t* os::atomic_add_ptr_func = os::atomic_add_ptr_bootstrap
|
|||
|
||||
cmpxchg_long_func_t* os::atomic_cmpxchg_long_func = os::atomic_cmpxchg_long_bootstrap;
|
||||
|
||||
#ifdef AMD64
|
||||
/*
|
||||
* Windows/x64 does not use stack frames the way expected by Java:
|
||||
* [1] in most cases, there is no frame pointer. All locals are addressed via RSP
|
||||
* [2] in rare cases, when alloca() is used, a frame pointer is used, but this may
|
||||
* not be RBP.
|
||||
* See http://msdn.microsoft.com/en-us/library/ew5tede7.aspx
|
||||
*
|
||||
* So it's not possible to print the native stack using the
|
||||
* while (...) {... fr = os::get_sender_for_C_frame(&fr); }
|
||||
* loop in vmError.cpp. We need to roll our own loop.
|
||||
*/
|
||||
bool os::platform_print_native_stack(outputStream* st, void* context,
|
||||
char *buf, int buf_size)
|
||||
{
|
||||
CONTEXT ctx;
|
||||
if (context != NULL) {
|
||||
memcpy(&ctx, context, sizeof(ctx));
|
||||
} else {
|
||||
RtlCaptureContext(&ctx);
|
||||
}
|
||||
|
||||
st->print_cr("Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)");
|
||||
|
||||
STACKFRAME stk;
|
||||
memset(&stk, 0, sizeof(stk));
|
||||
stk.AddrStack.Offset = ctx.Rsp;
|
||||
stk.AddrStack.Mode = AddrModeFlat;
|
||||
stk.AddrFrame.Offset = ctx.Rbp;
|
||||
stk.AddrFrame.Mode = AddrModeFlat;
|
||||
stk.AddrPC.Offset = ctx.Rip;
|
||||
stk.AddrPC.Mode = AddrModeFlat;
|
||||
|
||||
int count = 0;
|
||||
address lastpc = 0;
|
||||
while (count++ < StackPrintLimit) {
|
||||
intptr_t* sp = (intptr_t*)stk.AddrStack.Offset;
|
||||
intptr_t* fp = (intptr_t*)stk.AddrFrame.Offset; // NOT necessarily the same as ctx.Rbp!
|
||||
address pc = (address)stk.AddrPC.Offset;
|
||||
|
||||
if (pc != NULL && sp != NULL && fp != NULL) {
|
||||
if (count == 2 && lastpc == pc) {
|
||||
// Skip it -- StackWalk64() may return the same PC
|
||||
// (but different SP) on the first try.
|
||||
} else {
|
||||
// Don't try to create a frame(sp, fp, pc) -- on WinX64, stk.AddrFrame
|
||||
// may not contain what Java expects, and may cause the frame() constructor
|
||||
// to crash. Let's just print out the symbolic address.
|
||||
frame::print_C_frame(st, buf, buf_size, pc);
|
||||
st->cr();
|
||||
}
|
||||
lastpc = pc;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
PVOID p = WindowsDbgHelp::SymFunctionTableAccess64(GetCurrentProcess(), stk.AddrPC.Offset);
|
||||
if (!p) {
|
||||
// StackWalk64() can't handle this PC. Calling StackWalk64 again may cause crash.
|
||||
break;
|
||||
}
|
||||
|
||||
BOOL result = WindowsDbgHelp::StackWalk64(
|
||||
IMAGE_FILE_MACHINE_AMD64, // __in DWORD MachineType,
|
||||
GetCurrentProcess(), // __in HANDLE hProcess,
|
||||
GetCurrentThread(), // __in HANDLE hThread,
|
||||
&stk, // __inout LP STACKFRAME64 StackFrame,
|
||||
&ctx, // __inout PVOID ContextRecord,
|
||||
NULL, // __in_opt PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
|
||||
WindowsDbgHelp::pfnSymFunctionTableAccess64(),
|
||||
// __in_opt PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
|
||||
WindowsDbgHelp::pfnSymGetModuleBase64(),
|
||||
// __in_opt PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
|
||||
NULL); // __in_opt PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
|
||||
|
||||
if (!result) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (count > StackPrintLimit) {
|
||||
st->print_cr("...<more frames>...");
|
||||
}
|
||||
st->cr();
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif // AMD64
|
||||
|
||||
ExtendedPC os::fetch_frame_from_context(void* ucVoid,
|
||||
intptr_t** ret_sp, intptr_t** ret_fp) {
|
||||
|
||||
|
@ -401,6 +490,9 @@ frame os::current_frame() {
|
|||
StubRoutines::x86::get_previous_fp_entry());
|
||||
if (func == NULL) return frame();
|
||||
intptr_t* fp = (*func)();
|
||||
if (fp == NULL) {
|
||||
return frame();
|
||||
}
|
||||
#else
|
||||
intptr_t* fp = _get_previous_fp();
|
||||
#endif // AMD64
|
||||
|
|
|
@ -62,4 +62,10 @@
|
|||
|
||||
static bool register_code_area(char *low, char *high);
|
||||
|
||||
#ifdef AMD64
|
||||
#define PLATFORM_PRINT_NATIVE_STACK 1
|
||||
static bool platform_print_native_stack(outputStream* st, void* context,
|
||||
char *buf, int buf_size);
|
||||
#endif
|
||||
|
||||
#endif // OS_CPU_WINDOWS_X86_VM_OS_WINDOWS_X86_HPP
|
||||
|
|
|
@ -652,7 +652,7 @@ void frame::interpreter_frame_print_on(outputStream* st) const {
|
|||
// Return whether the frame is in the VM or os indicating a Hotspot problem.
|
||||
// Otherwise, it's likely a bug in the native library that the Java code calls,
|
||||
// hopefully indicating where to submit bugs.
|
||||
static void print_C_frame(outputStream* st, char* buf, int buflen, address pc) {
|
||||
void frame::print_C_frame(outputStream* st, char* buf, int buflen, address pc) {
|
||||
// C/C++ frame
|
||||
bool in_vm = os::address_is_in_vm(pc);
|
||||
st->print(in_vm ? "V" : "C");
|
||||
|
|
|
@ -406,6 +406,7 @@ class frame VALUE_OBJ_CLASS_SPEC {
|
|||
void print_on(outputStream* st) const;
|
||||
void interpreter_frame_print_on(outputStream* st) const;
|
||||
void print_on_error(outputStream* st, char* buf, int buflen, bool verbose = false) const;
|
||||
static void print_C_frame(outputStream* st, char* buf, int buflen, address pc);
|
||||
|
||||
// Add annotated descriptions of memory locations belonging to this frame to values
|
||||
void describe(FrameValues& values, int frame_no);
|
||||
|
|
|
@ -795,6 +795,14 @@ class os: AllStatic {
|
|||
#endif
|
||||
|
||||
public:
|
||||
#ifndef PLATFORM_PRINT_NATIVE_STACK
|
||||
// No platform-specific code for printing the native stack.
|
||||
static bool platform_print_native_stack(outputStream* st, void* context,
|
||||
char *buf, int buf_size) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// debugging support (mostly used by debug.cpp but also fatal error handler)
|
||||
static bool find(address pc, outputStream* st = tty); // OS specific function to make sense out of an address
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
|
||||
#include "precompiled.hpp"
|
||||
#include "prims/jvm.h"
|
||||
#include "runtime/mutexLocker.hpp"
|
||||
#include "runtime/os.hpp"
|
||||
#include "utilities/decoder.hpp"
|
||||
#include "utilities/vmError.hpp"
|
||||
|
@ -80,6 +79,23 @@ AbstractDecoder* Decoder::create_decoder() {
|
|||
return decoder;
|
||||
}
|
||||
|
||||
inline bool DecoderLocker::is_first_error_thread() {
|
||||
return (os::current_thread_id() == VMError::get_first_error_tid());
|
||||
}
|
||||
|
||||
DecoderLocker::DecoderLocker() :
|
||||
MutexLockerEx(DecoderLocker::is_first_error_thread() ?
|
||||
NULL : Decoder::shared_decoder_lock(), true) {
|
||||
_decoder = is_first_error_thread() ?
|
||||
Decoder::get_error_handler_instance() : Decoder::get_shared_instance();
|
||||
assert(_decoder != NULL, "null decoder");
|
||||
}
|
||||
|
||||
Mutex* Decoder::shared_decoder_lock() {
|
||||
assert(_shared_decoder_lock != NULL, "Just check");
|
||||
return _shared_decoder_lock;
|
||||
}
|
||||
|
||||
bool Decoder::decode(address addr, char* buf, int buflen, int* offset, const char* modulepath) {
|
||||
assert(_shared_decoder_lock != NULL, "Just check");
|
||||
bool error_handling_thread = os::current_thread_id() == VMError::first_error_tid;
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
#include "memory/allocation.hpp"
|
||||
#include "runtime/mutex.hpp"
|
||||
#include "runtime/mutexLocker.hpp"
|
||||
|
||||
class AbstractDecoder : public CHeapObj<mtInternal> {
|
||||
public:
|
||||
|
@ -124,6 +125,19 @@ private:
|
|||
|
||||
protected:
|
||||
static Mutex* _shared_decoder_lock;
|
||||
static Mutex* shared_decoder_lock();
|
||||
|
||||
friend class DecoderLocker;
|
||||
};
|
||||
|
||||
class DecoderLocker : public MutexLockerEx {
|
||||
AbstractDecoder* _decoder;
|
||||
inline bool is_first_error_thread();
|
||||
public:
|
||||
DecoderLocker();
|
||||
AbstractDecoder* decoder() {
|
||||
return _decoder;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // SHARE_VM_UTILITIES_DECODER_HPP
|
||||
|
|
|
@ -574,6 +574,10 @@ void VMError::report(outputStream* st) {
|
|||
STEP(120, "(printing native stack)" )
|
||||
|
||||
if (_verbose) {
|
||||
if (os::platform_print_native_stack(st, _context, buf, sizeof(buf))) {
|
||||
// We have printed the native stack in platform-specific code
|
||||
// Windows/x64 needs special handling.
|
||||
} else {
|
||||
frame fr = _context ? os::fetch_frame_from_context(_context)
|
||||
: os::current_frame();
|
||||
|
||||
|
@ -604,6 +608,7 @@ void VMError::report(outputStream* st) {
|
|||
st->cr();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
STEP(130, "(printing Java stack)" )
|
||||
|
||||
|
|
|
@ -136,6 +136,10 @@ public:
|
|||
|
||||
// check to see if fatal error reporting is in progress
|
||||
static bool fatal_error_in_progress() { return first_error != NULL; }
|
||||
|
||||
static jlong get_first_error_tid() {
|
||||
return first_error_tid;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // SHARE_VM_UTILITIES_VMERROR_HPP
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue