1 //
2 // Copyright 2014 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 // test_utils_win32.cpp: Implementation of OS-specific functions for Win32 (Windows)
8
9 #include "util/test_utils.h"
10
11 #include <windows.h>
12 #include <array>
13
14 #include "util/windows/third_party/StackWalker/src/StackWalker.h"
15
16 namespace angle
17 {
18 namespace
19 {
20 static const struct
21 {
22 const char *name;
23 const DWORD code;
24 } kExceptions[] = {
25 #define _(E) \
26 { \
27 # E, E \
28 }
29 _(EXCEPTION_ACCESS_VIOLATION),
30 _(EXCEPTION_BREAKPOINT),
31 _(EXCEPTION_INT_DIVIDE_BY_ZERO),
32 _(EXCEPTION_STACK_OVERFLOW),
33 #undef _
34 };
35
36 class CustomStackWalker : public StackWalker
37 {
38 public:
CustomStackWalker()39 CustomStackWalker() {}
~CustomStackWalker()40 ~CustomStackWalker() override {}
41
OnCallstackEntry(CallstackEntryType eType,CallstackEntry & entry)42 void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry) override
43 {
44 char buffer[STACKWALK_MAX_NAMELEN];
45 size_t maxLen = _TRUNCATE;
46 if ((eType != lastEntry) && (entry.offset != 0))
47 {
48 if (entry.name[0] == 0)
49 strncpy_s(entry.name, STACKWALK_MAX_NAMELEN, "(function-name not available)",
50 _TRUNCATE);
51 if (entry.undName[0] != 0)
52 strncpy_s(entry.name, STACKWALK_MAX_NAMELEN, entry.undName, _TRUNCATE);
53 if (entry.undFullName[0] != 0)
54 strncpy_s(entry.name, STACKWALK_MAX_NAMELEN, entry.undFullName, _TRUNCATE);
55 if (entry.lineFileName[0] == 0)
56 {
57 strncpy_s(entry.lineFileName, STACKWALK_MAX_NAMELEN, "(filename not available)",
58 _TRUNCATE);
59 if (entry.moduleName[0] == 0)
60 strncpy_s(entry.moduleName, STACKWALK_MAX_NAMELEN,
61 "(module-name not available)", _TRUNCATE);
62 _snprintf_s(buffer, maxLen, " %s - %p (%s): %s\n", entry.name,
63 reinterpret_cast<void *>(entry.offset), entry.moduleName,
64 entry.lineFileName);
65 }
66 else
67 _snprintf_s(buffer, maxLen, " %s (%s:%d)\n", entry.name, entry.lineFileName,
68 entry.lineNumber);
69 buffer[STACKWALK_MAX_NAMELEN - 1] = 0;
70 printf("%s", buffer);
71 OutputDebugStringA(buffer);
72 }
73 }
74 };
75
PrintBacktrace(CONTEXT * c)76 void PrintBacktrace(CONTEXT *c)
77 {
78 printf("Backtrace:\n");
79 OutputDebugStringA("Backtrace:\n");
80
81 CustomStackWalker sw;
82 sw.ShowCallstack(GetCurrentThread(), c);
83 }
84
StackTraceCrashHandler(EXCEPTION_POINTERS * e)85 LONG WINAPI StackTraceCrashHandler(EXCEPTION_POINTERS *e)
86 {
87 const DWORD code = e->ExceptionRecord->ExceptionCode;
88 printf("\nCaught exception %lu", code);
89 for (size_t i = 0; i < ArraySize(kExceptions); i++)
90 {
91 if (kExceptions[i].code == code)
92 {
93 printf(" %s", kExceptions[i].name);
94 }
95 }
96 printf("\n");
97
98 PrintBacktrace(e->ContextRecord);
99
100 // Exit NOW. Don't notify other threads, don't call anything registered with atexit().
101 _exit(1);
102
103 // The compiler wants us to return something. This is what we'd do if we didn't _exit().
104 return EXCEPTION_EXECUTE_HANDLER;
105 }
106
107 CrashCallback *gCrashHandlerCallback;
108
CrashHandler(EXCEPTION_POINTERS * e)109 LONG WINAPI CrashHandler(EXCEPTION_POINTERS *e)
110 {
111 if (gCrashHandlerCallback)
112 {
113 (*gCrashHandlerCallback)();
114 }
115 return StackTraceCrashHandler(e);
116 }
117 } // namespace
118
SetLowPriorityProcess()119 void SetLowPriorityProcess()
120 {
121 ::SetPriorityClass(::GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS);
122 }
123
StabilizeCPUForBenchmarking()124 bool StabilizeCPUForBenchmarking()
125 {
126 if (::SetThreadAffinityMask(::GetCurrentThread(), 1) == 0)
127 {
128 return false;
129 }
130 if (::SetPriorityClass(::GetCurrentProcess(), REALTIME_PRIORITY_CLASS) == FALSE)
131 {
132 return false;
133 }
134 if (::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL) == FALSE)
135 {
136 return false;
137 }
138
139 return true;
140 }
141
PrintStackBacktrace()142 void PrintStackBacktrace()
143 {
144 CONTEXT context;
145 ZeroMemory(&context, sizeof(CONTEXT));
146 RtlCaptureContext(&context);
147 PrintBacktrace(&context);
148 }
149
InitCrashHandler(CrashCallback * callback)150 void InitCrashHandler(CrashCallback *callback)
151 {
152 if (callback)
153 {
154 gCrashHandlerCallback = callback;
155 }
156 SetUnhandledExceptionFilter(CrashHandler);
157 }
158
TerminateCrashHandler()159 void TerminateCrashHandler()
160 {
161 gCrashHandlerCallback = nullptr;
162 SetUnhandledExceptionFilter(nullptr);
163 }
164
NumberOfProcessors()165 int NumberOfProcessors()
166 {
167 // A portable implementation could probably use GetLogicalProcessorInformation
168 return ::GetActiveProcessorCount(ALL_PROCESSOR_GROUPS);
169 }
170 } // namespace angle
171