Windows: Support backtraces on release builds.
This diff add supports for crash logs on windows for release builds. This can be toggled on/off with the `WITH_WINDOWS_PDB` cmake option. by default it is on. Things to take into consideration: Release builds are hightly optimized and the resulting backtraces can be wrong/misleading, take the backtrace as a general area where the problem resides rather than an exact location. By default we ship a minimized symbol file that can only resolve the function names. This was chosen to strike a balance between growth in size of the download vs functionality gained. If more detailed information is required such as source file + line number information a full pdb can be shipped by setting `WITH_WINDOWS_STRIPPED_PDB` to off. Differential Revision: https://developer.blender.org/D7520 Reviewed by: brecht
This commit is contained in:
parent
4cc8123377
commit
b523911e86
|
@ -546,6 +546,12 @@ if(WIN32)
|
|||
option(WITH_WINDOWS_SCCACHE "Use sccache to speed up builds (Ninja builder only)" OFF)
|
||||
mark_as_advanced(WITH_WINDOWS_SCCACHE)
|
||||
|
||||
option(WITH_WINDOWS_PDB "Generate a pdb file for client side stacktraces" ON)
|
||||
mark_as_advanced(WITH_WINDOWS_PDB)
|
||||
|
||||
option(WITH_WINDOWS_STRIPPED_PDB "Use a stripped PDB file" On)
|
||||
mark_as_advanced(WITH_WINDOWS_STRIPPED_PDB)
|
||||
|
||||
endif()
|
||||
|
||||
# The following only works with the Ninja generator in CMake >= 3.0.
|
||||
|
|
|
@ -51,6 +51,10 @@ if(CMAKE_C_COMPILER_ID MATCHES "Clang")
|
|||
endif()
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} \"${CLANG_OPENMP_LIB}\"")
|
||||
endif()
|
||||
if(WITH_WINDOWS_STRIPPED_PDB)
|
||||
message(WARNING "stripped pdb not supported with clang, disabling..")
|
||||
set(WITH_WINDOWS_STRIPPED_PDB Off)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ${WINDOWS_USE_VISUAL_STUDIO_PROJECT_FOLDERS})
|
||||
|
@ -112,7 +116,7 @@ set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /SAFESEH:NO")
|
|||
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /SAFESEH:NO")
|
||||
|
||||
list(APPEND PLATFORM_LINKLIBS
|
||||
ws2_32 vfw32 winmm kernel32 user32 gdi32 comdlg32 Comctl32
|
||||
ws2_32 vfw32 winmm kernel32 user32 gdi32 comdlg32 Comctl32 version
|
||||
advapi32 shfolder shell32 ole32 oleaut32 uuid psapi Dbghelp Shlwapi
|
||||
)
|
||||
|
||||
|
@ -136,6 +140,11 @@ add_definitions(-D_WIN32_WINNT=0x601)
|
|||
include(build_files/cmake/platform/platform_win32_bundle_crt.cmake)
|
||||
remove_cc_flag("/MDd" "/MD" "/Zi")
|
||||
|
||||
if(WITH_WINDOWS_PDB)
|
||||
set(PDB_INFO_OVERRIDE_FLAGS "/Z7")
|
||||
set(PDB_INFO_OVERRIDE_LINKER_FLAGS "/DEBUG /OPT:REF /OPT:ICF /INCREMENTAL:NO")
|
||||
endif()
|
||||
|
||||
if(MSVC_CLANG) # Clangs version of cl doesn't support all flags
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_WARN_FLAGS} /nologo /J /Gd /EHsc -Wno-unused-command-line-argument -Wno-microsoft-enum-forward-reference ")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /nologo /J /Gd -Wno-unused-command-line-argument -Wno-microsoft-enum-forward-reference")
|
||||
|
@ -168,10 +177,10 @@ endif()
|
|||
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MDd ${SYMBOL_FORMAT}")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /MDd ${SYMBOL_FORMAT}")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MD")
|
||||
set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} /MD")
|
||||
set(CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL} /MD")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD ${PDB_INFO_OVERRIDE_FLAGS}")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MD ${PDB_INFO_OVERRIDE_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} /MD ${PDB_INFO_OVERRIDE_FLAGS}")
|
||||
set(CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL} /MD ${PDB_INFO_OVERRIDE_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /MD ${SYMBOL_FORMAT}")
|
||||
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} /MD ${SYMBOL_FORMAT}")
|
||||
unset(SYMBOL_FORMAT)
|
||||
|
@ -186,6 +195,7 @@ set(PLATFORM_LINKFLAGS_DEBUG "${PLATFORM_LINKFLAGS_DEBUG} /IGNORE:4099 /NODEFAUL
|
|||
|
||||
# Ignore meaningless for us linker warnings.
|
||||
set(PLATFORM_LINKFLAGS "${PLATFORM_LINKFLAGS} /ignore:4049 /ignore:4217 /ignore:4221")
|
||||
set(PLATFORM_LINKFLAGS_RELEASE "${PLATFORM_LINKFLAGS} ${PDB_INFO_OVERRIDE_LINKER_FLAGS}")
|
||||
set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4221")
|
||||
|
||||
if(CMAKE_CL_64)
|
||||
|
|
|
@ -53,6 +53,10 @@ int BLI_system_memory_max_in_megabytes_int(void);
|
|||
/* getpid */
|
||||
#ifdef WIN32
|
||||
# define BLI_SYSTEM_PID_H <process.h>
|
||||
|
||||
/* void* since we really do not want to drag Windows.h in to get the proper typedef. */
|
||||
void BLI_windows_handle_exception(void *exception);
|
||||
|
||||
#else
|
||||
# define BLI_SYSTEM_PID_H <unistd.h>
|
||||
#endif
|
||||
|
|
|
@ -295,6 +295,9 @@ if(WIN32)
|
|||
list(APPEND LIB
|
||||
bf_intern_utfconv
|
||||
)
|
||||
list(APPEND SRC
|
||||
intern/system_win32.c
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
|
|
|
@ -32,11 +32,7 @@
|
|||
/* for backtrace and gethostname/GetComputerName */
|
||||
#if defined(WIN32)
|
||||
# include <intrin.h>
|
||||
# include <windows.h>
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable : 4091)
|
||||
# include <dbghelp.h>
|
||||
# pragma warning(pop)
|
||||
# include "BLI_winstuff.h"
|
||||
#else
|
||||
# include <execinfo.h>
|
||||
# include <unistd.h>
|
||||
|
@ -74,6 +70,8 @@ int BLI_cpu_support_sse2(void)
|
|||
#endif
|
||||
}
|
||||
|
||||
/* Windows stackwalk lives in system_win32.c */
|
||||
#if !defined(_MSC_VER)
|
||||
/**
|
||||
* Write a backtrace into a file for systems which support it.
|
||||
*/
|
||||
|
@ -81,9 +79,9 @@ void BLI_system_backtrace(FILE *fp)
|
|||
{
|
||||
/* ------------- */
|
||||
/* Linux / Apple */
|
||||
#if defined(__linux__) || defined(__APPLE__)
|
||||
# if defined(__linux__) || defined(__APPLE__)
|
||||
|
||||
# define SIZE 100
|
||||
# define SIZE 100
|
||||
void *buffer[SIZE];
|
||||
int nptrs;
|
||||
char **strings;
|
||||
|
@ -98,48 +96,15 @@ void BLI_system_backtrace(FILE *fp)
|
|||
}
|
||||
|
||||
free(strings);
|
||||
# undef SIZE
|
||||
|
||||
/* -------- */
|
||||
/* Windows */
|
||||
#elif defined(_MSC_VER)
|
||||
|
||||
# ifndef NDEBUG
|
||||
# define MAXSYMBOL 256
|
||||
# define SIZE 100
|
||||
unsigned short i;
|
||||
void *stack[SIZE];
|
||||
unsigned short nframes;
|
||||
SYMBOL_INFO *symbolinfo;
|
||||
HANDLE process;
|
||||
|
||||
process = GetCurrentProcess();
|
||||
|
||||
SymInitialize(process, NULL, TRUE);
|
||||
|
||||
nframes = CaptureStackBackTrace(0, SIZE, stack, NULL);
|
||||
symbolinfo = MEM_callocN(sizeof(SYMBOL_INFO) + MAXSYMBOL * sizeof(char), "crash Symbol table");
|
||||
symbolinfo->MaxNameLen = MAXSYMBOL - 1;
|
||||
symbolinfo->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
|
||||
for (i = 0; i < nframes; i++) {
|
||||
SymFromAddr(process, (DWORD64)(stack[i]), 0, symbolinfo);
|
||||
|
||||
fprintf(fp, "%u: %s - 0x%0llX\n", nframes - i - 1, symbolinfo->Name, symbolinfo->Address);
|
||||
}
|
||||
|
||||
MEM_freeN(symbolinfo);
|
||||
# undef MAXSYMBOL
|
||||
# undef SIZE
|
||||
|
||||
# else
|
||||
fprintf(fp, "Crash backtrace not supported on release builds\n");
|
||||
# endif /* NDEBUG */
|
||||
#else /* _MSC_VER */
|
||||
/* ------------------ */
|
||||
/* non msvc/osx/linux */
|
||||
(void)fp;
|
||||
#endif
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
/* end BLI_system_backtrace */
|
||||
|
||||
/* NOTE: The code for CPU brand string is adopted from Cycles. */
|
||||
|
|
|
@ -0,0 +1,375 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
#include <Windows.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <dbghelp.h>
|
||||
#include <shlwapi.h>
|
||||
#include <tlhelp32.h>
|
||||
|
||||
#include "BLI_fileops.h"
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
static EXCEPTION_POINTERS *current_exception;
|
||||
|
||||
static const char *bli_windows_get_exception_description(const DWORD exceptioncode)
|
||||
{
|
||||
switch (exceptioncode) {
|
||||
case EXCEPTION_ACCESS_VIOLATION:
|
||||
return "EXCEPTION_ACCESS_VIOLATION";
|
||||
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
|
||||
return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
|
||||
case EXCEPTION_BREAKPOINT:
|
||||
return "EXCEPTION_BREAKPOINT";
|
||||
case EXCEPTION_DATATYPE_MISALIGNMENT:
|
||||
return "EXCEPTION_DATATYPE_MISALIGNMENT";
|
||||
case EXCEPTION_FLT_DENORMAL_OPERAND:
|
||||
return "EXCEPTION_FLT_DENORMAL_OPERAND";
|
||||
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
|
||||
return "EXCEPTION_FLT_DIVIDE_BY_ZERO";
|
||||
case EXCEPTION_FLT_INEXACT_RESULT:
|
||||
return "EXCEPTION_FLT_INEXACT_RESULT";
|
||||
case EXCEPTION_FLT_INVALID_OPERATION:
|
||||
return "EXCEPTION_FLT_INVALID_OPERATION";
|
||||
case EXCEPTION_FLT_OVERFLOW:
|
||||
return "EXCEPTION_FLT_OVERFLOW";
|
||||
case EXCEPTION_FLT_STACK_CHECK:
|
||||
return "EXCEPTION_FLT_STACK_CHECK";
|
||||
case EXCEPTION_FLT_UNDERFLOW:
|
||||
return "EXCEPTION_FLT_UNDERFLOW";
|
||||
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
||||
return "EXCEPTION_ILLEGAL_INSTRUCTION";
|
||||
case EXCEPTION_IN_PAGE_ERROR:
|
||||
return "EXCEPTION_IN_PAGE_ERROR";
|
||||
case EXCEPTION_INT_DIVIDE_BY_ZERO:
|
||||
return "EXCEPTION_INT_DIVIDE_BY_ZERO";
|
||||
case EXCEPTION_INT_OVERFLOW:
|
||||
return "EXCEPTION_INT_OVERFLOW";
|
||||
case EXCEPTION_INVALID_DISPOSITION:
|
||||
return "EXCEPTION_INVALID_DISPOSITION";
|
||||
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
|
||||
return "EXCEPTION_NONCONTINUABLE_EXCEPTION";
|
||||
case EXCEPTION_PRIV_INSTRUCTION:
|
||||
return "EXCEPTION_PRIV_INSTRUCTION";
|
||||
case EXCEPTION_SINGLE_STEP:
|
||||
return "EXCEPTION_SINGLE_STEP";
|
||||
case EXCEPTION_STACK_OVERFLOW:
|
||||
return "EXCEPTION_STACK_OVERFLOW";
|
||||
default:
|
||||
return "UNKNOWN EXCEPTION";
|
||||
}
|
||||
}
|
||||
|
||||
static void bli_windows_get_module_name(LPVOID address, PCHAR buffer, size_t size)
|
||||
{
|
||||
HMODULE mod;
|
||||
buffer[0] = 0;
|
||||
if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, address, &mod)) {
|
||||
if (GetModuleFileName(mod, buffer, size)) {
|
||||
PathStripPath(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void bli_windows_get_module_version(const char *file, char *buffer, size_t buffersize)
|
||||
{
|
||||
buffer[0] = 0;
|
||||
DWORD verHandle = 0;
|
||||
UINT size = 0;
|
||||
LPBYTE lpBuffer = NULL;
|
||||
DWORD verSize = GetFileVersionInfoSize(file, &verHandle);
|
||||
if (verSize != 0) {
|
||||
LPSTR verData = (LPSTR)MEM_callocN(verSize, "crash module version");
|
||||
|
||||
if (GetFileVersionInfo(file, verHandle, verSize, verData)) {
|
||||
if (VerQueryValue(verData, "\\", (VOID FAR * FAR *)&lpBuffer, &size)) {
|
||||
if (size) {
|
||||
VS_FIXEDFILEINFO *verInfo = (VS_FIXEDFILEINFO *)lpBuffer;
|
||||
/* Magic value from
|
||||
* https://docs.microsoft.com/en-us/windows/win32/api/verrsrc/ns-verrsrc-vs_fixedfileinfo
|
||||
*/
|
||||
if (verInfo->dwSignature == 0xfeef04bd) {
|
||||
BLI_snprintf(buffer,
|
||||
buffersize,
|
||||
"%d.%d.%d.%d",
|
||||
(verInfo->dwFileVersionMS >> 16) & 0xffff,
|
||||
(verInfo->dwFileVersionMS >> 0) & 0xffff,
|
||||
(verInfo->dwFileVersionLS >> 16) & 0xffff,
|
||||
(verInfo->dwFileVersionLS >> 0) & 0xffff);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MEM_freeN(verData);
|
||||
}
|
||||
}
|
||||
|
||||
static void bli_windows_system_backtrace_exception_record(FILE *fp, PEXCEPTION_RECORD record)
|
||||
{
|
||||
char module[MAX_PATH];
|
||||
fprintf(fp, "Exception Record:\n\n");
|
||||
fprintf(fp,
|
||||
"ExceptionCode : %s\n",
|
||||
bli_windows_get_exception_description(record->ExceptionCode));
|
||||
fprintf(fp, "Exception Address : 0x%p\n", record->ExceptionAddress);
|
||||
bli_windows_get_module_name(record->ExceptionAddress, module, sizeof(module));
|
||||
fprintf(fp, "Exception Module : %s\n", module);
|
||||
fprintf(fp, "Exception Flags : 0x%.8x\n", record->ExceptionFlags);
|
||||
fprintf(fp, "Exception Parameters : 0x%x\n", record->NumberParameters);
|
||||
for (DWORD idx = 0; idx < record->NumberParameters; idx++) {
|
||||
fprintf(fp, "\tParameters[%d] : 0x%p\n", idx, (LPVOID *)record->ExceptionInformation[idx]);
|
||||
}
|
||||
if (record->ExceptionRecord) {
|
||||
fprintf(fp, "Nested ");
|
||||
bli_windows_system_backtrace_exception_record(fp, record->ExceptionRecord);
|
||||
}
|
||||
fprintf(fp, "\n\n");
|
||||
}
|
||||
|
||||
static bool BLI_windows_system_backtrace_run_trace(FILE *fp, HANDLE hThread, PCONTEXT context)
|
||||
{
|
||||
const int max_symbol_length = 100;
|
||||
|
||||
bool result = true;
|
||||
|
||||
PSYMBOL_INFO symbolinfo = MEM_callocN(sizeof(SYMBOL_INFO) + max_symbol_length * sizeof(char),
|
||||
"crash Symbol table");
|
||||
symbolinfo->MaxNameLen = max_symbol_length - 1;
|
||||
symbolinfo->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
|
||||
STACKFRAME frame = {0};
|
||||
frame.AddrPC.Offset = context->Rip;
|
||||
frame.AddrPC.Mode = AddrModeFlat;
|
||||
frame.AddrFrame.Offset = context->Rsp;
|
||||
frame.AddrFrame.Mode = AddrModeFlat;
|
||||
frame.AddrStack.Offset = context->Rsp;
|
||||
frame.AddrStack.Mode = AddrModeFlat;
|
||||
|
||||
while (true) {
|
||||
if (StackWalk64(IMAGE_FILE_MACHINE_AMD64,
|
||||
GetCurrentProcess(),
|
||||
hThread,
|
||||
&frame,
|
||||
context,
|
||||
NULL,
|
||||
SymFunctionTableAccess64,
|
||||
SymGetModuleBase64,
|
||||
0)) {
|
||||
if (frame.AddrPC.Offset) {
|
||||
char module[MAX_PATH];
|
||||
|
||||
bli_windows_get_module_name((LPVOID)frame.AddrPC.Offset, module, sizeof(module));
|
||||
|
||||
if (SymFromAddr(GetCurrentProcess(), (DWORD64)(frame.AddrPC.Offset), 0, symbolinfo)) {
|
||||
fprintf(fp, "%-20s:0x%p %s", module, (LPVOID)symbolinfo->Address, symbolinfo->Name);
|
||||
IMAGEHLP_LINE lineinfo;
|
||||
lineinfo.SizeOfStruct = sizeof(lineinfo);
|
||||
DWORD displacement = 0;
|
||||
if (SymGetLineFromAddr(
|
||||
GetCurrentProcess(), (DWORD64)(frame.AddrPC.Offset), &displacement, &lineinfo)) {
|
||||
fprintf(fp, " %s:%d", lineinfo.FileName, lineinfo.LineNumber);
|
||||
}
|
||||
fprintf(fp, "\n");
|
||||
}
|
||||
else {
|
||||
fprintf(fp,
|
||||
"%-20s:0x%p %s\n",
|
||||
module,
|
||||
(LPVOID)frame.AddrPC.Offset,
|
||||
"Symbols not available");
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
MEM_freeN(symbolinfo);
|
||||
fprintf(fp, "\n\n");
|
||||
return result;
|
||||
}
|
||||
|
||||
static void bli_windows_system_backtrace_stack_thread(FILE *fp, HANDLE hThread)
|
||||
{
|
||||
if (hThread != GetCurrentThread()) {
|
||||
SuspendThread(hThread);
|
||||
}
|
||||
CONTEXT context;
|
||||
context.ContextFlags = CONTEXT_ALL;
|
||||
if (!GetThreadContext(hThread, &context)) {
|
||||
fprintf(fp, "Cannot get thread context : 0x0%.8x\n", GetLastError());
|
||||
}
|
||||
BLI_windows_system_backtrace_run_trace(fp, hThread, &context);
|
||||
if (hThread != GetCurrentThread()) {
|
||||
ResumeThread(hThread);
|
||||
}
|
||||
}
|
||||
|
||||
static void bli_windows_system_backtrace_modules(FILE *fp)
|
||||
{
|
||||
fprintf(fp, "Loaded Modules :\n");
|
||||
HANDLE hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0);
|
||||
if (hModuleSnap == INVALID_HANDLE_VALUE)
|
||||
return;
|
||||
|
||||
MODULEENTRY32 me32;
|
||||
me32.dwSize = sizeof(MODULEENTRY32);
|
||||
|
||||
if (!Module32First(hModuleSnap, &me32)) {
|
||||
CloseHandle(hModuleSnap); // Must clean up the snapshot object!
|
||||
fprintf(fp, " Error getting module list.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
if (me32.th32ProcessID == GetCurrentProcessId()) {
|
||||
char version[MAX_PATH];
|
||||
bli_windows_get_module_version(me32.szExePath, version, sizeof(version));
|
||||
fprintf(fp, "0x%p %-20s %s\n", me32.modBaseAddr, version, me32.szModule);
|
||||
}
|
||||
} while (Module32Next(hModuleSnap, &me32));
|
||||
}
|
||||
|
||||
static void bli_windows_system_backtrace_threads(FILE *fp)
|
||||
{
|
||||
fprintf(fp, "Threads:\n");
|
||||
HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
|
||||
THREADENTRY32 te32;
|
||||
|
||||
hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
|
||||
if (hThreadSnap == INVALID_HANDLE_VALUE) {
|
||||
fprintf(fp, "Unable to retrieve threads list.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
te32.dwSize = sizeof(THREADENTRY32);
|
||||
|
||||
if (!Thread32First(hThreadSnap, &te32)) {
|
||||
CloseHandle(hThreadSnap);
|
||||
return;
|
||||
}
|
||||
do {
|
||||
if (te32.th32OwnerProcessID == GetCurrentProcessId()) {
|
||||
if (GetCurrentThreadId() != te32.th32ThreadID) {
|
||||
fprintf(fp, "Thread : %.8x\n", te32.th32ThreadID);
|
||||
HANDLE ht = OpenThread(THREAD_ALL_ACCESS, FALSE, te32.th32ThreadID);
|
||||
bli_windows_system_backtrace_stack_thread(fp, ht);
|
||||
CloseHandle(ht);
|
||||
}
|
||||
}
|
||||
} while (Thread32Next(hThreadSnap, &te32));
|
||||
CloseHandle(hThreadSnap);
|
||||
}
|
||||
|
||||
static bool BLI_windows_system_backtrace_stack(FILE *fp)
|
||||
{
|
||||
fprintf(fp, "Stack trace:\n");
|
||||
CONTEXT TempContext = *current_exception->ContextRecord;
|
||||
return BLI_windows_system_backtrace_run_trace(fp, GetCurrentThread(), &TempContext);
|
||||
}
|
||||
|
||||
static bool bli_private_symbols_loaded()
|
||||
{
|
||||
IMAGEHLP_MODULE64 m64;
|
||||
m64.SizeOfStruct = sizeof(m64);
|
||||
if (SymGetModuleInfo64(GetCurrentProcess(), (DWORD64)GetModuleHandle(NULL), &m64)) {
|
||||
PathStripPath(m64.LoadedPdbName);
|
||||
return BLI_strcasecmp(m64.LoadedPdbName, "blender_private.pdb") == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void bli_load_symbols()
|
||||
{
|
||||
/* If this is a developer station and the private pdb is already loaded leave it be. */
|
||||
if (bli_private_symbols_loaded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
char pdb_file[MAX_PATH] = {0};
|
||||
|
||||
/* get the currently executing image */
|
||||
if (GetModuleFileNameA(NULL, pdb_file, sizeof(pdb_file))) {
|
||||
/* remove the filename */
|
||||
PathRemoveFileSpecA(pdb_file);
|
||||
/* append blender.pdb */
|
||||
PathAppendA(pdb_file, "blender.pdb");
|
||||
if (BLI_exists(pdb_file)) {
|
||||
HMODULE mod = GetModuleHandle(NULL);
|
||||
if (mod) {
|
||||
size_t size = BLI_file_size(pdb_file);
|
||||
|
||||
/* SymInitialize will try to load symbols on its own, so we first must unload whatever it
|
||||
* did trying to help */
|
||||
SymUnloadModule64(GetCurrentProcess(), (DWORD64)mod);
|
||||
|
||||
DWORD64 module_base = SymLoadModule(
|
||||
GetCurrentProcess(), NULL, pdb_file, NULL, (DWORD64)mod, (DWORD)size);
|
||||
if (module_base == 0) {
|
||||
fprintf(stderr,
|
||||
"Error loading symbols %s\n\terror:0x%.8x\n\tsize = %zi\n\tbase=0x%p\n",
|
||||
pdb_file,
|
||||
GetLastError(),
|
||||
size,
|
||||
(LPVOID)mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BLI_system_backtrace(FILE *fp)
|
||||
{
|
||||
SymInitialize(GetCurrentProcess(), NULL, TRUE);
|
||||
bli_load_symbols();
|
||||
bli_windows_system_backtrace_exception_record(fp, current_exception->ExceptionRecord);
|
||||
if (BLI_windows_system_backtrace_stack(fp)) {
|
||||
/* When the blender symbols are missing the stack traces will be unreliable
|
||||
* so only run if the previous step completed successfully. */
|
||||
bli_windows_system_backtrace_threads(fp);
|
||||
}
|
||||
bli_windows_system_backtrace_modules(fp);
|
||||
fputc(0, fp); /* Give our selves a nice zero terminator for later on */
|
||||
}
|
||||
|
||||
void BLI_windows_handle_exception(EXCEPTION_POINTERS *exception)
|
||||
{
|
||||
current_exception = exception;
|
||||
fprintf(stderr,
|
||||
"Error : %s\n",
|
||||
bli_windows_get_exception_description(exception->ExceptionRecord->ExceptionCode));
|
||||
fflush(stderr);
|
||||
|
||||
LPVOID address = exception->ExceptionRecord->ExceptionAddress;
|
||||
fprintf(stderr, "Address : 0x%p\n", address);
|
||||
|
||||
CHAR modulename[MAX_PATH];
|
||||
bli_windows_get_module_name(address, modulename, sizeof(modulename));
|
||||
fprintf(stderr, "Module : %s\n", modulename);
|
||||
fflush(stderr);
|
||||
}
|
|
@ -687,6 +687,17 @@ elseif(WIN32)
|
|||
)
|
||||
endif()
|
||||
|
||||
if(WITH_WINDOWS_PDB)
|
||||
if(WITH_WINDOWS_STRIPPED_PDB)
|
||||
# Icky hack for older cmake from https://stackoverflow.com/a/21198501
|
||||
# $<CONFIG> will work in newer cmake but the version currently (3.12)
|
||||
# on the buildbot does not support this endavour.
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/\${CMAKE_INSTALL_CONFIG_NAME}/blender_public.pdb DESTINATION . RENAME blender.pdb)
|
||||
else()
|
||||
install(FILES $<TARGET_PDB_FILE:blender> DESTINATION . RENAME blender.pdb)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WITH_PYTHON)
|
||||
string(REPLACE "." "" _PYTHON_VERSION_NO_DOTS ${PYTHON_VERSION})
|
||||
|
||||
|
@ -1085,6 +1096,20 @@ endif()
|
|||
# the use of vcpkg
|
||||
if(WIN32)
|
||||
set_target_properties(blender PROPERTIES VS_GLOBAL_VcpkgEnabled "false")
|
||||
set_target_properties(blender PROPERTIES
|
||||
PDB_NAME "blender_private"
|
||||
PDB_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>")
|
||||
if (WITH_WINDOWS_PDB AND WITH_WINDOWS_STRIPPED_PDB)
|
||||
# This is slightly messy, but single target generators like ninja will not have the
|
||||
# CMAKE_CFG_INTDIR variable and multitarget generators like msbuild will not have
|
||||
# CMAKE_BUILD_TYPE. This can be simplified by target_link_options and the $<CONFIG>
|
||||
# generator expression in newer cmake (2.13+) but until that time this fill have suffice.
|
||||
if(CMAKE_BUILD_TYPE)
|
||||
set_property(TARGET blender APPEND PROPERTY LINK_FLAGS " /PDBSTRIPPED:${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/blender_public.pdb")
|
||||
else()
|
||||
set_property(TARGET blender APPEND PROPERTY LINK_FLAGS " /PDBSTRIPPED:${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/blender_public.pdb")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
|
|
@ -190,97 +190,25 @@ static void sig_handle_crash(int signum)
|
|||
}
|
||||
|
||||
# ifdef WIN32
|
||||
LONG WINAPI windows_exception_handler(EXCEPTION_POINTERS *ExceptionInfo)
|
||||
extern LONG WINAPI windows_exception_handler(EXCEPTION_POINTERS *ExceptionInfo)
|
||||
{
|
||||
switch (ExceptionInfo->ExceptionRecord->ExceptionCode) {
|
||||
case EXCEPTION_ACCESS_VIOLATION:
|
||||
fputs("Error : EXCEPTION_ACCESS_VIOLATION\n", stderr);
|
||||
break;
|
||||
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
|
||||
fputs("Error : EXCEPTION_ARRAY_BOUNDS_EXCEEDED\n", stderr);
|
||||
break;
|
||||
case EXCEPTION_BREAKPOINT:
|
||||
fputs("Error : EXCEPTION_BREAKPOINT\n", stderr);
|
||||
break;
|
||||
case EXCEPTION_DATATYPE_MISALIGNMENT:
|
||||
fputs("Error : EXCEPTION_DATATYPE_MISALIGNMENT\n", stderr);
|
||||
break;
|
||||
case EXCEPTION_FLT_DENORMAL_OPERAND:
|
||||
fputs("Error : EXCEPTION_FLT_DENORMAL_OPERAND\n", stderr);
|
||||
break;
|
||||
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
|
||||
fputs("Error : EXCEPTION_FLT_DIVIDE_BY_ZERO\n", stderr);
|
||||
break;
|
||||
case EXCEPTION_FLT_INEXACT_RESULT:
|
||||
fputs("Error : EXCEPTION_FLT_INEXACT_RESULT\n", stderr);
|
||||
break;
|
||||
case EXCEPTION_FLT_INVALID_OPERATION:
|
||||
fputs("Error : EXCEPTION_FLT_INVALID_OPERATION\n", stderr);
|
||||
break;
|
||||
case EXCEPTION_FLT_OVERFLOW:
|
||||
fputs("Error : EXCEPTION_FLT_OVERFLOW\n", stderr);
|
||||
break;
|
||||
case EXCEPTION_FLT_STACK_CHECK:
|
||||
fputs("Error : EXCEPTION_FLT_STACK_CHECK\n", stderr);
|
||||
break;
|
||||
case EXCEPTION_FLT_UNDERFLOW:
|
||||
fputs("Error : EXCEPTION_FLT_UNDERFLOW\n", stderr);
|
||||
break;
|
||||
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
||||
fputs("Error : EXCEPTION_ILLEGAL_INSTRUCTION\n", stderr);
|
||||
break;
|
||||
case EXCEPTION_IN_PAGE_ERROR:
|
||||
fputs("Error : EXCEPTION_IN_PAGE_ERROR\n", stderr);
|
||||
break;
|
||||
case EXCEPTION_INT_DIVIDE_BY_ZERO:
|
||||
fputs("Error : EXCEPTION_INT_DIVIDE_BY_ZERO\n", stderr);
|
||||
break;
|
||||
case EXCEPTION_INT_OVERFLOW:
|
||||
fputs("Error : EXCEPTION_INT_OVERFLOW\n", stderr);
|
||||
break;
|
||||
case EXCEPTION_INVALID_DISPOSITION:
|
||||
fputs("Error : EXCEPTION_INVALID_DISPOSITION\n", stderr);
|
||||
break;
|
||||
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
|
||||
fputs("Error : EXCEPTION_NONCONTINUABLE_EXCEPTION\n", stderr);
|
||||
break;
|
||||
case EXCEPTION_PRIV_INSTRUCTION:
|
||||
fputs("Error : EXCEPTION_PRIV_INSTRUCTION\n", stderr);
|
||||
break;
|
||||
case EXCEPTION_SINGLE_STEP:
|
||||
fputs("Error : EXCEPTION_SINGLE_STEP\n", stderr);
|
||||
break;
|
||||
case EXCEPTION_STACK_OVERFLOW:
|
||||
fputs("Error : EXCEPTION_STACK_OVERFLOW\n", stderr);
|
||||
break;
|
||||
default:
|
||||
fputs("Error : Unrecognized Exception\n", stderr);
|
||||
break;
|
||||
}
|
||||
|
||||
fflush(stderr);
|
||||
|
||||
/* If this is a stack overflow then we can't walk the stack, so just show
|
||||
/* If this is a stack overflow then we can't walk the stack, so just try to show
|
||||
* where the error happened */
|
||||
if (EXCEPTION_STACK_OVERFLOW != ExceptionInfo->ExceptionRecord->ExceptionCode) {
|
||||
HMODULE mod;
|
||||
CHAR modulename[MAX_PATH];
|
||||
LPVOID address = ExceptionInfo->ExceptionRecord->ExceptionAddress;
|
||||
|
||||
fprintf(stderr, "Address : 0x%p\n", address);
|
||||
if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, address, &mod)) {
|
||||
if (GetModuleFileName(mod, modulename, MAX_PATH)) {
|
||||
fprintf(stderr, "Module : %s\n", modulename);
|
||||
if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW) {
|
||||
HMODULE mod;
|
||||
CHAR modulename[MAX_PATH];
|
||||
LPVOID address = ExceptionInfo->ExceptionRecord->ExceptionAddress;
|
||||
fprintf(stderr, "Error : EXCEPTION_STACK_OVERFLOW\n");
|
||||
fprintf(stderr, "Address : 0x%p\n", address);
|
||||
if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, address, &mod)) {
|
||||
if (GetModuleFileName(mod, modulename, MAX_PATH)) {
|
||||
fprintf(stderr, "Module : %s\n", modulename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fflush(stderr);
|
||||
|
||||
# ifdef NDEBUG
|
||||
TerminateProcess(GetCurrentProcess(), SIGSEGV);
|
||||
# else
|
||||
}
|
||||
else {
|
||||
BLI_windows_handle_exception(ExceptionInfo);
|
||||
sig_handle_crash(SIGSEGV);
|
||||
# endif
|
||||
}
|
||||
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
|
|
Loading…
Reference in New Issue