From b523911e860e6602cf4dc3df67a405b469f22b74 Mon Sep 17 00:00:00 2001 From: Ray Molenkamp Date: Fri, 1 May 2020 07:37:48 -0600 Subject: 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 --- CMakeLists.txt | 6 + build_files/cmake/platform/platform_win32.cmake | 20 +- source/blender/blenlib/BLI_system.h | 4 + source/blender/blenlib/CMakeLists.txt | 3 + source/blender/blenlib/intern/system.c | 51 +--- source/blender/blenlib/intern/system_win32.c | 375 ++++++++++++++++++++++++ source/creator/CMakeLists.txt | 25 ++ source/creator/creator_signals.c | 102 +------ 8 files changed, 451 insertions(+), 135 deletions(-) create mode 100644 source/blender/blenlib/intern/system_win32.c diff --git a/CMakeLists.txt b/CMakeLists.txt index a168bff2377..19f25e3c108 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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. diff --git a/build_files/cmake/platform/platform_win32.cmake b/build_files/cmake/platform/platform_win32.cmake index 90c230f5ce5..25b4f5fd81d 100644 --- a/build_files/cmake/platform/platform_win32.cmake +++ b/build_files/cmake/platform/platform_win32.cmake @@ -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) diff --git a/source/blender/blenlib/BLI_system.h b/source/blender/blenlib/BLI_system.h index 8c0c9ad99bf..50f8adc20f6 100644 --- a/source/blender/blenlib/BLI_system.h +++ b/source/blender/blenlib/BLI_system.h @@ -53,6 +53,10 @@ int BLI_system_memory_max_in_megabytes_int(void); /* getpid */ #ifdef WIN32 # define BLI_SYSTEM_PID_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 #endif diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 52b302f99d4..d3bfb553329 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -295,6 +295,9 @@ if(WIN32) list(APPEND LIB bf_intern_utfconv ) + list(APPEND SRC + intern/system_win32.c + ) endif() diff --git a/source/blender/blenlib/intern/system.c b/source/blender/blenlib/intern/system.c index 7d9ed2598a6..f0597b0e9e7 100644 --- a/source/blender/blenlib/intern/system.c +++ b/source/blender/blenlib/intern/system.c @@ -32,11 +32,7 @@ /* for backtrace and gethostname/GetComputerName */ #if defined(WIN32) # include -# include -# pragma warning(push) -# pragma warning(disable : 4091) -# include -# pragma warning(pop) +# include "BLI_winstuff.h" #else # include # include @@ -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. */ diff --git a/source/blender/blenlib/intern/system_win32.c b/source/blender/blenlib/intern/system_win32.c new file mode 100644 index 00000000000..f4c9057114c --- /dev/null +++ b/source/blender/blenlib/intern/system_win32.c @@ -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 +#include + +#include +#include +#include + +#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); +} diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 4b51f9738b3..a634618ee94 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -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 + # $ 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 $ 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}/$") + 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 $ + # 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() # ----------------------------------------------------------------------------- diff --git a/source/creator/creator_signals.c b/source/creator/creator_signals.c index 7f7a03f0465..0ffa374a0ff 100644 --- a/source/creator/creator_signals.c +++ b/source/creator/creator_signals.c @@ -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; -- cgit v1.2.3