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