| // |
| // Copyright 2014 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| |
| // test_utils_win.cpp: Implementation of OS-specific functions for Windows |
| |
| #include "util/test_utils.h" |
| |
| #include <stdarg.h> |
| #include <windows.h> |
| #include <array> |
| |
| #include "common/angleutils.h" |
| #include "util/windows/third_party/StackWalker/src/StackWalker.h" |
| |
| namespace angle |
| { |
| namespace |
| { |
| static const struct |
| { |
| const char *name; |
| const DWORD code; |
| } kExceptions[] = { |
| #define _(E) \ |
| { \ |
| # E, E \ |
| } |
| _(EXCEPTION_ACCESS_VIOLATION), |
| _(EXCEPTION_BREAKPOINT), |
| _(EXCEPTION_INT_DIVIDE_BY_ZERO), |
| _(EXCEPTION_STACK_OVERFLOW), |
| #undef _ |
| }; |
| |
| class CustomStackWalker : public StackWalker |
| { |
| public: |
| CustomStackWalker() {} |
| ~CustomStackWalker() {} |
| |
| void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry) override |
| { |
| char buffer[STACKWALK_MAX_NAMELEN]; |
| size_t maxLen = _TRUNCATE; |
| if ((eType != lastEntry) && (entry.offset != 0)) |
| { |
| if (entry.name[0] == 0) |
| strncpy_s(entry.name, STACKWALK_MAX_NAMELEN, "(function-name not available)", |
| _TRUNCATE); |
| if (entry.undName[0] != 0) |
| strncpy_s(entry.name, STACKWALK_MAX_NAMELEN, entry.undName, _TRUNCATE); |
| if (entry.undFullName[0] != 0) |
| strncpy_s(entry.name, STACKWALK_MAX_NAMELEN, entry.undFullName, _TRUNCATE); |
| if (entry.lineFileName[0] == 0) |
| { |
| strncpy_s(entry.lineFileName, STACKWALK_MAX_NAMELEN, "(filename not available)", |
| _TRUNCATE); |
| if (entry.moduleName[0] == 0) |
| strncpy_s(entry.moduleName, STACKWALK_MAX_NAMELEN, |
| "(module-name not available)", _TRUNCATE); |
| _snprintf_s(buffer, maxLen, " %s - %p (%s): %s\n", entry.name, |
| reinterpret_cast<void *>(entry.offset), entry.moduleName, |
| entry.lineFileName); |
| } |
| else |
| _snprintf_s(buffer, maxLen, " %s (%s:%d)\n", entry.name, entry.lineFileName, |
| entry.lineNumber); |
| buffer[STACKWALK_MAX_NAMELEN - 1] = 0; |
| printf("%s", buffer); |
| OutputDebugStringA(buffer); |
| } |
| } |
| }; |
| |
| void PrintBacktrace(CONTEXT *c) |
| { |
| printf("Backtrace:\n"); |
| OutputDebugStringA("Backtrace:\n"); |
| |
| CustomStackWalker sw; |
| sw.ShowCallstack(GetCurrentThread(), c); |
| } |
| |
| LONG WINAPI StackTraceCrashHandler(EXCEPTION_POINTERS *e) |
| { |
| const DWORD code = e->ExceptionRecord->ExceptionCode; |
| printf("\nCaught exception %lu", code); |
| for (size_t i = 0; i < ArraySize(kExceptions); i++) |
| { |
| if (kExceptions[i].code == code) |
| { |
| printf(" %s", kExceptions[i].name); |
| } |
| } |
| printf("\n"); |
| |
| PrintBacktrace(e->ContextRecord); |
| |
| // Exit NOW. Don't notify other threads, don't call anything registered with atexit(). |
| _exit(1); |
| |
| // The compiler wants us to return something. This is what we'd do if we didn't _exit(). |
| return EXCEPTION_EXECUTE_HANDLER; |
| } |
| } // anonymous namespace |
| |
| void Sleep(unsigned int milliseconds) |
| { |
| ::Sleep(static_cast<DWORD>(milliseconds)); |
| } |
| |
| void WriteDebugMessage(const char *format, ...) |
| { |
| va_list args; |
| va_start(args, format); |
| int size = vsnprintf(nullptr, 0, format, args); |
| va_end(args); |
| |
| std::vector<char> buffer(size + 2); |
| va_start(args, format); |
| vsnprintf(buffer.data(), size + 1, format, args); |
| va_end(args); |
| |
| OutputDebugStringA(buffer.data()); |
| } |
| |
| void InitCrashHandler() |
| { |
| SetUnhandledExceptionFilter(StackTraceCrashHandler); |
| } |
| |
| void TerminateCrashHandler() |
| { |
| SetUnhandledExceptionFilter(nullptr); |
| } |
| |
| void PrintStackBacktrace() |
| { |
| CONTEXT context; |
| ZeroMemory(&context, sizeof(CONTEXT)); |
| RtlCaptureContext(&context); |
| PrintBacktrace(&context); |
| } |
| } // namespace angle |