rev |
line source |
nuclear@0
|
1 /************************************************************************************
|
nuclear@0
|
2
|
nuclear@0
|
3 Filename : ExceptionHandler.cpp
|
nuclear@0
|
4 Content : Platform-independent exception handling interface
|
nuclear@0
|
5 Created : October 6, 2014
|
nuclear@0
|
6
|
nuclear@0
|
7 Copyright : Copyright 2014 Oculus VR, LLC. All Rights reserved.
|
nuclear@0
|
8
|
nuclear@0
|
9 Licensed under the Apache License, Version 2.0 (the "License");
|
nuclear@0
|
10 you may not use this file except in compliance with the License.
|
nuclear@0
|
11 You may obtain a copy of the License at
|
nuclear@0
|
12
|
nuclear@0
|
13 http://www.apache.org/licenses/LICENSE-2.0
|
nuclear@0
|
14
|
nuclear@0
|
15 Unless required by applicable law or agreed to in writing, software
|
nuclear@0
|
16 distributed under the License is distributed on an "AS IS" BASIS,
|
nuclear@0
|
17 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
nuclear@0
|
18 See the License for the specific language governing permissions and
|
nuclear@0
|
19 limitations under the License.
|
nuclear@0
|
20
|
nuclear@0
|
21 ************************************************************************************/
|
nuclear@0
|
22
|
nuclear@0
|
23
|
nuclear@0
|
24 #include "OVR_DebugHelp.h"
|
nuclear@0
|
25 #include "OVR_Types.h"
|
nuclear@0
|
26 #include "OVR_UTF8Util.h"
|
nuclear@0
|
27 #include "../OVR_CAPI.h"
|
nuclear@0
|
28 #include "../OVR_CAPI_Keys.h"
|
nuclear@0
|
29 #include "../CAPI/CAPI_HMDState.h"
|
nuclear@0
|
30 #include <stdlib.h>
|
nuclear@0
|
31
|
nuclear@0
|
32 #if defined(OVR_OS_WIN32) || defined(OVR_OS_WIN64)
|
nuclear@0
|
33 #pragma warning(push, 0)
|
nuclear@0
|
34 #include <Windows.h>
|
nuclear@0
|
35 #include <WinNT.h>
|
nuclear@0
|
36 #include <DbgHelp.h>
|
nuclear@0
|
37 #include <WinVer.h>
|
nuclear@0
|
38 #include <share.h>
|
nuclear@0
|
39 #include <Psapi.h>
|
nuclear@0
|
40 #include <TlHelp32.h>
|
nuclear@0
|
41 #include <comutil.h>
|
nuclear@0
|
42 #include <Wbemcli.h>
|
nuclear@0
|
43 #include <Wbemidl.h>
|
nuclear@0
|
44 #include <ShlObj.h>
|
nuclear@0
|
45 #include <ObjBase.h>
|
nuclear@0
|
46 #pragma warning(pop)
|
nuclear@0
|
47
|
nuclear@0
|
48 #pragma comment(lib, "Psapi.lib") // To consider: It may be a problem to statically link to these libraries if the application at runtime intends to dynamically
|
nuclear@0
|
49 #pragma comment(lib, "ole32.lib") // link to a different version of the same library, and we are statically linked into that application instead of being a DLL.
|
nuclear@0
|
50 #pragma comment(lib, "shell32.lib")
|
nuclear@0
|
51
|
nuclear@0
|
52 // NtQueryInformation and THREAD_BASIC_INFORMATION are undocumented but frequently needed for digging into thread information.
|
nuclear@0
|
53 typedef LONG (WINAPI *NtQueryInformationThreadFunc)(HANDLE, int, PVOID, ULONG, PULONG);
|
nuclear@0
|
54
|
nuclear@0
|
55 struct THREAD_BASIC_INFORMATION
|
nuclear@0
|
56 {
|
nuclear@0
|
57 LONG ExitStatus;
|
nuclear@0
|
58 PVOID TebBaseAddress;
|
nuclear@0
|
59 PVOID UniqueProcessId;
|
nuclear@0
|
60 PVOID UniqueThreadId;
|
nuclear@0
|
61 PVOID Priority;
|
nuclear@0
|
62 PVOID BasePriority;
|
nuclear@0
|
63 };
|
nuclear@0
|
64
|
nuclear@0
|
65 #ifndef UNW_FLAG_NHANDLER // Older Windows SDKs don't support this.
|
nuclear@0
|
66 #define UNW_FLAG_NHANDLER 0
|
nuclear@0
|
67 #endif
|
nuclear@0
|
68
|
nuclear@0
|
69 #elif defined(OVR_OS_MAC)
|
nuclear@0
|
70 #include <unistd.h>
|
nuclear@0
|
71 #include <sys/sysctl.h>
|
nuclear@0
|
72 #include <sys/utsname.h>
|
nuclear@0
|
73 #include <sys/types.h>
|
nuclear@0
|
74 #include <sys/mman.h>
|
nuclear@0
|
75 #include <stdlib.h>
|
nuclear@0
|
76 #include <stdio.h>
|
nuclear@0
|
77 #include <pthread.h>
|
nuclear@0
|
78 #include <mach/mach.h>
|
nuclear@0
|
79 #include <mach/mach_error.h>
|
nuclear@0
|
80 #include <mach/thread_status.h>
|
nuclear@0
|
81 #include <mach/exception.h>
|
nuclear@0
|
82 #include <mach/task.h>
|
nuclear@0
|
83 #include <mach/thread_act.h>
|
nuclear@0
|
84 #include <mach-o/dyld.h>
|
nuclear@0
|
85 #include <mach-o/dyld_images.h>
|
nuclear@0
|
86 #include <libproc.h>
|
nuclear@0
|
87 #include <libgen.h>
|
nuclear@0
|
88 #include <execinfo.h>
|
nuclear@0
|
89 #include <cxxabi.h>
|
nuclear@0
|
90 #include "OVR_mach_exc_OSX.h"
|
nuclear@0
|
91
|
nuclear@0
|
92 #if defined(__LP64__)
|
nuclear@0
|
93 typedef struct mach_header_64 MachHeader;
|
nuclear@0
|
94 typedef struct segment_command_64 SegmentCommand;
|
nuclear@0
|
95 typedef struct section_64 Section;
|
nuclear@0
|
96 #define kLCSegment LC_SEGMENT_64
|
nuclear@0
|
97 #else
|
nuclear@0
|
98 typedef struct mach_header MachHeader;
|
nuclear@0
|
99 typedef struct segment_command SegmentCommand;
|
nuclear@0
|
100 typedef struct section Section;
|
nuclear@0
|
101 #define kLCSegment LC_SEGMENT
|
nuclear@0
|
102 #endif
|
nuclear@0
|
103
|
nuclear@0
|
104 extern "C" const struct dyld_all_image_infos* _dyld_get_all_image_infos(); // From libdyld.dylib
|
nuclear@0
|
105
|
nuclear@0
|
106 #elif defined(OVR_OS_UNIX)
|
nuclear@0
|
107 #include <unistd.h>
|
nuclear@0
|
108 #include <sys/sysctl.h>
|
nuclear@0
|
109 #include <sys/utsname.h>
|
nuclear@0
|
110 #include <sys/types.h>
|
nuclear@0
|
111 #include <sys/ptrace.h>
|
nuclear@0
|
112 #include <sys/wait.h>
|
nuclear@0
|
113 #include <sys/mman.h>
|
nuclear@0
|
114 #include <stdlib.h>
|
nuclear@0
|
115 #include <stdio.h>
|
nuclear@0
|
116 #include <pthread.h>
|
nuclear@0
|
117 #include <libgen.h>
|
nuclear@0
|
118 #include <execinfo.h>
|
nuclear@0
|
119 #include <cxxabi.h>
|
nuclear@0
|
120 //#include <libunwind.h> // Can't use this until we can ensure that we have an installed version of it.
|
nuclear@0
|
121 #endif
|
nuclear@0
|
122
|
nuclear@0
|
123 #if !defined(MIN)
|
nuclear@0
|
124 #define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
|
nuclear@0
|
125 #define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
|
nuclear@0
|
126 #endif
|
nuclear@0
|
127
|
nuclear@0
|
128
|
nuclear@0
|
129 OVR_DISABLE_MSVC_WARNING(4351) // new behavior: elements of array will be default initialized
|
nuclear@0
|
130 OVR_DISABLE_MSVC_WARNING(4996) // This function or variable may be unsafe
|
nuclear@0
|
131
|
nuclear@0
|
132
|
nuclear@0
|
133
|
nuclear@0
|
134
|
nuclear@0
|
135 #if defined(OVR_OS_APPLE)
|
nuclear@0
|
136 static OVR::ExceptionHandler* sExceptionHandler = nullptr;
|
nuclear@0
|
137 const uint32_t sMachCancelMessageType = 0x0ca9ce11; // This is a made-up value of our own choice.
|
nuclear@0
|
138
|
nuclear@0
|
139 extern "C"
|
nuclear@0
|
140 {
|
nuclear@0
|
141 kern_return_t catch_mach_exception_raise_OVR(mach_port_t /*exceptionPort*/, mach_port_t /*threadSysId*/,
|
nuclear@0
|
142 mach_port_t /*machTask*/, exception_type_t /*machExceptionType*/,
|
nuclear@0
|
143 mach_exception_data_t /*machExceptionData*/, mach_msg_type_number_t /*machExceptionDataCount*/)
|
nuclear@0
|
144 {
|
nuclear@0
|
145 return KERN_FAILURE;
|
nuclear@0
|
146 }
|
nuclear@0
|
147
|
nuclear@0
|
148 kern_return_t catch_mach_exception_raise_state_OVR(mach_port_t /*exceptionPort*/, exception_type_t /*exceptionType*/,
|
nuclear@0
|
149 const mach_exception_data_t /*machExceptionData*/, mach_msg_type_number_t /*machExceptionDataCount*/,
|
nuclear@0
|
150 int* /*pMachExceptionFlavor*/, const thread_state_t /*threadStatePrev*/, mach_msg_type_number_t /*threaStatePrevCount*/,
|
nuclear@0
|
151 thread_state_t /*threadStateNew*/, mach_msg_type_number_t* /*pThreadStateNewCount*/)
|
nuclear@0
|
152 {
|
nuclear@0
|
153 return KERN_FAILURE;
|
nuclear@0
|
154 }
|
nuclear@0
|
155
|
nuclear@0
|
156 kern_return_t catch_mach_exception_raise_state_identity_OVR(mach_port_t exceptionPort, mach_port_t threadSysId, mach_port_t machTask,
|
nuclear@0
|
157 exception_type_t exceptionType, mach_exception_data_type_t* machExceptionData,
|
nuclear@0
|
158 mach_msg_type_number_t machExceptionDataCount, int* pMachExceptionFlavor,
|
nuclear@0
|
159 thread_state_t threadStatePrev, mach_msg_type_number_t threadStatePrevCount,
|
nuclear@0
|
160 thread_state_t threadStateNew, mach_msg_type_number_t* pThreadStateNewCount)
|
nuclear@0
|
161 {
|
nuclear@0
|
162 return sExceptionHandler->HandleMachException(exceptionPort, threadSysId, machTask, exceptionType, machExceptionData,
|
nuclear@0
|
163 machExceptionDataCount, pMachExceptionFlavor, threadStatePrev, threadStatePrevCount,
|
nuclear@0
|
164 threadStateNew, pThreadStateNewCount);
|
nuclear@0
|
165 }
|
nuclear@0
|
166
|
nuclear@0
|
167 void* MachHandlerThreadFunctionStatic(void* pExceptionHandlerVoid)
|
nuclear@0
|
168 {
|
nuclear@0
|
169 return static_cast<OVR::ExceptionHandler*>(pExceptionHandlerVoid)->MachHandlerThreadFunction();
|
nuclear@0
|
170 }
|
nuclear@0
|
171
|
nuclear@0
|
172 } // extern "C"
|
nuclear@0
|
173 #endif
|
nuclear@0
|
174
|
nuclear@0
|
175
|
nuclear@0
|
176
|
nuclear@0
|
177
|
nuclear@0
|
178 namespace OVR {
|
nuclear@0
|
179
|
nuclear@0
|
180
|
nuclear@0
|
181 void GetInstructionPointer(void*& pInstruction)
|
nuclear@0
|
182 {
|
nuclear@0
|
183 #if defined(OVR_CC_MSVC)
|
nuclear@0
|
184 pInstruction = _ReturnAddress();
|
nuclear@0
|
185 #else // GCC, clang
|
nuclear@0
|
186 pInstruction = __builtin_return_address(0);
|
nuclear@0
|
187 #endif
|
nuclear@0
|
188 }
|
nuclear@0
|
189
|
nuclear@0
|
190
|
nuclear@0
|
191 static size_t SprintfAddress(char* threadHandleStr, size_t threadHandleStrCapacity, const void* pAddress)
|
nuclear@0
|
192 {
|
nuclear@0
|
193 #if defined(OVR_CC_MSVC)
|
nuclear@0
|
194 #if (OVR_PTR_SIZE >= 8)
|
nuclear@0
|
195 return OVR_snprintf(threadHandleStr, threadHandleStrCapacity, "0x%016I64x", pAddress); // e.g. 0x0123456789abcdef
|
nuclear@0
|
196 #else
|
nuclear@0
|
197 return OVR_snprintf(threadHandleStr, threadHandleStrCapacity, "0x%08x", pAddress); // e.g. 0x89abcdef
|
nuclear@0
|
198 #endif
|
nuclear@0
|
199 #else
|
nuclear@0
|
200 #if (OVR_PTR_SIZE >= 8)
|
nuclear@0
|
201 return OVR_snprintf(threadHandleStr, threadHandleStrCapacity, "%016llx", pAddress); // e.g. 0x0123456789abcdef
|
nuclear@0
|
202 #else
|
nuclear@0
|
203 return OVR_snprintf(threadHandleStr, threadHandleStrCapacity, "%08x", pAddress); // e.g. 0x89abcdef
|
nuclear@0
|
204 #endif
|
nuclear@0
|
205 #endif
|
nuclear@0
|
206 }
|
nuclear@0
|
207
|
nuclear@0
|
208
|
nuclear@0
|
209 static size_t SprintfThreadHandle(char* threadHandleStr, size_t threadHandleStrCapacity, const ThreadHandle& threadHandle)
|
nuclear@0
|
210 {
|
nuclear@0
|
211 return SprintfAddress(threadHandleStr, threadHandleStrCapacity, threadHandle);
|
nuclear@0
|
212 }
|
nuclear@0
|
213
|
nuclear@0
|
214
|
nuclear@0
|
215 static size_t SprintfThreadSysId(char* threadSysIdStr, size_t threadSysIdStrCapacity, const ThreadSysId& threadSysId)
|
nuclear@0
|
216 {
|
nuclear@0
|
217 #if defined(OVR_CC_MSVC) // Somebody could conceivably use VC++ with a different standard library that supports %ll. And VS2012+ also support %ll.
|
nuclear@0
|
218 return OVR_snprintf(threadSysIdStr, threadSysIdStrCapacity, "%I64u", (uint64_t)threadSysId);
|
nuclear@0
|
219 #else
|
nuclear@0
|
220 return OVR_snprintf(threadSysIdStr, threadSysIdStrCapacity, "%llu", (uint64_t)threadSysId);
|
nuclear@0
|
221 #endif
|
nuclear@0
|
222 }
|
nuclear@0
|
223
|
nuclear@0
|
224
|
nuclear@0
|
225
|
nuclear@0
|
226
|
nuclear@0
|
227
|
nuclear@0
|
228 void GetThreadStackBounds(void*& pStackBase, void*& pStackLimit, ThreadHandle threadHandle)
|
nuclear@0
|
229 {
|
nuclear@0
|
230 #if defined(OVR_OS_WIN64) || defined(OVR_OS_WIN32) || (defined(OVR_OS_MS) && defined(OVR_OS_CONSOLE))
|
nuclear@0
|
231 ThreadSysId threadSysIdCurrent = (ThreadSysId)GetCurrentThreadId();
|
nuclear@0
|
232 ThreadSysId threadSysId;
|
nuclear@0
|
233 NT_TIB* pTIB = nullptr;
|
nuclear@0
|
234
|
nuclear@0
|
235 if(threadHandle == OVR_THREADHANDLE_INVALID)
|
nuclear@0
|
236 threadSysId = threadSysIdCurrent;
|
nuclear@0
|
237 else
|
nuclear@0
|
238 threadSysId = ConvertThreadHandleToThreadSysId(threadHandle);
|
nuclear@0
|
239
|
nuclear@0
|
240 if(threadSysId == threadSysIdCurrent)
|
nuclear@0
|
241 {
|
nuclear@0
|
242 #if (OVR_PTR_SIZE == 4)
|
nuclear@0
|
243 // Need to use __asm__("movl %%fs:0x18, %0" : "=r" (pTIB) : : ); under gcc/clang.
|
nuclear@0
|
244 __asm {
|
nuclear@0
|
245 mov eax, fs:[18h]
|
nuclear@0
|
246 mov pTIB, eax
|
nuclear@0
|
247 }
|
nuclear@0
|
248 #else
|
nuclear@0
|
249 pTIB = (NT_TIB*)NtCurrentTeb();
|
nuclear@0
|
250 #endif
|
nuclear@0
|
251 }
|
nuclear@0
|
252 else
|
nuclear@0
|
253 {
|
nuclear@0
|
254 #if (OVR_PTR_SIZE == 4)
|
nuclear@0
|
255 // It turns out we don't need to suspend the thread when getting SegFs/SegGS, as that's
|
nuclear@0
|
256 // constant per thread and doesn't require the thread to be suspended.
|
nuclear@0
|
257 //SuspendThread((HANDLE)threadHandle);
|
nuclear@0
|
258 CONTEXT context;
|
nuclear@0
|
259 memset(&context, 0, sizeof(context));
|
nuclear@0
|
260 context.ContextFlags = CONTEXT_SEGMENTS;
|
nuclear@0
|
261 GetThreadContext((HANDLE)threadHandle, &context); // Requires THREAD_QUERY_INFORMATION privileges.
|
nuclear@0
|
262
|
nuclear@0
|
263 LDT_ENTRY ldtEntry;
|
nuclear@0
|
264 if(GetThreadSelectorEntry(threadHandle, context.SegFs, &ldtEntry)) // Requires THREAD_QUERY_INFORMATION
|
nuclear@0
|
265 pTIB = (NT_TIB*)((ldtEntry.HighWord.Bits.BaseHi << 24 ) | (ldtEntry.HighWord.Bits.BaseMid << 16) | ldtEntry.BaseLow);
|
nuclear@0
|
266
|
nuclear@0
|
267 //ResumeThread((HANDLE)threadHandle);
|
nuclear@0
|
268 #else
|
nuclear@0
|
269 // We cannot use GetThreadSelectorEntry or Wow64GetThreadSelectorEntry on Win64.
|
nuclear@0
|
270 // We need to read the SegGs qword at offset 0x30. We can't use pTIB = (NT_TIB*)__readgsqword(0x30) because that reads only the current setGs offset.
|
nuclear@0
|
271 // mov rax, qword ptr gs:[30h]
|
nuclear@0
|
272 // mov qword ptr [pTIB],rax
|
nuclear@0
|
273 // In the meantime we rely on the NtQueryInformationThread function.
|
nuclear@0
|
274
|
nuclear@0
|
275 static NtQueryInformationThreadFunc spNtQueryInformationThread = nullptr;
|
nuclear@0
|
276
|
nuclear@0
|
277 if(!spNtQueryInformationThread)
|
nuclear@0
|
278 {
|
nuclear@0
|
279 HMODULE hNTDLL = GetModuleHandleA("ntdll.dll");
|
nuclear@0
|
280 spNtQueryInformationThread = (NtQueryInformationThreadFunc)(uintptr_t)GetProcAddress(hNTDLL, "NtQueryInformationThread");
|
nuclear@0
|
281 }
|
nuclear@0
|
282
|
nuclear@0
|
283 if(spNtQueryInformationThread)
|
nuclear@0
|
284 {
|
nuclear@0
|
285 THREAD_BASIC_INFORMATION tbi;
|
nuclear@0
|
286
|
nuclear@0
|
287 memset(&tbi, 0, sizeof(tbi));
|
nuclear@0
|
288 LONG result = spNtQueryInformationThread(threadHandle, 0, &tbi, sizeof(tbi), nullptr); // Requires THREAD_QUERY_INFORMATION privileges
|
nuclear@0
|
289 if(result == 0)
|
nuclear@0
|
290 pTIB = (NT_TIB*)tbi.TebBaseAddress;
|
nuclear@0
|
291 }
|
nuclear@0
|
292 #endif
|
nuclear@0
|
293 }
|
nuclear@0
|
294
|
nuclear@0
|
295 if(pTIB)
|
nuclear@0
|
296 {
|
nuclear@0
|
297 pStackBase = (void*)pTIB->StackBase;
|
nuclear@0
|
298 pStackLimit = (void*)pTIB->StackLimit;
|
nuclear@0
|
299 }
|
nuclear@0
|
300 else
|
nuclear@0
|
301 {
|
nuclear@0
|
302 pStackBase = nullptr;
|
nuclear@0
|
303 pStackLimit = nullptr;
|
nuclear@0
|
304 }
|
nuclear@0
|
305
|
nuclear@0
|
306 #elif defined(OVR_OS_APPLE)
|
nuclear@0
|
307 if(!threadHandle)
|
nuclear@0
|
308 threadHandle = pthread_self();
|
nuclear@0
|
309
|
nuclear@0
|
310 pStackBase = pthread_get_stackaddr_np((pthread_t)threadHandle);
|
nuclear@0
|
311 size_t stackSize = pthread_get_stacksize_np((pthread_t)threadHandle);
|
nuclear@0
|
312 pStackLimit = (void*)((size_t)pStackBase - stackSize);
|
nuclear@0
|
313
|
nuclear@0
|
314 #elif defined(OVR_OS_UNIX)
|
nuclear@0
|
315 pStackBase = nullptr;
|
nuclear@0
|
316 pStackLimit = nullptr;
|
nuclear@0
|
317
|
nuclear@0
|
318 pthread_attr_t threadAttr;
|
nuclear@0
|
319 pthread_attr_init(&threadAttr);
|
nuclear@0
|
320
|
nuclear@0
|
321 #if defined(OVR_OS_LINUX)
|
nuclear@0
|
322 int result = pthread_getattr_np((pthread_t)threadHandle, &threadAttr);
|
nuclear@0
|
323 #else
|
nuclear@0
|
324 int result = pthread_attr_get_np((pthread_t)threadHandle, &threadAttr);
|
nuclear@0
|
325 #endif
|
nuclear@0
|
326
|
nuclear@0
|
327 if(result == 0)
|
nuclear@0
|
328 {
|
nuclear@0
|
329 size_t stackSize = 0;
|
nuclear@0
|
330 result = pthread_attr_getstack(&threadAttr, &pStackLimit, &stackSize);
|
nuclear@0
|
331
|
nuclear@0
|
332 if(result == 0)
|
nuclear@0
|
333 pStackBase = (void*)((uintptr_t)pStackLimit + stackSize); // We assume the stack grows downward.
|
nuclear@0
|
334 }
|
nuclear@0
|
335
|
nuclear@0
|
336 #endif
|
nuclear@0
|
337 }
|
nuclear@0
|
338
|
nuclear@0
|
339
|
nuclear@0
|
340 bool OVRIsDebuggerPresent()
|
nuclear@0
|
341 {
|
nuclear@0
|
342 #if defined(OVR_OS_MS)
|
nuclear@0
|
343 return ::IsDebuggerPresent() != 0;
|
nuclear@0
|
344
|
nuclear@0
|
345 #elif defined(OVR_OS_APPLE)
|
nuclear@0
|
346 int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() };
|
nuclear@0
|
347 struct kinfo_proc info;
|
nuclear@0
|
348 size_t size = sizeof(info);
|
nuclear@0
|
349
|
nuclear@0
|
350 info.kp_proc.p_flag = 0;
|
nuclear@0
|
351 sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0);
|
nuclear@0
|
352
|
nuclear@0
|
353 return ((info.kp_proc.p_flag & P_TRACED) != 0);
|
nuclear@0
|
354
|
nuclear@0
|
355 #elif (defined(OVR_OS_LINUX) || defined(OVR_OS_BSD)) && !defined(OVR_OS_ANDROID)
|
nuclear@0
|
356 // This works better than the PT_TRACE_ME approach.
|
nuclear@0
|
357 // However, it presents a problem:
|
nuclear@0
|
358 // http://pubs.opengroup.org/onlinepubs/009695399/functions/fork.html
|
nuclear@0
|
359 // When the application calls fork() from a signal handler and any of the
|
nuclear@0
|
360 // fork handlers registered by pthread_atfork() calls a function that is
|
nuclear@0
|
361 // not asynch-signal-safe, the behavior is undefined.
|
nuclear@0
|
362 // We may need to provide two pathways within this function, one of which
|
nuclear@0
|
363 // doesn't fork and instead uses PT_TRACE_ME.
|
nuclear@0
|
364 int pid = fork();
|
nuclear@0
|
365 int status;
|
nuclear@0
|
366 bool present = false;
|
nuclear@0
|
367
|
nuclear@0
|
368 if (pid == -1) // If fork failed...
|
nuclear@0
|
369 {
|
nuclear@0
|
370 // perror("fork");
|
nuclear@0
|
371 }
|
nuclear@0
|
372 else if (pid == 0) // If we are the child process...
|
nuclear@0
|
373 {
|
nuclear@0
|
374 int ppid = getppid();
|
nuclear@0
|
375
|
nuclear@0
|
376 #if defined(OVR_OS_LINUX)
|
nuclear@0
|
377 if (ptrace(PTRACE_ATTACH, ppid, nullptr, nullptr) == 0)
|
nuclear@0
|
378 #else
|
nuclear@0
|
379 if (ptrace(PT_ATTACH, ppid, nullptr, nullptr) == 0)
|
nuclear@0
|
380 #endif
|
nuclear@0
|
381 {
|
nuclear@0
|
382 waitpid(ppid, nullptr, 0);
|
nuclear@0
|
383
|
nuclear@0
|
384 #if defined(OVR_OS_LINUX)
|
nuclear@0
|
385 ptrace(PTRACE_CONT, getppid(), nullptr, nullptr);
|
nuclear@0
|
386 ptrace(PTRACE_DETACH, getppid(), nullptr, nullptr);
|
nuclear@0
|
387 #else
|
nuclear@0
|
388 ptrace(PT_CONTINUE, getppid(), nullptr, nullptr);
|
nuclear@0
|
389 ptrace(PT_DETACH, getppid(), nullptr, nullptr);
|
nuclear@0
|
390 #endif
|
nuclear@0
|
391 }
|
nuclear@0
|
392 else
|
nuclear@0
|
393 {
|
nuclear@0
|
394 // ptrace failed so the debugger is present.
|
nuclear@0
|
395 present = true;
|
nuclear@0
|
396 }
|
nuclear@0
|
397
|
nuclear@0
|
398 exit(present ? 1 : 0); // The WEXITSTATUS call below will read this exit value.
|
nuclear@0
|
399 }
|
nuclear@0
|
400 else // Else we are the original process.
|
nuclear@0
|
401 {
|
nuclear@0
|
402 waitpid(pid, &status, 0);
|
nuclear@0
|
403 present = WEXITSTATUS(status) ? true : false; // Read the exit value from the child's call to exit.
|
nuclear@0
|
404 }
|
nuclear@0
|
405
|
nuclear@0
|
406 return present;
|
nuclear@0
|
407
|
nuclear@0
|
408 #elif defined(PT_TRACE_ME) && !defined(OVR_OS_ANDROID)
|
nuclear@0
|
409 return (ptrace(PT_TRACE_ME, 0, 1, 0) < 0);
|
nuclear@0
|
410
|
nuclear@0
|
411 #else
|
nuclear@0
|
412 return false;
|
nuclear@0
|
413 #endif
|
nuclear@0
|
414 }
|
nuclear@0
|
415
|
nuclear@0
|
416
|
nuclear@0
|
417 // Exits the process with the given exit code.
|
nuclear@0
|
418 void ExitProcess(intptr_t processReturnValue)
|
nuclear@0
|
419 {
|
nuclear@0
|
420 exit((int)processReturnValue);
|
nuclear@0
|
421 }
|
nuclear@0
|
422
|
nuclear@0
|
423
|
nuclear@0
|
424 void* SafeMMapAlloc(size_t size)
|
nuclear@0
|
425 {
|
nuclear@0
|
426 #if defined(OVR_OS_MS)
|
nuclear@0
|
427 return VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); // size is rounded up to a page. // Returned memory is 0-filled.
|
nuclear@0
|
428
|
nuclear@0
|
429 #elif defined(OVR_OS_MAC) || defined(OVR_OS_UNIX)
|
nuclear@0
|
430 #if !defined(MAP_FAILED)
|
nuclear@0
|
431 #define MAP_FAILED ((void*)-1)
|
nuclear@0
|
432 #endif
|
nuclear@0
|
433
|
nuclear@0
|
434 void* result = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); // Returned memory is 0-filled.
|
nuclear@0
|
435 if(result == MAP_FAILED) // mmap returns MAP_FAILED (-1) upon failure.
|
nuclear@0
|
436 result = nullptr;
|
nuclear@0
|
437 return result;
|
nuclear@0
|
438 #endif
|
nuclear@0
|
439 }
|
nuclear@0
|
440
|
nuclear@0
|
441
|
nuclear@0
|
442 void SafeMMapFree(const void* memory, size_t size)
|
nuclear@0
|
443 {
|
nuclear@0
|
444 #if defined(OVR_OS_MS)
|
nuclear@0
|
445 OVR_UNUSED(size);
|
nuclear@0
|
446 VirtualFree(const_cast<void*>(memory), 0, MEM_RELEASE);
|
nuclear@0
|
447
|
nuclear@0
|
448 #elif defined(OVR_OS_MAC) || defined(OVR_OS_UNIX)
|
nuclear@0
|
449 size_t pageSize = getpagesize();
|
nuclear@0
|
450 size = (((size + (pageSize - 1)) / pageSize) * pageSize);
|
nuclear@0
|
451 munmap(const_cast<void*>(memory), size); // Must supply the size to munmap.
|
nuclear@0
|
452 #endif
|
nuclear@0
|
453 }
|
nuclear@0
|
454
|
nuclear@0
|
455
|
nuclear@0
|
456 // Note that we can't just return sizeof(void*) == 8, as we may have the case of a
|
nuclear@0
|
457 // 32 bit app running on a 64 bit operating system.
|
nuclear@0
|
458 static bool Is64BitOS()
|
nuclear@0
|
459 {
|
nuclear@0
|
460 #if (OVR_PTR_SIZE >= 8)
|
nuclear@0
|
461 return true;
|
nuclear@0
|
462
|
nuclear@0
|
463 #elif defined(OVR_OS_WIN32) || defined(OVR_OS_WIN64)
|
nuclear@0
|
464 BOOL is64BitOS = FALSE;
|
nuclear@0
|
465 bool IsWow64ProcessPresent = (GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "IsWow64Process") != nullptr);
|
nuclear@0
|
466 return (IsWow64ProcessPresent && IsWow64Process(GetCurrentProcess(), &is64BitOS) && is64BitOS);
|
nuclear@0
|
467
|
nuclear@0
|
468 #elif defined(OVR_OS_MAC) || defined(OVR_OS_UNIX)
|
nuclear@0
|
469 utsname utsName;
|
nuclear@0
|
470 memset(&utsName, 0, sizeof(utsName));
|
nuclear@0
|
471 return (uname(&utsName) == 0) && (strcmp(utsName.machine, "x86_64") == 0);
|
nuclear@0
|
472
|
nuclear@0
|
473 #else
|
nuclear@0
|
474 return false;
|
nuclear@0
|
475 #endif
|
nuclear@0
|
476 }
|
nuclear@0
|
477
|
nuclear@0
|
478
|
nuclear@0
|
479 // The output will always be 0-terminated.
|
nuclear@0
|
480 // Returns the required strlen of the output.
|
nuclear@0
|
481 // Returns (size_t)-1 on failure.
|
nuclear@0
|
482 size_t SpawnShellCommand(const char* shellCommand, char* output, size_t outputCapacity)
|
nuclear@0
|
483 {
|
nuclear@0
|
484 #if defined(OVR_OS_UNIX) || defined(OVR_OS_APPLE)
|
nuclear@0
|
485 FILE* pFile = popen(shellCommand, "r");
|
nuclear@0
|
486
|
nuclear@0
|
487 if(pFile)
|
nuclear@0
|
488 {
|
nuclear@0
|
489 size_t requiredLength = 0;
|
nuclear@0
|
490 char buffer[256];
|
nuclear@0
|
491
|
nuclear@0
|
492 while(fgets(buffer, sizeof(buffer), pFile)) // fgets 0-terminates the buffer.
|
nuclear@0
|
493 {
|
nuclear@0
|
494 size_t length = OVR_strlen(buffer);
|
nuclear@0
|
495 requiredLength += length;
|
nuclear@0
|
496
|
nuclear@0
|
497 if(outputCapacity)
|
nuclear@0
|
498 {
|
nuclear@0
|
499 OVR_strlcpy(output, buffer, outputCapacity);
|
nuclear@0
|
500 length = MIN(outputCapacity, length);
|
nuclear@0
|
501 }
|
nuclear@0
|
502
|
nuclear@0
|
503 output += length;
|
nuclear@0
|
504 outputCapacity -= length;
|
nuclear@0
|
505 }
|
nuclear@0
|
506
|
nuclear@0
|
507 pclose(pFile);
|
nuclear@0
|
508 return requiredLength;
|
nuclear@0
|
509 }
|
nuclear@0
|
510 #else
|
nuclear@0
|
511 // To do. Properly solving this on Windows requires a bit of code.
|
nuclear@0
|
512 OVR_UNUSED(shellCommand);
|
nuclear@0
|
513 OVR_UNUSED(output);
|
nuclear@0
|
514 OVR_UNUSED(outputCapacity);
|
nuclear@0
|
515 #endif
|
nuclear@0
|
516
|
nuclear@0
|
517 return (size_t)-1;
|
nuclear@0
|
518 }
|
nuclear@0
|
519
|
nuclear@0
|
520
|
nuclear@0
|
521 // Retrieves a directory path which ends with a path separator.
|
nuclear@0
|
522 // Returns the required strlen of the path.
|
nuclear@0
|
523 // Guarantees the presence of the directory upon returning true.
|
nuclear@0
|
524 static size_t GetUserDocumentsDirectory(char* directoryPath, size_t directoryPathCapacity)
|
nuclear@0
|
525 {
|
nuclear@0
|
526 #if defined(OVR_OS_MS)
|
nuclear@0
|
527 wchar_t pathW[MAX_PATH + 1]; // +1 because we append a path separator.
|
nuclear@0
|
528 HRESULT hr = SHGetFolderPathW(nullptr, CSIDL_APPDATA | CSIDL_FLAG_CREATE, nullptr, SHGFP_TYPE_CURRENT, pathW);
|
nuclear@0
|
529
|
nuclear@0
|
530 if(SUCCEEDED(hr))
|
nuclear@0
|
531 {
|
nuclear@0
|
532 OVR_UNUSED(directoryPathCapacity);
|
nuclear@0
|
533
|
nuclear@0
|
534 intptr_t requiredUTF8Length = OVR::UTF8Util::GetEncodeStringSize(pathW); // Returns required strlen.
|
nuclear@0
|
535 if(requiredUTF8Length < MAX_PATH) // We need space for a trailing path separator.
|
nuclear@0
|
536 {
|
nuclear@0
|
537 OVR::UTF8Util::EncodeString(directoryPath, pathW, -1);
|
nuclear@0
|
538 OVR::OVR_strlcat(directoryPath, "\\", directoryPathCapacity);
|
nuclear@0
|
539 }
|
nuclear@0
|
540
|
nuclear@0
|
541 return (requiredUTF8Length + 1);
|
nuclear@0
|
542 }
|
nuclear@0
|
543
|
nuclear@0
|
544 #elif defined(OVR_OS_MAC)
|
nuclear@0
|
545 // This is the same location that Apple puts its OS-generated .crash files.
|
nuclear@0
|
546 const char* home = getenv("HOME");
|
nuclear@0
|
547 size_t requiredStrlen = OVR::OVR_snprintf(directoryPath, directoryPathCapacity, "%s/Library/Logs/DiagnosticReports/", home ? home : "/Users/Shared/Logs/DiagnosticReports/");
|
nuclear@0
|
548 // To do: create the directory if it doesn't already exist.
|
nuclear@0
|
549 return requiredStrlen;
|
nuclear@0
|
550
|
nuclear@0
|
551 #elif defined(OVR_OS_UNIX) || defined(OVR_OS_MAC)
|
nuclear@0
|
552 const char* home = getenv("HOME");
|
nuclear@0
|
553 size_t requiredStrlen = OVR::OVR_snprintf(directoryPath, directoryPathCapacity, "%s/Library/", home ? home : "/Users/Shared/");
|
nuclear@0
|
554 // To do: create the directory if it doesn't already exist.
|
nuclear@0
|
555 return requiredStrlen;
|
nuclear@0
|
556 #endif
|
nuclear@0
|
557
|
nuclear@0
|
558 return 0;
|
nuclear@0
|
559 }
|
nuclear@0
|
560
|
nuclear@0
|
561
|
nuclear@0
|
562 // Retrieves the name of the given thread.
|
nuclear@0
|
563 // To do: Move this to OVR_Threads.h
|
nuclear@0
|
564 bool GetThreadName(OVR::ThreadHandle threadHandle, char* threadName, size_t threadNameCapacity)
|
nuclear@0
|
565 {
|
nuclear@0
|
566 #if defined(OVR_OS_APPLE) || defined(OVR_OS_LINUX)
|
nuclear@0
|
567 int result = pthread_getname_np((pthread_t)threadHandle, threadName, threadNameCapacity);
|
nuclear@0
|
568 if(result == 0)
|
nuclear@0
|
569 return true;
|
nuclear@0
|
570 #else
|
nuclear@0
|
571 // This is not currently possible on Windows, as only the debugger stores the thread name. We could potentially use a vectored
|
nuclear@0
|
572 // exception handler to catch all thread name exceptions (0x406d1388) and record them in a static list at runtime. To detect
|
nuclear@0
|
573 // thread exit we could use WMI Win32_ThreadStopTrace. Maintain a list of thread names between these two events.
|
nuclear@0
|
574 OVR_UNUSED(threadHandle);
|
nuclear@0
|
575 OVR_UNUSED(threadNameCapacity);
|
nuclear@0
|
576 #endif
|
nuclear@0
|
577
|
nuclear@0
|
578 if(threadNameCapacity)
|
nuclear@0
|
579 threadName[0] = 0;
|
nuclear@0
|
580
|
nuclear@0
|
581 return false;
|
nuclear@0
|
582 }
|
nuclear@0
|
583
|
nuclear@0
|
584
|
nuclear@0
|
585 OVR::ThreadSysId ConvertThreadHandleToThreadSysId(OVR::ThreadHandle threadHandle)
|
nuclear@0
|
586 {
|
nuclear@0
|
587 #if defined(OVR_OS_WIN64)
|
nuclear@0
|
588 return (OVR::ThreadSysId)::GetThreadId(threadHandle); // Requires THREAD_QUERY_INFORMATION privileges.
|
nuclear@0
|
589
|
nuclear@0
|
590 #elif defined(OVR_OS_WIN32)
|
nuclear@0
|
591 typedef DWORD (WINAPI *GetThreadIdFunc)(HANDLE);
|
nuclear@0
|
592
|
nuclear@0
|
593 static volatile bool sInitialized = false;
|
nuclear@0
|
594 static GetThreadIdFunc spGetThreadIdFunc = nullptr;
|
nuclear@0
|
595 static NtQueryInformationThreadFunc spNtQueryInformationThread = nullptr;
|
nuclear@0
|
596
|
nuclear@0
|
597 if(!sInitialized)
|
nuclear@0
|
598 {
|
nuclear@0
|
599 HMODULE hKernel32 = GetModuleHandleA("kernel32.dll");
|
nuclear@0
|
600 if(hKernel32)
|
nuclear@0
|
601 spGetThreadIdFunc = (GetThreadIdFunc)(uintptr_t)GetProcAddress(hKernel32, "GetThreadId");
|
nuclear@0
|
602
|
nuclear@0
|
603 if(!spGetThreadIdFunc)
|
nuclear@0
|
604 {
|
nuclear@0
|
605 HMODULE hNTDLL = GetModuleHandleA("ntdll.dll");
|
nuclear@0
|
606
|
nuclear@0
|
607 if(hNTDLL)
|
nuclear@0
|
608 spNtQueryInformationThread = (NtQueryInformationThreadFunc)(uintptr_t)GetProcAddress(hNTDLL, "NtQueryInformationThread");
|
nuclear@0
|
609 }
|
nuclear@0
|
610
|
nuclear@0
|
611 sInitialized = true;
|
nuclear@0
|
612 }
|
nuclear@0
|
613
|
nuclear@0
|
614 if(spGetThreadIdFunc)
|
nuclear@0
|
615 return (OVR::ThreadSysId)spGetThreadIdFunc(threadHandle);
|
nuclear@0
|
616
|
nuclear@0
|
617 if(spNtQueryInformationThread)
|
nuclear@0
|
618 {
|
nuclear@0
|
619 THREAD_BASIC_INFORMATION tbi;
|
nuclear@0
|
620
|
nuclear@0
|
621 if(spNtQueryInformationThread(threadHandle, 0, &tbi, sizeof(tbi), nullptr) == 0)
|
nuclear@0
|
622 return (OVR::ThreadSysId)tbi.UniqueThreadId;
|
nuclear@0
|
623 }
|
nuclear@0
|
624
|
nuclear@0
|
625 return OVR_THREADSYSID_INVALID;
|
nuclear@0
|
626
|
nuclear@0
|
627 #elif defined(OVR_OS_APPLE)
|
nuclear@0
|
628 mach_port_t threadSysId = pthread_mach_thread_np((pthread_t)threadHandle); // OS 10.4 and later.
|
nuclear@0
|
629 return (ThreadSysId)threadSysId;
|
nuclear@0
|
630
|
nuclear@0
|
631 #elif defined(OVR_OS_LINUX)
|
nuclear@0
|
632
|
nuclear@0
|
633 // I believe we can usually (though not portably) intepret the pthread_t as a pointer to a struct whose first member is a lwp id.
|
nuclear@0
|
634 OVR_UNUSED(threadHandle);
|
nuclear@0
|
635 return OVR_THREADSYSID_INVALID;
|
nuclear@0
|
636
|
nuclear@0
|
637 #else
|
nuclear@0
|
638 OVR_UNUSED(threadHandle);
|
nuclear@0
|
639 return OVR_THREADSYSID_INVALID;
|
nuclear@0
|
640 #endif
|
nuclear@0
|
641 }
|
nuclear@0
|
642
|
nuclear@0
|
643
|
nuclear@0
|
644 OVR::ThreadHandle ConvertThreadSysIdToThreadHandle(OVR::ThreadSysId threadSysId)
|
nuclear@0
|
645 {
|
nuclear@0
|
646 #if defined(OVR_OS_MS)
|
nuclear@0
|
647 // We currently request the given rights because that's what users of this function typically need it for. Ideally there would
|
nuclear@0
|
648 // be a way to specify the requested rights in order to avoid the problem if we need only a subset of them but can't get it.
|
nuclear@0
|
649 // The solution we use below to try opening with successively reduced rights will work for our uses here but isn't a good general solution to this.
|
nuclear@0
|
650 OVR::ThreadHandle threadHandle = ::OpenThread(THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION, TRUE, (DWORD)threadSysId);
|
nuclear@0
|
651
|
nuclear@0
|
652 if(threadHandle == OVR_THREADHANDLE_INVALID)
|
nuclear@0
|
653 {
|
nuclear@0
|
654 threadHandle = ::OpenThread(THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION, TRUE, (DWORD)threadSysId);
|
nuclear@0
|
655
|
nuclear@0
|
656 if(threadHandle == OVR_THREADHANDLE_INVALID)
|
nuclear@0
|
657 threadHandle = ::OpenThread(THREAD_QUERY_INFORMATION, TRUE, (DWORD)threadSysId);
|
nuclear@0
|
658 }
|
nuclear@0
|
659
|
nuclear@0
|
660 return threadHandle;
|
nuclear@0
|
661 #elif defined(OVR_OS_MAC)
|
nuclear@0
|
662 return (ThreadHandle)pthread_from_mach_thread_np((mach_port_t)threadSysId);
|
nuclear@0
|
663 #else
|
nuclear@0
|
664 return (ThreadHandle)threadSysId;
|
nuclear@0
|
665 #endif
|
nuclear@0
|
666 }
|
nuclear@0
|
667
|
nuclear@0
|
668
|
nuclear@0
|
669 void FreeThreadHandle(OVR::ThreadHandle threadHandle)
|
nuclear@0
|
670 {
|
nuclear@0
|
671 #if defined(OVR_OS_MS)
|
nuclear@0
|
672 if(threadHandle != OVR_THREADHANDLE_INVALID)
|
nuclear@0
|
673 ::CloseHandle(threadHandle);
|
nuclear@0
|
674 #else
|
nuclear@0
|
675 OVR_UNUSED(threadHandle);
|
nuclear@0
|
676 #endif
|
nuclear@0
|
677 }
|
nuclear@0
|
678
|
nuclear@0
|
679
|
nuclear@0
|
680 OVR::ThreadSysId GetCurrentThreadSysId()
|
nuclear@0
|
681 {
|
nuclear@0
|
682 #if defined(OVR_OS_MS)
|
nuclear@0
|
683 return ::GetCurrentThreadId();
|
nuclear@0
|
684 #elif defined(OVR_OS_APPLE)
|
nuclear@0
|
685 return (ThreadSysId)mach_thread_self();
|
nuclear@0
|
686 #else
|
nuclear@0
|
687 return (ThreadSysId)pthread_self();
|
nuclear@0
|
688 #endif
|
nuclear@0
|
689 }
|
nuclear@0
|
690
|
nuclear@0
|
691
|
nuclear@0
|
692
|
nuclear@0
|
693 static void GetCurrentProcessFilePath(char* appPath, size_t appPathCapacity)
|
nuclear@0
|
694 {
|
nuclear@0
|
695 appPath[0] = 0;
|
nuclear@0
|
696
|
nuclear@0
|
697 #if defined(OVR_OS_MS)
|
nuclear@0
|
698 wchar_t pathW[MAX_PATH];
|
nuclear@0
|
699 GetModuleFileNameW(0, pathW, (DWORD)OVR_ARRAY_COUNT(pathW));
|
nuclear@0
|
700
|
nuclear@0
|
701 size_t requiredUTF8Length = (size_t)OVR::UTF8Util::GetEncodeStringSize(pathW); // Returns required strlen.
|
nuclear@0
|
702
|
nuclear@0
|
703 if(requiredUTF8Length < appPathCapacity)
|
nuclear@0
|
704 {
|
nuclear@0
|
705 OVR::UTF8Util::EncodeString(appPath, pathW, -1);
|
nuclear@0
|
706 }
|
nuclear@0
|
707 else
|
nuclear@0
|
708 {
|
nuclear@0
|
709 appPath[0] = 0;
|
nuclear@0
|
710 }
|
nuclear@0
|
711
|
nuclear@0
|
712 #elif defined(OVR_OS_APPLE)
|
nuclear@0
|
713 struct BunderFolder
|
nuclear@0
|
714 {
|
nuclear@0
|
715 // Returns true if pStr ends with pFind, case insensitively.
|
nuclear@0
|
716 // To do: Move OVR_striend to OVRKernel/Std.h
|
nuclear@0
|
717 static bool OVR_striend(const char* pStr, const char* pFind, size_t strLength = (size_t)-1, size_t findLength = (size_t)-1)
|
nuclear@0
|
718 {
|
nuclear@0
|
719 if(strLength == (size_t)-1)
|
nuclear@0
|
720 strLength = OVR_strlen(pStr);
|
nuclear@0
|
721 if(findLength == (size_t)-1)
|
nuclear@0
|
722 findLength = OVR_strlen(pFind);
|
nuclear@0
|
723 if(strLength >= findLength)
|
nuclear@0
|
724 return (OVR_stricmp(pStr + strLength - findLength, pFind) == 0);
|
nuclear@0
|
725 return false;
|
nuclear@0
|
726 }
|
nuclear@0
|
727
|
nuclear@0
|
728 static bool IsBundleFolder(const char* filePath)
|
nuclear@0
|
729 {
|
nuclear@0
|
730 // https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/AboutBundles/AboutBundles.html#//apple_ref/doc/uid/10000123i-CH100-SW1
|
nuclear@0
|
731 static const char* extensionArray[] = { ".app", ".bundle", ".framework", ".plugin", ".kext" };
|
nuclear@0
|
732
|
nuclear@0
|
733 for(size_t i = 0; i < OVR_ARRAY_COUNT(extensionArray); i++)
|
nuclear@0
|
734 {
|
nuclear@0
|
735 if(OVR_striend(filePath, extensionArray[i]))
|
nuclear@0
|
736 return true;
|
nuclear@0
|
737 }
|
nuclear@0
|
738
|
nuclear@0
|
739 return false;
|
nuclear@0
|
740 }
|
nuclear@0
|
741 };
|
nuclear@0
|
742
|
nuclear@0
|
743 char appPathTemp[PATH_MAX];
|
nuclear@0
|
744 uint32_t appPathTempCapacity32 = PATH_MAX;
|
nuclear@0
|
745 size_t requiredStrlen = appPathCapacity;
|
nuclear@0
|
746
|
nuclear@0
|
747 if(_NSGetExecutablePath(appPathTemp, &appPathTempCapacity32) == 0)
|
nuclear@0
|
748 {
|
nuclear@0
|
749 char appPathTempReal[PATH_MAX];
|
nuclear@0
|
750
|
nuclear@0
|
751 if(realpath(appPathTemp, appPathTempReal)) // If the path is a symbolic link, this converts it to the real path.
|
nuclear@0
|
752 {
|
nuclear@0
|
753 // To consider: Enable reading the internal bundle executable path. An application on Mac may in
|
nuclear@0
|
754 // fact be within a file bundle, which is an private file system within a file. With Objective C
|
nuclear@0
|
755 // we could use: [[NSWorkspace sharedWorkspace] isFilePackageAtPath:fullPath];
|
nuclear@0
|
756 bool shouldReadTheBunderPath = false;
|
nuclear@0
|
757
|
nuclear@0
|
758 if(shouldReadTheBunderPath)
|
nuclear@0
|
759 {
|
nuclear@0
|
760 // We recursively call dirname() until we find .app/.bundle/.plugin as a directory name.
|
nuclear@0
|
761 OVR_strlcpy(appPathTemp, appPathTempReal, OVR_ARRAY_COUNT(appPathTemp));
|
nuclear@0
|
762 bool found = BunderFolder::IsBundleFolder(appPathTemp);
|
nuclear@0
|
763
|
nuclear@0
|
764 while(!found && OVR_strcmp(appPathTemp, ".") && OVR_strcmp(appPathTemp, "/"))
|
nuclear@0
|
765 {
|
nuclear@0
|
766 OVR_strlcpy(appPathTemp, dirname(appPathTemp), OVR_ARRAY_COUNT(appPathTemp));
|
nuclear@0
|
767 found = BunderFolder::IsBundleFolder(appPathTemp);
|
nuclear@0
|
768 }
|
nuclear@0
|
769
|
nuclear@0
|
770 if(found) // If somewhere above we found a parent bundle container...
|
nuclear@0
|
771 requiredStrlen = OVR_strlcpy(appPath, appPathTemp, appPathCapacity);
|
nuclear@0
|
772 else
|
nuclear@0
|
773 requiredStrlen = OVR_strlcpy(appPath, appPathTempReal, appPathCapacity);
|
nuclear@0
|
774 }
|
nuclear@0
|
775 else
|
nuclear@0
|
776 {
|
nuclear@0
|
777 requiredStrlen = OVR_strlcpy(appPath, appPathTempReal, appPathCapacity);
|
nuclear@0
|
778 }
|
nuclear@0
|
779 }
|
nuclear@0
|
780 }
|
nuclear@0
|
781
|
nuclear@0
|
782 if(requiredStrlen >= appPathCapacity)
|
nuclear@0
|
783 appPath[0] = '\0';
|
nuclear@0
|
784
|
nuclear@0
|
785 #elif defined(OVR_OS_LINUX)
|
nuclear@0
|
786 ssize_t length = readlink("/proc/self/exe", appPath, appPathCapacity);
|
nuclear@0
|
787
|
nuclear@0
|
788 if((length != -1) && ((size_t)length < (appPathCapacity - 1)))
|
nuclear@0
|
789 {
|
nuclear@0
|
790 appPath[length] = '\0';
|
nuclear@0
|
791 }
|
nuclear@0
|
792 #endif
|
nuclear@0
|
793 }
|
nuclear@0
|
794
|
nuclear@0
|
795
|
nuclear@0
|
796 static const char* GetFileNameFromPath(const char* filePath)
|
nuclear@0
|
797 {
|
nuclear@0
|
798 #if defined(OVR_OS_MS)
|
nuclear@0
|
799 const char* lastPathSeparator = max(strrchr(filePath, '\\'), strrchr(filePath, '/')); // Microsoft APIs are inconsistent with respect to allowing / as a path separator.
|
nuclear@0
|
800 #else
|
nuclear@0
|
801 const char* lastPathSeparator = strrchr(filePath, '/');
|
nuclear@0
|
802 #endif
|
nuclear@0
|
803
|
nuclear@0
|
804 if(lastPathSeparator)
|
nuclear@0
|
805 return lastPathSeparator + 1;
|
nuclear@0
|
806
|
nuclear@0
|
807 return filePath;
|
nuclear@0
|
808 }
|
nuclear@0
|
809
|
nuclear@0
|
810
|
nuclear@0
|
811
|
nuclear@0
|
812 static void FormatDateTime(char* buffer, size_t bufferCapacity, time_t timeValue, bool getDate, bool getTime, bool localDateTime, bool fileNameSafeCharacters = false)
|
nuclear@0
|
813 {
|
nuclear@0
|
814 char temp[128];
|
nuclear@0
|
815 const tm* pTime = localDateTime ? localtime(&timeValue) : gmtime(&timeValue);
|
nuclear@0
|
816
|
nuclear@0
|
817 if(bufferCapacity)
|
nuclear@0
|
818 buffer[0] = 0;
|
nuclear@0
|
819
|
nuclear@0
|
820 if(getDate)
|
nuclear@0
|
821 {
|
nuclear@0
|
822 const char* format = fileNameSafeCharacters ? "%Y-%m-%d" : "%Y/%m/%d";
|
nuclear@0
|
823 strftime(temp, OVR_ARRAY_COUNT(temp), format, pTime);
|
nuclear@0
|
824 OVR::OVR_strlcpy(buffer, temp, bufferCapacity);
|
nuclear@0
|
825 }
|
nuclear@0
|
826
|
nuclear@0
|
827 if(getTime)
|
nuclear@0
|
828 {
|
nuclear@0
|
829 const char* format = fileNameSafeCharacters ? " %H.%M.%S" : " %H:%M:%S";
|
nuclear@0
|
830 strftime(temp, OVR_ARRAY_COUNT(temp), (getDate ? format : format + 1), pTime);
|
nuclear@0
|
831 OVR::OVR_strlcat(buffer, temp, bufferCapacity);
|
nuclear@0
|
832 }
|
nuclear@0
|
833 }
|
nuclear@0
|
834
|
nuclear@0
|
835
|
nuclear@0
|
836 static void GetOSVersionName(char* versionName, size_t versionNameCapacity)
|
nuclear@0
|
837 {
|
nuclear@0
|
838 #if defined(OVR_OS_MS)
|
nuclear@0
|
839 const char* name = "unknown";
|
nuclear@0
|
840
|
nuclear@0
|
841 OSVERSIONINFOEXW vi;
|
nuclear@0
|
842 memset(&vi, 0, sizeof(vi));
|
nuclear@0
|
843 vi.dwOSVersionInfoSize = sizeof(vi);
|
nuclear@0
|
844
|
nuclear@0
|
845 if(GetVersionExW((LPOSVERSIONINFOW)&vi))
|
nuclear@0
|
846 {
|
nuclear@0
|
847 if(vi.dwMajorVersion >= 7)
|
nuclear@0
|
848 {
|
nuclear@0
|
849 // Unknown recent version.
|
nuclear@0
|
850 }
|
nuclear@0
|
851 if(vi.dwMajorVersion >= 6)
|
nuclear@0
|
852 {
|
nuclear@0
|
853 if(vi.dwMinorVersion >= 4)
|
nuclear@0
|
854 name = "Windows 10";
|
nuclear@0
|
855 else if(vi.dwMinorVersion >= 3)
|
nuclear@0
|
856 {
|
nuclear@0
|
857 if(vi.wProductType == VER_NT_WORKSTATION)
|
nuclear@0
|
858 name = "Windows 8.1";
|
nuclear@0
|
859 else
|
nuclear@0
|
860 name = "Windows Server 2012 R2";
|
nuclear@0
|
861 }
|
nuclear@0
|
862 else if(vi.dwMinorVersion >= 2)
|
nuclear@0
|
863 {
|
nuclear@0
|
864 if(vi.wProductType == VER_NT_WORKSTATION)
|
nuclear@0
|
865 name = "Windows 8";
|
nuclear@0
|
866 else
|
nuclear@0
|
867 name = "Windows Server 2012";
|
nuclear@0
|
868 }
|
nuclear@0
|
869 else if(vi.dwMinorVersion >= 1)
|
nuclear@0
|
870 {
|
nuclear@0
|
871 if(vi.wProductType == VER_NT_WORKSTATION)
|
nuclear@0
|
872 name = "Windows 7";
|
nuclear@0
|
873 else
|
nuclear@0
|
874 name = "Windows Server 2008 R2";
|
nuclear@0
|
875 }
|
nuclear@0
|
876 else
|
nuclear@0
|
877 {
|
nuclear@0
|
878 if(vi.wProductType == VER_NT_WORKSTATION)
|
nuclear@0
|
879 name = "Windows Vista";
|
nuclear@0
|
880 else
|
nuclear@0
|
881 name = "Windows Server 2008";
|
nuclear@0
|
882 }
|
nuclear@0
|
883 }
|
nuclear@0
|
884 else if(vi.dwMajorVersion >= 5)
|
nuclear@0
|
885 {
|
nuclear@0
|
886 if(vi.dwMinorVersion == 0)
|
nuclear@0
|
887 name = "Windows 2000";
|
nuclear@0
|
888 else if(vi.dwMinorVersion == 1)
|
nuclear@0
|
889 name = "Windows XP";
|
nuclear@0
|
890 else // vi.dwMinorVersion == 2
|
nuclear@0
|
891 {
|
nuclear@0
|
892 if(GetSystemMetrics(SM_SERVERR2) != 0)
|
nuclear@0
|
893 name = "Windows Server 2003 R2";
|
nuclear@0
|
894 else if(vi.wSuiteMask & VER_SUITE_WH_SERVER)
|
nuclear@0
|
895 name = "Windows Home Server";
|
nuclear@0
|
896 if(GetSystemMetrics(SM_SERVERR2) == 0)
|
nuclear@0
|
897 name = "Windows Server 2003";
|
nuclear@0
|
898 else
|
nuclear@0
|
899 name = "Windows XP Professional x64 Edition";
|
nuclear@0
|
900 }
|
nuclear@0
|
901 }
|
nuclear@0
|
902 else
|
nuclear@0
|
903 name = "Windows 98 or earlier";
|
nuclear@0
|
904 }
|
nuclear@0
|
905
|
nuclear@0
|
906 OVR_strlcpy(versionName, name, versionNameCapacity);
|
nuclear@0
|
907
|
nuclear@0
|
908 #elif defined(OVR_OS_UNIX) || defined(OVR_OS_APPLE)
|
nuclear@0
|
909 utsname utsName;
|
nuclear@0
|
910 memset(&utsName, 0, sizeof(utsName));
|
nuclear@0
|
911
|
nuclear@0
|
912 if(uname(&utsName) == 0)
|
nuclear@0
|
913 OVR_snprintf(versionName, versionNameCapacity, "%s %s %s %s", utsName.sysname, utsName.release, utsName.version, utsName.machine);
|
nuclear@0
|
914 else
|
nuclear@0
|
915 OVR_snprintf(versionName, versionNameCapacity, "Unix");
|
nuclear@0
|
916 #endif
|
nuclear@0
|
917 }
|
nuclear@0
|
918
|
nuclear@0
|
919
|
nuclear@0
|
920
|
nuclear@0
|
921
|
nuclear@0
|
922 void CreateException(CreateExceptionType exceptionType)
|
nuclear@0
|
923 {
|
nuclear@0
|
924 char buffer[1024] = {};
|
nuclear@0
|
925
|
nuclear@0
|
926 switch(exceptionType)
|
nuclear@0
|
927 {
|
nuclear@0
|
928 case kCETAccessViolation:
|
nuclear@0
|
929 {
|
nuclear@0
|
930 int* pNullPtr = reinterpret_cast<int*>((rand() / 2) / RAND_MAX);
|
nuclear@0
|
931 pNullPtr[0] = 0; // This line should generate an exception.
|
nuclear@0
|
932 sprintf(buffer, "%p", pNullPtr);
|
nuclear@0
|
933 break;
|
nuclear@0
|
934 }
|
nuclear@0
|
935
|
nuclear@0
|
936 case kCETDivideByZero:
|
nuclear@0
|
937 {
|
nuclear@0
|
938 int smallValue = 1;
|
nuclear@0
|
939 int largeValue = (1000 * exceptionType);
|
nuclear@0
|
940 int divByZero = (smallValue / largeValue); // This line should generate a div/0 exception.
|
nuclear@0
|
941 sprintf(buffer, "%d", divByZero);
|
nuclear@0
|
942 break;
|
nuclear@0
|
943 }
|
nuclear@0
|
944
|
nuclear@0
|
945 case kCETIllegalInstruction:
|
nuclear@0
|
946 {
|
nuclear@0
|
947 #if defined(OVR_CPU_X86) || (defined(OVR_CPU_X86_64) && !defined(OVR_CC_MSVC)) // (if x86) or (if x64 and any computer but VC++)...
|
nuclear@0
|
948 #if defined(OVR_CC_MSVC)
|
nuclear@0
|
949 __asm ud2
|
nuclear@0
|
950 #else // e.g. GCC
|
nuclear@0
|
951 asm volatile("ud2");
|
nuclear@0
|
952 #endif
|
nuclear@0
|
953
|
nuclear@0
|
954 #elif defined(OVR_CPU_X86_64) && (defined(OVR_OS_MS) && defined(PAGE_EXECUTE_READWRITE))
|
nuclear@0
|
955 // VC++ for x64 doesn't support inline asm.
|
nuclear@0
|
956 void* pVoid = _AddressOfReturnAddress();
|
nuclear@0
|
957 void** ppVoid = reinterpret_cast<void**>(pVoid);
|
nuclear@0
|
958 void* pReturnAddress = *ppVoid;
|
nuclear@0
|
959 DWORD dwProtectPrev = 0;
|
nuclear@0
|
960
|
nuclear@0
|
961 if(VirtualProtect(pReturnAddress, 2, PAGE_EXECUTE_READWRITE, &dwProtectPrev)) // If we can set the memory to be executable...
|
nuclear@0
|
962 {
|
nuclear@0
|
963 // Modify the code we return to.
|
nuclear@0
|
964 uint8_t asm_ud2[] = { 0x0f, 0x0b };
|
nuclear@0
|
965 memcpy(pReturnAddress, asm_ud2, sizeof(asm_ud2));
|
nuclear@0
|
966 VirtualProtect(pReturnAddress, 2, dwProtectPrev, &dwProtectPrev);
|
nuclear@0
|
967 }
|
nuclear@0
|
968 else
|
nuclear@0
|
969 {
|
nuclear@0
|
970 // To do: Fix this.
|
nuclear@0
|
971 }
|
nuclear@0
|
972
|
nuclear@0
|
973 #else
|
nuclear@0
|
974 // To do: Fix this.
|
nuclear@0
|
975 #endif
|
nuclear@0
|
976
|
nuclear@0
|
977 break;
|
nuclear@0
|
978 }
|
nuclear@0
|
979
|
nuclear@0
|
980 case kCETStackCorruption:
|
nuclear@0
|
981 {
|
nuclear@0
|
982 size_t size = (sizeof(buffer) * 16) - (rand() % 16);
|
nuclear@0
|
983 char* pOutsizeStack = buffer - ((sizeof(buffer) * 16) + (rand() % 16));
|
nuclear@0
|
984
|
nuclear@0
|
985 memset(buffer, 0, size);
|
nuclear@0
|
986 memset(pOutsizeStack, 0, size); // This line should generate an exception, or an exception will be generated upon return from this function.
|
nuclear@0
|
987 break;
|
nuclear@0
|
988 }
|
nuclear@0
|
989
|
nuclear@0
|
990 case kCETStackOverflow:
|
nuclear@0
|
991 {
|
nuclear@0
|
992 CreateException(exceptionType); // Call ourselves recursively. This line should generate a div/0 exception.
|
nuclear@0
|
993 sprintf(buffer, "%d", exceptionType);
|
nuclear@0
|
994 break;
|
nuclear@0
|
995 }
|
nuclear@0
|
996
|
nuclear@0
|
997 case kCETAlignment:
|
nuclear@0
|
998 {
|
nuclear@0
|
999 // Not all platforms generate alignment exceptions. Some internally handle it.
|
nuclear@0
|
1000 void* pAligned = malloc(16);
|
nuclear@0
|
1001 char* pMisaligned = (char*)pAligned + 1;
|
nuclear@0
|
1002 uint64_t* pMisaligned64 = reinterpret_cast<uint64_t*>(pMisaligned);
|
nuclear@0
|
1003
|
nuclear@0
|
1004 *pMisaligned64 = 0; // This line should generate an exception.
|
nuclear@0
|
1005 free(pAligned);
|
nuclear@0
|
1006 break;
|
nuclear@0
|
1007 }
|
nuclear@0
|
1008
|
nuclear@0
|
1009 case kCETFPU:
|
nuclear@0
|
1010 // Platforms usually have FPU exceptions disabled. In order to test FPU exceptions we will need to at least
|
nuclear@0
|
1011 // temporarily disable them before executing code here to generate such exceptions.
|
nuclear@0
|
1012 // To do.
|
nuclear@0
|
1013 break;
|
nuclear@0
|
1014
|
nuclear@0
|
1015 case kCETTrap:
|
nuclear@0
|
1016 // To do. This is hardware-specific.
|
nuclear@0
|
1017 break;
|
nuclear@0
|
1018 }
|
nuclear@0
|
1019 }
|
nuclear@0
|
1020
|
nuclear@0
|
1021
|
nuclear@0
|
1022
|
nuclear@0
|
1023
|
nuclear@0
|
1024 #if defined(OVR_OS_MS)
|
nuclear@0
|
1025 typedef BOOL (WINAPI * StackWalk64Type)(DWORD MachineType, HANDLE hProcess, HANDLE hThread, LPSTACKFRAME64 StackFrame, PVOID ContextRecord, PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress);
|
nuclear@0
|
1026 typedef PVOID (WINAPI * SymFunctionTableAccess64Type)(HANDLE hProcess, DWORD64 dwAddr);
|
nuclear@0
|
1027 typedef DWORD64 (WINAPI * SymGetModuleBase64Type)(HANDLE hProcess, DWORD64 dwAddr);
|
nuclear@0
|
1028 typedef DWORD (WINAPI * SymSetOptionsType)(DWORD SymOptions);
|
nuclear@0
|
1029 typedef BOOL (WINAPI * SymInitializeWType)(HANDLE hProcess, PCWSTR UserSearchPath, BOOL fInvadeProcess);
|
nuclear@0
|
1030 typedef BOOL (WINAPI * SymCleanupType)(HANDLE hProcess);
|
nuclear@0
|
1031 typedef DWORD64 (WINAPI * SymLoadModule64Type)(HANDLE hProcess, HANDLE hFile, PCSTR ImageName, PCSTR ModuleName, DWORD64 BaseOfDll, DWORD SizeOfDll);
|
nuclear@0
|
1032 typedef BOOL (WINAPI * SymFromAddrType)(HANDLE hProcess, DWORD64 Address, PDWORD64 Displacement, PSYMBOL_INFO Symbol);
|
nuclear@0
|
1033 typedef BOOL (WINAPI * SymGetLineFromAddr64Type)(HANDLE hProcess, DWORD64 qwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINE64 Line64);
|
nuclear@0
|
1034
|
nuclear@0
|
1035 StackWalk64Type pStackWalk64;
|
nuclear@0
|
1036 SymFunctionTableAccess64Type pSymFunctionTableAccess64;
|
nuclear@0
|
1037 SymGetModuleBase64Type pSymGetModuleBase64;
|
nuclear@0
|
1038 SymSetOptionsType pSymSetOptions;
|
nuclear@0
|
1039 SymInitializeWType pSymInitializeW;
|
nuclear@0
|
1040 SymCleanupType pSymCleanup;
|
nuclear@0
|
1041 SymLoadModule64Type pSymLoadModule64;
|
nuclear@0
|
1042 SymFromAddrType pSymFromAddr;
|
nuclear@0
|
1043 SymGetLineFromAddr64Type pSymGetLineFromAddr64;
|
nuclear@0
|
1044 #endif
|
nuclear@0
|
1045
|
nuclear@0
|
1046
|
nuclear@0
|
1047
|
nuclear@0
|
1048 SymbolLookup::SymbolLookup()
|
nuclear@0
|
1049 : initialized(false),
|
nuclear@0
|
1050 allowMemoryAllocation(true),
|
nuclear@0
|
1051 moduleListUpdated(false),
|
nuclear@0
|
1052 moduleInfoArray(),
|
nuclear@0
|
1053 moduleInfoArraySize(0)
|
nuclear@0
|
1054 {
|
nuclear@0
|
1055 }
|
nuclear@0
|
1056
|
nuclear@0
|
1057 SymbolLookup::~SymbolLookup()
|
nuclear@0
|
1058 {
|
nuclear@0
|
1059 Shutdown();
|
nuclear@0
|
1060 }
|
nuclear@0
|
1061
|
nuclear@0
|
1062 void SymbolLookup::AddSourceCodeDirectory(const char* pDirectory)
|
nuclear@0
|
1063 {
|
nuclear@0
|
1064 OVR_UNUSED(pDirectory);
|
nuclear@0
|
1065 }
|
nuclear@0
|
1066
|
nuclear@0
|
1067 bool SymbolLookup::Initialize()
|
nuclear@0
|
1068 {
|
nuclear@0
|
1069 if(!initialized)
|
nuclear@0
|
1070 {
|
nuclear@0
|
1071 #if defined(OVR_OS_MS)
|
nuclear@0
|
1072 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms679294%28v=vs.85%29.aspx
|
nuclear@0
|
1073 HANDLE hProcess = GetCurrentProcess();
|
nuclear@0
|
1074 HMODULE hDbgHelp = LoadLibraryW(L"DbgHelp.dll"); // It's best if the application supplies a recent version of this.
|
nuclear@0
|
1075
|
nuclear@0
|
1076 if(hDbgHelp)
|
nuclear@0
|
1077 {
|
nuclear@0
|
1078 pStackWalk64 = (StackWalk64Type)(uintptr_t)::GetProcAddress(hDbgHelp, "StackWalk64");
|
nuclear@0
|
1079 pSymFunctionTableAccess64 = (SymFunctionTableAccess64Type)(uintptr_t)::GetProcAddress(hDbgHelp, "SymFunctionTableAccess64");
|
nuclear@0
|
1080 pSymGetModuleBase64 = (SymGetModuleBase64Type)(uintptr_t)::GetProcAddress(hDbgHelp, "SymGetModuleBase64");
|
nuclear@0
|
1081 pSymSetOptions = (SymSetOptionsType)(uintptr_t)::GetProcAddress(hDbgHelp, "SymSetOptions");
|
nuclear@0
|
1082 pSymInitializeW = (SymInitializeWType)(uintptr_t)::GetProcAddress(hDbgHelp, "SymInitializeW");
|
nuclear@0
|
1083 pSymCleanup = (SymCleanupType)(uintptr_t)::GetProcAddress(hDbgHelp, "SymCleanup");
|
nuclear@0
|
1084 pSymLoadModule64 = (SymLoadModule64Type)(uintptr_t)::GetProcAddress(hDbgHelp, "SymLoadModule64");
|
nuclear@0
|
1085 pSymFromAddr = (SymFromAddrType)(uintptr_t)::GetProcAddress(hDbgHelp, "SymFromAddr");
|
nuclear@0
|
1086 pSymGetLineFromAddr64 = (SymGetLineFromAddr64Type)(uintptr_t)::GetProcAddress(hDbgHelp, "SymGetLineFromAddr64");
|
nuclear@0
|
1087 }
|
nuclear@0
|
1088
|
nuclear@0
|
1089 pSymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS);
|
nuclear@0
|
1090
|
nuclear@0
|
1091 // To consider: Use a manually created search path:
|
nuclear@0
|
1092 // wchar_t searchPathW[4096]; // Semicolon-separated strings.
|
nuclear@0
|
1093 // The current working directory of the application.
|
nuclear@0
|
1094 // The directory of the application itself (GetModuleFileName).
|
nuclear@0
|
1095 // The _NT_SYMBOL_PATH environment variable.
|
nuclear@0
|
1096 // The _NT_ALTERNATE_SYMBOL_PATH environment variable.
|
nuclear@0
|
1097
|
nuclear@0
|
1098 if(pSymInitializeW(hProcess, nullptr /*searchPathW*/, FALSE))
|
nuclear@0
|
1099 {
|
nuclear@0
|
1100 initialized = true;
|
nuclear@0
|
1101 }
|
nuclear@0
|
1102 #endif
|
nuclear@0
|
1103 }
|
nuclear@0
|
1104
|
nuclear@0
|
1105 return true;
|
nuclear@0
|
1106 }
|
nuclear@0
|
1107
|
nuclear@0
|
1108 void SymbolLookup::Shutdown()
|
nuclear@0
|
1109 {
|
nuclear@0
|
1110 if(initialized)
|
nuclear@0
|
1111 {
|
nuclear@0
|
1112 initialized = false;
|
nuclear@0
|
1113
|
nuclear@0
|
1114 #if defined(OVR_OS_MS)
|
nuclear@0
|
1115 HANDLE hProcess = GetCurrentProcess();
|
nuclear@0
|
1116
|
nuclear@0
|
1117 // SymCleanup should handle this for us.
|
nuclear@0
|
1118 //if(moduleListUpdated)
|
nuclear@0
|
1119 //{
|
nuclear@0
|
1120 // for(size_t i = 0; i < moduleInfoArraySize; i++)
|
nuclear@0
|
1121 // pSymUnloadModule64(hProcess, moduleInfoArray[i].baseAddress);
|
nuclear@0
|
1122 //}
|
nuclear@0
|
1123
|
nuclear@0
|
1124 moduleInfoArraySize = 0;
|
nuclear@0
|
1125
|
nuclear@0
|
1126 pSymCleanup(hProcess);
|
nuclear@0
|
1127 #endif
|
nuclear@0
|
1128 }
|
nuclear@0
|
1129 }
|
nuclear@0
|
1130
|
nuclear@0
|
1131
|
nuclear@0
|
1132 void SymbolLookup::EnableMemoryAllocation(bool enabled)
|
nuclear@0
|
1133 {
|
nuclear@0
|
1134 allowMemoryAllocation = enabled;
|
nuclear@0
|
1135 }
|
nuclear@0
|
1136
|
nuclear@0
|
1137
|
nuclear@0
|
1138 OVR_DISABLE_MSVC_WARNING(4740) // flow in or out of inline asm code suppresses global optimization
|
nuclear@0
|
1139 OVR_DISABLE_MSVC_WARNING(4748) // /GS can not protect parameters and local variables from local buffer overrun because optimizations are disabled in function
|
nuclear@0
|
1140
|
nuclear@0
|
1141
|
nuclear@0
|
1142 size_t SymbolLookup::GetBacktrace(void* addressArray[], size_t addressArrayCapacity, size_t skipCount, void* platformThreadContext, OVR::ThreadSysId threadSysIdHelp)
|
nuclear@0
|
1143 {
|
nuclear@0
|
1144 #if defined(OVR_OS_WIN64) || (defined(OVR_OS_MS) && defined(OVR_OS_CONSOLE))
|
nuclear@0
|
1145 OVR_UNUSED(threadSysIdHelp);
|
nuclear@0
|
1146
|
nuclear@0
|
1147 if(platformThreadContext == nullptr)
|
nuclear@0
|
1148 return RtlCaptureStackBackTrace(1, (ULONG)addressArrayCapacity, addressArray, nullptr);
|
nuclear@0
|
1149
|
nuclear@0
|
1150 // We need to get the call stack of another thread.
|
nuclear@0
|
1151 size_t frameIndex = 0;
|
nuclear@0
|
1152 CONTEXT context;
|
nuclear@0
|
1153 PRUNTIME_FUNCTION pRuntimeFunction;
|
nuclear@0
|
1154 ULONG64 imageBase = 0;
|
nuclear@0
|
1155 ULONG64 imageBasePrev = 0;
|
nuclear@0
|
1156
|
nuclear@0
|
1157 memcpy(&context, (CONTEXT*)platformThreadContext, sizeof(CONTEXT));
|
nuclear@0
|
1158 context.ContextFlags = CONTEXT_CONTROL;
|
nuclear@0
|
1159
|
nuclear@0
|
1160 if(context.Rip && (frameIndex < addressArrayCapacity))
|
nuclear@0
|
1161 addressArray[frameIndex++] = (void*)(uintptr_t)context.Rip;
|
nuclear@0
|
1162
|
nuclear@0
|
1163 while(context.Rip && (frameIndex < addressArrayCapacity))
|
nuclear@0
|
1164 {
|
nuclear@0
|
1165 imageBasePrev = imageBase;
|
nuclear@0
|
1166 pRuntimeFunction = (PRUNTIME_FUNCTION)RtlLookupFunctionEntry(context.Rip, &imageBase, nullptr);
|
nuclear@0
|
1167
|
nuclear@0
|
1168 if(pRuntimeFunction)
|
nuclear@0
|
1169 {
|
nuclear@0
|
1170 VOID* handlerData = nullptr;
|
nuclear@0
|
1171 ULONG64 establisherFramePointers[2] = { 0, 0 };
|
nuclear@0
|
1172 RtlVirtualUnwind(UNW_FLAG_NHANDLER, imageBase, context.Rip, pRuntimeFunction, &context, &handlerData, establisherFramePointers, nullptr);
|
nuclear@0
|
1173 }
|
nuclear@0
|
1174 else
|
nuclear@0
|
1175 {
|
nuclear@0
|
1176 context.Rip = (ULONG64)(*(PULONG64)context.Rsp);
|
nuclear@0
|
1177 context.Rsp += 8;
|
nuclear@0
|
1178 }
|
nuclear@0
|
1179
|
nuclear@0
|
1180 if(context.Rip && (frameIndex < addressArrayCapacity))
|
nuclear@0
|
1181 {
|
nuclear@0
|
1182 if(skipCount)
|
nuclear@0
|
1183 --skipCount;
|
nuclear@0
|
1184 else
|
nuclear@0
|
1185 addressArray[frameIndex++] = (void*)(uintptr_t)context.Rip;
|
nuclear@0
|
1186 }
|
nuclear@0
|
1187 }
|
nuclear@0
|
1188
|
nuclear@0
|
1189 return frameIndex;
|
nuclear@0
|
1190
|
nuclear@0
|
1191 #elif defined(OVR_OS_WIN32)
|
nuclear@0
|
1192 OVR_UNUSED(threadSysIdHelp);
|
nuclear@0
|
1193
|
nuclear@0
|
1194 size_t frameIndex = 0;
|
nuclear@0
|
1195
|
nuclear@0
|
1196 if(pStackWalk64)
|
nuclear@0
|
1197 {
|
nuclear@0
|
1198 CONTEXT context;
|
nuclear@0
|
1199
|
nuclear@0
|
1200 if(platformThreadContext)
|
nuclear@0
|
1201 {
|
nuclear@0
|
1202 memcpy(&context, platformThreadContext, sizeof(context));
|
nuclear@0
|
1203 context.ContextFlags = CONTEXT_CONTROL;
|
nuclear@0
|
1204 }
|
nuclear@0
|
1205 else
|
nuclear@0
|
1206 {
|
nuclear@0
|
1207 memset(&context, 0, sizeof(context));
|
nuclear@0
|
1208 context.ContextFlags = CONTEXT_CONTROL;
|
nuclear@0
|
1209
|
nuclear@0
|
1210 __asm {
|
nuclear@0
|
1211 mov context.Ebp, EBP
|
nuclear@0
|
1212 mov context.Esp, ESP
|
nuclear@0
|
1213 call GetEIP
|
nuclear@0
|
1214 GetEIP:
|
nuclear@0
|
1215 pop context.Eip
|
nuclear@0
|
1216 }
|
nuclear@0
|
1217 }
|
nuclear@0
|
1218
|
nuclear@0
|
1219 STACKFRAME64 sf;
|
nuclear@0
|
1220 memset(&sf, 0, sizeof(sf));
|
nuclear@0
|
1221 sf.AddrPC.Offset = context.Eip;
|
nuclear@0
|
1222 sf.AddrPC.Mode = AddrModeFlat;
|
nuclear@0
|
1223 sf.AddrStack.Offset = context.Esp;
|
nuclear@0
|
1224 sf.AddrStack.Mode = AddrModeFlat;
|
nuclear@0
|
1225 sf.AddrFrame.Offset = context.Ebp;
|
nuclear@0
|
1226 sf.AddrFrame.Mode = AddrModeFlat;
|
nuclear@0
|
1227
|
nuclear@0
|
1228 const HANDLE hCurrentProcess = ::GetCurrentProcess();
|
nuclear@0
|
1229 const HANDLE hCurrentThread = ::GetCurrentThread();
|
nuclear@0
|
1230
|
nuclear@0
|
1231 if(!platformThreadContext) // If we are reading the current thread's call stack then we ignore this current function.
|
nuclear@0
|
1232 skipCount++;
|
nuclear@0
|
1233
|
nuclear@0
|
1234 while(frameIndex < addressArrayCapacity)
|
nuclear@0
|
1235 {
|
nuclear@0
|
1236 if(!pStackWalk64(IMAGE_FILE_MACHINE_I386, hCurrentProcess, hCurrentThread, &sf, &context, nullptr, pSymFunctionTableAccess64, pSymGetModuleBase64, nullptr))
|
nuclear@0
|
1237 break;
|
nuclear@0
|
1238
|
nuclear@0
|
1239 if(sf.AddrFrame.Offset == 0)
|
nuclear@0
|
1240 break;
|
nuclear@0
|
1241
|
nuclear@0
|
1242 if(skipCount)
|
nuclear@0
|
1243 --skipCount;
|
nuclear@0
|
1244 else
|
nuclear@0
|
1245 addressArray[frameIndex++] = ((void*)(uintptr_t)sf.AddrPC.Offset);
|
nuclear@0
|
1246 }
|
nuclear@0
|
1247 }
|
nuclear@0
|
1248
|
nuclear@0
|
1249 return frameIndex;
|
nuclear@0
|
1250
|
nuclear@0
|
1251 #elif defined(OVR_OS_APPLE)
|
nuclear@0
|
1252 struct StackFrame
|
nuclear@0
|
1253 {
|
nuclear@0
|
1254 StackFrame* pParentStackFrame;
|
nuclear@0
|
1255 void* pReturnPC;
|
nuclear@0
|
1256 };
|
nuclear@0
|
1257
|
nuclear@0
|
1258 void* pInstruction;
|
nuclear@0
|
1259 StackFrame* pStackFrame;
|
nuclear@0
|
1260 size_t frameIndex = 0;
|
nuclear@0
|
1261
|
nuclear@0
|
1262 if(platformThreadContext)
|
nuclear@0
|
1263 {
|
nuclear@0
|
1264 #if defined(OVR_CPU_ARM)
|
nuclear@0
|
1265 arm_thread_state_t* pThreadState = (arm_thread_state_t*)platformThreadContext;
|
nuclear@0
|
1266 pStackFrame = (StackFrame*)pThreadState->__fp;
|
nuclear@0
|
1267 pInstruction = (void*) pThreadState->__pc;
|
nuclear@0
|
1268 #define FrameIsAligned(pStackFrame) ((((uintptr_t)pStackFrame) & 0x1) == 0)
|
nuclear@0
|
1269 #elif defined(OVR_CPU_X86_64)
|
nuclear@0
|
1270 x86_thread_state_t* pThreadState = (x86_thread_state_t*)platformThreadContext;
|
nuclear@0
|
1271 pInstruction = (void*) pThreadState->uts.ts64.__rip;
|
nuclear@0
|
1272 pStackFrame = (StackFrame*)pThreadState->uts.ts64.__rbp;
|
nuclear@0
|
1273 #define FrameIsAligned(pStackFrame) ((((uintptr_t)pStackFrame) & 0xf) == 0)
|
nuclear@0
|
1274 #elif defined(OVR_CPU_X86)
|
nuclear@0
|
1275 x86_thread_state_t* pThreadState = (x86_thread_state_t*)platformThreadContext;
|
nuclear@0
|
1276 pInstruction = (void*) pThreadState->uts.ts32.__eip;
|
nuclear@0
|
1277 pStackFrame = (StackFrame*)pThreadState->uts.ts32.__ebp;
|
nuclear@0
|
1278 #define FrameIsAligned(pStackFrame) ((((uintptr_t)pStackFrame) & 0xf) == 8)
|
nuclear@0
|
1279 #endif
|
nuclear@0
|
1280
|
nuclear@0
|
1281 if(frameIndex < addressArrayCapacity)
|
nuclear@0
|
1282 addressArray[frameIndex++] = pInstruction;
|
nuclear@0
|
1283 }
|
nuclear@0
|
1284 else // Else get the current values...
|
nuclear@0
|
1285 {
|
nuclear@0
|
1286 pStackFrame = (StackFrame*)__builtin_frame_address(0);
|
nuclear@0
|
1287 GetInstructionPointer(pInstruction);
|
nuclear@0
|
1288 }
|
nuclear@0
|
1289
|
nuclear@0
|
1290 pthread_t threadSelf = pthread_self();
|
nuclear@0
|
1291 void* pCurrentStackBase = pthread_get_stackaddr_np(threadSelf);
|
nuclear@0
|
1292 void* pCurrentStackLimit = (void*)((uintptr_t)pCurrentStackBase - pthread_get_stacksize_np(threadSelf));
|
nuclear@0
|
1293 bool threadIsCurrent = (platformThreadContext == nullptr) || (((void*)pStackFrame > pCurrentStackLimit) && ((void*)pStackFrame <= pCurrentStackBase));
|
nuclear@0
|
1294 StackFrame* pStackBase;
|
nuclear@0
|
1295 StackFrame* pStackLimit;
|
nuclear@0
|
1296
|
nuclear@0
|
1297 if(threadIsCurrent)
|
nuclear@0
|
1298 {
|
nuclear@0
|
1299 pStackBase = (StackFrame*)pCurrentStackBase;
|
nuclear@0
|
1300 pStackLimit = (StackFrame*)pCurrentStackLimit;
|
nuclear@0
|
1301 }
|
nuclear@0
|
1302 else if(threadSysIdHelp)
|
nuclear@0
|
1303 {
|
nuclear@0
|
1304 pthread_t threadHandle = pthread_from_mach_thread_np((mach_port_t)threadSysIdHelp);
|
nuclear@0
|
1305 pStackBase = (StackFrame*)pthread_get_stackaddr_np(threadHandle);
|
nuclear@0
|
1306 pStackLimit = (StackFrame*)((uintptr_t)pStackBase - pthread_get_stacksize_np(threadHandle));
|
nuclear@0
|
1307 }
|
nuclear@0
|
1308 else
|
nuclear@0
|
1309 { // We guess what the limits are.
|
nuclear@0
|
1310 pStackBase = pStackFrame + ((384 * 1024) / sizeof(StackFrame));
|
nuclear@0
|
1311 pStackLimit = pStackFrame - ((384 * 1024) / sizeof(StackFrame));
|
nuclear@0
|
1312 }
|
nuclear@0
|
1313
|
nuclear@0
|
1314 if((frameIndex < addressArrayCapacity) && pStackFrame && FrameIsAligned(pStackFrame))
|
nuclear@0
|
1315 {
|
nuclear@0
|
1316 addressArray[frameIndex++] = pStackFrame->pReturnPC;
|
nuclear@0
|
1317
|
nuclear@0
|
1318 while(pStackFrame && pStackFrame->pReturnPC && (frameIndex < addressArrayCapacity))
|
nuclear@0
|
1319 {
|
nuclear@0
|
1320 pStackFrame = pStackFrame->pParentStackFrame;
|
nuclear@0
|
1321
|
nuclear@0
|
1322 if(pStackFrame && FrameIsAligned(pStackFrame) && pStackFrame->pReturnPC && (pStackFrame > pStackLimit) && (pStackFrame < pStackBase))
|
nuclear@0
|
1323 {
|
nuclear@0
|
1324 if(skipCount)
|
nuclear@0
|
1325 --skipCount;
|
nuclear@0
|
1326 else
|
nuclear@0
|
1327 addressArray[frameIndex++] = pStackFrame->pReturnPC;
|
nuclear@0
|
1328 }
|
nuclear@0
|
1329 else
|
nuclear@0
|
1330 break;
|
nuclear@0
|
1331 }
|
nuclear@0
|
1332 }
|
nuclear@0
|
1333
|
nuclear@0
|
1334 return frameIndex;
|
nuclear@0
|
1335
|
nuclear@0
|
1336 #elif defined(OVR_OS_LINUX) && (defined( __LIBUNWIND__) || defined(LIBUNWIND_AVAIL))
|
nuclear@0
|
1337 // Libunwind-based solution. Requires installation of libunwind package.
|
nuclear@0
|
1338 // Libunwind isn't always safe for threads that are in signal handlers.
|
nuclear@0
|
1339 // An approach to get the callstack of another thread is to use signal injection into the target thread.
|
nuclear@0
|
1340
|
nuclear@0
|
1341 OVR_UNUSED(platformThreadContext);
|
nuclear@0
|
1342 OVR_UNUSED(threadSysIdHelp);
|
nuclear@0
|
1343
|
nuclear@0
|
1344 size_t frameIndex = 0;
|
nuclear@0
|
1345 unw_cursor_t cursor;
|
nuclear@0
|
1346 unw_context_t uc;
|
nuclear@0
|
1347 unw_word_t ip, sp;
|
nuclear@0
|
1348
|
nuclear@0
|
1349 unw_getcontext(&uc); // This gets the current thread's context. We could alternatively initialize another thread's context with it.
|
nuclear@0
|
1350 unw_init_local(&cursor, &uc);
|
nuclear@0
|
1351
|
nuclear@0
|
1352 while((unw_step(&cursor) > 0) && (frameIndex < addressArrayCapacity))
|
nuclear@0
|
1353 {
|
nuclear@0
|
1354 // We can get the function name here too on some platforms with unw_get_proc_info() and unw_get_proc_name().
|
nuclear@0
|
1355
|
nuclear@0
|
1356 if(skipCount)
|
nuclear@0
|
1357 --skipCount;
|
nuclear@0
|
1358 else
|
nuclear@0
|
1359 {
|
nuclear@0
|
1360 unw_get_reg(&cursor, UNW_REG_IP, &ip);
|
nuclear@0
|
1361 addressArray[frameIndex++] = (void*)ip;
|
nuclear@0
|
1362 }
|
nuclear@0
|
1363 }
|
nuclear@0
|
1364
|
nuclear@0
|
1365 return frameIndex;
|
nuclear@0
|
1366 #else
|
nuclear@0
|
1367 OVR_UNUSED(addressArray);
|
nuclear@0
|
1368 OVR_UNUSED(addressArrayCapacity);
|
nuclear@0
|
1369 OVR_UNUSED(skipCount);
|
nuclear@0
|
1370 OVR_UNUSED(platformThreadContext);
|
nuclear@0
|
1371 OVR_UNUSED(threadSysIdHelp);
|
nuclear@0
|
1372
|
nuclear@0
|
1373 return 0;
|
nuclear@0
|
1374 #endif
|
nuclear@0
|
1375 }
|
nuclear@0
|
1376
|
nuclear@0
|
1377
|
nuclear@0
|
1378 size_t SymbolLookup::GetBacktraceFromThreadHandle(void* addressArray[], size_t addressArrayCapacity, size_t skipCount, OVR::ThreadHandle threadHandle)
|
nuclear@0
|
1379 {
|
nuclear@0
|
1380 #if defined(OVR_OS_MS)
|
nuclear@0
|
1381 size_t count = 0;
|
nuclear@0
|
1382 DWORD threadSysId = (DWORD)ConvertThreadHandleToThreadSysId(threadHandle);
|
nuclear@0
|
1383
|
nuclear@0
|
1384 // Compare to 0, compare to the self 'pseudohandle' and compare to the self id.
|
nuclear@0
|
1385 if((threadHandle == OVR_THREADHANDLE_INVALID) || (threadHandle == ::GetCurrentThread()) || (threadSysId == ::GetCurrentThreadId())) // If threadSysId refers to the current thread...
|
nuclear@0
|
1386 return GetBacktrace(addressArray, addressArrayCapacity, skipCount, nullptr);
|
nuclear@0
|
1387
|
nuclear@0
|
1388 // We are working with another thread. We need to suspend it and get its CONTEXT.
|
nuclear@0
|
1389 // Suspending other threads is risky, as they may be in some state that cannot be safely blocked.
|
nuclear@0
|
1390 BOOL result = false;
|
nuclear@0
|
1391 DWORD suspendResult = ::SuspendThread(threadHandle); // Requires that the handle have THREAD_SUSPEND_RESUME rights.
|
nuclear@0
|
1392
|
nuclear@0
|
1393 if(suspendResult != (DWORD)-1) // Returns previous suspend count, or -1 if failed.
|
nuclear@0
|
1394 {
|
nuclear@0
|
1395 CONTEXT context;
|
nuclear@0
|
1396 context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; // Requires that the handle have THREAD_GET_CONTEXT rights.
|
nuclear@0
|
1397 result = ::GetThreadContext(threadHandle, &context);
|
nuclear@0
|
1398 count = GetBacktrace(addressArray, addressArrayCapacity, skipCount, &context);
|
nuclear@0
|
1399 suspendResult = ::ResumeThread(threadHandle);
|
nuclear@0
|
1400 OVR_ASSERT_AND_UNUSED(suspendResult != (DWORD)-1, suspendResult);
|
nuclear@0
|
1401 }
|
nuclear@0
|
1402
|
nuclear@0
|
1403 return count;
|
nuclear@0
|
1404
|
nuclear@0
|
1405 #elif defined(OVR_OS_APPLE)
|
nuclear@0
|
1406 mach_port_t threadSysID = pthread_mach_thread_np((pthread_t)threadHandle); // Convert pthread_t to mach thread id.
|
nuclear@0
|
1407 return GetBacktraceFromThreadSysId(addressArray, addressArrayCapacity, skipCount, (OVR::ThreadSysId)threadSysID);
|
nuclear@0
|
1408
|
nuclear@0
|
1409 #elif defined(OVR_OS_LINUX)
|
nuclear@0
|
1410 // To do.
|
nuclear@0
|
1411 OVR_UNUSED(addressArray);
|
nuclear@0
|
1412 OVR_UNUSED(addressArrayCapacity);
|
nuclear@0
|
1413 OVR_UNUSED(skipCount);
|
nuclear@0
|
1414 OVR_UNUSED(threadHandle);
|
nuclear@0
|
1415 return 0;
|
nuclear@0
|
1416 #endif
|
nuclear@0
|
1417 }
|
nuclear@0
|
1418
|
nuclear@0
|
1419
|
nuclear@0
|
1420 size_t SymbolLookup::GetBacktraceFromThreadSysId(void* addressArray[], size_t addressArrayCapacity, size_t skipCount, OVR::ThreadSysId threadSysId)
|
nuclear@0
|
1421 {
|
nuclear@0
|
1422 #if defined(OVR_OS_MS)
|
nuclear@0
|
1423 OVR::ThreadHandle threadHandle = ConvertThreadSysIdToThreadHandle(threadSysId);
|
nuclear@0
|
1424 if(threadHandle)
|
nuclear@0
|
1425 {
|
nuclear@0
|
1426 size_t count = GetBacktraceFromThreadHandle(addressArray, addressArrayCapacity, skipCount, threadHandle);
|
nuclear@0
|
1427 FreeThreadHandle(threadHandle);
|
nuclear@0
|
1428 return count;
|
nuclear@0
|
1429 }
|
nuclear@0
|
1430 return 0;
|
nuclear@0
|
1431
|
nuclear@0
|
1432 #elif defined(OVR_OS_APPLE)
|
nuclear@0
|
1433 mach_port_t threadCurrent = pthread_mach_thread_np(pthread_self());
|
nuclear@0
|
1434 mach_port_t thread = (mach_port_t)threadSysId;
|
nuclear@0
|
1435
|
nuclear@0
|
1436 if(thread == threadCurrent)
|
nuclear@0
|
1437 {
|
nuclear@0
|
1438 return GetBacktrace(addressArray, addressArrayCapacity, skipCount, nullptr);
|
nuclear@0
|
1439 }
|
nuclear@0
|
1440 else
|
nuclear@0
|
1441 {
|
nuclear@0
|
1442 kern_return_t result = thread_suspend(thread); // Do we need to do this if it's an thread who exception is being handled?
|
nuclear@0
|
1443 size_t count = 0;
|
nuclear@0
|
1444
|
nuclear@0
|
1445 if(result == KERN_SUCCESS)
|
nuclear@0
|
1446 {
|
nuclear@0
|
1447 #if defined(OVR_CPU_X86) || defined(OVR_CPU_X86_64)
|
nuclear@0
|
1448 x86_thread_state_t threadState;
|
nuclear@0
|
1449 #elif defined(OVR_CPU_ARM)
|
nuclear@0
|
1450 arm_thread_state_t threadState;
|
nuclear@0
|
1451 #endif
|
nuclear@0
|
1452 mach_msg_type_number_t stateCount = MACHINE_THREAD_STATE_COUNT;
|
nuclear@0
|
1453
|
nuclear@0
|
1454 result = thread_get_state(thread, MACHINE_THREAD_STATE, (natural_t*)(uintptr_t)&threadState, &stateCount);
|
nuclear@0
|
1455
|
nuclear@0
|
1456 if(result == KERN_SUCCESS)
|
nuclear@0
|
1457 count = GetBacktrace(addressArray, addressArrayCapacity, skipCount, &threadState, threadSysId);
|
nuclear@0
|
1458
|
nuclear@0
|
1459 thread_resume(thread);
|
nuclear@0
|
1460
|
nuclear@0
|
1461 return count;
|
nuclear@0
|
1462 }
|
nuclear@0
|
1463 }
|
nuclear@0
|
1464
|
nuclear@0
|
1465 return 0;
|
nuclear@0
|
1466
|
nuclear@0
|
1467 #elif defined(OVR_OS_LINUX)
|
nuclear@0
|
1468 // To do.
|
nuclear@0
|
1469 OVR_UNUSED(addressArray);
|
nuclear@0
|
1470 OVR_UNUSED(addressArrayCapacity);
|
nuclear@0
|
1471 OVR_UNUSED(skipCount);
|
nuclear@0
|
1472 OVR_UNUSED(threadSysId);
|
nuclear@0
|
1473 return 0;
|
nuclear@0
|
1474 #endif
|
nuclear@0
|
1475 }
|
nuclear@0
|
1476
|
nuclear@0
|
1477
|
nuclear@0
|
1478 // We need to return the required moduleInfoArrayCapacity.
|
nuclear@0
|
1479 size_t SymbolLookup::GetModuleInfoArray(ModuleInfo* pModuleInfoArray, size_t moduleInfoArrayCapacity)
|
nuclear@0
|
1480 {
|
nuclear@0
|
1481 #if defined(OVR_OS_MS)
|
nuclear@0
|
1482 size_t moduleCountRequired = 0; // The count we would copy to pModuleInfoArray if moduleInfoArrayCapacity was enough.
|
nuclear@0
|
1483 size_t moduleCount = 0; // The count we actually copy to pModuleInfoArray. Will be <= moduleInfoArrayCapacity.
|
nuclear@0
|
1484 HANDLE hProcess = GetCurrentProcess();
|
nuclear@0
|
1485 HMODULE hModuleArray[200];
|
nuclear@0
|
1486 DWORD cbNeeded = 0;
|
nuclear@0
|
1487 MODULEINFO mi;
|
nuclear@0
|
1488
|
nuclear@0
|
1489 if(EnumProcessModules(hProcess, hModuleArray, sizeof(hModuleArray), &cbNeeded))
|
nuclear@0
|
1490 {
|
nuclear@0
|
1491 moduleCountRequired = ((cbNeeded / sizeof(HMODULE)) < OVR_ARRAY_COUNT(hModuleArray)) ? (cbNeeded / sizeof(HMODULE)) : OVR_ARRAY_COUNT(hModuleArray);
|
nuclear@0
|
1492 moduleCount = MIN(moduleCountRequired, OVR_ARRAY_COUNT(hModuleArray));
|
nuclear@0
|
1493 moduleCount = MIN(moduleCount, moduleInfoArrayCapacity);
|
nuclear@0
|
1494
|
nuclear@0
|
1495 for(size_t i = 0; i < moduleCount; i++)
|
nuclear@0
|
1496 {
|
nuclear@0
|
1497 ModuleInfo& moduleInfo = pModuleInfoArray[i];
|
nuclear@0
|
1498
|
nuclear@0
|
1499 memset(&mi, 0, sizeof(mi));
|
nuclear@0
|
1500 BOOL result = GetModuleInformation(hProcess, hModuleArray[i], &mi, sizeof(mi));
|
nuclear@0
|
1501
|
nuclear@0
|
1502 if(result)
|
nuclear@0
|
1503 {
|
nuclear@0
|
1504 wchar_t pathW[MAX_PATH];
|
nuclear@0
|
1505 char pathA[MAX_PATH * 3]; // *3 to handle UTF8 multibyte encoding.
|
nuclear@0
|
1506
|
nuclear@0
|
1507 moduleInfo.handle = hModuleArray[i];
|
nuclear@0
|
1508 moduleInfo.baseAddress = (uintptr_t)mi.lpBaseOfDll;
|
nuclear@0
|
1509 moduleInfo.size = mi.SizeOfImage;
|
nuclear@0
|
1510
|
nuclear@0
|
1511 GetModuleFileNameW(hModuleArray[i], pathW, OVR_ARRAY_COUNT(pathW));
|
nuclear@0
|
1512 OVR::UTF8Util::EncodeString(pathA, pathW, -1); // Problem: DecodeString provides no way to specify the destination capacity.
|
nuclear@0
|
1513 OVR::OVR_strlcpy(moduleInfo.filePath, pathA, OVR_ARRAY_COUNT(moduleInfo.filePath));
|
nuclear@0
|
1514
|
nuclear@0
|
1515 const char* fileName = GetFileNameFromPath(pathA);
|
nuclear@0
|
1516 OVR::OVR_strlcpy(moduleInfo.name, fileName, OVR_ARRAY_COUNT(moduleInfo.name));
|
nuclear@0
|
1517 }
|
nuclear@0
|
1518 else
|
nuclear@0
|
1519 {
|
nuclear@0
|
1520 moduleInfo.handle = 0;
|
nuclear@0
|
1521 moduleInfo.baseAddress = 0;
|
nuclear@0
|
1522 moduleInfo.size = 0;
|
nuclear@0
|
1523 moduleInfo.filePath[0] = 0;
|
nuclear@0
|
1524 moduleInfo.name[0] = 0;
|
nuclear@0
|
1525 }
|
nuclear@0
|
1526 }
|
nuclear@0
|
1527 }
|
nuclear@0
|
1528
|
nuclear@0
|
1529 return moduleCountRequired;
|
nuclear@0
|
1530
|
nuclear@0
|
1531 #elif defined(OVR_OS_MAC)
|
nuclear@0
|
1532 size_t moduleCountRequired = 0;
|
nuclear@0
|
1533 size_t moduleCount = 0;
|
nuclear@0
|
1534
|
nuclear@0
|
1535 struct MacModuleInfo // This struct exists solely so we can have a local function within this function..
|
nuclear@0
|
1536 {
|
nuclear@0
|
1537 static void AddMacModuleInfo(ModuleInfo* pModuleInfoArrayL, size_t& moduleCountRequiredL, size_t& moduleCountL, size_t moduleInfoArrayCapacityL,
|
nuclear@0
|
1538 const char* pTypeFilterL, const char* pModulePath, uintptr_t currentSegmentPos, const MachHeader* pMachHeader, uint64_t offset)
|
nuclear@0
|
1539 {
|
nuclear@0
|
1540 for(size_t i = 0; i < pMachHeader->ncmds; i++)
|
nuclear@0
|
1541 {
|
nuclear@0
|
1542 const SegmentCommand* pSegmentCommand = reinterpret_cast<const SegmentCommand*>(currentSegmentPos);
|
nuclear@0
|
1543
|
nuclear@0
|
1544 if(pSegmentCommand->cmd == kLCSegment)
|
nuclear@0
|
1545 {
|
nuclear@0
|
1546 const size_t segnameSize = (sizeof(pSegmentCommand->segname) + 1); // +1 so we can have a trailing '\0'.
|
nuclear@0
|
1547 char segname[segnameSize];
|
nuclear@0
|
1548
|
nuclear@0
|
1549 memcpy(segname, pSegmentCommand->segname, sizeof(pSegmentCommand->segname));
|
nuclear@0
|
1550 segname[segnameSize - 1] = '\0';
|
nuclear@0
|
1551
|
nuclear@0
|
1552 if(!pTypeFilterL || OVR_strncmp(segname, pTypeFilterL, sizeof(segname)))
|
nuclear@0
|
1553 {
|
nuclear@0
|
1554 moduleCountRequiredL++;
|
nuclear@0
|
1555
|
nuclear@0
|
1556 if(moduleCountL < moduleInfoArrayCapacityL)
|
nuclear@0
|
1557 {
|
nuclear@0
|
1558 ModuleInfo& info = pModuleInfoArrayL[moduleCountL++];
|
nuclear@0
|
1559
|
nuclear@0
|
1560 info.baseAddress = (uint64_t)(pSegmentCommand->vmaddr + offset);
|
nuclear@0
|
1561 info.handle = reinterpret_cast<ModuleHandle>((uintptr_t)info.baseAddress);
|
nuclear@0
|
1562 info.size = (uint64_t)pSegmentCommand->vmsize;
|
nuclear@0
|
1563 OVR_strlcpy(info.filePath, pModulePath, OVR_ARRAY_COUNT(info.filePath));
|
nuclear@0
|
1564 OVR_strlcpy(info.name, GetFileNameFromPath(pModulePath), OVR_ARRAY_COUNT(info.name));
|
nuclear@0
|
1565
|
nuclear@0
|
1566 info.permissions[0] = (pSegmentCommand->initprot & VM_PROT_READ) ? 'r' : '-';
|
nuclear@0
|
1567 info.permissions[1] = (pSegmentCommand->initprot & VM_PROT_WRITE) ? 'w' : '-';
|
nuclear@0
|
1568 info.permissions[2] = (pSegmentCommand->initprot & VM_PROT_EXECUTE) ? 'x' : '-';
|
nuclear@0
|
1569 info.permissions[3] = '/';
|
nuclear@0
|
1570 info.permissions[4] = (pSegmentCommand->maxprot & VM_PROT_READ) ? 'r' : '-';
|
nuclear@0
|
1571 info.permissions[5] = (pSegmentCommand->maxprot & VM_PROT_WRITE) ? 'w' : '-';
|
nuclear@0
|
1572 info.permissions[6] = (pSegmentCommand->maxprot & VM_PROT_EXECUTE) ? 'x' : '-';
|
nuclear@0
|
1573 info.permissions[7] = '\0';
|
nuclear@0
|
1574
|
nuclear@0
|
1575 OVR_strlcpy(info.type, pSegmentCommand->segname, OVR_ARRAY_COUNT(info.type));
|
nuclear@0
|
1576 }
|
nuclear@0
|
1577 }
|
nuclear@0
|
1578 }
|
nuclear@0
|
1579
|
nuclear@0
|
1580 currentSegmentPos += pSegmentCommand->cmdsize;
|
nuclear@0
|
1581 }
|
nuclear@0
|
1582 }
|
nuclear@0
|
1583 };
|
nuclear@0
|
1584
|
nuclear@0
|
1585 // Iterate dyld_all_image_infos->infoArray
|
nuclear@0
|
1586 const struct dyld_all_image_infos* pAllImageInfos = _dyld_get_all_image_infos();
|
nuclear@0
|
1587
|
nuclear@0
|
1588 for(uint32_t i = 0; i < pAllImageInfos->infoArrayCount; i++)
|
nuclear@0
|
1589 {
|
nuclear@0
|
1590 const char* pModulePath = pAllImageInfos->infoArray[i].imageFilePath;
|
nuclear@0
|
1591
|
nuclear@0
|
1592 if(pModulePath && *pModulePath)
|
nuclear@0
|
1593 {
|
nuclear@0
|
1594 uintptr_t currentSegmentPos = (uintptr_t)pAllImageInfos->infoArray[i].imageLoadAddress;
|
nuclear@0
|
1595 const MachHeader* pMachHeader = reinterpret_cast<const MachHeader*>(currentSegmentPos);
|
nuclear@0
|
1596 uint64_t offset = (uint64_t)_dyld_get_image_vmaddr_slide(i);
|
nuclear@0
|
1597
|
nuclear@0
|
1598 currentSegmentPos += sizeof(*pMachHeader);
|
nuclear@0
|
1599
|
nuclear@0
|
1600 MacModuleInfo::AddMacModuleInfo(pModuleInfoArray, moduleCountRequired, moduleCount, moduleInfoArrayCapacity,
|
nuclear@0
|
1601 nullptr /*"__TEXT"*/, pModulePath, currentSegmentPos, pMachHeader, offset);
|
nuclear@0
|
1602 }
|
nuclear@0
|
1603 }
|
nuclear@0
|
1604
|
nuclear@0
|
1605 // In addition to iterating dyld_all_image_infos->infoArray we need to also iterate /usr/lib/dyld entries.
|
nuclear@0
|
1606 const MachHeader* pMachHeader = (const MachHeader*)pAllImageInfos->dyldImageLoadAddress;
|
nuclear@0
|
1607 uintptr_t currentSegmentPos = (uintptr_t)pMachHeader + sizeof(*pMachHeader);
|
nuclear@0
|
1608 char modulePath[OVR_MAX_PATH] = "";
|
nuclear@0
|
1609 pid_t pid = getpid();
|
nuclear@0
|
1610 int filenameLen = proc_regionfilename((int)pid, currentSegmentPos, modulePath, (uint32_t)sizeof(modulePath));
|
nuclear@0
|
1611
|
nuclear@0
|
1612 if(filenameLen > 0)
|
nuclear@0
|
1613 MacModuleInfo::AddMacModuleInfo(pModuleInfoArray, moduleCountRequired, moduleCount, moduleInfoArrayCapacity,
|
nuclear@0
|
1614 "__TEXT", modulePath, currentSegmentPos, pMachHeader, 0);
|
nuclear@0
|
1615
|
nuclear@0
|
1616 return moduleCountRequired;
|
nuclear@0
|
1617
|
nuclear@0
|
1618 #elif defined(EA_PLATFORM_LINUX)
|
nuclear@0
|
1619 // One approach is to read /proc/self/maps, which is supported by Linux (though not BSD).
|
nuclear@0
|
1620 // Linux glibc dladdr() can tell us what module an arbitrary function address comes from, but can't tell us the list of modules.
|
nuclear@0
|
1621 OVR_UNUSED(pModuleInfoArray);
|
nuclear@0
|
1622 OVR_UNUSED(moduleInfoArrayCapacity);
|
nuclear@0
|
1623 return 0;
|
nuclear@0
|
1624
|
nuclear@0
|
1625 #else
|
nuclear@0
|
1626 OVR_UNUSED(pModuleInfoArray);
|
nuclear@0
|
1627 OVR_UNUSED(moduleInfoArrayCapacity);
|
nuclear@0
|
1628 return 0;
|
nuclear@0
|
1629 #endif
|
nuclear@0
|
1630 }
|
nuclear@0
|
1631
|
nuclear@0
|
1632
|
nuclear@0
|
1633 size_t SymbolLookup::GetThreadList(ThreadHandle* threadHandleArray, ThreadSysId* threadSysIdArray, size_t threadArrayCapacity)
|
nuclear@0
|
1634 {
|
nuclear@0
|
1635 size_t countRequired = 0;
|
nuclear@0
|
1636 size_t count = 0;
|
nuclear@0
|
1637
|
nuclear@0
|
1638 #if defined(OVR_OS_MS)
|
nuclear@0
|
1639 // Print a list of threads.
|
nuclear@0
|
1640 DWORD currentProcessId = GetCurrentProcessId();
|
nuclear@0
|
1641 HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, currentProcessId); // ICreateToolhelp32Snapshot actually ignores currentProcessId.
|
nuclear@0
|
1642
|
nuclear@0
|
1643 if(hThreadSnap != INVALID_HANDLE_VALUE)
|
nuclear@0
|
1644 {
|
nuclear@0
|
1645 THREADENTRY32 te32;
|
nuclear@0
|
1646 te32.dwSize = sizeof(THREADENTRY32);
|
nuclear@0
|
1647
|
nuclear@0
|
1648 if(Thread32First(hThreadSnap, &te32))
|
nuclear@0
|
1649 {
|
nuclear@0
|
1650 do
|
nuclear@0
|
1651 {
|
nuclear@0
|
1652 if(te32.th32OwnerProcessID == currentProcessId)
|
nuclear@0
|
1653 {
|
nuclear@0
|
1654 HANDLE hThread = ConvertThreadSysIdToThreadHandle(te32.th32ThreadID);
|
nuclear@0
|
1655
|
nuclear@0
|
1656 if(hThread)
|
nuclear@0
|
1657 {
|
nuclear@0
|
1658 ++countRequired;
|
nuclear@0
|
1659
|
nuclear@0
|
1660 if((threadHandleArray || threadSysIdArray) && (count < threadArrayCapacity))
|
nuclear@0
|
1661 {
|
nuclear@0
|
1662 if(threadHandleArray)
|
nuclear@0
|
1663 threadHandleArray[count] = hThread; // The caller must call CloseHandle on this thread, or call DoneThreadList on the returned array.
|
nuclear@0
|
1664 if(threadSysIdArray)
|
nuclear@0
|
1665 threadSysIdArray[count] = ConvertThreadHandleToThreadSysId(hThread);
|
nuclear@0
|
1666 ++count;
|
nuclear@0
|
1667 }
|
nuclear@0
|
1668
|
nuclear@0
|
1669 if(!threadHandleArray) // If we aren't giving this back to the user...
|
nuclear@0
|
1670 FreeThreadHandle(hThread);
|
nuclear@0
|
1671 }
|
nuclear@0
|
1672 }
|
nuclear@0
|
1673 } while(Thread32Next(hThreadSnap, &te32));
|
nuclear@0
|
1674 }
|
nuclear@0
|
1675
|
nuclear@0
|
1676 CloseHandle(hThreadSnap);
|
nuclear@0
|
1677 }
|
nuclear@0
|
1678
|
nuclear@0
|
1679 #elif defined(OVR_OS_APPLE)
|
nuclear@0
|
1680 mach_port_t taskSelf = mach_task_self();
|
nuclear@0
|
1681 thread_act_port_array_t threadArray;
|
nuclear@0
|
1682 mach_msg_type_number_t threadCount;
|
nuclear@0
|
1683
|
nuclear@0
|
1684 kern_return_t result = task_threads(taskSelf, &threadArray, &threadCount);
|
nuclear@0
|
1685
|
nuclear@0
|
1686 if(result == KERN_SUCCESS)
|
nuclear@0
|
1687 {
|
nuclear@0
|
1688 for(mach_msg_type_number_t i = 0; i < threadCount; i++)
|
nuclear@0
|
1689 {
|
nuclear@0
|
1690 ++countRequired;
|
nuclear@0
|
1691
|
nuclear@0
|
1692 if((threadHandleArray || threadSysIdArray) && (count < threadArrayCapacity))
|
nuclear@0
|
1693 {
|
nuclear@0
|
1694 if(threadHandleArray)
|
nuclear@0
|
1695 threadHandleArray[count] = pthread_from_mach_thread_np(threadArray[i]);
|
nuclear@0
|
1696 if(threadSysIdArray)
|
nuclear@0
|
1697 threadSysIdArray[count] = threadArray[i];
|
nuclear@0
|
1698 ++count;
|
nuclear@0
|
1699 }
|
nuclear@0
|
1700 }
|
nuclear@0
|
1701
|
nuclear@0
|
1702 vm_deallocate(taskSelf, (vm_address_t)threadArray, threadCount * sizeof(thread_act_t));
|
nuclear@0
|
1703 }
|
nuclear@0
|
1704
|
nuclear@0
|
1705 #elif defined(OVR_OS_LINUX)
|
nuclear@0
|
1706 // To do.
|
nuclear@0
|
1707 OVR_UNUSED(count);
|
nuclear@0
|
1708 OVR_UNUSED(threadHandleArray);
|
nuclear@0
|
1709 OVR_UNUSED(threadSysIdArray);
|
nuclear@0
|
1710 OVR_UNUSED(threadArrayCapacity);
|
nuclear@0
|
1711 #endif
|
nuclear@0
|
1712
|
nuclear@0
|
1713 return countRequired;
|
nuclear@0
|
1714 }
|
nuclear@0
|
1715
|
nuclear@0
|
1716
|
nuclear@0
|
1717 void SymbolLookup::DoneThreadList(ThreadHandle* threadHandleArray, ThreadSysId* threadSysIdArray, size_t threadArrayCount)
|
nuclear@0
|
1718 {
|
nuclear@0
|
1719 #if defined(OVR_OS_MS)
|
nuclear@0
|
1720 for(size_t i = 0; i != threadArrayCount; ++i)
|
nuclear@0
|
1721 {
|
nuclear@0
|
1722 if(threadHandleArray[i])
|
nuclear@0
|
1723 {
|
nuclear@0
|
1724 CloseHandle(threadHandleArray[i]);
|
nuclear@0
|
1725 threadHandleArray[i] = OVR_THREADHANDLE_INVALID;
|
nuclear@0
|
1726 }
|
nuclear@0
|
1727 }
|
nuclear@0
|
1728
|
nuclear@0
|
1729 OVR_UNUSED(threadSysIdArray);
|
nuclear@0
|
1730 #else
|
nuclear@0
|
1731 OVR_UNUSED(threadHandleArray);
|
nuclear@0
|
1732 OVR_UNUSED(threadSysIdArray);
|
nuclear@0
|
1733 OVR_UNUSED(threadArrayCount);
|
nuclear@0
|
1734 #endif
|
nuclear@0
|
1735 }
|
nuclear@0
|
1736
|
nuclear@0
|
1737
|
nuclear@0
|
1738 // Writes a given thread's callstack wity symbols to the given output.
|
nuclear@0
|
1739 // It may not be safe to call this from an exception handler, as sOutput allocates memory.
|
nuclear@0
|
1740 bool SymbolLookup::ReportThreadCallstack(OVR::String& sOutput, size_t skipCount, ThreadSysId threadSysId)
|
nuclear@0
|
1741 {
|
nuclear@0
|
1742 if(!threadSysId)
|
nuclear@0
|
1743 threadSysId = GetCurrentThreadSysId();
|
nuclear@0
|
1744
|
nuclear@0
|
1745 void* addressArray[64];
|
nuclear@0
|
1746 size_t addressCount = GetBacktraceFromThreadSysId(addressArray, OVR_ARRAY_COUNT(addressArray), skipCount, threadSysId);
|
nuclear@0
|
1747
|
nuclear@0
|
1748 // Print the header
|
nuclear@0
|
1749 char headerBuffer[256];
|
nuclear@0
|
1750 char threadName[32];
|
nuclear@0
|
1751 char threadHandleStr[24];
|
nuclear@0
|
1752 char threadSysIdStr[24];
|
nuclear@0
|
1753 char stackBaseStr[24];
|
nuclear@0
|
1754 char stackLimitStr[24];
|
nuclear@0
|
1755 void* pStackBase;
|
nuclear@0
|
1756 void* pStackLimit;
|
nuclear@0
|
1757 //void* pStackCurrent; // Current stack pointer. To do: support reporting this.
|
nuclear@0
|
1758 ThreadHandle threadHandle = ConvertThreadSysIdToThreadHandle(threadSysId);
|
nuclear@0
|
1759 OVR::GetThreadStackBounds(pStackBase, pStackLimit, threadHandle);
|
nuclear@0
|
1760
|
nuclear@0
|
1761 Thread::GetThreadName(threadName, OVR_ARRAY_COUNT(threadName), threadName);
|
nuclear@0
|
1762 SprintfThreadHandle(threadHandleStr, OVR_ARRAY_COUNT(threadHandleStr), threadHandle);
|
nuclear@0
|
1763 SprintfThreadSysId(threadSysIdStr, OVR_ARRAY_COUNT(threadSysIdStr), threadSysId);
|
nuclear@0
|
1764 SprintfAddress(stackBaseStr, OVR_ARRAY_COUNT(stackBaseStr), pStackBase);
|
nuclear@0
|
1765 SprintfAddress(stackLimitStr, OVR_ARRAY_COUNT(stackLimitStr), pStackLimit);
|
nuclear@0
|
1766
|
nuclear@0
|
1767 if(threadName[0])
|
nuclear@0
|
1768 OVR_snprintf(headerBuffer, OVR_ARRAY_COUNT(headerBuffer), "Thread \"%s\" handle: %s, id: %s, stack base: %s, stack limit: %s\r\n", threadName, threadHandleStr, threadSysIdStr, stackBaseStr, stackLimitStr);
|
nuclear@0
|
1769 else
|
nuclear@0
|
1770 OVR_snprintf(headerBuffer, OVR_ARRAY_COUNT(headerBuffer), "Thread handle: %s, id: %s, stack base: %s, stack limit: %s\r\n", threadHandleStr, threadSysIdStr, stackBaseStr, stackLimitStr);
|
nuclear@0
|
1771
|
nuclear@0
|
1772 sOutput += headerBuffer;
|
nuclear@0
|
1773
|
nuclear@0
|
1774 // Print the backtrace info
|
nuclear@0
|
1775 char backtraceBuffer[1024]; // Sometimes function symbol names are very long.
|
nuclear@0
|
1776 SymbolInfo symbolInfo;
|
nuclear@0
|
1777 const char* pModuleName;
|
nuclear@0
|
1778
|
nuclear@0
|
1779 if(addressCount == 0)
|
nuclear@0
|
1780 {
|
nuclear@0
|
1781 sOutput += "<Unable to read backtrace>\r\n";
|
nuclear@0
|
1782 }
|
nuclear@0
|
1783 else
|
nuclear@0
|
1784 {
|
nuclear@0
|
1785 for(size_t i = 0; i < addressCount; ++i)
|
nuclear@0
|
1786 {
|
nuclear@0
|
1787 LookupSymbol((uint64_t)addressArray[i], symbolInfo);
|
nuclear@0
|
1788
|
nuclear@0
|
1789 if(symbolInfo.pModuleInfo && symbolInfo.pModuleInfo->name[0])
|
nuclear@0
|
1790 pModuleName = symbolInfo.pModuleInfo->name;
|
nuclear@0
|
1791 else
|
nuclear@0
|
1792 pModuleName = "(unknown module)";
|
nuclear@0
|
1793
|
nuclear@0
|
1794 char addressStr[24];
|
nuclear@0
|
1795 SprintfAddress(addressStr, OVR_ARRAY_COUNT(addressStr), addressArray[i]);
|
nuclear@0
|
1796
|
nuclear@0
|
1797 if(symbolInfo.filePath[0])
|
nuclear@0
|
1798 OVR_snprintf(backtraceBuffer, OVR_ARRAY_COUNT(backtraceBuffer), "%-2u %-24s %s %s+%d %s:%d\r\n", (unsigned)i, pModuleName, addressStr, symbolInfo.function, symbolInfo.functionOffset, symbolInfo.filePath, symbolInfo.fileLineNumber);
|
nuclear@0
|
1799 else
|
nuclear@0
|
1800 OVR_snprintf(backtraceBuffer, OVR_ARRAY_COUNT(backtraceBuffer), "%-2u %-24s %s %s+%d\r\n", (unsigned)i, pModuleName, addressStr, symbolInfo.function, symbolInfo.functionOffset);
|
nuclear@0
|
1801
|
nuclear@0
|
1802 sOutput += backtraceBuffer;
|
nuclear@0
|
1803 }
|
nuclear@0
|
1804 }
|
nuclear@0
|
1805
|
nuclear@0
|
1806 FreeThreadHandle(threadHandle);
|
nuclear@0
|
1807
|
nuclear@0
|
1808 return (addressCount > 0);
|
nuclear@0
|
1809 }
|
nuclear@0
|
1810
|
nuclear@0
|
1811
|
nuclear@0
|
1812 // Writes all thread's callstacks with symbols to the given output.
|
nuclear@0
|
1813 // It may not be safe to call this from an exception handler, as sOutput allocates memory.
|
nuclear@0
|
1814 bool SymbolLookup::ReportThreadCallstacks(OVR::String& sOutput, size_t skipCount)
|
nuclear@0
|
1815 {
|
nuclear@0
|
1816 ThreadSysId threadSysIdArray[64];
|
nuclear@0
|
1817 size_t threadSysIdCount = GetThreadList(nullptr, threadSysIdArray, OVR_ARRAY_COUNT(threadSysIdArray));
|
nuclear@0
|
1818
|
nuclear@0
|
1819 if(threadSysIdCount > OVR_ARRAY_COUNT(threadSysIdArray))
|
nuclear@0
|
1820 threadSysIdCount = OVR_ARRAY_COUNT(threadSysIdArray);
|
nuclear@0
|
1821
|
nuclear@0
|
1822 for(size_t i = 0; i < threadSysIdCount; i++)
|
nuclear@0
|
1823 {
|
nuclear@0
|
1824 String sTemp;
|
nuclear@0
|
1825 ReportThreadCallstack(sTemp, skipCount, threadSysIdArray[i]);
|
nuclear@0
|
1826 if(i > 0)
|
nuclear@0
|
1827 sOutput += "\r\n";
|
nuclear@0
|
1828 sOutput += sTemp;
|
nuclear@0
|
1829 }
|
nuclear@0
|
1830
|
nuclear@0
|
1831 return (threadSysIdCount > 0);
|
nuclear@0
|
1832 }
|
nuclear@0
|
1833
|
nuclear@0
|
1834
|
nuclear@0
|
1835 bool SymbolLookup::RefreshModuleList()
|
nuclear@0
|
1836 {
|
nuclear@0
|
1837 if(!moduleListUpdated)
|
nuclear@0
|
1838 {
|
nuclear@0
|
1839 #if defined(OVR_OS_MS)
|
nuclear@0
|
1840 // We can't rely on SymRefreshModuleList because it's present in DbgHelp 6.5,
|
nuclear@0
|
1841 // which doesn't distribute with Windows 7.
|
nuclear@0
|
1842
|
nuclear@0
|
1843 // Currently we support only refreshing the list once ever. With a little effort we could revise this code to
|
nuclear@0
|
1844 // support re-refreshing the list at runtime to account for the possibility that modules have recently been
|
nuclear@0
|
1845 // added or removed.
|
nuclear@0
|
1846 if(pSymLoadModule64)
|
nuclear@0
|
1847 {
|
nuclear@0
|
1848 const size_t requiredCount = GetModuleInfoArray(moduleInfoArray, OVR_ARRAY_COUNT(moduleInfoArray));
|
nuclear@0
|
1849 moduleInfoArraySize = MIN(requiredCount, OVR_ARRAY_COUNT(moduleInfoArray));
|
nuclear@0
|
1850
|
nuclear@0
|
1851 HANDLE hProcess = GetCurrentProcess();
|
nuclear@0
|
1852
|
nuclear@0
|
1853 for(size_t i = 0; i < moduleInfoArraySize; i++)
|
nuclear@0
|
1854 pSymLoadModule64(hProcess, nullptr, moduleInfoArray[i].filePath, nullptr, moduleInfoArray[i].baseAddress, (DWORD)moduleInfoArray[i].size);
|
nuclear@0
|
1855
|
nuclear@0
|
1856 moduleListUpdated = true;
|
nuclear@0
|
1857 }
|
nuclear@0
|
1858 #else
|
nuclear@0
|
1859 const size_t requiredCount = GetModuleInfoArray(moduleInfoArray, OVR_ARRAY_COUNT(moduleInfoArray));
|
nuclear@0
|
1860 moduleInfoArraySize = MIN(requiredCount, OVR_ARRAY_COUNT(moduleInfoArray));
|
nuclear@0
|
1861 moduleListUpdated = true;
|
nuclear@0
|
1862 #endif
|
nuclear@0
|
1863 }
|
nuclear@0
|
1864
|
nuclear@0
|
1865 return true;
|
nuclear@0
|
1866 }
|
nuclear@0
|
1867
|
nuclear@0
|
1868
|
nuclear@0
|
1869 bool SymbolLookup::LookupSymbol(uint64_t address, SymbolInfo& symbolInfo)
|
nuclear@0
|
1870 {
|
nuclear@0
|
1871 return LookupSymbols(&address, &symbolInfo, 1);
|
nuclear@0
|
1872 }
|
nuclear@0
|
1873
|
nuclear@0
|
1874
|
nuclear@0
|
1875 bool SymbolLookup::LookupSymbols(uint64_t* addressArray, SymbolInfo* pSymbolInfoArray, size_t arraySize)
|
nuclear@0
|
1876 {
|
nuclear@0
|
1877 if(!moduleListUpdated)
|
nuclear@0
|
1878 {
|
nuclear@0
|
1879 RefreshModuleList();
|
nuclear@0
|
1880 }
|
nuclear@0
|
1881
|
nuclear@0
|
1882 #if defined(OVR_OS_MS)
|
nuclear@0
|
1883 union SYMBOL_INFO_UNION
|
nuclear@0
|
1884 {
|
nuclear@0
|
1885 SYMBOL_INFO msSymbolInfo;
|
nuclear@0
|
1886 char suffixPadding[sizeof(SYMBOL_INFO) + 1024];
|
nuclear@0
|
1887 };
|
nuclear@0
|
1888
|
nuclear@0
|
1889 for(size_t i = 0; i < arraySize; i++)
|
nuclear@0
|
1890 {
|
nuclear@0
|
1891 uint64_t& address = addressArray[i];
|
nuclear@0
|
1892 SymbolInfo& symbolInfo = pSymbolInfoArray[i];
|
nuclear@0
|
1893
|
nuclear@0
|
1894 // Copy the address and ModuleInfo
|
nuclear@0
|
1895 symbolInfo.address = addressArray[i];
|
nuclear@0
|
1896 symbolInfo.pModuleInfo = GetModuleInfoForAddress(address); // We could also use siu.msSymbolInfo.ModBase to get the module slightly faster.
|
nuclear@0
|
1897
|
nuclear@0
|
1898 // Get the function/offset.
|
nuclear@0
|
1899 SYMBOL_INFO_UNION siu;
|
nuclear@0
|
1900 memset(&siu, 0, sizeof(siu));
|
nuclear@0
|
1901 siu.msSymbolInfo.SizeOfStruct = sizeof(siu.msSymbolInfo);
|
nuclear@0
|
1902 siu.msSymbolInfo.MaxNameLen = sizeof(siu.suffixPadding) - sizeof(SYMBOL_INFO) + 1; // +1 because SYMBOL_INFO itself has Name[1].
|
nuclear@0
|
1903
|
nuclear@0
|
1904 HANDLE hProcess = GetCurrentProcess();
|
nuclear@0
|
1905 DWORD64 displacement64 = 0;
|
nuclear@0
|
1906 bool bResult = (pSymFromAddr != nullptr) && (pSymFromAddr(hProcess, address, &displacement64, &siu.msSymbolInfo) != FALSE);
|
nuclear@0
|
1907
|
nuclear@0
|
1908 if(bResult)
|
nuclear@0
|
1909 {
|
nuclear@0
|
1910 symbolInfo.size = siu.msSymbolInfo.Size;
|
nuclear@0
|
1911 OVR_strlcpy(symbolInfo.function, siu.msSymbolInfo.Name, OVR_ARRAY_COUNT(symbolInfo.function));
|
nuclear@0
|
1912 symbolInfo.functionOffset = (int32_t)displacement64;
|
nuclear@0
|
1913 }
|
nuclear@0
|
1914 else
|
nuclear@0
|
1915 {
|
nuclear@0
|
1916 symbolInfo.size = kMISizeInvalid;
|
nuclear@0
|
1917 symbolInfo.function[0] = 0;
|
nuclear@0
|
1918 symbolInfo.functionOffset = kMIFunctionOffsetInvalid;
|
nuclear@0
|
1919 }
|
nuclear@0
|
1920
|
nuclear@0
|
1921 // Get the file/line
|
nuclear@0
|
1922 IMAGEHLP_LINE64 iLine64;
|
nuclear@0
|
1923 DWORD displacement = 0;
|
nuclear@0
|
1924 memset(&iLine64, 0, sizeof(iLine64));
|
nuclear@0
|
1925 iLine64.SizeOfStruct = sizeof(iLine64);
|
nuclear@0
|
1926
|
nuclear@0
|
1927 bResult = (pSymGetLineFromAddr64 != nullptr) && (pSymGetLineFromAddr64(hProcess, address, &displacement, &iLine64) != FALSE);
|
nuclear@0
|
1928
|
nuclear@0
|
1929 if(bResult)
|
nuclear@0
|
1930 {
|
nuclear@0
|
1931 OVR_strlcpy(symbolInfo.filePath, iLine64.FileName, OVR_ARRAY_COUNT(symbolInfo.filePath));
|
nuclear@0
|
1932 symbolInfo.fileLineNumber = (int32_t)iLine64.LineNumber;
|
nuclear@0
|
1933 }
|
nuclear@0
|
1934 else
|
nuclear@0
|
1935 {
|
nuclear@0
|
1936 symbolInfo.filePath[0] = 0;
|
nuclear@0
|
1937 symbolInfo.fileLineNumber = kMILineNumberInvalid;
|
nuclear@0
|
1938 }
|
nuclear@0
|
1939
|
nuclear@0
|
1940 // To do: get the source code when possible. We need to use the user-registered directory paths and the symbolInfo.filePath
|
nuclear@0
|
1941 // and find the given file in the tree(s), then open the file and find the symbolInfo.fileLineNumber line (and surrounding lines).
|
nuclear@0
|
1942 // symbolInfo.sourceCode[1024]
|
nuclear@0
|
1943 symbolInfo.sourceCode[0] = '\0';
|
nuclear@0
|
1944 }
|
nuclear@0
|
1945
|
nuclear@0
|
1946 #elif defined(OVR_OS_APPLE)
|
nuclear@0
|
1947 // Apple has an internal CoreSymbolication library which could help with this.
|
nuclear@0
|
1948 // Third party implementations of the CoreSymbolication header are available and could be used
|
nuclear@0
|
1949 // to get file/line info better than other means. It used Objective C, so we'll need a .m or .mm file.
|
nuclear@0
|
1950
|
nuclear@0
|
1951 memset(pSymbolInfoArray, 0, arraySize * sizeof(SymbolInfo));
|
nuclear@0
|
1952
|
nuclear@0
|
1953 for(size_t i = 0; i < arraySize; i++)
|
nuclear@0
|
1954 {
|
nuclear@0
|
1955 pSymbolInfoArray[i].address = addressArray[i];
|
nuclear@0
|
1956 pSymbolInfoArray[i].pModuleInfo = GetModuleInfoForAddress(addressArray[i]);
|
nuclear@0
|
1957 }
|
nuclear@0
|
1958
|
nuclear@0
|
1959 // Problem: backtrace_symbols allocates memory from malloc. If you got into a SIGSEGV due to
|
nuclear@0
|
1960 // malloc arena corruption (quite common) you will likely fault in backtrace_symbols.
|
nuclear@0
|
1961 // To do: Use allowMemoryAllocation here.
|
nuclear@0
|
1962
|
nuclear@0
|
1963 #if (OVR_PTR_SIZE == 4)
|
nuclear@0
|
1964 // backtrace_symbols takes a void* array, but we have a uint64_t array. So for 32 bit we
|
nuclear@0
|
1965 // need to convert the 64 bit array to 32 bit temporarily for the backtrace_symbols call.
|
nuclear@0
|
1966 void* ptr32Array[256]; // To do: Remove this limit.
|
nuclear@0
|
1967 for(size_t i = 0, iEnd = MIN(arraySize, OVR_ARRAY_COUNT(ptr32Array)); i < iEnd; i++)
|
nuclear@0
|
1968 ptr32Array[i] = reinterpret_cast<void*>(addressArray[i]);
|
nuclear@0
|
1969 char** symbolArray = backtrace_symbols(reinterpret_cast<void**>(ptr32Array), (int)arraySize);
|
nuclear@0
|
1970 #else
|
nuclear@0
|
1971 char** symbolArray = backtrace_symbols(reinterpret_cast<void**>(addressArray), (int)arraySize);
|
nuclear@0
|
1972 #endif
|
nuclear@0
|
1973
|
nuclear@0
|
1974 if(symbolArray)
|
nuclear@0
|
1975 {
|
nuclear@0
|
1976 for(size_t i = 0; i < arraySize; i++)
|
nuclear@0
|
1977 {
|
nuclear@0
|
1978
|
nuclear@0
|
1979 // Generates a string like this: "0 OculusWorldDemo 0x000000010000cfd5 _ZN18OculusWorldDemoApp9OnStartupEiPPKc + 213"
|
nuclear@0
|
1980 static_assert(OVR_ARRAY_COUNT(pSymbolInfoArray[i].function) == 128, "Need to change the string format size below");
|
nuclear@0
|
1981
|
nuclear@0
|
1982 sscanf(symbolArray[i], "%*d %*s %*x %128s + %d", pSymbolInfoArray[i].function, &pSymbolInfoArray[i].functionOffset);
|
nuclear@0
|
1983
|
nuclear@0
|
1984 if(allowMemoryAllocation)
|
nuclear@0
|
1985 {
|
nuclear@0
|
1986 int status = 0;
|
nuclear@0
|
1987 char* strDemangled = abi::__cxa_demangle(pSymbolInfoArray[i].function, nullptr, nullptr, &status);
|
nuclear@0
|
1988
|
nuclear@0
|
1989 if(strDemangled)
|
nuclear@0
|
1990 {
|
nuclear@0
|
1991 OVR_strlcpy(pSymbolInfoArray[i].function, strDemangled, OVR_ARRAY_COUNT(pSymbolInfoArray[i].function));
|
nuclear@0
|
1992 free(strDemangled);
|
nuclear@0
|
1993 }
|
nuclear@0
|
1994 }
|
nuclear@0
|
1995 }
|
nuclear@0
|
1996
|
nuclear@0
|
1997 free(symbolArray);
|
nuclear@0
|
1998 }
|
nuclear@0
|
1999
|
nuclear@0
|
2000 // To consider: use CoreSybolication to get file/line info instead. atos is a bit slow and cumbersome.
|
nuclear@0
|
2001 // https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/atos.1.html
|
nuclear@0
|
2002 // atos -p <pid> <addr> <addr> ...
|
nuclear@0
|
2003 // atos -o <binary image path> -l <load-address> <addr> <addr> ...
|
nuclear@0
|
2004 // Generates output like this: "OVR::CreateException(OVR::CreateExceptionType) (in OculusWorldDemo) (ExceptionHandler.cpp:598)"
|
nuclear@0
|
2005 for(size_t i = 0; i < arraySize; i++)
|
nuclear@0
|
2006 {
|
nuclear@0
|
2007 struct stat statStruct;
|
nuclear@0
|
2008
|
nuclear@0
|
2009 if(pSymbolInfoArray[i].pModuleInfo && pSymbolInfoArray[i].pModuleInfo->filePath[0] && (stat(pSymbolInfoArray[i].pModuleInfo->filePath, &statStruct) == 0))
|
nuclear@0
|
2010 {
|
nuclear@0
|
2011 char command[PATH_MAX * 2]; // Problem: We can't unilaterally use pSymbolInfoArray[0] for all addresses. We need to match addresses to the corresponding modules.
|
nuclear@0
|
2012 OVR_snprintf(command, OVR_ARRAY_COUNT(command), "atos -o %s -l 0x%llx 0x%llx",
|
nuclear@0
|
2013 pSymbolInfoArray[i].pModuleInfo->filePath, (int64_t)pSymbolInfoArray[i].pModuleInfo->baseAddress, (int64_t)pSymbolInfoArray[i].address);
|
nuclear@0
|
2014
|
nuclear@0
|
2015 char output[512];
|
nuclear@0
|
2016 if(SpawnShellCommand(command, output, OVR_ARRAY_COUNT(output)) != (size_t)-1)
|
nuclear@0
|
2017 {
|
nuclear@0
|
2018 char* pLastOpenParen = strrchr(output, '(');
|
nuclear@0
|
2019 char* pColon = strrchr(output, ':');
|
nuclear@0
|
2020
|
nuclear@0
|
2021 if(pLastOpenParen && (pColon > pLastOpenParen))
|
nuclear@0
|
2022 {
|
nuclear@0
|
2023 *pColon = '\0';
|
nuclear@0
|
2024 OVR_strlcpy(pSymbolInfoArray[i].filePath, pLastOpenParen + 1, OVR_ARRAY_COUNT(pSymbolInfoArray[i].filePath));
|
nuclear@0
|
2025 }
|
nuclear@0
|
2026 }
|
nuclear@0
|
2027 }
|
nuclear@0
|
2028 }
|
nuclear@0
|
2029
|
nuclear@0
|
2030 #elif defined(OVR_OS_LINUX)
|
nuclear@0
|
2031 // We can use libunwind's unw_get_proc_name to try to get function name info. It can work regardless of relocation.
|
nuclear@0
|
2032 // Use backtrace_symbols and addr2line. Need to watch out for module load-time relocation.
|
nuclear@0
|
2033 // Ned to pass the -rdynamic flag to the linker. It will cause the linker to out in the link
|
nuclear@0
|
2034 // tables the name of all the none static functions in your code, not just the exported ones.
|
nuclear@0
|
2035 OVR_UNUSED(addressArray);
|
nuclear@0
|
2036 OVR_UNUSED(pSymbolInfoArray);
|
nuclear@0
|
2037 OVR_UNUSED(arraySize);
|
nuclear@0
|
2038 #endif
|
nuclear@0
|
2039
|
nuclear@0
|
2040 return true; // To do: Return true only if something was found.
|
nuclear@0
|
2041 }
|
nuclear@0
|
2042
|
nuclear@0
|
2043
|
nuclear@0
|
2044 const ModuleInfo* SymbolLookup::GetModuleInfoForAddress(uint64_t address)
|
nuclear@0
|
2045 {
|
nuclear@0
|
2046 // This is a linear seach. To consider: it would be significantly faster to search by
|
nuclear@0
|
2047 // address if we ordered it by base address and did a binary search.
|
nuclear@0
|
2048 for(size_t i = 0; i < moduleInfoArraySize; ++i)
|
nuclear@0
|
2049 {
|
nuclear@0
|
2050 const ModuleInfo& mi = moduleInfoArray[i];
|
nuclear@0
|
2051
|
nuclear@0
|
2052 if((mi.baseAddress <= address) && (address < (mi.baseAddress + mi.size)))
|
nuclear@0
|
2053 return &mi;
|
nuclear@0
|
2054 }
|
nuclear@0
|
2055
|
nuclear@0
|
2056 return nullptr;
|
nuclear@0
|
2057 }
|
nuclear@0
|
2058
|
nuclear@0
|
2059
|
nuclear@0
|
2060
|
nuclear@0
|
2061
|
nuclear@0
|
2062 ExceptionInfo::ExceptionInfo()
|
nuclear@0
|
2063 : time()
|
nuclear@0
|
2064 , timeVal(0)
|
nuclear@0
|
2065 , backtrace()
|
nuclear@0
|
2066 , backtraceCount(0)
|
nuclear@0
|
2067 , threadHandle(OVR_THREADHANDLE_INVALID)
|
nuclear@0
|
2068 , threadSysId(OVR_THREADSYSID_INVALID)
|
nuclear@0
|
2069 , threadName()
|
nuclear@0
|
2070 , pExceptionInstructionAddress(nullptr)
|
nuclear@0
|
2071 , pExceptionMemoryAddress(nullptr)
|
nuclear@0
|
2072 , cpuContext()
|
nuclear@0
|
2073 , exceptionDescription()
|
nuclear@0
|
2074 , symbolInfo()
|
nuclear@0
|
2075 #if defined(OVR_OS_MS)
|
nuclear@0
|
2076 , exceptionRecord()
|
nuclear@0
|
2077 #elif defined(OVR_OS_APPLE)
|
nuclear@0
|
2078 , exceptionType(0)
|
nuclear@0
|
2079 , cpuExceptionId(0)
|
nuclear@0
|
2080 , cpuExceptionIdError(0)
|
nuclear@0
|
2081 , machExceptionDetail()
|
nuclear@0
|
2082 , machExceptionDetailCount(0)
|
nuclear@0
|
2083 #endif
|
nuclear@0
|
2084 {
|
nuclear@0
|
2085 }
|
nuclear@0
|
2086
|
nuclear@0
|
2087
|
nuclear@0
|
2088
|
nuclear@0
|
2089 ExceptionHandler::ExceptionHandler()
|
nuclear@0
|
2090 : enabled(false)
|
nuclear@0
|
2091 , reportPrivacyEnabled(true)
|
nuclear@0
|
2092 , exceptionResponse(kERHandle)
|
nuclear@0
|
2093 , exceptionListener(nullptr)
|
nuclear@0
|
2094 , exceptionListenerUserValue(0)
|
nuclear@0
|
2095 , appDescription()
|
nuclear@0
|
2096 , codeBasePathArray()
|
nuclear@0
|
2097 , reportFilePath()
|
nuclear@0
|
2098 , miniDumpFlags(0)
|
nuclear@0
|
2099 , miniDumpFilePath()
|
nuclear@0
|
2100 , file(nullptr)
|
nuclear@0
|
2101 , scratchBuffer()
|
nuclear@0
|
2102 , exceptionOccurred(false)
|
nuclear@0
|
2103 , handlingBusy(0)
|
nuclear@0
|
2104 , reportFilePathActual()
|
nuclear@0
|
2105 , minidumpFilePathActual()
|
nuclear@0
|
2106 , terminateReturnValue(0)
|
nuclear@0
|
2107 , exceptionInfo()
|
nuclear@0
|
2108 #if defined(OVR_OS_MS)
|
nuclear@0
|
2109 , vectoredHandle(nullptr)
|
nuclear@0
|
2110 , previousFilter(nullptr)
|
nuclear@0
|
2111 , pExceptionPointers(nullptr)
|
nuclear@0
|
2112 #elif defined(OVR_OS_MAC)
|
nuclear@0
|
2113 , machHandlerInitialized(false)
|
nuclear@0
|
2114 , machExceptionPort(0)
|
nuclear@0
|
2115 , machExceptionPortsSaved()
|
nuclear@0
|
2116 , machThreadShouldContinue(false)
|
nuclear@0
|
2117 , machThreadExecuting(false)
|
nuclear@0
|
2118 , machThread((pthread_t)OVR_THREADHANDLE_INVALID)
|
nuclear@0
|
2119 #endif
|
nuclear@0
|
2120 {
|
nuclear@0
|
2121 SetExceptionPaths("default", "default");
|
nuclear@0
|
2122 }
|
nuclear@0
|
2123
|
nuclear@0
|
2124
|
nuclear@0
|
2125 ExceptionHandler::~ExceptionHandler()
|
nuclear@0
|
2126 {
|
nuclear@0
|
2127 if(enabled)
|
nuclear@0
|
2128 {
|
nuclear@0
|
2129 Enable(false);
|
nuclear@0
|
2130 }
|
nuclear@0
|
2131 }
|
nuclear@0
|
2132
|
nuclear@0
|
2133
|
nuclear@0
|
2134 #if defined(OVR_OS_MS)
|
nuclear@0
|
2135 static ExceptionHandler* sExceptionHandler = nullptr;
|
nuclear@0
|
2136
|
nuclear@0
|
2137 LONG WINAPI Win32ExceptionFilter(LPEXCEPTION_POINTERS pExceptionPointers)
|
nuclear@0
|
2138 {
|
nuclear@0
|
2139 if(sExceptionHandler)
|
nuclear@0
|
2140 return (LONG)sExceptionHandler->ExceptionFilter(pExceptionPointers);
|
nuclear@0
|
2141 return EXCEPTION_CONTINUE_SEARCH;
|
nuclear@0
|
2142 }
|
nuclear@0
|
2143
|
nuclear@0
|
2144 LONG ExceptionHandler::ExceptionFilter(LPEXCEPTION_POINTERS pExceptionPointers)
|
nuclear@0
|
2145 {
|
nuclear@0
|
2146 // Exception codes < 0x80000000 are not true exceptions but rather are debugger notifications. They include DBG_TERMINATE_THREAD,
|
nuclear@0
|
2147 // DBG_TERMINATE_PROCESS, DBG_CONTROL_BREAK, DBG_COMMAND_EXCEPTION, DBG_CONTROL_C, DBG_PRINTEXCEPTION_C, DBG_RIPEXCEPTION,
|
nuclear@0
|
2148 // and 0x406d1388 (thread named, http://blogs.msdn.com/b/stevejs/archive/2005/12/19/505815.aspx).
|
nuclear@0
|
2149
|
nuclear@0
|
2150 if(pExceptionPointers->ExceptionRecord->ExceptionCode < 0x80000000)
|
nuclear@0
|
2151 return EXCEPTION_CONTINUE_SEARCH;
|
nuclear@0
|
2152
|
nuclear@0
|
2153 // VC++ C++ exceptions use code 0xe06d7363 ('Emsc')
|
nuclear@0
|
2154 // http://support.microsoft.com/kb/185294
|
nuclear@0
|
2155 // http://blogs.msdn.com/b/oldnewthing/archive/2010/07/30/10044061.aspx
|
nuclear@0
|
2156 if(pExceptionPointers->ExceptionRecord->ExceptionCode == 0xe06d7363)
|
nuclear@0
|
2157 return EXCEPTION_CONTINUE_SEARCH;
|
nuclear@0
|
2158
|
nuclear@0
|
2159 if(handlingBusy.CompareAndSet_Acquire(0, 1)) // If we can successfully change it from 0 to 1.
|
nuclear@0
|
2160 {
|
nuclear@0
|
2161 exceptionOccurred = true;
|
nuclear@0
|
2162
|
nuclear@0
|
2163 this->pExceptionPointers = pExceptionPointers;
|
nuclear@0
|
2164
|
nuclear@0
|
2165 // Disable the handler while we do this processing.
|
nuclear@0
|
2166 ULONG result = RemoveVectoredExceptionHandler(vectoredHandle);
|
nuclear@0
|
2167 OVR_ASSERT_AND_UNUSED(result != 0, result);
|
nuclear@0
|
2168
|
nuclear@0
|
2169 // Time
|
nuclear@0
|
2170 exceptionInfo.timeVal = time(nullptr);
|
nuclear@0
|
2171 exceptionInfo.time = *gmtime(&exceptionInfo.timeVal);
|
nuclear@0
|
2172
|
nuclear@0
|
2173 // Thread id
|
nuclear@0
|
2174 // This is the thread id of the current thread and not the exception thread.
|
nuclear@0
|
2175 if(!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &exceptionInfo.threadHandle, 0, true, DUPLICATE_SAME_ACCESS))
|
nuclear@0
|
2176 exceptionInfo.threadHandle = 0;
|
nuclear@0
|
2177 exceptionInfo.threadSysId = ConvertThreadHandleToThreadSysId(exceptionInfo.threadHandle);
|
nuclear@0
|
2178
|
nuclear@0
|
2179 OVR::GetThreadName(exceptionInfo.threadHandle, exceptionInfo.threadName, OVR_ARRAY_COUNT(exceptionInfo.threadName));
|
nuclear@0
|
2180
|
nuclear@0
|
2181 // Backtraces
|
nuclear@0
|
2182 exceptionInfo.backtraceCount = symbolLookup.GetBacktrace(exceptionInfo.backtrace, OVR_ARRAY_COUNT(exceptionInfo.backtrace));
|
nuclear@0
|
2183
|
nuclear@0
|
2184 // Context
|
nuclear@0
|
2185 exceptionInfo.cpuContext = *pExceptionPointers->ContextRecord;
|
nuclear@0
|
2186 exceptionInfo.exceptionRecord = *pExceptionPointers->ExceptionRecord;
|
nuclear@0
|
2187 exceptionInfo.pExceptionInstructionAddress = exceptionInfo.exceptionRecord.ExceptionAddress;
|
nuclear@0
|
2188 if((exceptionInfo.exceptionRecord.ExceptionCode == EXCEPTION_ACCESS_VIOLATION) || (exceptionInfo.exceptionRecord.ExceptionCode == EXCEPTION_IN_PAGE_ERROR))
|
nuclear@0
|
2189 exceptionInfo.pExceptionMemoryAddress = (void*)exceptionInfo.exceptionRecord.ExceptionInformation[1]; // ExceptionInformation[0] indicates if it was a read (0), write (1), or data execution attempt (8).
|
nuclear@0
|
2190 else
|
nuclear@0
|
2191 exceptionInfo.pExceptionMemoryAddress = pExceptionPointers->ExceptionRecord->ExceptionAddress;
|
nuclear@0
|
2192
|
nuclear@0
|
2193 WriteExceptionDescription();
|
nuclear@0
|
2194
|
nuclear@0
|
2195 if(miniDumpFilePath[0])
|
nuclear@0
|
2196 WriteMiniDump();
|
nuclear@0
|
2197
|
nuclear@0
|
2198 if(reportFilePath[0])
|
nuclear@0
|
2199 WriteReport();
|
nuclear@0
|
2200
|
nuclear@0
|
2201 if(exceptionListener)
|
nuclear@0
|
2202 exceptionListener->HandleException(exceptionListenerUserValue, this, &exceptionInfo, reportFilePathActual);
|
nuclear@0
|
2203
|
nuclear@0
|
2204 if(exceptionInfo.threadHandle)
|
nuclear@0
|
2205 {
|
nuclear@0
|
2206 CloseHandle(exceptionInfo.threadHandle);
|
nuclear@0
|
2207 exceptionInfo.threadHandle = 0;
|
nuclear@0
|
2208 }
|
nuclear@0
|
2209
|
nuclear@0
|
2210 // Restore the handler that we temporarily disabled above.
|
nuclear@0
|
2211 vectoredHandle = AddVectoredExceptionHandler(1, Win32ExceptionFilter);
|
nuclear@0
|
2212
|
nuclear@0
|
2213 handlingBusy.Store_Release(0);
|
nuclear@0
|
2214 }
|
nuclear@0
|
2215
|
nuclear@0
|
2216 if(exceptionResponse == ExceptionHandler::kERTerminate)
|
nuclear@0
|
2217 {
|
nuclear@0
|
2218 TerminateProcess(GetCurrentProcess(), (UINT)terminateReturnValue);
|
nuclear@0
|
2219 return terminateReturnValue;
|
nuclear@0
|
2220 }
|
nuclear@0
|
2221 else if(exceptionResponse == ExceptionHandler::kERThrow)
|
nuclear@0
|
2222 return EXCEPTION_CONTINUE_SEARCH;
|
nuclear@0
|
2223 else if(exceptionResponse == ExceptionHandler::kERContinue)
|
nuclear@0
|
2224 return EXCEPTION_CONTINUE_EXECUTION;
|
nuclear@0
|
2225 return EXCEPTION_EXECUTE_HANDLER;
|
nuclear@0
|
2226 }
|
nuclear@0
|
2227
|
nuclear@0
|
2228 #endif // defined(OVR_OS_MS)
|
nuclear@0
|
2229
|
nuclear@0
|
2230
|
nuclear@0
|
2231 #if defined(OVR_OS_APPLE)
|
nuclear@0
|
2232 // http://www.opensource.apple.com/source/xnu/xnu-2050.22.13/
|
nuclear@0
|
2233 // http://www.opensource.apple.com/source/xnu/xnu-2050.22.13/osfmk/man/
|
nuclear@0
|
2234 // http://www.opensource.apple.com/source/Libc/Libc-825.26/
|
nuclear@0
|
2235 // https://mikeash.com/pyblog/friday-qa-2013-01-11-mach-exception-handlers.html
|
nuclear@0
|
2236
|
nuclear@0
|
2237 void* ExceptionHandler::MachHandlerThreadFunction()
|
nuclear@0
|
2238 {
|
nuclear@0
|
2239 __Request__mach_exception_raise_state_identity_t msg;
|
nuclear@0
|
2240 __Reply__mach_exception_raise_state_identity_t reply;
|
nuclear@0
|
2241 mach_msg_return_t result;
|
nuclear@0
|
2242
|
nuclear@0
|
2243 machThreadExecuting = true;
|
nuclear@0
|
2244 pthread_setname_np("ExceptionHandler");
|
nuclear@0
|
2245
|
nuclear@0
|
2246 while(machThreadShouldContinue)
|
nuclear@0
|
2247 {
|
nuclear@0
|
2248 mach_msg_option_t options = MACH_RCV_MSG | MACH_RCV_LARGE;
|
nuclear@0
|
2249 natural_t timeout = 0; // Would be better to support a non-zero time.
|
nuclear@0
|
2250
|
nuclear@0
|
2251 if(timeout)
|
nuclear@0
|
2252 options |= MACH_RCV_TIMEOUT;
|
nuclear@0
|
2253
|
nuclear@0
|
2254 result = mach_msg(&msg.Head, options, 0, sizeof(msg), machExceptionPort, timeout, MACH_PORT_NULL);
|
nuclear@0
|
2255
|
nuclear@0
|
2256 if(msg.Head.msgh_id != sMachCancelMessageType)
|
nuclear@0
|
2257 {
|
nuclear@0
|
2258 if(result == MACH_MSG_SUCCESS)
|
nuclear@0
|
2259 {
|
nuclear@0
|
2260 if(mach_exc_server_OVR(&msg.Head, &reply.Head) == 0) //This will call our HandleMachException function.
|
nuclear@0
|
2261 result = ~MACH_MSG_SUCCESS;
|
nuclear@0
|
2262 }
|
nuclear@0
|
2263
|
nuclear@0
|
2264 // Send the reply
|
nuclear@0
|
2265 if(result == MACH_MSG_SUCCESS)
|
nuclear@0
|
2266 {
|
nuclear@0
|
2267 result = mach_msg(&reply.Head, MACH_SEND_MSG, reply.Head.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
nuclear@0
|
2268
|
nuclear@0
|
2269 if(result != MACH_MSG_SUCCESS)
|
nuclear@0
|
2270 {
|
nuclear@0
|
2271 // Failure.
|
nuclear@0
|
2272 }
|
nuclear@0
|
2273 }
|
nuclear@0
|
2274 }
|
nuclear@0
|
2275 }
|
nuclear@0
|
2276
|
nuclear@0
|
2277 machThreadExecuting = false;
|
nuclear@0
|
2278
|
nuclear@0
|
2279 return nullptr;
|
nuclear@0
|
2280 }
|
nuclear@0
|
2281
|
nuclear@0
|
2282
|
nuclear@0
|
2283 kern_return_t ExceptionHandler::HandleMachException(mach_port_t /*machPort*/, mach_port_t threadSysId, mach_port_t machTask,
|
nuclear@0
|
2284 exception_type_t machExceptionType, mach_exception_data_type_t* pExceptionDetail,
|
nuclear@0
|
2285 mach_msg_type_number_t exceptionDetailCount, int* /*pMachExceptionFlavor*/, thread_state_t threadStatePrev,
|
nuclear@0
|
2286 mach_msg_type_number_t /*threadStatePrevCount*/, thread_state_t /*threadStateNew*/,
|
nuclear@0
|
2287 mach_msg_type_number_t* /*pThreadStateNewCount*/)
|
nuclear@0
|
2288 {
|
nuclear@0
|
2289 // We don't want to handle exceptions for other processes.
|
nuclear@0
|
2290 if(machTask != mach_task_self())
|
nuclear@0
|
2291 return ForwardMachException(threadSysId, machTask, machExceptionType, pExceptionDetail, exceptionDetailCount);
|
nuclear@0
|
2292
|
nuclear@0
|
2293 if(handlingBusy.CompareAndSet_Acquire(0, 1)) // If we can successfully change it from 0 to 1.
|
nuclear@0
|
2294 {
|
nuclear@0
|
2295 exceptionOccurred = true;
|
nuclear@0
|
2296
|
nuclear@0
|
2297 // Disable the handler while we do this processing.
|
nuclear@0
|
2298 // To do.
|
nuclear@0
|
2299
|
nuclear@0
|
2300 // Time
|
nuclear@0
|
2301 exceptionInfo.timeVal = time(nullptr);
|
nuclear@0
|
2302 exceptionInfo.time = *gmtime(&exceptionInfo.timeVal);
|
nuclear@0
|
2303
|
nuclear@0
|
2304 // Thread id
|
nuclear@0
|
2305 exceptionInfo.threadHandle = pthread_from_mach_thread_np(threadSysId);
|
nuclear@0
|
2306 exceptionInfo.threadSysId = threadSysId;
|
nuclear@0
|
2307 pthread_getname_np((pthread_t)exceptionInfo.threadHandle, exceptionInfo.threadName, sizeof(exceptionInfo.threadName));
|
nuclear@0
|
2308
|
nuclear@0
|
2309 // Backtraces
|
nuclear@0
|
2310 exceptionInfo.backtraceCount = symbolLookup.GetBacktraceFromThreadSysId(exceptionInfo.backtrace, OVR_ARRAY_COUNT(exceptionInfo.backtrace), 0, threadSysId);
|
nuclear@0
|
2311
|
nuclear@0
|
2312 // Context
|
nuclear@0
|
2313 #if defined(OVR_CPU_X86) || defined(OVR_CPU_X86_64)
|
nuclear@0
|
2314 // We can read x86_THREAD_STATE directly fromk threadStatePrev.
|
nuclear@0
|
2315 exceptionInfo.cpuContext.threadState = *reinterpret_cast<x86_thread_state_t*>(threadStatePrev);
|
nuclear@0
|
2316
|
nuclear@0
|
2317 mach_msg_type_number_t stateCount = x86_FLOAT_STATE_COUNT;
|
nuclear@0
|
2318 thread_get_state(threadSysId, x86_FLOAT_STATE, (natural_t*)&exceptionInfo.cpuContext.floatState, &stateCount);
|
nuclear@0
|
2319
|
nuclear@0
|
2320 stateCount = x86_DEBUG_STATE_COUNT;
|
nuclear@0
|
2321 thread_get_state(threadSysId, x86_DEBUG_STATE, (natural_t*)&exceptionInfo.cpuContext.debugState, &stateCount);
|
nuclear@0
|
2322
|
nuclear@0
|
2323 stateCount = x86_AVX_STATE_COUNT;
|
nuclear@0
|
2324 thread_get_state(threadSysId, x86_AVX_STATE, (natural_t*)&exceptionInfo.cpuContext.avxState, &stateCount);
|
nuclear@0
|
2325
|
nuclear@0
|
2326 stateCount = x86_EXCEPTION_STATE_COUNT;
|
nuclear@0
|
2327 thread_get_state(threadSysId, x86_EXCEPTION_STATE, (natural_t*)&exceptionInfo.cpuContext.exceptionState, &stateCount);
|
nuclear@0
|
2328
|
nuclear@0
|
2329 #if defined(OVR_CPU_X86)
|
nuclear@0
|
2330 exceptionInfo.pExceptionInstructionAddress = (void*)exceptionInfo.cpuContext.threadState.uts.ts32.__eip;
|
nuclear@0
|
2331 exceptionInfo.pExceptionMemoryAddress = (void*)exceptionInfo.cpuContext.exceptionState.ues.es32.__faultvaddr;
|
nuclear@0
|
2332 exceptionInfo.cpuExceptionId = exceptionInfo.cpuContext.exceptionState.ues.es32.__trapno;
|
nuclear@0
|
2333 exceptionInfo.cpuExceptionIdError = exceptionInfo.cpuContext.exceptionState.ues.es32.__err;
|
nuclear@0
|
2334 #else
|
nuclear@0
|
2335 exceptionInfo.pExceptionInstructionAddress = (void*)exceptionInfo.cpuContext.threadState.uts.ts64.__rip;
|
nuclear@0
|
2336 exceptionInfo.pExceptionMemoryAddress = (void*)exceptionInfo.cpuContext.exceptionState.ues.es64.__faultvaddr;
|
nuclear@0
|
2337 exceptionInfo.cpuExceptionId = exceptionInfo.cpuContext.exceptionState.ues.es64.__trapno;
|
nuclear@0
|
2338 exceptionInfo.cpuExceptionIdError = exceptionInfo.cpuContext.exceptionState.ues.es64.__err;
|
nuclear@0
|
2339 #endif
|
nuclear@0
|
2340 #endif
|
nuclear@0
|
2341
|
nuclear@0
|
2342 exceptionInfo.exceptionType = machExceptionType;
|
nuclear@0
|
2343
|
nuclear@0
|
2344 exceptionInfo.machExceptionDetailCount = MIN(exceptionDetailCount, OVR_ARRAY_COUNT(exceptionInfo.machExceptionDetail));
|
nuclear@0
|
2345 for(int i = 0; i < exceptionInfo.machExceptionDetailCount; i++)
|
nuclear@0
|
2346 exceptionInfo.machExceptionDetail[i] = pExceptionDetail[i];
|
nuclear@0
|
2347
|
nuclear@0
|
2348 WriteExceptionDescription();
|
nuclear@0
|
2349
|
nuclear@0
|
2350 if(reportFilePath[0])
|
nuclear@0
|
2351 WriteReport();
|
nuclear@0
|
2352
|
nuclear@0
|
2353 if(miniDumpFilePath[0])
|
nuclear@0
|
2354 WriteMiniDump();
|
nuclear@0
|
2355
|
nuclear@0
|
2356 if(exceptionListener)
|
nuclear@0
|
2357 exceptionListener->HandleException(exceptionListenerUserValue, this, &exceptionInfo, reportFilePathActual);
|
nuclear@0
|
2358
|
nuclear@0
|
2359 // Re-restore the handler.
|
nuclear@0
|
2360 // To do.
|
nuclear@0
|
2361
|
nuclear@0
|
2362 handlingBusy.Store_Release(0);
|
nuclear@0
|
2363 }
|
nuclear@0
|
2364
|
nuclear@0
|
2365 kern_return_t result = KERN_FAILURE; // By default pass on the exception to another handler after we are done here.
|
nuclear@0
|
2366
|
nuclear@0
|
2367 if(exceptionResponse == ExceptionHandler::kERTerminate)
|
nuclear@0
|
2368 ::exit(terminateReturnValue);
|
nuclear@0
|
2369 else if(exceptionResponse == ExceptionHandler::kERThrow)
|
nuclear@0
|
2370 ForwardMachException(threadSysId, machTask, machExceptionType, pExceptionDetail, exceptionDetailCount);
|
nuclear@0
|
2371 else if(exceptionResponse == ExceptionHandler::kERDefault)
|
nuclear@0
|
2372 ::exit(terminateReturnValue);
|
nuclear@0
|
2373 else if(exceptionResponse == ExceptionHandler::kERContinue)
|
nuclear@0
|
2374 result = KERN_SUCCESS; // This will trigger a re-execution of the function.
|
nuclear@0
|
2375
|
nuclear@0
|
2376 return result;
|
nuclear@0
|
2377 }
|
nuclear@0
|
2378
|
nuclear@0
|
2379
|
nuclear@0
|
2380 bool ExceptionHandler::InitMachExceptionHandler()
|
nuclear@0
|
2381 {
|
nuclear@0
|
2382 if(!machHandlerInitialized)
|
nuclear@0
|
2383 {
|
nuclear@0
|
2384 mach_port_t machTaskSelf = mach_task_self();
|
nuclear@0
|
2385 kern_return_t result = MACH_MSG_SUCCESS;
|
nuclear@0
|
2386 exception_mask_t mask = EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_CRASH;
|
nuclear@0
|
2387
|
nuclear@0
|
2388 if(machExceptionPort == MACH_PORT_NULL)
|
nuclear@0
|
2389 {
|
nuclear@0
|
2390 result = mach_port_allocate(machTaskSelf, MACH_PORT_RIGHT_RECEIVE, &machExceptionPort);
|
nuclear@0
|
2391
|
nuclear@0
|
2392 if(result == MACH_MSG_SUCCESS)
|
nuclear@0
|
2393 {
|
nuclear@0
|
2394 result = mach_port_insert_right(machTaskSelf, machExceptionPort, machExceptionPort, MACH_MSG_TYPE_MAKE_SEND);
|
nuclear@0
|
2395
|
nuclear@0
|
2396 if(result == MACH_MSG_SUCCESS)
|
nuclear@0
|
2397 result = task_get_exception_ports(machTaskSelf, mask, machExceptionPortsSaved.masks, &machExceptionPortsSaved.count,
|
nuclear@0
|
2398 machExceptionPortsSaved.ports, machExceptionPortsSaved.behaviors, machExceptionPortsSaved.flavors);
|
nuclear@0
|
2399 }
|
nuclear@0
|
2400 }
|
nuclear@0
|
2401
|
nuclear@0
|
2402 if(result == MACH_MSG_SUCCESS)
|
nuclear@0
|
2403 {
|
nuclear@0
|
2404 result = task_set_exception_ports(machTaskSelf, mask, machExceptionPort, EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, MACHINE_THREAD_STATE);
|
nuclear@0
|
2405
|
nuclear@0
|
2406 if(result == MACH_MSG_SUCCESS)
|
nuclear@0
|
2407 {
|
nuclear@0
|
2408 machThreadShouldContinue = true;
|
nuclear@0
|
2409
|
nuclear@0
|
2410 pthread_attr_t attr;
|
nuclear@0
|
2411 pthread_attr_init(&attr);
|
nuclear@0
|
2412
|
nuclear@0
|
2413 result = pthread_create(&machThread, &attr, MachHandlerThreadFunctionStatic, (void*)this);
|
nuclear@0
|
2414 pthread_attr_destroy(&attr);
|
nuclear@0
|
2415
|
nuclear@0
|
2416 machHandlerInitialized = (result == 0);
|
nuclear@0
|
2417 }
|
nuclear@0
|
2418 }
|
nuclear@0
|
2419
|
nuclear@0
|
2420 if(!machHandlerInitialized)
|
nuclear@0
|
2421 ShutdownMachExceptionHandler();
|
nuclear@0
|
2422 }
|
nuclear@0
|
2423
|
nuclear@0
|
2424 return machHandlerInitialized;
|
nuclear@0
|
2425 }
|
nuclear@0
|
2426
|
nuclear@0
|
2427
|
nuclear@0
|
2428 void ExceptionHandler::ShutdownMachExceptionHandler()
|
nuclear@0
|
2429 {
|
nuclear@0
|
2430 if(machThreadExecuting)
|
nuclear@0
|
2431 {
|
nuclear@0
|
2432 machThreadShouldContinue = false; // Tell it to stop.
|
nuclear@0
|
2433
|
nuclear@0
|
2434 // Cancel the current exception handler thread (which is probably blocking in a call to mach_msg) by sending it a cencel message.
|
nuclear@0
|
2435 struct CancelMessage
|
nuclear@0
|
2436 {
|
nuclear@0
|
2437 mach_msg_header_t msgHeader;
|
nuclear@0
|
2438 };
|
nuclear@0
|
2439
|
nuclear@0
|
2440 CancelMessage msg;
|
nuclear@0
|
2441 memset(&msg.msgHeader, 0, sizeof(CancelMessage));
|
nuclear@0
|
2442 msg.msgHeader.msgh_id = sMachCancelMessageType;
|
nuclear@0
|
2443 msg.msgHeader.msgh_size = sizeof(CancelMessage);
|
nuclear@0
|
2444 msg.msgHeader.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MAKE_SEND);
|
nuclear@0
|
2445 msg.msgHeader.msgh_remote_port = machExceptionPort;
|
nuclear@0
|
2446 msg.msgHeader.msgh_local_port = MACH_PORT_NULL;
|
nuclear@0
|
2447
|
nuclear@0
|
2448 mach_msg_return_t result = mach_msg(&msg.msgHeader, MACH_SEND_MSG, msg.msgHeader.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
nuclear@0
|
2449
|
nuclear@0
|
2450 if(result == MACH_MSG_SUCCESS)
|
nuclear@0
|
2451 {
|
nuclear@0
|
2452 const double threeSecondsLater = ovr_GetTimeInSeconds() + 3.f;
|
nuclear@0
|
2453
|
nuclear@0
|
2454 while(machThreadExecuting && (ovr_GetTimeInSeconds() < threeSecondsLater))
|
nuclear@0
|
2455 {
|
nuclear@0
|
2456 timespec ts = { 0, 1000000000 };
|
nuclear@0
|
2457 nanosleep(&ts, nullptr);
|
nuclear@0
|
2458 }
|
nuclear@0
|
2459 }
|
nuclear@0
|
2460
|
nuclear@0
|
2461 void* joinResult = nullptr;
|
nuclear@0
|
2462 pthread_join(machThread, &joinResult);
|
nuclear@0
|
2463 machThread = 0;
|
nuclear@0
|
2464 }
|
nuclear@0
|
2465
|
nuclear@0
|
2466 if(machExceptionPort != MACH_PORT_NULL)
|
nuclear@0
|
2467 {
|
nuclear@0
|
2468 // Restore the previous ports
|
nuclear@0
|
2469 kern_return_t result = KERN_SUCCESS;
|
nuclear@0
|
2470 mach_port_t machTaskSelf = mach_task_self();
|
nuclear@0
|
2471
|
nuclear@0
|
2472 for(unsigned i = 0; (i < machExceptionPortsSaved.count) && (result == KERN_SUCCESS); i++)
|
nuclear@0
|
2473 {
|
nuclear@0
|
2474 result = task_set_exception_ports(machTaskSelf, machExceptionPortsSaved.masks[i], machExceptionPortsSaved.ports[i],
|
nuclear@0
|
2475 machExceptionPortsSaved.behaviors[i], machExceptionPortsSaved.flavors[i]);
|
nuclear@0
|
2476 }
|
nuclear@0
|
2477
|
nuclear@0
|
2478 mach_port_deallocate(machTaskSelf, machExceptionPort);
|
nuclear@0
|
2479 machExceptionPort = MACH_PORT_NULL;
|
nuclear@0
|
2480 }
|
nuclear@0
|
2481
|
nuclear@0
|
2482 machHandlerInitialized = false;
|
nuclear@0
|
2483 }
|
nuclear@0
|
2484
|
nuclear@0
|
2485
|
nuclear@0
|
2486 kern_return_t ExceptionHandler::ForwardMachException(mach_port_t thread, mach_port_t task, exception_type_t exceptionType,
|
nuclear@0
|
2487 mach_exception_data_t pExceptionDetail, mach_msg_type_number_t exceptionDetailCount)
|
nuclear@0
|
2488 {
|
nuclear@0
|
2489 kern_return_t result = KERN_FAILURE;
|
nuclear@0
|
2490 mach_msg_type_number_t i;
|
nuclear@0
|
2491
|
nuclear@0
|
2492 for(i = 0; i < machExceptionPortsSaved.count; i++)
|
nuclear@0
|
2493 {
|
nuclear@0
|
2494 if(machExceptionPortsSaved.masks[i] & (1 << exceptionType))
|
nuclear@0
|
2495 break;
|
nuclear@0
|
2496 }
|
nuclear@0
|
2497
|
nuclear@0
|
2498 if(i < machExceptionPortsSaved.count)
|
nuclear@0
|
2499 {
|
nuclear@0
|
2500 mach_port_t port = machExceptionPortsSaved.ports[i];
|
nuclear@0
|
2501 exception_behavior_t behavior = machExceptionPortsSaved.behaviors[i];
|
nuclear@0
|
2502 thread_state_flavor_t flavor = machExceptionPortsSaved.flavors[i];
|
nuclear@0
|
2503 mach_msg_type_number_t threadStateCount = THREAD_STATE_MAX;
|
nuclear@0
|
2504 thread_state_data_t threadState;
|
nuclear@0
|
2505
|
nuclear@0
|
2506 if(behavior != EXCEPTION_DEFAULT)
|
nuclear@0
|
2507 thread_get_state(thread, flavor, threadState, &threadStateCount);
|
nuclear@0
|
2508
|
nuclear@0
|
2509 switch(behavior)
|
nuclear@0
|
2510 {
|
nuclear@0
|
2511 case EXCEPTION_DEFAULT:
|
nuclear@0
|
2512 result = mach_exception_raise_OVR(port, thread, task, exceptionType, pExceptionDetail, exceptionDetailCount);
|
nuclear@0
|
2513 break;
|
nuclear@0
|
2514
|
nuclear@0
|
2515 case EXCEPTION_STATE:
|
nuclear@0
|
2516 result = mach_exception_raise_state_OVR(port, exceptionType, pExceptionDetail, exceptionDetailCount,
|
nuclear@0
|
2517 &flavor, threadState, threadStateCount, threadState, &threadStateCount);
|
nuclear@0
|
2518 break;
|
nuclear@0
|
2519
|
nuclear@0
|
2520 case EXCEPTION_STATE_IDENTITY:
|
nuclear@0
|
2521 result = mach_exception_raise_state_identity_OVR(port, thread, task, exceptionType, pExceptionDetail,
|
nuclear@0
|
2522 exceptionDetailCount, &flavor, threadState, threadStateCount, threadState, &threadStateCount);
|
nuclear@0
|
2523 break;
|
nuclear@0
|
2524
|
nuclear@0
|
2525 default:
|
nuclear@0
|
2526 result = KERN_FAILURE;
|
nuclear@0
|
2527 break;
|
nuclear@0
|
2528 }
|
nuclear@0
|
2529
|
nuclear@0
|
2530 if(behavior != EXCEPTION_DEFAULT)
|
nuclear@0
|
2531 result = thread_set_state(thread, flavor, threadState, threadStateCount);
|
nuclear@0
|
2532 }
|
nuclear@0
|
2533
|
nuclear@0
|
2534 return result;
|
nuclear@0
|
2535 }
|
nuclear@0
|
2536
|
nuclear@0
|
2537
|
nuclear@0
|
2538 #endif // OVR_OS_APPLE
|
nuclear@0
|
2539
|
nuclear@0
|
2540
|
nuclear@0
|
2541 bool ExceptionHandler::Enable(bool enable)
|
nuclear@0
|
2542 {
|
nuclear@0
|
2543 #if defined(OVR_OS_MS)
|
nuclear@0
|
2544 if(enable && !enabled)
|
nuclear@0
|
2545 {
|
nuclear@0
|
2546 OVR_ASSERT(vectoredHandle == nullptr);
|
nuclear@0
|
2547 vectoredHandle = AddVectoredExceptionHandler(1, Win32ExceptionFilter); // Windows call.
|
nuclear@0
|
2548 enabled = (vectoredHandle != nullptr);
|
nuclear@0
|
2549 OVR_ASSERT(enabled);
|
nuclear@0
|
2550 sExceptionHandler = this;
|
nuclear@0
|
2551 return enabled;
|
nuclear@0
|
2552 }
|
nuclear@0
|
2553 else if(!enable && enabled)
|
nuclear@0
|
2554 {
|
nuclear@0
|
2555 if(sExceptionHandler == this)
|
nuclear@0
|
2556 sExceptionHandler = nullptr;
|
nuclear@0
|
2557 OVR_ASSERT(vectoredHandle != nullptr);
|
nuclear@0
|
2558 ULONG result = RemoveVectoredExceptionHandler(vectoredHandle); // Windows call.
|
nuclear@0
|
2559 OVR_ASSERT_AND_UNUSED(result != 0, result);
|
nuclear@0
|
2560 vectoredHandle = nullptr;
|
nuclear@0
|
2561 enabled = false;
|
nuclear@0
|
2562 return true;
|
nuclear@0
|
2563 }
|
nuclear@0
|
2564
|
nuclear@0
|
2565 #elif defined(OVR_OS_APPLE)
|
nuclear@0
|
2566
|
nuclear@0
|
2567 if(enable && !enabled)
|
nuclear@0
|
2568 {
|
nuclear@0
|
2569 enabled = InitMachExceptionHandler();
|
nuclear@0
|
2570 OVR_ASSERT(enabled);
|
nuclear@0
|
2571 sExceptionHandler = this;
|
nuclear@0
|
2572 return enabled;
|
nuclear@0
|
2573 }
|
nuclear@0
|
2574 else if(!enable && enabled)
|
nuclear@0
|
2575 {
|
nuclear@0
|
2576 if(sExceptionHandler == this)
|
nuclear@0
|
2577 sExceptionHandler = nullptr;
|
nuclear@0
|
2578 ShutdownMachExceptionHandler();
|
nuclear@0
|
2579 enabled = false;
|
nuclear@0
|
2580 return true;
|
nuclear@0
|
2581 }
|
nuclear@0
|
2582 #else
|
nuclear@0
|
2583 OVR_UNUSED(enable);
|
nuclear@0
|
2584 #endif
|
nuclear@0
|
2585
|
nuclear@0
|
2586 return true;
|
nuclear@0
|
2587 }
|
nuclear@0
|
2588
|
nuclear@0
|
2589
|
nuclear@0
|
2590 void ExceptionHandler::EnableReportPrivacy(bool enable)
|
nuclear@0
|
2591 {
|
nuclear@0
|
2592 reportPrivacyEnabled = enable;
|
nuclear@0
|
2593 }
|
nuclear@0
|
2594
|
nuclear@0
|
2595 void ExceptionHandler::WriteExceptionDescription()
|
nuclear@0
|
2596 {
|
nuclear@0
|
2597 #if defined(OVR_OS_MS)
|
nuclear@0
|
2598 // There is some extra information available for AV exception.
|
nuclear@0
|
2599 if(exceptionInfo.exceptionRecord.ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
|
nuclear@0
|
2600 {
|
nuclear@0
|
2601 const char* error = (exceptionInfo.exceptionRecord.ExceptionInformation[0] == 0) ? "reading" :
|
nuclear@0
|
2602 ((exceptionInfo.exceptionRecord.ExceptionInformation[0] == 1) ? "writing" : "executing");
|
nuclear@0
|
2603
|
nuclear@0
|
2604 char addressStr[24];
|
nuclear@0
|
2605 SprintfAddress(addressStr, OVR_ARRAY_COUNT(addressStr), exceptionInfo.pExceptionMemoryAddress);
|
nuclear@0
|
2606 OVR::OVR_snprintf(exceptionInfo.exceptionDescription, OVR_ARRAY_COUNT(exceptionInfo.exceptionDescription), "ACCESS_VIOLATION %s address %s", error, addressStr);
|
nuclear@0
|
2607 }
|
nuclear@0
|
2608 else
|
nuclear@0
|
2609 {
|
nuclear@0
|
2610 exceptionInfo.exceptionDescription[0] = 0;
|
nuclear@0
|
2611
|
nuclear@0
|
2612 // Process "standard" exceptions, other than 'access violation'
|
nuclear@0
|
2613 #define FORMAT_EXCEPTION(x) \
|
nuclear@0
|
2614 case EXCEPTION_##x: \
|
nuclear@0
|
2615 OVR::OVR_strlcpy(exceptionInfo.exceptionDescription, #x, OVR_ARRAY_COUNT(exceptionInfo.exceptionDescription)); \
|
nuclear@0
|
2616 break;
|
nuclear@0
|
2617
|
nuclear@0
|
2618 switch(exceptionInfo.exceptionRecord.ExceptionCode)
|
nuclear@0
|
2619 {
|
nuclear@0
|
2620 //FORMAT_EXCEPTION(ACCESS_VIOLATION) Already handled above.
|
nuclear@0
|
2621 FORMAT_EXCEPTION(DATATYPE_MISALIGNMENT)
|
nuclear@0
|
2622 FORMAT_EXCEPTION(BREAKPOINT)
|
nuclear@0
|
2623 FORMAT_EXCEPTION(SINGLE_STEP)
|
nuclear@0
|
2624 FORMAT_EXCEPTION(ARRAY_BOUNDS_EXCEEDED)
|
nuclear@0
|
2625 FORMAT_EXCEPTION(FLT_DENORMAL_OPERAND)
|
nuclear@0
|
2626 FORMAT_EXCEPTION(FLT_DIVIDE_BY_ZERO)
|
nuclear@0
|
2627 FORMAT_EXCEPTION(FLT_INEXACT_RESULT)
|
nuclear@0
|
2628 FORMAT_EXCEPTION(FLT_INVALID_OPERATION)
|
nuclear@0
|
2629 FORMAT_EXCEPTION(FLT_OVERFLOW)
|
nuclear@0
|
2630 FORMAT_EXCEPTION(FLT_STACK_CHECK)
|
nuclear@0
|
2631 FORMAT_EXCEPTION(FLT_UNDERFLOW)
|
nuclear@0
|
2632 FORMAT_EXCEPTION(INT_DIVIDE_BY_ZERO)
|
nuclear@0
|
2633 FORMAT_EXCEPTION(INT_OVERFLOW)
|
nuclear@0
|
2634 FORMAT_EXCEPTION(PRIV_INSTRUCTION)
|
nuclear@0
|
2635 FORMAT_EXCEPTION(IN_PAGE_ERROR)
|
nuclear@0
|
2636 FORMAT_EXCEPTION(ILLEGAL_INSTRUCTION)
|
nuclear@0
|
2637 FORMAT_EXCEPTION(NONCONTINUABLE_EXCEPTION)
|
nuclear@0
|
2638 FORMAT_EXCEPTION(STACK_OVERFLOW)
|
nuclear@0
|
2639 FORMAT_EXCEPTION(INVALID_DISPOSITION)
|
nuclear@0
|
2640 FORMAT_EXCEPTION(GUARD_PAGE)
|
nuclear@0
|
2641 FORMAT_EXCEPTION(INVALID_HANDLE)
|
nuclear@0
|
2642 #if defined(EXCEPTION_POSSIBLE_DEADLOCK) && defined(STATUS_POSSIBLE_DEADLOCK) // This type seems to be non-existant in practice.
|
nuclear@0
|
2643 FORMAT_EXCEPTION(POSSIBLE_DEADLOCK)
|
nuclear@0
|
2644 #endif
|
nuclear@0
|
2645 }
|
nuclear@0
|
2646
|
nuclear@0
|
2647 // If not one of the "known" exceptions, try to get the string from NTDLL.DLL's message table.
|
nuclear@0
|
2648 if(exceptionInfo.exceptionDescription[0] == 0)
|
nuclear@0
|
2649 {
|
nuclear@0
|
2650 char addressStr[24];
|
nuclear@0
|
2651 SprintfAddress(addressStr, OVR_ARRAY_COUNT(addressStr), exceptionInfo.pExceptionMemoryAddress);
|
nuclear@0
|
2652
|
nuclear@0
|
2653 #if !defined(OVR_OS_CONSOLE) // If FormatMessage is supported...
|
nuclear@0
|
2654 char buffer[384];
|
nuclear@0
|
2655 DWORD capacity = OVR_ARRAY_COUNT(buffer);
|
nuclear@0
|
2656
|
nuclear@0
|
2657 const size_t length = (size_t)FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE,
|
nuclear@0
|
2658 GetModuleHandleW(L"NTDLL.DLL"), exceptionInfo.exceptionRecord.ExceptionCode, 0, buffer, capacity, nullptr);
|
nuclear@0
|
2659 if(length)
|
nuclear@0
|
2660 OVR::OVR_snprintf(exceptionInfo.exceptionDescription, OVR_ARRAY_COUNT(exceptionInfo.exceptionDescription),
|
nuclear@0
|
2661 "%s at instruction %s", buffer, addressStr);
|
nuclear@0
|
2662 #endif
|
nuclear@0
|
2663
|
nuclear@0
|
2664 // If everything else failed just show the hex code.
|
nuclear@0
|
2665 if(exceptionInfo.exceptionDescription[0] == 0)
|
nuclear@0
|
2666 OVR::OVR_snprintf(exceptionInfo.exceptionDescription, OVR_ARRAY_COUNT(exceptionInfo.exceptionDescription),
|
nuclear@0
|
2667 "Unknown exception 0x%08x at instruction %s", exceptionInfo.exceptionRecord.ExceptionCode, addressStr);
|
nuclear@0
|
2668 }
|
nuclear@0
|
2669 }
|
nuclear@0
|
2670
|
nuclear@0
|
2671 #elif defined(OVR_OS_APPLE)
|
nuclear@0
|
2672 struct MachExceptionInfo
|
nuclear@0
|
2673 {
|
nuclear@0
|
2674 static const char* GetCPUExceptionIdString(uint32_t cpuExceptionId)
|
nuclear@0
|
2675 {
|
nuclear@0
|
2676 const char* id;
|
nuclear@0
|
2677
|
nuclear@0
|
2678 #if defined(OVR_CPU_X86) || defined(OVR_CPU_X86_64)
|
nuclear@0
|
2679 switch (cpuExceptionId)
|
nuclear@0
|
2680 {
|
nuclear@0
|
2681 case 0: id = "integer div/0"; break;
|
nuclear@0
|
2682 case 1: id = "breakpoint fault"; break;
|
nuclear@0
|
2683 case 2: id = "non-maskable interrupt"; break;
|
nuclear@0
|
2684 case 3: id = "int 3"; break;
|
nuclear@0
|
2685 case 4: id = "overflow"; break;
|
nuclear@0
|
2686 case 5: id = "bounds check failure"; break;
|
nuclear@0
|
2687 case 6: id = "invalid instruction"; break;
|
nuclear@0
|
2688 case 7: id = "coprocessor unavailable"; break;
|
nuclear@0
|
2689 case 8: id = "exception within exception"; break;
|
nuclear@0
|
2690 case 9: id = "coprocessor segment overrun"; break;
|
nuclear@0
|
2691 case 10: id = "invalid task switch"; break;
|
nuclear@0
|
2692 case 11: id = "segment not present"; break;
|
nuclear@0
|
2693 case 12: id = "stack exception"; break;
|
nuclear@0
|
2694 case 13: id = "general protection fault"; break;
|
nuclear@0
|
2695 case 14: id = "page fault"; break;
|
nuclear@0
|
2696 case 16: id = "coprocessor error"; break;
|
nuclear@0
|
2697 default: id = "<unknown>"; break;
|
nuclear@0
|
2698 }
|
nuclear@0
|
2699 #else
|
nuclear@0
|
2700 // To do: Support ARM or others.
|
nuclear@0
|
2701 #endif
|
nuclear@0
|
2702
|
nuclear@0
|
2703 return id;
|
nuclear@0
|
2704 }
|
nuclear@0
|
2705
|
nuclear@0
|
2706 static const char* GetMachExceptionTypeString(uint64_t exceptionCause)
|
nuclear@0
|
2707 {
|
nuclear@0
|
2708 switch (exceptionCause)
|
nuclear@0
|
2709 {
|
nuclear@0
|
2710 case EXC_ARITHMETIC: return "EXC_ARITHMETIC";
|
nuclear@0
|
2711 case EXC_BAD_ACCESS: return "EXC_BAD_ACCESS";
|
nuclear@0
|
2712 case EXC_BAD_INSTRUCTION: return "EXC_BAD_INSTRUCTION";
|
nuclear@0
|
2713 case EXC_BREAKPOINT: return "EXC_BREAKPOINT";
|
nuclear@0
|
2714 case EXC_CRASH: return "EXC_CRASH";
|
nuclear@0
|
2715 case EXC_EMULATION: return "EXC_EMULATION";
|
nuclear@0
|
2716 case EXC_MACH_SYSCALL: return "EXC_MACH_SYSCALL";
|
nuclear@0
|
2717 case EXC_RPC_ALERT: return "EXC_RPC_ALERT";
|
nuclear@0
|
2718 case EXC_SOFTWARE: return "EXC_SOFTWARE";
|
nuclear@0
|
2719 case EXC_SYSCALL: return "EXC_SYSCALL";
|
nuclear@0
|
2720 };
|
nuclear@0
|
2721
|
nuclear@0
|
2722 return "EXC_<unknown>";
|
nuclear@0
|
2723 }
|
nuclear@0
|
2724
|
nuclear@0
|
2725 static const char* GetMachExceptionIdString(uint64_t machExceptionId, uint64_t code0)
|
nuclear@0
|
2726 {
|
nuclear@0
|
2727 const char* id = "<unknown>";
|
nuclear@0
|
2728
|
nuclear@0
|
2729 #if defined(OVR_CPU_X86) || defined(OVR_CPU_X86_64)
|
nuclear@0
|
2730 switch (machExceptionId)
|
nuclear@0
|
2731 {
|
nuclear@0
|
2732 case EXC_ARITHMETIC:
|
nuclear@0
|
2733 switch (code0)
|
nuclear@0
|
2734 {
|
nuclear@0
|
2735 case EXC_I386_BOUND: id = "EXC_I386_BOUND"; break;
|
nuclear@0
|
2736 case EXC_I386_DIV: id = "EXC_I386_DIV"; break;
|
nuclear@0
|
2737 case EXC_I386_EMERR: id = "EXC_I386_EMERR"; break;
|
nuclear@0
|
2738 case EXC_I386_EXTERR: id = "EXC_I386_EXTERR"; break;
|
nuclear@0
|
2739 case EXC_I386_EXTOVR: id = "EXC_I386_EXTOVR"; break;
|
nuclear@0
|
2740 case EXC_I386_INTO: id = "EXC_I386_INTO"; break;
|
nuclear@0
|
2741 case EXC_I386_NOEXT: id = "EXC_I386_NOEXT"; break;
|
nuclear@0
|
2742 case EXC_I386_SSEEXTERR: id = "EXC_I386_SSEEXTERR"; break;
|
nuclear@0
|
2743 }
|
nuclear@0
|
2744 break;
|
nuclear@0
|
2745
|
nuclear@0
|
2746 case EXC_BAD_INSTRUCTION:
|
nuclear@0
|
2747 if(code0 == EXC_I386_INVOP)
|
nuclear@0
|
2748 id = "EXC_I386_INVOP";
|
nuclear@0
|
2749 break;
|
nuclear@0
|
2750
|
nuclear@0
|
2751 case EXC_BREAKPOINT:
|
nuclear@0
|
2752 if(code0 == EXC_I386_BPT)
|
nuclear@0
|
2753 id = "EXC_I386_BPT";
|
nuclear@0
|
2754 else if(code0 == EXC_I386_SGL)
|
nuclear@0
|
2755 id = "EXC_I386_SGL";
|
nuclear@0
|
2756 break;
|
nuclear@0
|
2757 };
|
nuclear@0
|
2758 #else
|
nuclear@0
|
2759 // To do.
|
nuclear@0
|
2760 #endif
|
nuclear@0
|
2761
|
nuclear@0
|
2762 return id;
|
nuclear@0
|
2763 }
|
nuclear@0
|
2764 };
|
nuclear@0
|
2765
|
nuclear@0
|
2766 OVR::OVR_snprintf(exceptionInfo.exceptionDescription, OVR_ARRAY_COUNT(exceptionInfo.exceptionDescription),
|
nuclear@0
|
2767 "Mach exception type: %llu (%s)\r\n", exceptionInfo.exceptionType, MachExceptionInfo::GetMachExceptionTypeString(exceptionInfo.exceptionType));
|
nuclear@0
|
2768
|
nuclear@0
|
2769 OVR::OVR_snprintf(scratchBuffer, OVR_ARRAY_COUNT(scratchBuffer), "CPU exception info: exception id: %u (%s), exception id error: %u, fault memory address: %p\r\n",
|
nuclear@0
|
2770 exceptionInfo.cpuExceptionId, MachExceptionInfo::GetCPUExceptionIdString(exceptionInfo.cpuExceptionId), exceptionInfo.cpuExceptionIdError, exceptionInfo.pExceptionMemoryAddress);
|
nuclear@0
|
2771 OVR::OVR_strlcat(exceptionInfo.exceptionDescription, scratchBuffer, OVR_ARRAY_COUNT(exceptionInfo.exceptionDescription));
|
nuclear@0
|
2772
|
nuclear@0
|
2773
|
nuclear@0
|
2774 OVR::OVR_snprintf(scratchBuffer, OVR_ARRAY_COUNT(scratchBuffer), "Mach exception info: exception id: %llu (%s), 0x%llx (%llu)\r\n", (uint64_t)exceptionInfo.machExceptionDetail[0],
|
nuclear@0
|
2775 MachExceptionInfo::GetMachExceptionIdString(exceptionInfo.exceptionType, exceptionInfo.machExceptionDetail[0]),
|
nuclear@0
|
2776 (uint64_t)exceptionInfo.machExceptionDetail[1], (uint64_t)exceptionInfo.machExceptionDetail[1]);
|
nuclear@0
|
2777 OVR::OVR_strlcat(exceptionInfo.exceptionDescription, scratchBuffer, OVR_ARRAY_COUNT(exceptionInfo.exceptionDescription));
|
nuclear@0
|
2778 #else
|
nuclear@0
|
2779 // To do.
|
nuclear@0
|
2780 exceptionInfo.exceptionDescription[0] = 0;
|
nuclear@0
|
2781 #endif
|
nuclear@0
|
2782 }
|
nuclear@0
|
2783
|
nuclear@0
|
2784
|
nuclear@0
|
2785 void ExceptionHandler::WriteReportLine(const char* pLine)
|
nuclear@0
|
2786 {
|
nuclear@0
|
2787 fwrite(pLine, strlen(pLine), 1, file);
|
nuclear@0
|
2788 }
|
nuclear@0
|
2789
|
nuclear@0
|
2790
|
nuclear@0
|
2791 void ExceptionHandler::WriteReportLineF(const char* format, ...)
|
nuclear@0
|
2792 {
|
nuclear@0
|
2793 va_list args;
|
nuclear@0
|
2794 va_start(args, format);
|
nuclear@0
|
2795 int length = OVR_vsnprintf(scratchBuffer, OVR_ARRAY_COUNT(scratchBuffer), format, args);
|
nuclear@0
|
2796 if(length >= (int)OVR_ARRAY_COUNT(scratchBuffer)) // If we didn't have enough space...
|
nuclear@0
|
2797 length = (OVR_ARRAY_COUNT(scratchBuffer) - 1); // ... use what we have.
|
nuclear@0
|
2798 va_end(args);
|
nuclear@0
|
2799
|
nuclear@0
|
2800 fwrite(scratchBuffer, length, 1, file);
|
nuclear@0
|
2801 }
|
nuclear@0
|
2802
|
nuclear@0
|
2803
|
nuclear@0
|
2804 // Thread <name> <handle> <id>
|
nuclear@0
|
2805 // 0 <module> <address> <function> <file>:<line>
|
nuclear@0
|
2806 // 1 <module> <address> <function> <file>:<line>
|
nuclear@0
|
2807 // . . .
|
nuclear@0
|
2808 //
|
nuclear@0
|
2809 void ExceptionHandler::WriteThreadCallstack(ThreadHandle threadHandle, ThreadSysId threadSysId, const char* additionalInfo)
|
nuclear@0
|
2810 {
|
nuclear@0
|
2811 // We intentionally do not directly use the SymbolInfo::ReportThreadCallstack function because that function allocates memory,
|
nuclear@0
|
2812 // which we cannot do due to possibly being within an exception handler.
|
nuclear@0
|
2813
|
nuclear@0
|
2814 // Print the header
|
nuclear@0
|
2815 char threadName[32];
|
nuclear@0
|
2816 char threadHandleStr[32];
|
nuclear@0
|
2817 char threadSysIdStr[32];
|
nuclear@0
|
2818 char stackBaseStr[24];
|
nuclear@0
|
2819 char stackLimitStr[24];
|
nuclear@0
|
2820 char stackCurrentStr[24];
|
nuclear@0
|
2821 void* pStackBase;
|
nuclear@0
|
2822 void* pStackLimit;
|
nuclear@0
|
2823 bool isExceptionThread = (threadSysId == exceptionInfo.threadSysId);
|
nuclear@0
|
2824
|
nuclear@0
|
2825 #if defined(OVR_OS_MS) && (OVR_PTR_SIZE == 8)
|
nuclear@0
|
2826 void* pStackCurrent = (threadSysId == exceptionInfo.threadSysId) ? (void*)exceptionInfo.cpuContext.Rsp : nullptr; // We would need to suspend the thread, get its context, resume it, then read the rsp register. It turns out we are already doing that suspend/resume below in the backtrace call.
|
nuclear@0
|
2827 #elif defined(OVR_OS_MS)
|
nuclear@0
|
2828 void* pStackCurrent = (threadSysId == exceptionInfo.threadSysId) ? (void*)exceptionInfo.cpuContext.Esp : nullptr;
|
nuclear@0
|
2829 #elif defined(OVR_OS_MAC) && (OVR_PTR_SIZE == 8)
|
nuclear@0
|
2830 void* pStackCurrent = (threadSysId == exceptionInfo.threadSysId) ? (void*)exceptionInfo.cpuContext.threadState.uts.ts64.__rsp : nullptr;
|
nuclear@0
|
2831 #elif defined(OVR_OS_MAC)
|
nuclear@0
|
2832 void* pStackCurrent = (threadSysId == exceptionInfo.threadSysId) ? (void*)exceptionInfo.cpuContext.threadState.uts.ts32.__esp : nullptr;
|
nuclear@0
|
2833 #elif defined(OVR_OS_LINUX)
|
nuclear@0
|
2834 void* pStackCurrent = nullptr; // To do.
|
nuclear@0
|
2835 #endif
|
nuclear@0
|
2836
|
nuclear@0
|
2837 OVR::GetThreadStackBounds(pStackBase, pStackLimit, threadHandle);
|
nuclear@0
|
2838
|
nuclear@0
|
2839 OVR::Thread::GetThreadName(threadName, OVR_ARRAY_COUNT(threadName), threadName);
|
nuclear@0
|
2840 SprintfThreadHandle(threadHandleStr, OVR_ARRAY_COUNT(threadHandleStr), threadHandle);
|
nuclear@0
|
2841 SprintfThreadSysId(threadSysIdStr, OVR_ARRAY_COUNT(threadSysIdStr), threadSysId);
|
nuclear@0
|
2842 SprintfAddress(stackBaseStr, OVR_ARRAY_COUNT(stackBaseStr), pStackBase);
|
nuclear@0
|
2843 SprintfAddress(stackLimitStr, OVR_ARRAY_COUNT(stackLimitStr), pStackLimit);
|
nuclear@0
|
2844 SprintfAddress(stackCurrentStr, OVR_ARRAY_COUNT(stackCurrentStr), pStackCurrent);
|
nuclear@0
|
2845
|
nuclear@0
|
2846 if(threadName[0])
|
nuclear@0
|
2847 WriteReportLineF("Thread \"%s\" handle: %s, id: %s, stack base: %s, stack limit: %s, stack current: %s, %s\r\n", threadName, threadHandleStr, threadSysIdStr, stackBaseStr, stackLimitStr, stackCurrentStr, additionalInfo ? additionalInfo : "");
|
nuclear@0
|
2848 else
|
nuclear@0
|
2849 WriteReportLineF("Thread handle: %s, id: %s, stack base: %s, stack limit: %s, stack current: %s, %s\r\n", threadHandleStr, threadSysIdStr, stackBaseStr, stackLimitStr, stackCurrentStr, additionalInfo ? additionalInfo : "");
|
nuclear@0
|
2850
|
nuclear@0
|
2851 // Print the backtrace info
|
nuclear@0
|
2852 void* addressArray[64];
|
nuclear@0
|
2853 size_t addressCount = symbolLookup.GetBacktraceFromThreadSysId(addressArray, OVR_ARRAY_COUNT(addressArray), 0, threadSysId);
|
nuclear@0
|
2854 SymbolInfo symbolInfo;
|
nuclear@0
|
2855 const char* pModuleName;
|
nuclear@0
|
2856 size_t backtraceSkipCount = 0;
|
nuclear@0
|
2857
|
nuclear@0
|
2858 if(isExceptionThread)
|
nuclear@0
|
2859 {
|
nuclear@0
|
2860 // If this thread is the exception thread, skip some frames.
|
nuclear@0
|
2861 #if defined(OVR_OS_MS)
|
nuclear@0
|
2862 size_t i, iEnd = MIN(16, addressCount);
|
nuclear@0
|
2863
|
nuclear@0
|
2864 for(i = 0; i < iEnd; i++)
|
nuclear@0
|
2865 {
|
nuclear@0
|
2866 symbolLookup.LookupSymbol((uint64_t)addressArray[i], symbolInfo);
|
nuclear@0
|
2867 if(strstr(symbolInfo.function, "UserExceptionDispatcher") != nullptr)
|
nuclear@0
|
2868 break;
|
nuclear@0
|
2869 }
|
nuclear@0
|
2870
|
nuclear@0
|
2871 if(i < iEnd) // If found...
|
nuclear@0
|
2872 backtraceSkipCount = i;
|
nuclear@0
|
2873 else if(addressCount >= 9) // Else default to 9, which is coincidentally what works.
|
nuclear@0
|
2874 backtraceSkipCount = 9;
|
nuclear@0
|
2875 else
|
nuclear@0
|
2876 backtraceSkipCount = 0;
|
nuclear@0
|
2877
|
nuclear@0
|
2878 addressArray[backtraceSkipCount] = exceptionInfo.pExceptionInstructionAddress;
|
nuclear@0
|
2879 #endif
|
nuclear@0
|
2880 }
|
nuclear@0
|
2881
|
nuclear@0
|
2882 if(addressCount == 0)
|
nuclear@0
|
2883 {
|
nuclear@0
|
2884 WriteReportLine("<Unable to read backtrace>\r\n\r\n");
|
nuclear@0
|
2885 }
|
nuclear@0
|
2886 else
|
nuclear@0
|
2887 {
|
nuclear@0
|
2888 for(size_t i = backtraceSkipCount; i < addressCount; ++i)
|
nuclear@0
|
2889 {
|
nuclear@0
|
2890 symbolLookup.LookupSymbol((uint64_t)addressArray[i], symbolInfo);
|
nuclear@0
|
2891
|
nuclear@0
|
2892 if(symbolInfo.pModuleInfo && symbolInfo.pModuleInfo->name[0])
|
nuclear@0
|
2893 pModuleName = symbolInfo.pModuleInfo->name;
|
nuclear@0
|
2894 else
|
nuclear@0
|
2895 pModuleName = "(unknown module)";
|
nuclear@0
|
2896
|
nuclear@0
|
2897 char addressStr[24];
|
nuclear@0
|
2898 SprintfAddress(addressStr, OVR_ARRAY_COUNT(addressStr), addressArray[i]);
|
nuclear@0
|
2899
|
nuclear@0
|
2900 if(symbolInfo.filePath[0])
|
nuclear@0
|
2901 WriteReportLineF("%-2u %-24s %s %s+%d %s:%d\r\n%s", (unsigned)i, pModuleName, addressStr,
|
nuclear@0
|
2902 symbolInfo.function, symbolInfo.functionOffset, symbolInfo.filePath,
|
nuclear@0
|
2903 symbolInfo.fileLineNumber, (i + 1) == addressCount ? "\r\n" : "");
|
nuclear@0
|
2904 else
|
nuclear@0
|
2905 WriteReportLineF("%-2u %-24s %s %s+%d\r\n%s", (unsigned)i, pModuleName, addressStr,
|
nuclear@0
|
2906 symbolInfo.function, symbolInfo.functionOffset, (i + 1) == addressCount ? "\r\n" : ""); // If this is the last line, append another \r\n.
|
nuclear@0
|
2907 }
|
nuclear@0
|
2908 }
|
nuclear@0
|
2909 }
|
nuclear@0
|
2910
|
nuclear@0
|
2911
|
nuclear@0
|
2912 void ExceptionHandler::WriteReport()
|
nuclear@0
|
2913 {
|
nuclear@0
|
2914 // It's important that we don't allocate any memory here if we can help it.
|
nuclear@0
|
2915 using namespace OVR;
|
nuclear@0
|
2916
|
nuclear@0
|
2917 if(strstr(reportFilePath, "%s")) // If the user-specified file path includes a date/time component...
|
nuclear@0
|
2918 {
|
nuclear@0
|
2919 char dateTimeBuffer[64];
|
nuclear@0
|
2920 FormatDateTime(dateTimeBuffer, OVR_ARRAY_COUNT(dateTimeBuffer), exceptionInfo.timeVal, true, true, false, true);
|
nuclear@0
|
2921 OVR_snprintf(reportFilePathActual, OVR_ARRAY_COUNT(reportFilePathActual), reportFilePath, dateTimeBuffer);
|
nuclear@0
|
2922 }
|
nuclear@0
|
2923 else
|
nuclear@0
|
2924 {
|
nuclear@0
|
2925 OVR_strlcpy(reportFilePathActual, reportFilePath, OVR_ARRAY_COUNT(reportFilePathActual));
|
nuclear@0
|
2926 }
|
nuclear@0
|
2927
|
nuclear@0
|
2928 file = fopen(reportFilePathActual, "w");
|
nuclear@0
|
2929 OVR_ASSERT(file != nullptr);
|
nuclear@0
|
2930 if(!file)
|
nuclear@0
|
2931 return;
|
nuclear@0
|
2932
|
nuclear@0
|
2933 symbolLookup.Initialize();
|
nuclear@0
|
2934
|
nuclear@0
|
2935 {
|
nuclear@0
|
2936 // Exception information
|
nuclear@0
|
2937 WriteReportLine("Exception Info\r\n");
|
nuclear@0
|
2938
|
nuclear@0
|
2939 WriteReportLineF("Exception report file: %s\r\n", reportFilePathActual);
|
nuclear@0
|
2940
|
nuclear@0
|
2941 #if defined(OVR_OS_MS)
|
nuclear@0
|
2942 if(miniDumpFilePath[0])
|
nuclear@0
|
2943 WriteReportLineF("Exception minidump file: %s\r\n", minidumpFilePathActual);
|
nuclear@0
|
2944 #endif
|
nuclear@0
|
2945
|
nuclear@0
|
2946 char dateTimeBuffer[64];
|
nuclear@0
|
2947 FormatDateTime(dateTimeBuffer, OVR_ARRAY_COUNT(dateTimeBuffer), exceptionInfo.timeVal, true, true, false, false);
|
nuclear@0
|
2948 WriteReportLineF("Time (GMT): %s\r\n", dateTimeBuffer);
|
nuclear@0
|
2949
|
nuclear@0
|
2950 FormatDateTime(dateTimeBuffer, OVR_ARRAY_COUNT(scratchBuffer), exceptionInfo.timeVal, true, true, true, false);
|
nuclear@0
|
2951 WriteReportLineF("Time (local): %s\r\n", dateTimeBuffer);
|
nuclear@0
|
2952 WriteReportLineF("Thread name: %s\r\n", exceptionInfo.threadName[0] ? exceptionInfo.threadName : "(not available)"); // It's never possible on Windows to get thread names, as they are stored in the debugger at runtime.
|
nuclear@0
|
2953
|
nuclear@0
|
2954 SprintfThreadHandle(scratchBuffer, OVR_ARRAY_COUNT(scratchBuffer), exceptionInfo.threadHandle);
|
nuclear@0
|
2955 OVR_strlcat(scratchBuffer, "\r\n", OVR_ARRAY_COUNT(scratchBuffer));
|
nuclear@0
|
2956 WriteReportLine("Thread handle: ");
|
nuclear@0
|
2957 WriteReportLine(scratchBuffer);
|
nuclear@0
|
2958
|
nuclear@0
|
2959 SprintfThreadSysId(scratchBuffer, OVR_ARRAY_COUNT(scratchBuffer), exceptionInfo.threadSysId);
|
nuclear@0
|
2960 OVR_strlcat(scratchBuffer, "\r\n", OVR_ARRAY_COUNT(scratchBuffer));
|
nuclear@0
|
2961 WriteReportLine("Thread sys id: ");
|
nuclear@0
|
2962 WriteReportLine(scratchBuffer);
|
nuclear@0
|
2963
|
nuclear@0
|
2964 char addressStr[24];
|
nuclear@0
|
2965 SprintfAddress(addressStr, OVR_ARRAY_COUNT(addressStr), exceptionInfo.pExceptionInstructionAddress);
|
nuclear@0
|
2966 WriteReportLineF("Exception instruction address: %s (see callstack below)\r\n", addressStr);
|
nuclear@0
|
2967 WriteReportLineF("Exception description: %s\r\n", exceptionInfo.exceptionDescription);
|
nuclear@0
|
2968
|
nuclear@0
|
2969 if(symbolLookup.LookupSymbol((uint64_t)exceptionInfo.pExceptionInstructionAddress, exceptionInfo.symbolInfo))
|
nuclear@0
|
2970 {
|
nuclear@0
|
2971 if(exceptionInfo.symbolInfo.filePath[0])
|
nuclear@0
|
2972 WriteReportLineF("Exception location: %s (%d)\r\n", exceptionInfo.symbolInfo.filePath, exceptionInfo.symbolInfo.fileLineNumber);
|
nuclear@0
|
2973 else
|
nuclear@0
|
2974 WriteReportLineF("Exception location: %s (%d)\r\n", exceptionInfo.symbolInfo.function, exceptionInfo.symbolInfo.functionOffset);
|
nuclear@0
|
2975 }
|
nuclear@0
|
2976
|
nuclear@0
|
2977 // To consider: print exceptionInfo.cpuContext registers
|
nuclear@0
|
2978 }
|
nuclear@0
|
2979
|
nuclear@0
|
2980 // OVR information
|
nuclear@0
|
2981 WriteReportLine("\r\nOVR Info\r\n");
|
nuclear@0
|
2982 WriteReportLineF("OVR time: %f\r\n", ovr_GetTimeInSeconds());
|
nuclear@0
|
2983 WriteReportLineF("OVR version: %s\r\n", ovr_GetVersionString());
|
nuclear@0
|
2984
|
nuclear@0
|
2985 // OVR util information
|
nuclear@0
|
2986 // The following would be useful to use if they didn't allocate memory, which we can't do.
|
nuclear@0
|
2987 // To do: see if we can have versions of the functions below which don't allocate memory
|
nuclear@0
|
2988 // or allocate it safely (e.g. use an alternative heap).
|
nuclear@0
|
2989 // String OVR::GetDisplayDriverVersion();
|
nuclear@0
|
2990 // String OVR::GetCameraDriverVersion();
|
nuclear@0
|
2991
|
nuclear@0
|
2992 // OVR HMD information
|
nuclear@0
|
2993 WriteReportLine("\r\nOVR HMD Info\r\n");
|
nuclear@0
|
2994
|
nuclear@0
|
2995 const OVR::List<OVR::CAPI::HMDState>& hmdStateList = OVR::CAPI::HMDState::GetHMDStateList();
|
nuclear@0
|
2996 const OVR::CAPI::HMDState* pHMDState = hmdStateList.GetFirst();
|
nuclear@0
|
2997
|
nuclear@0
|
2998 if(hmdStateList.IsNull(pHMDState))
|
nuclear@0
|
2999 {
|
nuclear@0
|
3000 WriteReportLine("No HMDs found.\r\n");
|
nuclear@0
|
3001 }
|
nuclear@0
|
3002
|
nuclear@0
|
3003 while(!hmdStateList.IsNull(pHMDState))
|
nuclear@0
|
3004 {
|
nuclear@0
|
3005 if(pHMDState->pProfile)
|
nuclear@0
|
3006 {
|
nuclear@0
|
3007 const char* user = pHMDState->pProfile->GetValue(OVR_KEY_USER);
|
nuclear@0
|
3008
|
nuclear@0
|
3009 if(user)
|
nuclear@0
|
3010 WriteReportLineF("Profile user: %s\r\n", reportPrivacyEnabled ? "<disabled by report privacy settings>" : user);
|
nuclear@0
|
3011 else
|
nuclear@0
|
3012 WriteReportLine("Null profile user\r\n");
|
nuclear@0
|
3013
|
nuclear@0
|
3014 float NeckEyeDistance[2];
|
nuclear@0
|
3015 float EyeToNoseDistance[2];
|
nuclear@0
|
3016 float MaxEyeToPlateDist[2];
|
nuclear@0
|
3017 pHMDState->pProfile->GetFloatValues(OVR_KEY_NECK_TO_EYE_DISTANCE, NeckEyeDistance, 2);
|
nuclear@0
|
3018 pHMDState->pProfile->GetFloatValues(OVR_KEY_EYE_TO_NOSE_DISTANCE, EyeToNoseDistance, 2);
|
nuclear@0
|
3019 pHMDState->pProfile->GetFloatValues(OVR_KEY_MAX_EYE_TO_PLATE_DISTANCE, MaxEyeToPlateDist, 2);
|
nuclear@0
|
3020
|
nuclear@0
|
3021 WriteReportLineF("Player height: %f, eye height: %f, IPD: %f, Neck eye distance: %f,%f, eye relief dial: %d, eye to nose distance: %f,%f, max eye to plate distance: %f,%f, custom eye render: %s\r\n",
|
nuclear@0
|
3022 pHMDState->pProfile->GetFloatValue(OVR_KEY_PLAYER_HEIGHT, 0.f),
|
nuclear@0
|
3023 pHMDState->pProfile->GetFloatValue(OVR_KEY_EYE_HEIGHT, 0.f),
|
nuclear@0
|
3024 pHMDState->pProfile->GetFloatValue(OVR_KEY_IPD, 0.f),
|
nuclear@0
|
3025 NeckEyeDistance[0], NeckEyeDistance[1],
|
nuclear@0
|
3026 pHMDState->pProfile->GetIntValue(OVR_KEY_EYE_RELIEF_DIAL, 0),
|
nuclear@0
|
3027 EyeToNoseDistance[0], EyeToNoseDistance[1],
|
nuclear@0
|
3028 MaxEyeToPlateDist[0], MaxEyeToPlateDist[1],
|
nuclear@0
|
3029 pHMDState->pProfile->GetBoolValue(OVR_KEY_CUSTOM_EYE_RENDER, false) ? "yes" : "no");
|
nuclear@0
|
3030
|
nuclear@0
|
3031 // Not currently used:
|
nuclear@0
|
3032 // OVR_KEY_NAME
|
nuclear@0
|
3033 // OVR_KEY_GENDER
|
nuclear@0
|
3034 // OVR_KEY_EYE_CUP
|
nuclear@0
|
3035 // OVR_KEY_CAMERA_POSITION
|
nuclear@0
|
3036 }
|
nuclear@0
|
3037 else
|
nuclear@0
|
3038 {
|
nuclear@0
|
3039 WriteReportLine("Null HMD profile\r\n");
|
nuclear@0
|
3040 }
|
nuclear@0
|
3041
|
nuclear@0
|
3042 if(pHMDState->pHmdDesc) // This should usually be true.
|
nuclear@0
|
3043 {
|
nuclear@0
|
3044 WriteReportLineF("HMD %d: Type: %u ProductName: %s, Manufacturer: %s VendorId: %d, ProductId: %d, SerialNumber: %s, FirmwareMajor: %d, FirmwareMinor: %d, Resolution: %dx%d, DisplayDeviceName: %s, DisplayId: %d\r\n",
|
nuclear@0
|
3045 0, (unsigned)pHMDState->pHmdDesc->Type, pHMDState->pHmdDesc->ProductName, pHMDState->pHmdDesc->Manufacturer, pHMDState->pHmdDesc->VendorId,
|
nuclear@0
|
3046 pHMDState->pHmdDesc->ProductId, pHMDState->pHmdDesc->SerialNumber, pHMDState->pHmdDesc->FirmwareMajor, pHMDState->pHmdDesc->FirmwareMinor,
|
nuclear@0
|
3047 pHMDState->pHmdDesc->Resolution.w, pHMDState->pHmdDesc->Resolution.h, pHMDState->pHmdDesc->DisplayDeviceName, pHMDState->pHmdDesc->DisplayId);
|
nuclear@0
|
3048
|
nuclear@0
|
3049 // HSW display state
|
nuclear@0
|
3050 ovrHSWDisplayState hswDS;
|
nuclear@0
|
3051 ovrHmd_GetHSWDisplayState(pHMDState->pHmdDesc, &hswDS);
|
nuclear@0
|
3052 WriteReportLineF("HSW displayed for hmd: %s\r\n", hswDS.Displayed ? "yes" : "no");
|
nuclear@0
|
3053 }
|
nuclear@0
|
3054
|
nuclear@0
|
3055 char threadIdStr[24];
|
nuclear@0
|
3056 SprintfAddress(threadIdStr, OVR_ARRAY_COUNT(threadIdStr), pHMDState->BeginFrameThreadId);
|
nuclear@0
|
3057
|
nuclear@0
|
3058 WriteReportLineF("Hmd Caps: %x, Hmd Service Caps: %x, Latency test active: %s, Last frame time: %f, Last get frame time: %f, Rendering configred: %s, Begin frame called: %s, Begin frame thread id: %s\r\n",
|
nuclear@0
|
3059 pHMDState->EnabledHmdCaps, pHMDState->EnabledServiceHmdCaps, pHMDState->LatencyTestActive ? "yes" : "no", pHMDState->LastFrameTimeSeconds, pHMDState->LastGetFrameTimeSeconds, pHMDState->RenderingConfigured ? "yes" : "no",
|
nuclear@0
|
3060 pHMDState->BeginFrameCalled ? "yes" : "no", threadIdStr);
|
nuclear@0
|
3061
|
nuclear@0
|
3062 if(pHMDState->pLastError)
|
nuclear@0
|
3063 {
|
nuclear@0
|
3064 WriteReportLineF("OVR last error for hmd: %s\r\n", pHMDState->pLastError);
|
nuclear@0
|
3065 }
|
nuclear@0
|
3066
|
nuclear@0
|
3067 pHMDState = hmdStateList.GetNext(pHMDState);
|
nuclear@0
|
3068 }
|
nuclear@0
|
3069
|
nuclear@0
|
3070 #if defined(OVR_OS_WIN32)
|
nuclear@0
|
3071 {
|
nuclear@0
|
3072 WriteReportLine("\r\nApp Info\r\n");
|
nuclear@0
|
3073
|
nuclear@0
|
3074 // Print the app path.
|
nuclear@0
|
3075 char appPath[MAX_PATH];
|
nuclear@0
|
3076 GetCurrentProcessFilePath(appPath, OVR_ARRAY_COUNT(appPath));
|
nuclear@0
|
3077 WriteReportLineF("Process path: %s\r\n", appPath);
|
nuclear@0
|
3078
|
nuclear@0
|
3079 #if (OVR_PTR_SIZE == 4)
|
nuclear@0
|
3080 WriteReportLine("App format: 32 bit\r\n");
|
nuclear@0
|
3081 #else
|
nuclear@0
|
3082 WriteReportLine("App format: 64 bit\r\n");
|
nuclear@0
|
3083 #endif
|
nuclear@0
|
3084
|
nuclear@0
|
3085 // Print the app version
|
nuclear@0
|
3086 wchar_t pathW[MAX_PATH] = {};
|
nuclear@0
|
3087 GetModuleFileNameW(0, pathW, (DWORD)OVR_ARRAY_COUNT(pathW));
|
nuclear@0
|
3088 DWORD dwUnused;
|
nuclear@0
|
3089 DWORD dwSize = GetFileVersionInfoSizeW(pathW, &dwUnused);
|
nuclear@0
|
3090 scratchBuffer[0] = 0;
|
nuclear@0
|
3091
|
nuclear@0
|
3092 if(dwSize > 0)
|
nuclear@0
|
3093 {
|
nuclear@0
|
3094 void* const pVersionData = SafeMMapAlloc(dwSize);
|
nuclear@0
|
3095
|
nuclear@0
|
3096 if(pVersionData)
|
nuclear@0
|
3097 {
|
nuclear@0
|
3098 if(GetFileVersionInfoW(pathW, 0, dwSize, pVersionData))
|
nuclear@0
|
3099 {
|
nuclear@0
|
3100 VS_FIXEDFILEINFO* pFFI;
|
nuclear@0
|
3101 UINT size;
|
nuclear@0
|
3102
|
nuclear@0
|
3103 if(VerQueryValueA(pVersionData, "\\", (void**)&pFFI, &size))
|
nuclear@0
|
3104 {
|
nuclear@0
|
3105 WriteReportLineF("App version: %u.%u.%u.%u\r\n",
|
nuclear@0
|
3106 HIWORD(pFFI->dwFileVersionMS), LOWORD(pFFI->dwFileVersionMS),
|
nuclear@0
|
3107 HIWORD(pFFI->dwFileVersionLS), LOWORD(pFFI->dwFileVersionLS));
|
nuclear@0
|
3108 }
|
nuclear@0
|
3109 }
|
nuclear@0
|
3110
|
nuclear@0
|
3111 SafeMMapFree(pVersionData, dwSize);
|
nuclear@0
|
3112 }
|
nuclear@0
|
3113 }
|
nuclear@0
|
3114
|
nuclear@0
|
3115 if(!scratchBuffer[0]) // If version info couldn't be found or read...
|
nuclear@0
|
3116 WriteReportLine("App version info not present\r\n");
|
nuclear@0
|
3117 }
|
nuclear@0
|
3118
|
nuclear@0
|
3119 {
|
nuclear@0
|
3120 WriteReportLine("\r\nSystem Info\r\n");
|
nuclear@0
|
3121
|
nuclear@0
|
3122 OSVERSIONINFOEXW vi;
|
nuclear@0
|
3123 memset(&vi, 0, sizeof(vi));
|
nuclear@0
|
3124 vi.dwOSVersionInfoSize = sizeof(vi);
|
nuclear@0
|
3125 GetVersionExW((LPOSVERSIONINFOW)&vi); // Cast to the older type.
|
nuclear@0
|
3126
|
nuclear@0
|
3127 char osVersionName[256];
|
nuclear@0
|
3128 GetOSVersionName(osVersionName, OVR_ARRAY_COUNT(osVersionName));
|
nuclear@0
|
3129 WriteReportLineF("OS name: %s, version: %u.%u build %u, %s, platform id: %u, service pack: %ls\r\n",
|
nuclear@0
|
3130 osVersionName, vi.dwMajorVersion, vi.dwMinorVersion, vi.dwBuildNumber, Is64BitOS() ? "64 bit" : "32 bit",
|
nuclear@0
|
3131 vi.dwPlatformId, vi.szCSDVersion[0] ? vi.szCSDVersion : L"<none>");
|
nuclear@0
|
3132
|
nuclear@0
|
3133 WriteReportLineF("Debugger present: %s\r\n", OVRIsDebuggerPresent() ? "yes" : "no");
|
nuclear@0
|
3134
|
nuclear@0
|
3135 // System info
|
nuclear@0
|
3136 SYSTEM_INFO systemInfo;
|
nuclear@0
|
3137 GetNativeSystemInfo(&systemInfo);
|
nuclear@0
|
3138
|
nuclear@0
|
3139 WriteReportLineF("Processor count: %u\r\n", systemInfo.dwNumberOfProcessors);
|
nuclear@0
|
3140
|
nuclear@0
|
3141 // Windows Vista and later:
|
nuclear@0
|
3142 // BOOL WINAPI GetLogicalProcessorInformation(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION Buffer, PDWORD ReturnLength);
|
nuclear@0
|
3143
|
nuclear@0
|
3144 if(systemInfo.wProcessorArchitecture == 0)
|
nuclear@0
|
3145 WriteReportLineF("Processor type: x86\r\n");
|
nuclear@0
|
3146 else if(systemInfo.wProcessorArchitecture == 9)
|
nuclear@0
|
3147 WriteReportLineF("Processor type: x86-64\r\n");
|
nuclear@0
|
3148 else if(systemInfo.wProcessorArchitecture == 10)
|
nuclear@0
|
3149 WriteReportLineF("Processor type: x86 on x86-64\r\n");
|
nuclear@0
|
3150
|
nuclear@0
|
3151 WriteReportLineF("Processor level: %u\r\n", systemInfo.wProcessorLevel);
|
nuclear@0
|
3152 WriteReportLineF("Processor revision: %u\r\n", systemInfo.wProcessorRevision);
|
nuclear@0
|
3153
|
nuclear@0
|
3154 // Memory information
|
nuclear@0
|
3155 MEMORYSTATUSEX memoryStatusEx;
|
nuclear@0
|
3156 memset(&memoryStatusEx, 0, sizeof(memoryStatusEx));
|
nuclear@0
|
3157 memoryStatusEx.dwLength = sizeof(memoryStatusEx);
|
nuclear@0
|
3158 GlobalMemoryStatusEx(&memoryStatusEx);
|
nuclear@0
|
3159
|
nuclear@0
|
3160 WriteReportLineF("Memory load: %d%%\r\n", memoryStatusEx.dwMemoryLoad);
|
nuclear@0
|
3161 WriteReportLineF("Total physical memory: %I64d MiB\r\n", memoryStatusEx.ullTotalPhys / (1024 * 1024)); // Or are Mebibytes equal to (1024 * 1000)
|
nuclear@0
|
3162 WriteReportLineF("Available physical memory: %I64d MiB\r\n", memoryStatusEx.ullAvailPhys / (1024 * 1024));
|
nuclear@0
|
3163 WriteReportLineF("Total page file memory: %I64d MiB\r\n", memoryStatusEx.ullTotalPageFile / (1024 * 1024));
|
nuclear@0
|
3164 WriteReportLineF("Available page file memory: %I64d MiB\r\n", memoryStatusEx.ullAvailPageFile / (1024 * 1024));
|
nuclear@0
|
3165 WriteReportLineF("Total virtual memory: %I64d MiB\r\n", memoryStatusEx.ullTotalVirtual / (1024 * 1024));
|
nuclear@0
|
3166 WriteReportLineF("Free virtual memory: %I64d MiB\r\n", memoryStatusEx.ullAvailVirtual / (1024 * 1024));
|
nuclear@0
|
3167
|
nuclear@0
|
3168 DISPLAY_DEVICE dd;
|
nuclear@0
|
3169 memset(&dd, 0, sizeof(DISPLAY_DEVICE));
|
nuclear@0
|
3170 dd.cb = sizeof(DISPLAY_DEVICE);
|
nuclear@0
|
3171
|
nuclear@0
|
3172 for(int i = 0; EnumDisplayDevicesW(nullptr, (DWORD)i, &dd, EDD_GET_DEVICE_INTERFACE_NAME); ++i)
|
nuclear@0
|
3173 {
|
nuclear@0
|
3174 WriteReportLineF("Display Device %d name: %ls, context: %ls, primary: %s, mirroring: %s\r\n",
|
nuclear@0
|
3175 i, dd.DeviceName, dd.DeviceString, (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) ? "yes" : "no", (dd.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) ? "yes" : "no");
|
nuclear@0
|
3176 }
|
nuclear@0
|
3177 }
|
nuclear@0
|
3178
|
nuclear@0
|
3179 // Print video card information
|
nuclear@0
|
3180 // http://msdn.microsoft.com/en-us/library/aa394512%28v=vs.85%29.aspx
|
nuclear@0
|
3181 {
|
nuclear@0
|
3182 IWbemLocator* pIWbemLocator = nullptr;
|
nuclear@0
|
3183 BSTR bstrServer = nullptr;
|
nuclear@0
|
3184 IWbemServices* pIWbemServices = nullptr;
|
nuclear@0
|
3185 BSTR bstrWQL = nullptr;
|
nuclear@0
|
3186 BSTR bstrPath = nullptr;
|
nuclear@0
|
3187 IEnumWbemClassObject* pEnum = nullptr;
|
nuclear@0
|
3188
|
nuclear@0
|
3189 CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
nuclear@0
|
3190
|
nuclear@0
|
3191 HRESULT hr = CoCreateInstance(__uuidof(WbemLocator), nullptr, CLSCTX_INPROC_SERVER, __uuidof(IWbemLocator), (LPVOID*)&pIWbemLocator);
|
nuclear@0
|
3192 if(FAILED(hr))
|
nuclear@0
|
3193 goto End;
|
nuclear@0
|
3194
|
nuclear@0
|
3195 bstrServer = SysAllocString(L"\\\\.\\root\\cimv2");
|
nuclear@0
|
3196 hr = pIWbemLocator->ConnectServer(bstrServer, nullptr, nullptr, 0L, 0L, nullptr, nullptr, &pIWbemServices);
|
nuclear@0
|
3197 if(FAILED(hr))
|
nuclear@0
|
3198 goto End;
|
nuclear@0
|
3199
|
nuclear@0
|
3200 hr = CoSetProxyBlanket(pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr, RPC_C_AUTHN_LEVEL_CALL,
|
nuclear@0
|
3201 RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_DEFAULT);
|
nuclear@0
|
3202 if(FAILED(hr))
|
nuclear@0
|
3203 goto End;
|
nuclear@0
|
3204
|
nuclear@0
|
3205 bstrWQL = SysAllocString(L"WQL");
|
nuclear@0
|
3206 bstrPath = SysAllocString(L"select * from Win32_VideoController");
|
nuclear@0
|
3207 hr = pIWbemServices->ExecQuery(bstrWQL, bstrPath, WBEM_FLAG_FORWARD_ONLY, nullptr, &pEnum);
|
nuclear@0
|
3208 if(FAILED(hr))
|
nuclear@0
|
3209 goto End;
|
nuclear@0
|
3210
|
nuclear@0
|
3211 ULONG uReturned;
|
nuclear@0
|
3212 IWbemClassObject* pObj = nullptr;
|
nuclear@0
|
3213 hr = pEnum->Next(WBEM_INFINITE, 1, &pObj, &uReturned);
|
nuclear@0
|
3214 if(FAILED(hr))
|
nuclear@0
|
3215 goto End;
|
nuclear@0
|
3216
|
nuclear@0
|
3217 WriteReportLine("\r\nDisplay adapter list\r\n");
|
nuclear@0
|
3218
|
nuclear@0
|
3219 for(unsigned i = 0; SUCCEEDED(hr) && uReturned; i++)
|
nuclear@0
|
3220 {
|
nuclear@0
|
3221 char sString[256];
|
nuclear@0
|
3222 VARIANT var;
|
nuclear@0
|
3223
|
nuclear@0
|
3224 if(i > 0)
|
nuclear@0
|
3225 WriteReportLine("\r\n");
|
nuclear@0
|
3226
|
nuclear@0
|
3227 WriteReportLineF("Info for display adapter %u\r\n", i);
|
nuclear@0
|
3228
|
nuclear@0
|
3229 hr = pObj->Get(L"Name", 0, &var, nullptr, nullptr);
|
nuclear@0
|
3230 if(SUCCEEDED(hr))
|
nuclear@0
|
3231 {
|
nuclear@0
|
3232 WideCharToMultiByte(CP_ACP, 0, var.bstrVal, -1, sString, sizeof(sString), nullptr, nullptr);
|
nuclear@0
|
3233 WriteReportLineF("Display Adapter Name: %s\r\n", sString);
|
nuclear@0
|
3234 }
|
nuclear@0
|
3235
|
nuclear@0
|
3236 hr = pObj->Get(L"AdapterRAM", 0, &var, nullptr, nullptr);
|
nuclear@0
|
3237 if(SUCCEEDED(hr))
|
nuclear@0
|
3238 {
|
nuclear@0
|
3239 WriteReportLineF("Display Adapter RAM: %u %s\r\n",
|
nuclear@0
|
3240 ((uint32_t)var.lVal > (1024*1024*1024) ? (uint32_t)var.lVal/(1024*1024*1024) : (uint32_t)var.lVal/(1024*1024)), ((uint32_t)var.lVal > (1024*1024*1024) ? "GiB" : "MiB"));
|
nuclear@0
|
3241 }
|
nuclear@0
|
3242
|
nuclear@0
|
3243 hr = pObj->Get(L"DeviceID", 0, &var, nullptr, nullptr);
|
nuclear@0
|
3244 if(SUCCEEDED(hr))
|
nuclear@0
|
3245 {
|
nuclear@0
|
3246 WideCharToMultiByte(CP_ACP, 0, var.bstrVal, -1, sString, sizeof(sString), nullptr, nullptr);
|
nuclear@0
|
3247 WriteReportLineF("Display Adapter DeviceID: %s\r\n", sString);
|
nuclear@0
|
3248 }
|
nuclear@0
|
3249
|
nuclear@0
|
3250 hr = pObj->Get(L"DriverVersion", 0, &var, nullptr, nullptr);
|
nuclear@0
|
3251 if(SUCCEEDED(hr))
|
nuclear@0
|
3252 {
|
nuclear@0
|
3253 WideCharToMultiByte(CP_ACP, 0, var.bstrVal, -1, sString, sizeof(sString), nullptr, nullptr);
|
nuclear@0
|
3254 WriteReportLineF("Display Adapter DriverVersion: %s\r\n", sString);
|
nuclear@0
|
3255 }
|
nuclear@0
|
3256
|
nuclear@0
|
3257 hr = pObj->Get(L"DriverDate", 0, &var, nullptr, nullptr);
|
nuclear@0
|
3258 if(SUCCEEDED(hr))
|
nuclear@0
|
3259 {
|
nuclear@0
|
3260 // http://technet.microsoft.com/en-us/library/ee156576.aspx
|
nuclear@0
|
3261 wchar_t year[5] = { var.bstrVal[0], var.bstrVal[1], var.bstrVal[2], var.bstrVal[3], 0 };
|
nuclear@0
|
3262 wchar_t month[3] = { var.bstrVal[4], var.bstrVal[5], 0 };
|
nuclear@0
|
3263 wchar_t monthDay[3] = { var.bstrVal[6], var.bstrVal[7], 0 };
|
nuclear@0
|
3264
|
nuclear@0
|
3265 WriteReportLineF("Display Adapter DriverDate (US format): %ls/%ls/%ls\r\n", month, monthDay, year);
|
nuclear@0
|
3266 }
|
nuclear@0
|
3267
|
nuclear@0
|
3268 // VideoProcessor
|
nuclear@0
|
3269 hr = pObj->Get(L"VideoProcessor", 0, &var, nullptr, nullptr);
|
nuclear@0
|
3270 if(SUCCEEDED(hr))
|
nuclear@0
|
3271 {
|
nuclear@0
|
3272 WideCharToMultiByte(CP_ACP, 0, var.bstrVal, -1, sString, sizeof(sString), nullptr, nullptr);
|
nuclear@0
|
3273 WriteReportLineF("Display Adapter VideoProcessor %s\r\n", sString);
|
nuclear@0
|
3274 }
|
nuclear@0
|
3275
|
nuclear@0
|
3276 hr = pObj->Get(L"VideoModeDescription", 0, &var, nullptr, nullptr);
|
nuclear@0
|
3277 if(SUCCEEDED(hr))
|
nuclear@0
|
3278 {
|
nuclear@0
|
3279 WideCharToMultiByte(CP_ACP, 0, var.bstrVal, -1, sString, sizeof(sString), nullptr, nullptr);
|
nuclear@0
|
3280 WriteReportLineF("Display Adapter VideoModeDescription: %s\r\n", sString);
|
nuclear@0
|
3281 }
|
nuclear@0
|
3282
|
nuclear@0
|
3283 pObj->Release();
|
nuclear@0
|
3284
|
nuclear@0
|
3285 hr = pEnum->Next(WBEM_INFINITE, 1, &pObj, &uReturned);
|
nuclear@0
|
3286 }
|
nuclear@0
|
3287
|
nuclear@0
|
3288 End:
|
nuclear@0
|
3289 if(pEnum)
|
nuclear@0
|
3290 pEnum->Release();
|
nuclear@0
|
3291 if(bstrPath)
|
nuclear@0
|
3292 SysFreeString(bstrPath);
|
nuclear@0
|
3293 if(bstrWQL)
|
nuclear@0
|
3294 SysFreeString(bstrWQL);
|
nuclear@0
|
3295 if(pIWbemServices)
|
nuclear@0
|
3296 pIWbemServices->Release();
|
nuclear@0
|
3297 if(bstrServer)
|
nuclear@0
|
3298 SysFreeString(bstrServer);
|
nuclear@0
|
3299 if(pIWbemLocator)
|
nuclear@0
|
3300 pIWbemLocator->Release();
|
nuclear@0
|
3301
|
nuclear@0
|
3302 CoUninitialize();
|
nuclear@0
|
3303 }
|
nuclear@0
|
3304
|
nuclear@0
|
3305 {
|
nuclear@0
|
3306 // Print a list of threads.
|
nuclear@0
|
3307 DWORD currentProcessId = GetCurrentProcessId();
|
nuclear@0
|
3308 HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, currentProcessId); // ICreateToolhelp32Snapshot actually ignores currentProcessId.
|
nuclear@0
|
3309
|
nuclear@0
|
3310 if(hThreadSnap != INVALID_HANDLE_VALUE)
|
nuclear@0
|
3311 {
|
nuclear@0
|
3312 THREADENTRY32 te32;
|
nuclear@0
|
3313 te32.dwSize = sizeof(THREADENTRY32);
|
nuclear@0
|
3314
|
nuclear@0
|
3315 if(Thread32First(hThreadSnap, &te32))
|
nuclear@0
|
3316 {
|
nuclear@0
|
3317 WriteReportLine("\r\nThread list\r\n");
|
nuclear@0
|
3318
|
nuclear@0
|
3319 do {
|
nuclear@0
|
3320 if(te32.th32OwnerProcessID == currentProcessId)
|
nuclear@0
|
3321 {
|
nuclear@0
|
3322 HANDLE hThread = ConvertThreadSysIdToThreadHandle(te32.th32ThreadID);
|
nuclear@0
|
3323
|
nuclear@0
|
3324 if(hThread)
|
nuclear@0
|
3325 {
|
nuclear@0
|
3326 char buffer[96]; // Can't use scratchBuffer, because it's used by WriteThreadCallstack.
|
nuclear@0
|
3327 OVR_snprintf(buffer, OVR_ARRAY_COUNT(buffer), "base priority: %ld, delta priority: %ld", te32.tpBasePri, te32.tpDeltaPri);
|
nuclear@0
|
3328
|
nuclear@0
|
3329 bool threadIsExceptionThread = (te32.th32ThreadID == (DWORD)exceptionInfo.threadSysId);
|
nuclear@0
|
3330 if(threadIsExceptionThread)
|
nuclear@0
|
3331 OVR_strlcat(buffer, ", exception thread", OVR_ARRAY_COUNT(buffer));
|
nuclear@0
|
3332
|
nuclear@0
|
3333 WriteThreadCallstack(hThread, (OVR::ThreadSysId)te32.th32ThreadID, buffer);
|
nuclear@0
|
3334 FreeThreadHandle(hThread);
|
nuclear@0
|
3335 }
|
nuclear@0
|
3336 }
|
nuclear@0
|
3337 } while(Thread32Next(hThreadSnap, &te32));
|
nuclear@0
|
3338 }
|
nuclear@0
|
3339
|
nuclear@0
|
3340 CloseHandle(hThreadSnap);
|
nuclear@0
|
3341 }
|
nuclear@0
|
3342 }
|
nuclear@0
|
3343
|
nuclear@0
|
3344 {
|
nuclear@0
|
3345 // Print a list of the current modules within this process.
|
nuclear@0
|
3346 // DbgHelp.dll also provides a EnumerateLoadedModules64 function.
|
nuclear@0
|
3347 // To do: Convert the code below to use the GetModuleInfoArray function which we now have.
|
nuclear@0
|
3348 #if defined(OVR_OS_CONSOLE)
|
nuclear@0
|
3349 struct MODULEINFO {
|
nuclear@0
|
3350 LPVOID lpBaseOfDll;
|
nuclear@0
|
3351 DWORD SizeOfImage;
|
nuclear@0
|
3352 LPVOID EntryPoint;
|
nuclear@0
|
3353 };
|
nuclear@0
|
3354 HMODULE hModule = LoadLibraryW(L"toolhelpx.dll");
|
nuclear@0
|
3355 #else
|
nuclear@0
|
3356 HMODULE hModule = LoadLibraryW(L"psapi.dll");
|
nuclear@0
|
3357 #endif
|
nuclear@0
|
3358
|
nuclear@0
|
3359 if(hModule)
|
nuclear@0
|
3360 {
|
nuclear@0
|
3361 typedef BOOL (WINAPI * ENUMPROCESSMODULES) (HANDLE hProcess, HMODULE* phModule, DWORD cb, LPDWORD lpcbNeeded);
|
nuclear@0
|
3362 typedef DWORD (WINAPI * GETMODULEBASENAME) (HANDLE hProcess, HMODULE hModule, LPWSTR lpFilename, DWORD nSize);
|
nuclear@0
|
3363 typedef DWORD (WINAPI * GETMODULEFILENAMEEX) (HANDLE hProcess, HMODULE hModule, LPWSTR lpFilename, DWORD nSize);
|
nuclear@0
|
3364 typedef BOOL (WINAPI * GETMODULEINFORMATION)(HANDLE hProcess, HMODULE hModule, MODULEINFO* pmi, DWORD nSize);
|
nuclear@0
|
3365
|
nuclear@0
|
3366 #if defined(OVR_OS_CONSOLE)
|
nuclear@0
|
3367 ENUMPROCESSMODULES pEnumProcessModules = (ENUMPROCESSMODULES) (uintptr_t)GetProcAddress(hModule, "K32EnumProcessModules");
|
nuclear@0
|
3368 GETMODULEBASENAME pGetModuleBaseName = (GETMODULEBASENAME) (uintptr_t)GetProcAddress(hModule, "K32GetModuleBaseNameW");
|
nuclear@0
|
3369 GETMODULEFILENAMEEX pGetModuleFileNameEx = (GETMODULEFILENAMEEX) (uintptr_t)GetProcAddress(hModule, "K32GetModuleFileNameExW");
|
nuclear@0
|
3370 GETMODULEINFORMATION pGetModuleInformation = (GETMODULEINFORMATION)(uintptr_t)GetProcAddress(hModule, "K32GetModuleInformation");
|
nuclear@0
|
3371 #else
|
nuclear@0
|
3372 ENUMPROCESSMODULES pEnumProcessModules = (ENUMPROCESSMODULES) (uintptr_t)GetProcAddress(hModule, "EnumProcessModules");
|
nuclear@0
|
3373 GETMODULEBASENAME pGetModuleBaseName = (GETMODULEBASENAME) (uintptr_t)GetProcAddress(hModule, "GetModuleBaseNameW");
|
nuclear@0
|
3374 GETMODULEFILENAMEEX pGetModuleFileNameEx = (GETMODULEFILENAMEEX) (uintptr_t)GetProcAddress(hModule, "GetModuleFileNameExW");
|
nuclear@0
|
3375 GETMODULEINFORMATION pGetModuleInformation = (GETMODULEINFORMATION)(uintptr_t)GetProcAddress(hModule, "GetModuleInformation");
|
nuclear@0
|
3376 #endif
|
nuclear@0
|
3377
|
nuclear@0
|
3378 HANDLE hProcess = GetCurrentProcess();
|
nuclear@0
|
3379 HMODULE hModuleArray[200];
|
nuclear@0
|
3380 DWORD cbNeeded;
|
nuclear@0
|
3381
|
nuclear@0
|
3382 if(pEnumProcessModules(hProcess, hModuleArray, sizeof(hModuleArray), &cbNeeded))
|
nuclear@0
|
3383 {
|
nuclear@0
|
3384 size_t actualModuleCount = (cbNeeded / sizeof(HMODULE));
|
nuclear@0
|
3385
|
nuclear@0
|
3386 if(actualModuleCount > OVR_ARRAY_COUNT(hModuleArray)) //If hModuleArray's capacity was not enough...
|
nuclear@0
|
3387 actualModuleCount = OVR_ARRAY_COUNT(hModuleArray);
|
nuclear@0
|
3388
|
nuclear@0
|
3389 // Print a header
|
nuclear@0
|
3390 WriteReportLine("\r\nModule list\r\n");
|
nuclear@0
|
3391
|
nuclear@0
|
3392 #if (OVR_PTR_SIZE == 4)
|
nuclear@0
|
3393 WriteReportLine("Base Size Entrypoint Name Path\r\n");
|
nuclear@0
|
3394 #else
|
nuclear@0
|
3395 WriteReportLine("Base Size Entrypoint Name Path\r\n");
|
nuclear@0
|
3396 #endif
|
nuclear@0
|
3397
|
nuclear@0
|
3398 // And go through the list one by one
|
nuclear@0
|
3399 for(size_t i = 0; i < actualModuleCount; i++)
|
nuclear@0
|
3400 {
|
nuclear@0
|
3401 MODULEINFO mi;
|
nuclear@0
|
3402 size_t length;
|
nuclear@0
|
3403
|
nuclear@0
|
3404 if(!pGetModuleInformation(hProcess, hModuleArray[i], &mi, sizeof(mi)))
|
nuclear@0
|
3405 {
|
nuclear@0
|
3406 mi.EntryPoint = nullptr;
|
nuclear@0
|
3407 mi.lpBaseOfDll = nullptr;
|
nuclear@0
|
3408 mi.SizeOfImage = 0;
|
nuclear@0
|
3409 }
|
nuclear@0
|
3410
|
nuclear@0
|
3411 // Write the base name.
|
nuclear@0
|
3412 wchar_t name[MAX_PATH + 3];
|
nuclear@0
|
3413 name[0] = '"';
|
nuclear@0
|
3414 if(pGetModuleBaseName(hProcess, hModuleArray[i], name + 1, MAX_PATH))
|
nuclear@0
|
3415 length = wcslen(name);
|
nuclear@0
|
3416 else
|
nuclear@0
|
3417 {
|
nuclear@0
|
3418 wcscpy(name + 1, L"(unknown)");
|
nuclear@0
|
3419 length = 10;
|
nuclear@0
|
3420 }
|
nuclear@0
|
3421
|
nuclear@0
|
3422 name[length] = '"';
|
nuclear@0
|
3423 name[length + 1] = '\0';
|
nuclear@0
|
3424
|
nuclear@0
|
3425 // Write the path
|
nuclear@0
|
3426 wchar_t path[MAX_PATH + 3];
|
nuclear@0
|
3427 path[0] = '"';
|
nuclear@0
|
3428 if(pGetModuleFileNameEx(hProcess, hModuleArray[i], path + 1, MAX_PATH))
|
nuclear@0
|
3429 length = wcslen(path);
|
nuclear@0
|
3430 else
|
nuclear@0
|
3431 {
|
nuclear@0
|
3432 wcscpy(path + 1, L"(unknown)");
|
nuclear@0
|
3433 length = 10;
|
nuclear@0
|
3434 }
|
nuclear@0
|
3435 path[length] = '"';
|
nuclear@0
|
3436 path[length + 1] = '\0';
|
nuclear@0
|
3437
|
nuclear@0
|
3438 #if (OVR_PTR_SIZE == 4)
|
nuclear@0
|
3439 WriteReportLineF("0x%08x, 0x%08x 0x%08x %-24ls %ls\r\n", (uint32_t)mi.lpBaseOfDll, (uint32_t)mi.SizeOfImage, (uint32_t)mi.EntryPoint, name, path);
|
nuclear@0
|
3440 #else
|
nuclear@0
|
3441 WriteReportLineF("0x%016I64x 0x%016I64x 0x%016I64x %-24ls %ls\r\n", (uint64_t)mi.lpBaseOfDll, (uint64_t)mi.SizeOfImage, (uint64_t)mi.EntryPoint, name, path);
|
nuclear@0
|
3442 #endif
|
nuclear@0
|
3443 }
|
nuclear@0
|
3444 }
|
nuclear@0
|
3445 }
|
nuclear@0
|
3446 }
|
nuclear@0
|
3447
|
nuclear@0
|
3448 {
|
nuclear@0
|
3449 // Print a list of processes.
|
nuclear@0
|
3450 // DbgHelp.dll provides a SymEnumProcesses function, but it's available with DbgHelp.dll v6.2 which doesn't ship with Windows until Windows 8.
|
nuclear@0
|
3451 WriteReportLine("\r\nProcess list\r\n");
|
nuclear@0
|
3452
|
nuclear@0
|
3453 if(reportPrivacyEnabled)
|
nuclear@0
|
3454 WriteReportLine("Disabled by report privacy settings\r\n");
|
nuclear@0
|
3455 else
|
nuclear@0
|
3456 {
|
nuclear@0
|
3457 HANDLE hProcessSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
nuclear@0
|
3458
|
nuclear@0
|
3459 if(hProcessSnapshot != INVALID_HANDLE_VALUE)
|
nuclear@0
|
3460 {
|
nuclear@0
|
3461 PROCESSENTRY32W pe32;
|
nuclear@0
|
3462 memset(&pe32, 0, sizeof(pe32));
|
nuclear@0
|
3463 pe32.dwSize = sizeof(pe32);
|
nuclear@0
|
3464
|
nuclear@0
|
3465 if(Process32FirstW(hProcessSnapshot, &pe32))
|
nuclear@0
|
3466 {
|
nuclear@0
|
3467 WriteReportLine("Process Id File\r\n");
|
nuclear@0
|
3468
|
nuclear@0
|
3469 do {
|
nuclear@0
|
3470 // Try to get the full path to the process, as pe32.szExeFile holds only the process file name.
|
nuclear@0
|
3471 // This will typically fail with a privilege error unless this process has debug privileges: http://support.microsoft.com/kb/131065/en-us
|
nuclear@0
|
3472 wchar_t filePathW[MAX_PATH];
|
nuclear@0
|
3473 const wchar_t* pFilePathW = pe32.szExeFile;
|
nuclear@0
|
3474 HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pe32.th32ProcessID); // With Windows Vista+ we can use PROCESS_QUERY_LIMITED_INFORMATION.
|
nuclear@0
|
3475 if(hProcess)
|
nuclear@0
|
3476 {
|
nuclear@0
|
3477 if(GetProcessImageFileName(hProcess, filePathW, (DWORD)OVR_ARRAY_COUNT(filePathW)))
|
nuclear@0
|
3478 pFilePathW = filePathW;
|
nuclear@0
|
3479 }
|
nuclear@0
|
3480
|
nuclear@0
|
3481 WriteReportLineF("0x%08x %ls\r\n", pe32.th32ProcessID, pFilePathW);
|
nuclear@0
|
3482 } while(Process32NextW(hProcessSnapshot, &pe32));
|
nuclear@0
|
3483 }
|
nuclear@0
|
3484
|
nuclear@0
|
3485 CloseHandle(hProcessSnapshot);
|
nuclear@0
|
3486 }
|
nuclear@0
|
3487 else
|
nuclear@0
|
3488 {
|
nuclear@0
|
3489 WriteReportLine("Unable to read process list\r\n");
|
nuclear@0
|
3490 }
|
nuclear@0
|
3491 }
|
nuclear@0
|
3492 }
|
nuclear@0
|
3493
|
nuclear@0
|
3494 #elif defined(OVR_OS_APPLE)
|
nuclear@0
|
3495
|
nuclear@0
|
3496 WriteReportLine("\r\nApp Info\r\n");
|
nuclear@0
|
3497
|
nuclear@0
|
3498 // App path
|
nuclear@0
|
3499 const pid_t processId = getpid();
|
nuclear@0
|
3500 WriteReportLineF("Process id: ", "%lld (0x%llx)\r\n", (int64_t)processId, (int64_t)processId);
|
nuclear@0
|
3501
|
nuclear@0
|
3502 char appPath[PATH_MAX];
|
nuclear@0
|
3503 GetCurrentProcessFilePath(appPath, OVR_ARRAY_COUNT(appPath));
|
nuclear@0
|
3504 WriteReportLineF("Process path: %s\r\n", appPath);
|
nuclear@0
|
3505
|
nuclear@0
|
3506 #if (OVR_PTR_SIZE == 4)
|
nuclear@0
|
3507 WriteReportLine("App format: 32 bit\r\n");
|
nuclear@0
|
3508 #else
|
nuclear@0
|
3509 WriteReportLine("App format: 64 bit\r\n");
|
nuclear@0
|
3510 #endif
|
nuclear@0
|
3511
|
nuclear@0
|
3512 // App version
|
nuclear@0
|
3513 // To do.
|
nuclear@0
|
3514
|
nuclear@0
|
3515 // System Info
|
nuclear@0
|
3516 WriteReportLine("\r\nSystem Info\r\n");
|
nuclear@0
|
3517
|
nuclear@0
|
3518 char osVersionName[256];
|
nuclear@0
|
3519 GetOSVersionName(osVersionName, OVR_ARRAY_COUNT(osVersionName));
|
nuclear@0
|
3520 WriteReportLineF("OS name: %s, %s\r\n", osVersionName, Is64BitOS() ? "64 bit" : "32 bit");
|
nuclear@0
|
3521
|
nuclear@0
|
3522 int name[2];
|
nuclear@0
|
3523 int intValue;
|
nuclear@0
|
3524 size_t length;
|
nuclear@0
|
3525 char tempBuffer[256];
|
nuclear@0
|
3526
|
nuclear@0
|
3527 name[0] = CTL_KERN;
|
nuclear@0
|
3528 name[1] = KERN_OSTYPE;
|
nuclear@0
|
3529 length = sizeof(tempBuffer);
|
nuclear@0
|
3530 tempBuffer[0] = 0;
|
nuclear@0
|
3531 if(sysctl(name, 2, tempBuffer, &length, nullptr, 0) == 0)
|
nuclear@0
|
3532 {
|
nuclear@0
|
3533 WriteReportLineF("KERN_OSTYPE: %s\r\n", tempBuffer);
|
nuclear@0
|
3534 }
|
nuclear@0
|
3535
|
nuclear@0
|
3536 name[0] = CTL_KERN;
|
nuclear@0
|
3537 name[1] = KERN_OSREV;
|
nuclear@0
|
3538 length = sizeof(intValue);
|
nuclear@0
|
3539 intValue = 0;
|
nuclear@0
|
3540 if(sysctl(name, 2, &intValue, &length, nullptr, 0) == 0)
|
nuclear@0
|
3541 {
|
nuclear@0
|
3542 WriteReportLineF("KERN_OSREV: %d\r\n", intValue);
|
nuclear@0
|
3543 }
|
nuclear@0
|
3544
|
nuclear@0
|
3545 name[0] = CTL_KERN;
|
nuclear@0
|
3546 name[1] = KERN_OSRELEASE;
|
nuclear@0
|
3547 length = sizeof(tempBuffer);
|
nuclear@0
|
3548 tempBuffer[0] = 0;
|
nuclear@0
|
3549 if(sysctl(name, 2, tempBuffer, &length, nullptr, 0) == 0)
|
nuclear@0
|
3550 WriteReportLineF("KERN_OSRELEASE: %s\r\n", tempBuffer);
|
nuclear@0
|
3551
|
nuclear@0
|
3552 name[0] = CTL_HW;
|
nuclear@0
|
3553 name[1] = HW_MACHINE;
|
nuclear@0
|
3554 length = sizeof(tempBuffer);
|
nuclear@0
|
3555 tempBuffer[0] = 0;
|
nuclear@0
|
3556 if(sysctl(name, 2, tempBuffer, &length, nullptr, 0) == 0)
|
nuclear@0
|
3557 WriteReportLineF("HW_MACHINE: %s\r\n", tempBuffer);
|
nuclear@0
|
3558
|
nuclear@0
|
3559 name[0] = CTL_HW;
|
nuclear@0
|
3560 name[1] = HW_MODEL;
|
nuclear@0
|
3561 length = sizeof(tempBuffer);
|
nuclear@0
|
3562 tempBuffer[0] = 0;
|
nuclear@0
|
3563 if(sysctl(name, 2, tempBuffer, &length, nullptr, 0) == 0)
|
nuclear@0
|
3564 WriteReportLineF("sHW_MODEL: %s\r\n", tempBuffer);
|
nuclear@0
|
3565
|
nuclear@0
|
3566 name[0] = CTL_HW;
|
nuclear@0
|
3567 name[1] = HW_NCPU;
|
nuclear@0
|
3568 length = sizeof(intValue);
|
nuclear@0
|
3569 intValue = 0;
|
nuclear@0
|
3570 if(sysctl(name, 2, &intValue, &length, nullptr, 0) == 0)
|
nuclear@0
|
3571 WriteReportLineF("HW_NCPU: %d\r\n", intValue);
|
nuclear@0
|
3572
|
nuclear@0
|
3573 length = sizeof(tempBuffer);
|
nuclear@0
|
3574 tempBuffer[0] = 0;
|
nuclear@0
|
3575 if(sysctlbyname("machdep.cpu.brand_string", &tempBuffer, &length, nullptr, 0) == 0)
|
nuclear@0
|
3576 WriteReportLineF("machdep.cpu.brand_string: %s\r\n", tempBuffer);
|
nuclear@0
|
3577
|
nuclear@0
|
3578 length = sizeof(tempBuffer);
|
nuclear@0
|
3579 tempBuffer[0] = 0;
|
nuclear@0
|
3580 if(sysctlbyname("hw.acpi.thermal.tz0.temperature", &tempBuffer, &length, nullptr, 0) == 0)
|
nuclear@0
|
3581 WriteReportLineF("hw.acpi.thermal.tz0.temperature: %s\r\n", tempBuffer);
|
nuclear@0
|
3582
|
nuclear@0
|
3583 host_basic_info_data_t hostinfo;
|
nuclear@0
|
3584 mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
|
nuclear@0
|
3585 kern_return_t kr = host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)&hostinfo, &count);
|
nuclear@0
|
3586
|
nuclear@0
|
3587 if(kr == KERN_SUCCESS)
|
nuclear@0
|
3588 {
|
nuclear@0
|
3589 const uint64_t memoryMib = (uint64_t)hostinfo.max_mem / (1024 * 1024);
|
nuclear@0
|
3590 WriteReportLineF("System memory: %lld Mib (%.1f Gib)\r\n", memoryMib, (double)memoryMib / 1024);
|
nuclear@0
|
3591 }
|
nuclear@0
|
3592
|
nuclear@0
|
3593 // Video card info
|
nuclear@0
|
3594 // To do.
|
nuclear@0
|
3595
|
nuclear@0
|
3596 // Thread list
|
nuclear@0
|
3597 mach_port_t taskSelf = mach_task_self();
|
nuclear@0
|
3598 thread_act_port_array_t threadArray;
|
nuclear@0
|
3599 mach_msg_type_number_t threadCount;
|
nuclear@0
|
3600
|
nuclear@0
|
3601 kern_return_t result = task_threads(taskSelf, &threadArray, &threadCount);
|
nuclear@0
|
3602
|
nuclear@0
|
3603 if(result == KERN_SUCCESS)
|
nuclear@0
|
3604 {
|
nuclear@0
|
3605 WriteReportLine("\r\nThread list\r\n");
|
nuclear@0
|
3606
|
nuclear@0
|
3607 for(mach_msg_type_number_t i = 0; i < threadCount; i++)
|
nuclear@0
|
3608 {
|
nuclear@0
|
3609 union TBIUnion{
|
nuclear@0
|
3610 natural_t words[THREAD_INFO_MAX];
|
nuclear@0
|
3611 thread_basic_info tbi;
|
nuclear@0
|
3612 };
|
nuclear@0
|
3613
|
nuclear@0
|
3614 TBIUnion tbiUnion;
|
nuclear@0
|
3615 mach_port_t thread = threadArray[i];
|
nuclear@0
|
3616 pthread_t pthread = pthread_from_mach_thread_np(thread); // We assume the thread was created through pthreads.
|
nuclear@0
|
3617
|
nuclear@0
|
3618 char threadState[32] = "unknown";
|
nuclear@0
|
3619 mach_msg_type_number_t threadInfoCount = THREAD_INFO_MAX;
|
nuclear@0
|
3620 result = thread_info(thread, THREAD_BASIC_INFO, (thread_info_t)&tbiUnion, &threadInfoCount);
|
nuclear@0
|
3621
|
nuclear@0
|
3622 if(result == KERN_SUCCESS)
|
nuclear@0
|
3623 {
|
nuclear@0
|
3624 const char* state;
|
nuclear@0
|
3625
|
nuclear@0
|
3626 switch (tbiUnion.tbi.run_state)
|
nuclear@0
|
3627 {
|
nuclear@0
|
3628 case TH_STATE_HALTED: state = "halted"; break;
|
nuclear@0
|
3629 case TH_STATE_RUNNING: state = "running"; break;
|
nuclear@0
|
3630 case TH_STATE_STOPPED: state = "stopped"; break;
|
nuclear@0
|
3631 case TH_STATE_UNINTERRUPTIBLE: state = "uninterruptible"; break;
|
nuclear@0
|
3632 case TH_STATE_WAITING: state = "waiting"; break;
|
nuclear@0
|
3633 default: state = "<unknown>"; break;
|
nuclear@0
|
3634 }
|
nuclear@0
|
3635
|
nuclear@0
|
3636 OVR_snprintf(threadState, OVR_ARRAY_COUNT(threadState), "%s", state);
|
nuclear@0
|
3637 if(tbiUnion.tbi.flags & TH_FLAGS_IDLE)
|
nuclear@0
|
3638 OVR_strlcat(threadState, ", idle", sizeof(threadState));
|
nuclear@0
|
3639 if(tbiUnion.tbi.flags & TH_FLAGS_SWAPPED)
|
nuclear@0
|
3640 OVR_strlcat(threadState, ", swapped", sizeof(threadState));
|
nuclear@0
|
3641 }
|
nuclear@0
|
3642
|
nuclear@0
|
3643 thread_identifier_info threadIdentifierInfo;
|
nuclear@0
|
3644 memset(&threadIdentifierInfo, 0, sizeof(threadIdentifierInfo));
|
nuclear@0
|
3645
|
nuclear@0
|
3646 mach_msg_type_number_t threadIdentifierInfoCount = THREAD_IDENTIFIER_INFO_COUNT;
|
nuclear@0
|
3647 thread_info(thread, THREAD_IDENTIFIER_INFO, (thread_info_t)&threadIdentifierInfo, &threadIdentifierInfoCount);
|
nuclear@0
|
3648
|
nuclear@0
|
3649 proc_threadinfo procThreadInfo;
|
nuclear@0
|
3650 memset(&procThreadInfo, 0, sizeof(procThreadInfo));
|
nuclear@0
|
3651 result = proc_pidinfo(processId, PROC_PIDTHREADINFO, threadIdentifierInfo.thread_handle, &procThreadInfo, sizeof(procThreadInfo));
|
nuclear@0
|
3652 OVR_UNUSED(result);
|
nuclear@0
|
3653
|
nuclear@0
|
3654 char buffer[256]; // Can't use scratchBuffer, because it's used by WriteThreadCallstack.
|
nuclear@0
|
3655 OVR_snprintf(buffer, OVR_ARRAY_COUNT(buffer), "state: %s, suspend count: %d, kernel priority: %d", threadState, (int)tbiUnion.tbi.suspend_count, (int)procThreadInfo.pth_curpri);
|
nuclear@0
|
3656
|
nuclear@0
|
3657 bool threadIsExceptionThread = (thread == exceptionInfo.threadSysId);
|
nuclear@0
|
3658 if(threadIsExceptionThread)
|
nuclear@0
|
3659 OVR_strlcat(buffer, ", exception thread", OVR_ARRAY_COUNT(buffer));
|
nuclear@0
|
3660
|
nuclear@0
|
3661 WriteThreadCallstack(pthread, thread, buffer);
|
nuclear@0
|
3662 }
|
nuclear@0
|
3663
|
nuclear@0
|
3664 vm_deallocate(taskSelf, (vm_address_t)threadArray, threadCount * sizeof(thread_act_t));
|
nuclear@0
|
3665 }
|
nuclear@0
|
3666
|
nuclear@0
|
3667
|
nuclear@0
|
3668 WriteReportLine("\r\nModule list\r\n");
|
nuclear@0
|
3669
|
nuclear@0
|
3670 const size_t mifCapacity = 256;
|
nuclear@0
|
3671 const size_t mifAllocSize = mifCapacity * sizeof(ModuleInfo);
|
nuclear@0
|
3672 ModuleInfo* moduleInfoArray = (ModuleInfo*)SafeMMapAlloc(mifAllocSize);
|
nuclear@0
|
3673
|
nuclear@0
|
3674 if(moduleInfoArray)
|
nuclear@0
|
3675 {
|
nuclear@0
|
3676 #if (OVR_PTR_SIZE == 4)
|
nuclear@0
|
3677 WriteReportLine("Base Size Name Path\r\n");
|
nuclear@0
|
3678 #else
|
nuclear@0
|
3679 WriteReportLine("Base Size Name Path\r\n");
|
nuclear@0
|
3680 #endif
|
nuclear@0
|
3681
|
nuclear@0
|
3682 size_t moduleCount = symbolLookup.GetModuleInfoArray(moduleInfoArray, mifCapacity);
|
nuclear@0
|
3683 if(moduleCount > mifCapacity)
|
nuclear@0
|
3684 moduleCount = mifCapacity;
|
nuclear@0
|
3685
|
nuclear@0
|
3686 for(size_t i = 0; i < moduleCount; i++)
|
nuclear@0
|
3687 {
|
nuclear@0
|
3688 const ModuleInfo& mi = moduleInfoArray[i];
|
nuclear@0
|
3689
|
nuclear@0
|
3690 #if (OVR_PTR_SIZE == 4)
|
nuclear@0
|
3691 WriteReportLineF("0x%08x, 0x%08x %-24s %s\r\n", (uint32_t)mi.baseAddress, (uint32_t)mi.size, mi.name, mi.filePath);
|
nuclear@0
|
3692 #else
|
nuclear@0
|
3693 WriteReportLineF("0x%016llx 0x%016llx %-24s %s\r\n", (uint64_t)mi.baseAddress, (uint64_t)mi.size, mi.name, mi.filePath);
|
nuclear@0
|
3694 #endif
|
nuclear@0
|
3695 }
|
nuclear@0
|
3696
|
nuclear@0
|
3697 SafeMMapFree(moduleInfoArray, mifAllocSize);
|
nuclear@0
|
3698 }
|
nuclear@0
|
3699
|
nuclear@0
|
3700
|
nuclear@0
|
3701 WriteReportLine("\r\nProcess list\r\n");
|
nuclear@0
|
3702
|
nuclear@0
|
3703 if(reportPrivacyEnabled)
|
nuclear@0
|
3704 WriteReportLine("Disabled by report privacy settings\r\n");
|
nuclear@0
|
3705 else
|
nuclear@0
|
3706 {
|
nuclear@0
|
3707 WriteReportLine("Process Id File\r\n");
|
nuclear@0
|
3708
|
nuclear@0
|
3709 pid_t pidArray[1024];
|
nuclear@0
|
3710 int processCount = proc_listpids(PROC_ALL_PIDS, 0, pidArray, sizeof(pidArray)); // Important that we use sizeof not OVR_ARRAY_COUNT.
|
nuclear@0
|
3711 char processFilePath[PATH_MAX];
|
nuclear@0
|
3712
|
nuclear@0
|
3713 for(int i = 0; i < processCount; i++)
|
nuclear@0
|
3714 {
|
nuclear@0
|
3715 if(proc_pidpath(pidArray[i], processFilePath, sizeof(processFilePath)) > 0)
|
nuclear@0
|
3716 WriteReportLineF("%-10d %s\r\n", pidArray[i], processFilePath);
|
nuclear@0
|
3717 }
|
nuclear@0
|
3718
|
nuclear@0
|
3719 if(!processCount)
|
nuclear@0
|
3720 WriteReportLine("Unable to read process list\r\n");
|
nuclear@0
|
3721 }
|
nuclear@0
|
3722
|
nuclear@0
|
3723 #elif defined(OVR_OS_UNIX)
|
nuclear@0
|
3724 Is64BitOS();
|
nuclear@0
|
3725 GetCurrentProcessFilePath(nullptr, 0);
|
nuclear@0
|
3726 GetFileNameFromPath(nullptr);
|
nuclear@0
|
3727 GetOSVersionName(nullptr, 0);
|
nuclear@0
|
3728
|
nuclear@0
|
3729 #endif // OVR_OS_MS
|
nuclear@0
|
3730
|
nuclear@0
|
3731 symbolLookup.Shutdown();
|
nuclear@0
|
3732
|
nuclear@0
|
3733 fclose(file);
|
nuclear@0
|
3734 file = nullptr;
|
nuclear@0
|
3735 }
|
nuclear@0
|
3736
|
nuclear@0
|
3737
|
nuclear@0
|
3738 void ExceptionHandler::WriteMiniDump()
|
nuclear@0
|
3739 {
|
nuclear@0
|
3740 if(strstr(miniDumpFilePath, "%s")) // If the user-specified file path includes a date/time component...
|
nuclear@0
|
3741 {
|
nuclear@0
|
3742 char dateTimeBuffer[64];
|
nuclear@0
|
3743 FormatDateTime(dateTimeBuffer, OVR_ARRAY_COUNT(dateTimeBuffer), exceptionInfo.timeVal, true, true, false, true);
|
nuclear@0
|
3744 OVR_snprintf(minidumpFilePathActual, OVR_ARRAY_COUNT(minidumpFilePathActual), miniDumpFilePath, dateTimeBuffer);
|
nuclear@0
|
3745 }
|
nuclear@0
|
3746 else
|
nuclear@0
|
3747 {
|
nuclear@0
|
3748 OVR_strlcpy(minidumpFilePathActual, miniDumpFilePath, OVR_ARRAY_COUNT(minidumpFilePathActual));
|
nuclear@0
|
3749 }
|
nuclear@0
|
3750
|
nuclear@0
|
3751 #if defined(OVR_OS_WIN32) || defined(OVR_OS_WIN64) || (defined(OVR_OS_MS) && defined(OVR_OS_CONSOLE))
|
nuclear@0
|
3752 #if defined(OVR_OS_CONSOLE)
|
nuclear@0
|
3753 typedef BOOL (WINAPI * MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD ProcessId, HANDLE hFile, MINIDUMP_TYPE dumpType, CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, CONST PVOID CallbackParam);
|
nuclear@0
|
3754 HMODULE hModuleDbgHelp = LoadLibraryW(L"toolhelpx.dll");
|
nuclear@0
|
3755 #else
|
nuclear@0
|
3756 typedef BOOL (WINAPI * MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD ProcessId, HANDLE hFile, MINIDUMP_TYPE dumpType, CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam);
|
nuclear@0
|
3757 HMODULE hModuleDbgHelp = LoadLibraryW(L"DbgHelp.dll");
|
nuclear@0
|
3758 #endif
|
nuclear@0
|
3759
|
nuclear@0
|
3760 MINIDUMPWRITEDUMP pMiniDumpWriteDump = hModuleDbgHelp ? (MINIDUMPWRITEDUMP)(void*)GetProcAddress(hModuleDbgHelp, "MiniDumpWriteDump") : nullptr;
|
nuclear@0
|
3761
|
nuclear@0
|
3762 if(pMiniDumpWriteDump)
|
nuclear@0
|
3763 {
|
nuclear@0
|
3764 wchar_t miniDumpFilePathW[OVR_MAX_PATH];
|
nuclear@0
|
3765 OVR::UTF8Util::DecodeString(miniDumpFilePathW, minidumpFilePathActual, -1); // Problem: DecodeString provides no way to specify the destination capacity.
|
nuclear@0
|
3766
|
nuclear@0
|
3767 HANDLE hFile = CreateFileW(miniDumpFilePathW, GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_FLAG_WRITE_THROUGH, 0);
|
nuclear@0
|
3768
|
nuclear@0
|
3769 if(hFile != INVALID_HANDLE_VALUE)
|
nuclear@0
|
3770 {
|
nuclear@0
|
3771 MINIDUMP_EXCEPTION_INFORMATION minidumpExceptionInfo = { ::GetCurrentThreadId(), pExceptionPointers, TRUE };
|
nuclear@0
|
3772
|
nuclear@0
|
3773 #if defined(OVR_OS_CONSOLE)
|
nuclear@0
|
3774 BOOL result = pMiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile,
|
nuclear@0
|
3775 (MINIDUMP_TYPE)miniDumpType, &exceptionInfo,
|
nuclear@0
|
3776 (CONST PMINIDUMP_USER_STREAM_INFORMATION)nullptr, nullptr);
|
nuclear@0
|
3777 #else
|
nuclear@0
|
3778 BOOL result = pMiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile,
|
nuclear@0
|
3779 (MINIDUMP_TYPE)miniDumpFlags, &minidumpExceptionInfo,
|
nuclear@0
|
3780 (CONST PMINIDUMP_USER_STREAM_INFORMATION)nullptr, (CONST PMINIDUMP_CALLBACK_INFORMATION)nullptr);
|
nuclear@0
|
3781 #endif
|
nuclear@0
|
3782
|
nuclear@0
|
3783 OVR_ASSERT_AND_UNUSED(result, result);
|
nuclear@0
|
3784 CloseHandle(hFile);
|
nuclear@0
|
3785 hFile = 0;
|
nuclear@0
|
3786 }
|
nuclear@0
|
3787 else
|
nuclear@0
|
3788 {
|
nuclear@0
|
3789 OVR_ASSERT(pMiniDumpWriteDump); // OVR_FAIL_F(("ExceptionHandler::WriteMiniDump: Failed to create minidump file at %s", minidumpFilePathActual));
|
nuclear@0
|
3790 }
|
nuclear@0
|
3791 }
|
nuclear@0
|
3792
|
nuclear@0
|
3793 FreeLibrary(hModuleDbgHelp);
|
nuclear@0
|
3794 #else
|
nuclear@0
|
3795 // Some platforms support various forms or exception reports and core dumps which are automatically generated upon us
|
nuclear@0
|
3796 // returning from our own exception handling. We might want to put something here if we are using a custom version of
|
nuclear@0
|
3797 // this, such as Google Breakpad.
|
nuclear@0
|
3798 #endif
|
nuclear@0
|
3799 }
|
nuclear@0
|
3800
|
nuclear@0
|
3801
|
nuclear@0
|
3802 void ExceptionHandler::SetExceptionListener(ExceptionListener* pExceptionListener, uintptr_t userValue)
|
nuclear@0
|
3803 {
|
nuclear@0
|
3804 exceptionListener = pExceptionListener;
|
nuclear@0
|
3805 exceptionListenerUserValue = userValue;
|
nuclear@0
|
3806 }
|
nuclear@0
|
3807
|
nuclear@0
|
3808
|
nuclear@0
|
3809 void ExceptionHandler::SetAppDescription(const char* pAppDescription)
|
nuclear@0
|
3810 {
|
nuclear@0
|
3811 appDescription = pAppDescription;
|
nuclear@0
|
3812 }
|
nuclear@0
|
3813
|
nuclear@0
|
3814
|
nuclear@0
|
3815 void ExceptionHandler::SetExceptionPaths(const char* exceptionReportPath, const char* exceptionMiniDumpFilePath)
|
nuclear@0
|
3816 {
|
nuclear@0
|
3817 char tempPath[OVR_MAX_PATH];
|
nuclear@0
|
3818
|
nuclear@0
|
3819 if(exceptionReportPath)
|
nuclear@0
|
3820 {
|
nuclear@0
|
3821 if(OVR_stricmp(exceptionReportPath, "default") == 0)
|
nuclear@0
|
3822 {
|
nuclear@0
|
3823 GetUserDocumentsDirectory(tempPath, OVR_ARRAY_COUNT(tempPath));
|
nuclear@0
|
3824 OVR::OVR_strlcat(tempPath, "Exception Report (%s).txt", OVR_ARRAY_COUNT(tempPath));
|
nuclear@0
|
3825 exceptionReportPath = tempPath;
|
nuclear@0
|
3826 }
|
nuclear@0
|
3827
|
nuclear@0
|
3828 OVR_strlcpy(reportFilePath, exceptionReportPath, OVR_ARRAY_COUNT(reportFilePath));
|
nuclear@0
|
3829 }
|
nuclear@0
|
3830 else
|
nuclear@0
|
3831 {
|
nuclear@0
|
3832 reportFilePath[0] = '\0';
|
nuclear@0
|
3833 }
|
nuclear@0
|
3834
|
nuclear@0
|
3835 if(exceptionMiniDumpFilePath)
|
nuclear@0
|
3836 {
|
nuclear@0
|
3837 if(OVR_stricmp(exceptionMiniDumpFilePath, "default") == 0)
|
nuclear@0
|
3838 {
|
nuclear@0
|
3839 GetUserDocumentsDirectory(tempPath, OVR_ARRAY_COUNT(tempPath));
|
nuclear@0
|
3840 OVR::OVR_strlcat(tempPath, "Exception Minidump (%s).mdmp", OVR_ARRAY_COUNT(tempPath));
|
nuclear@0
|
3841 exceptionMiniDumpFilePath = tempPath;
|
nuclear@0
|
3842 }
|
nuclear@0
|
3843
|
nuclear@0
|
3844 OVR_strlcpy(miniDumpFilePath, exceptionMiniDumpFilePath, OVR_ARRAY_COUNT(miniDumpFilePath));
|
nuclear@0
|
3845 }
|
nuclear@0
|
3846 else
|
nuclear@0
|
3847 {
|
nuclear@0
|
3848 miniDumpFilePath[0] = '\0';
|
nuclear@0
|
3849 }
|
nuclear@0
|
3850 }
|
nuclear@0
|
3851
|
nuclear@0
|
3852
|
nuclear@0
|
3853 void ExceptionHandler::SetCodeBaseDirectoryPaths(const char* codeBaseDirectoryPathArray[], size_t codeBaseDirectoryPathCount)
|
nuclear@0
|
3854 {
|
nuclear@0
|
3855 for(size_t i = 0, iCount = OVR::Alg::Min<size_t>(codeBaseDirectoryPathCount, OVR_ARRAY_COUNT(codeBasePathArray)); i != iCount; ++i)
|
nuclear@0
|
3856 {
|
nuclear@0
|
3857 codeBasePathArray[i] = codeBaseDirectoryPathArray[i];
|
nuclear@0
|
3858 }
|
nuclear@0
|
3859 }
|
nuclear@0
|
3860
|
nuclear@0
|
3861 const char* ExceptionHandler::GetExceptionUIText(const char* exceptionReportPath)
|
nuclear@0
|
3862 {
|
nuclear@0
|
3863 char* uiText = nullptr;
|
nuclear@0
|
3864 OVR::SysFile file(exceptionReportPath, SysFile::Open_Read, SysFile::Mode_ReadWrite);
|
nuclear@0
|
3865
|
nuclear@0
|
3866 if(file.IsValid())
|
nuclear@0
|
3867 {
|
nuclear@0
|
3868 size_t length = (size_t)file.GetLength();
|
nuclear@0
|
3869 uiText = (char*)OVR::SafeMMapAlloc(length + 1);
|
nuclear@0
|
3870
|
nuclear@0
|
3871 if(uiText)
|
nuclear@0
|
3872 {
|
nuclear@0
|
3873 file.Read((uint8_t*)uiText, (int)length);
|
nuclear@0
|
3874 uiText[length] = '\0';
|
nuclear@0
|
3875 file.Close();
|
nuclear@0
|
3876
|
nuclear@0
|
3877 // Currently on Mac our message box implementation is unable to display arbitrarily large amounts of text.
|
nuclear@0
|
3878 // So we reduce its size to a more summary version before presenting.
|
nuclear@0
|
3879 #if defined(OVR_OS_MAC)
|
nuclear@0
|
3880 struct Find { static char* PreviousChar(char* p, char c){ while(*p != c) p--; return p; } }; // Assumes the given c is present prior to p.
|
nuclear@0
|
3881
|
nuclear@0
|
3882 // Print that the full report is at <file path>
|
nuclear@0
|
3883 // Exception Info section
|
nuclear@0
|
3884 // Exception thread callstack.
|
nuclear@0
|
3885 char empty[] = "";
|
nuclear@0
|
3886 char* pExceptionInfoBegin = strstr(uiText, "Exception Info") ? strstr(uiText, "Exception Info") : empty;
|
nuclear@0
|
3887 char* pExceptionInfoEnd = (pExceptionInfoBegin == empty) ? (empty + 1) : strstr(uiText, "\r\n\r\n");
|
nuclear@0
|
3888 char* pExceptionThreadArea = strstr(uiText, ", exception thread");
|
nuclear@0
|
3889 char* pExceptionThreadBegin = pExceptionThreadArea ? Find::PreviousChar(pExceptionThreadArea, '\n') + 1 : empty;
|
nuclear@0
|
3890 char* pExceptionThreadEnd = (pExceptionThreadBegin == empty) ? (empty + 1) : strstr(pExceptionThreadArea, "\r\n\r\n");
|
nuclear@0
|
3891
|
nuclear@0
|
3892 if(!pExceptionInfoEnd)
|
nuclear@0
|
3893 pExceptionInfoEnd = pExceptionInfoBegin;
|
nuclear@0
|
3894 *pExceptionInfoEnd = '\0';
|
nuclear@0
|
3895
|
nuclear@0
|
3896 if(!pExceptionThreadEnd)
|
nuclear@0
|
3897 pExceptionThreadEnd = pExceptionThreadBegin;
|
nuclear@0
|
3898 *pExceptionThreadEnd = '\0';
|
nuclear@0
|
3899
|
nuclear@0
|
3900 size_t uiTextBriefLength = OVR_snprintf(nullptr, 0, "Full report:%s\n\nSummary report:\n%s\n\n%s", exceptionReportPath, pExceptionInfoBegin, pExceptionThreadBegin);
|
nuclear@0
|
3901 char* uiTextBrief = (char*)OVR::SafeMMapAlloc(uiTextBriefLength + 1);
|
nuclear@0
|
3902
|
nuclear@0
|
3903 if(uiTextBrief)
|
nuclear@0
|
3904 {
|
nuclear@0
|
3905 OVR_snprintf(uiTextBrief, uiTextBriefLength + 1, "Full report:%s\n\nSummary report:\n%s\n\n%s", exceptionReportPath, pExceptionInfoBegin, pExceptionThreadBegin);
|
nuclear@0
|
3906 OVR::SafeMMapFree(uiText, length);
|
nuclear@0
|
3907 uiText = uiTextBrief;
|
nuclear@0
|
3908 }
|
nuclear@0
|
3909 #endif
|
nuclear@0
|
3910 }
|
nuclear@0
|
3911 }
|
nuclear@0
|
3912
|
nuclear@0
|
3913 return uiText;
|
nuclear@0
|
3914 }
|
nuclear@0
|
3915
|
nuclear@0
|
3916 void ExceptionHandler::FreeExceptionUIText(const char* messageBoxText)
|
nuclear@0
|
3917 {
|
nuclear@0
|
3918 OVR::SafeMMapFree(messageBoxText, OVR_strlen(messageBoxText));
|
nuclear@0
|
3919 }
|
nuclear@0
|
3920
|
nuclear@0
|
3921
|
nuclear@0
|
3922 } // namespace OVR
|
nuclear@0
|
3923
|
nuclear@0
|
3924
|
nuclear@0
|
3925 OVR_RESTORE_MSVC_WARNING()
|
nuclear@0
|
3926
|