ovr_sdk

diff LibOVR/Src/Kernel/OVR_DebugHelp.cpp @ 0:1b39a1b46319

initial 0.4.4
author John Tsiombikas <nuclear@member.fsf.org>
date Wed, 14 Jan 2015 06:51:16 +0200
parents
children
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/LibOVR/Src/Kernel/OVR_DebugHelp.cpp	Wed Jan 14 06:51:16 2015 +0200
     1.3 @@ -0,0 +1,3926 @@
     1.4 +/************************************************************************************
     1.5 +
     1.6 +Filename    :   ExceptionHandler.cpp
     1.7 +Content     :   Platform-independent exception handling interface
     1.8 +Created     :   October 6, 2014
     1.9 +
    1.10 +Copyright   :   Copyright 2014 Oculus VR, LLC. All Rights reserved.
    1.11 +
    1.12 +Licensed under the Apache License, Version 2.0 (the "License");
    1.13 +you may not use this file except in compliance with the License.
    1.14 +You may obtain a copy of the License at
    1.15 +
    1.16 +http://www.apache.org/licenses/LICENSE-2.0
    1.17 +
    1.18 +Unless required by applicable law or agreed to in writing, software
    1.19 +distributed under the License is distributed on an "AS IS" BASIS,
    1.20 +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    1.21 +See the License for the specific language governing permissions and
    1.22 +limitations under the License.
    1.23 +
    1.24 +************************************************************************************/
    1.25 +
    1.26 +
    1.27 +#include "OVR_DebugHelp.h"
    1.28 +#include "OVR_Types.h"
    1.29 +#include "OVR_UTF8Util.h"
    1.30 +#include "../OVR_CAPI.h"
    1.31 +#include "../OVR_CAPI_Keys.h"
    1.32 +#include "../CAPI/CAPI_HMDState.h"
    1.33 +#include <stdlib.h>
    1.34 +
    1.35 +#if defined(OVR_OS_WIN32) || defined(OVR_OS_WIN64)
    1.36 +    #pragma warning(push, 0)
    1.37 +    #include <Windows.h>
    1.38 +    #include <WinNT.h>
    1.39 +    #include <DbgHelp.h>
    1.40 +    #include <WinVer.h>
    1.41 +    #include <share.h>
    1.42 +    #include <Psapi.h>
    1.43 +    #include <TlHelp32.h>
    1.44 +    #include <comutil.h>
    1.45 +    #include <Wbemcli.h>
    1.46 +    #include <Wbemidl.h>
    1.47 +    #include <ShlObj.h>
    1.48 +    #include <ObjBase.h>
    1.49 +    #pragma warning(pop)
    1.50 +
    1.51 +    #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
    1.52 +    #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.
    1.53 +    #pragma comment(lib, "shell32.lib")
    1.54 +
    1.55 +    // NtQueryInformation and THREAD_BASIC_INFORMATION are undocumented but frequently needed for digging into thread information.
    1.56 +    typedef LONG (WINAPI *NtQueryInformationThreadFunc)(HANDLE, int, PVOID, ULONG, PULONG);
    1.57 +
    1.58 +    struct THREAD_BASIC_INFORMATION
    1.59 +    {
    1.60 +        LONG  ExitStatus;
    1.61 +        PVOID TebBaseAddress;
    1.62 +        PVOID UniqueProcessId;
    1.63 +        PVOID UniqueThreadId;
    1.64 +        PVOID Priority;
    1.65 +        PVOID BasePriority;
    1.66 +    };
    1.67 +
    1.68 +    #ifndef UNW_FLAG_NHANDLER // Older Windows SDKs don't support this.
    1.69 +        #define UNW_FLAG_NHANDLER 0
    1.70 +    #endif
    1.71 +
    1.72 +#elif defined(OVR_OS_MAC)
    1.73 +    #include <unistd.h>
    1.74 +    #include <sys/sysctl.h>
    1.75 +    #include <sys/utsname.h>
    1.76 +    #include <sys/types.h>
    1.77 +    #include <sys/mman.h>
    1.78 +    #include <stdlib.h>
    1.79 +    #include <stdio.h>
    1.80 +    #include <pthread.h>
    1.81 +    #include <mach/mach.h>
    1.82 +    #include <mach/mach_error.h>
    1.83 +    #include <mach/thread_status.h>
    1.84 +    #include <mach/exception.h>
    1.85 +    #include <mach/task.h>
    1.86 +    #include <mach/thread_act.h>
    1.87 +    #include <mach-o/dyld.h>
    1.88 +    #include <mach-o/dyld_images.h>
    1.89 +    #include <libproc.h>
    1.90 +    #include <libgen.h>
    1.91 +    #include <execinfo.h>
    1.92 +    #include <cxxabi.h>
    1.93 +    #include "OVR_mach_exc_OSX.h"
    1.94 +
    1.95 +    #if defined(__LP64__)
    1.96 +        typedef struct mach_header_64     MachHeader;
    1.97 +        typedef struct segment_command_64 SegmentCommand;
    1.98 +        typedef struct section_64         Section;
    1.99 +        #define kLCSegment                LC_SEGMENT_64
   1.100 +    #else
   1.101 +        typedef struct mach_header        MachHeader;
   1.102 +        typedef struct segment_command    SegmentCommand;
   1.103 +        typedef struct section            Section;
   1.104 +        #define kLCSegment                LC_SEGMENT
   1.105 +    #endif
   1.106 +
   1.107 +    extern "C" const struct dyld_all_image_infos* _dyld_get_all_image_infos(); // From libdyld.dylib
   1.108 +
   1.109 +#elif defined(OVR_OS_UNIX)
   1.110 +	#include <unistd.h>
   1.111 +	#include <sys/sysctl.h>
   1.112 +	#include <sys/utsname.h>
   1.113 +	#include <sys/types.h>
   1.114 +	#include <sys/ptrace.h>
   1.115 +	#include <sys/wait.h>
   1.116 +	#include <sys/mman.h>
   1.117 +	#include <stdlib.h>
   1.118 +	#include <stdio.h>
   1.119 +	#include <pthread.h>
   1.120 +	#include <libgen.h>
   1.121 +	#include <execinfo.h>
   1.122 +	#include <cxxabi.h>
   1.123 +  //#include <libunwind.h> // Can't use this until we can ensure that we have an installed version of it.
   1.124 +#endif
   1.125 +
   1.126 +#if !defined(MIN)
   1.127 +    #define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
   1.128 +    #define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
   1.129 +#endif
   1.130 +
   1.131 +
   1.132 +OVR_DISABLE_MSVC_WARNING(4351) // new behavior: elements of array will be default initialized
   1.133 +OVR_DISABLE_MSVC_WARNING(4996) // This function or variable may be unsafe
   1.134 +
   1.135 +
   1.136 +
   1.137 +
   1.138 +#if defined(OVR_OS_APPLE)
   1.139 +    static OVR::ExceptionHandler* sExceptionHandler = nullptr;
   1.140 +    const uint32_t sMachCancelMessageType = 0x0ca9ce11;  // This is a made-up value of our own choice.
   1.141 +
   1.142 +    extern "C"
   1.143 +    {
   1.144 +        kern_return_t catch_mach_exception_raise_OVR(mach_port_t /*exceptionPort*/, mach_port_t /*threadSysId*/,
   1.145 +                                        mach_port_t /*machTask*/, exception_type_t /*machExceptionType*/,
   1.146 +                                        mach_exception_data_t /*machExceptionData*/, mach_msg_type_number_t /*machExceptionDataCount*/)
   1.147 +        {    
   1.148 +            return KERN_FAILURE;
   1.149 +        }
   1.150 +
   1.151 +        kern_return_t catch_mach_exception_raise_state_OVR(mach_port_t /*exceptionPort*/, exception_type_t /*exceptionType*/,
   1.152 +                                        const mach_exception_data_t /*machExceptionData*/, mach_msg_type_number_t /*machExceptionDataCount*/,
   1.153 +                                        int* /*pMachExceptionFlavor*/, const thread_state_t /*threadStatePrev*/, mach_msg_type_number_t /*threaStatePrevCount*/,
   1.154 +                                        thread_state_t /*threadStateNew*/, mach_msg_type_number_t* /*pThreadStateNewCount*/)
   1.155 +        {    
   1.156 +            return KERN_FAILURE;
   1.157 +        }
   1.158 +
   1.159 +        kern_return_t catch_mach_exception_raise_state_identity_OVR(mach_port_t exceptionPort, mach_port_t threadSysId, mach_port_t machTask,
   1.160 +                                            exception_type_t exceptionType, mach_exception_data_type_t* machExceptionData,
   1.161 +                                            mach_msg_type_number_t machExceptionDataCount, int* pMachExceptionFlavor,
   1.162 +                                            thread_state_t threadStatePrev, mach_msg_type_number_t threadStatePrevCount,
   1.163 +                                            thread_state_t threadStateNew, mach_msg_type_number_t* pThreadStateNewCount)
   1.164 +        {
   1.165 +            return sExceptionHandler->HandleMachException(exceptionPort, threadSysId, machTask, exceptionType, machExceptionData,
   1.166 +                                                machExceptionDataCount, pMachExceptionFlavor, threadStatePrev, threadStatePrevCount,
   1.167 +                                                threadStateNew, pThreadStateNewCount);
   1.168 +        }
   1.169 +
   1.170 +        void* MachHandlerThreadFunctionStatic(void* pExceptionHandlerVoid)
   1.171 +        {
   1.172 +            return static_cast<OVR::ExceptionHandler*>(pExceptionHandlerVoid)->MachHandlerThreadFunction();
   1.173 +        }
   1.174 +    
   1.175 +    } // extern "C"
   1.176 +#endif
   1.177 +
   1.178 +
   1.179 +
   1.180 +
   1.181 +namespace OVR {
   1.182 +
   1.183 +
   1.184 +void GetInstructionPointer(void*& pInstruction)
   1.185 +{
   1.186 +    #if defined(OVR_CC_MSVC)
   1.187 +        pInstruction = _ReturnAddress();
   1.188 +    #else // GCC, clang
   1.189 +        pInstruction = __builtin_return_address(0);
   1.190 +    #endif
   1.191 +}
   1.192 +
   1.193 +
   1.194 +static size_t SprintfAddress(char* threadHandleStr, size_t threadHandleStrCapacity, const void* pAddress)
   1.195 +{
   1.196 +    #if defined(OVR_CC_MSVC)
   1.197 +        #if (OVR_PTR_SIZE >= 8)
   1.198 +            return OVR_snprintf(threadHandleStr, threadHandleStrCapacity, "0x%016I64x", pAddress);  // e.g. 0x0123456789abcdef
   1.199 +        #else
   1.200 +            return OVR_snprintf(threadHandleStr, threadHandleStrCapacity, "0x%08x", pAddress);      // e.g. 0x89abcdef
   1.201 +        #endif
   1.202 +    #else
   1.203 +        #if (OVR_PTR_SIZE >= 8)
   1.204 +            return OVR_snprintf(threadHandleStr, threadHandleStrCapacity, "%016llx", pAddress);     // e.g. 0x0123456789abcdef
   1.205 +        #else
   1.206 +            return OVR_snprintf(threadHandleStr, threadHandleStrCapacity, "%08x", pAddress);        // e.g. 0x89abcdef
   1.207 +        #endif
   1.208 +    #endif
   1.209 +}
   1.210 +
   1.211 +
   1.212 +static size_t SprintfThreadHandle(char* threadHandleStr, size_t threadHandleStrCapacity, const ThreadHandle& threadHandle)
   1.213 +{
   1.214 +    return SprintfAddress(threadHandleStr, threadHandleStrCapacity, threadHandle);
   1.215 +}
   1.216 +
   1.217 +
   1.218 +static size_t SprintfThreadSysId(char* threadSysIdStr, size_t threadSysIdStrCapacity, const ThreadSysId& threadSysId)
   1.219 +{
   1.220 +    #if defined(OVR_CC_MSVC) // Somebody could conceivably use VC++ with a different standard library that supports %ll. And VS2012+ also support %ll.
   1.221 +        return OVR_snprintf(threadSysIdStr, threadSysIdStrCapacity, "%I64u", (uint64_t)threadSysId);        
   1.222 +    #else
   1.223 +        return OVR_snprintf(threadSysIdStr, threadSysIdStrCapacity, "%llu", (uint64_t)threadSysId);        
   1.224 +    #endif
   1.225 +}
   1.226 +
   1.227 +
   1.228 +
   1.229 +
   1.230 +
   1.231 +void GetThreadStackBounds(void*& pStackBase, void*& pStackLimit, ThreadHandle threadHandle)
   1.232 +{
   1.233 +    #if defined(OVR_OS_WIN64) || defined(OVR_OS_WIN32) || (defined(OVR_OS_MS) && defined(OVR_OS_CONSOLE))
   1.234 +        ThreadSysId threadSysIdCurrent = (ThreadSysId)GetCurrentThreadId();
   1.235 +        ThreadSysId threadSysId;
   1.236 +        NT_TIB*     pTIB = nullptr;
   1.237 +
   1.238 +        if(threadHandle == OVR_THREADHANDLE_INVALID)
   1.239 +            threadSysId = threadSysIdCurrent;
   1.240 +        else
   1.241 +            threadSysId = ConvertThreadHandleToThreadSysId(threadHandle);
   1.242 +
   1.243 +        if(threadSysId == threadSysIdCurrent)
   1.244 +        {
   1.245 +            #if (OVR_PTR_SIZE == 4)
   1.246 +                // Need to use __asm__("movl %%fs:0x18, %0" : "=r" (pTIB) : : ); under gcc/clang.
   1.247 +                __asm {
   1.248 +                    mov eax, fs:[18h]
   1.249 +                    mov pTIB, eax
   1.250 +                }
   1.251 +            #else
   1.252 +                pTIB = (NT_TIB*)NtCurrentTeb();
   1.253 +            #endif
   1.254 +        }
   1.255 +        else
   1.256 +        {
   1.257 +            #if (OVR_PTR_SIZE == 4)
   1.258 +                // It turns out we don't need to suspend the thread when getting SegFs/SegGS, as that's 
   1.259 +                // constant per thread and doesn't require the thread to be suspended.
   1.260 +                //SuspendThread((HANDLE)threadHandle); 
   1.261 +                CONTEXT context;
   1.262 +                memset(&context, 0, sizeof(context));
   1.263 +                context.ContextFlags = CONTEXT_SEGMENTS;
   1.264 +                GetThreadContext((HANDLE)threadHandle, &context); // Requires THREAD_QUERY_INFORMATION privileges.
   1.265 +
   1.266 +                LDT_ENTRY ldtEntry;
   1.267 +                if(GetThreadSelectorEntry(threadHandle, context.SegFs, &ldtEntry)) // Requires THREAD_QUERY_INFORMATION
   1.268 +                    pTIB = (NT_TIB*)((ldtEntry.HighWord.Bits.BaseHi << 24 ) | (ldtEntry.HighWord.Bits.BaseMid << 16) | ldtEntry.BaseLow);
   1.269 +
   1.270 +                //ResumeThread((HANDLE)threadHandle);
   1.271 +            #else
   1.272 +                // We cannot use GetThreadSelectorEntry or Wow64GetThreadSelectorEntry on Win64.
   1.273 +                // 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.
   1.274 +                //    mov rax, qword ptr gs:[30h]  
   1.275 +                //    mov qword ptr [pTIB],rax
   1.276 +                // In the meantime we rely on the NtQueryInformationThread function.
   1.277 +
   1.278 +                static NtQueryInformationThreadFunc spNtQueryInformationThread = nullptr;
   1.279 +
   1.280 +                if(!spNtQueryInformationThread)
   1.281 +                {
   1.282 +                    HMODULE hNTDLL = GetModuleHandleA("ntdll.dll");
   1.283 +                    spNtQueryInformationThread = (NtQueryInformationThreadFunc)(uintptr_t)GetProcAddress(hNTDLL, "NtQueryInformationThread");
   1.284 +                }
   1.285 +
   1.286 +                if(spNtQueryInformationThread)
   1.287 +                {
   1.288 +                    THREAD_BASIC_INFORMATION tbi;
   1.289 +
   1.290 +                    memset(&tbi, 0, sizeof(tbi));
   1.291 +                    LONG result = spNtQueryInformationThread(threadHandle, 0, &tbi, sizeof(tbi), nullptr); // Requires THREAD_QUERY_INFORMATION privileges
   1.292 +                    if(result == 0)
   1.293 +                        pTIB = (NT_TIB*)tbi.TebBaseAddress;
   1.294 +                }
   1.295 +            #endif
   1.296 +        }
   1.297 +
   1.298 +        if(pTIB)
   1.299 +        {
   1.300 +            pStackBase  = (void*)pTIB->StackBase;
   1.301 +            pStackLimit = (void*)pTIB->StackLimit;
   1.302 +        }
   1.303 +        else
   1.304 +        {
   1.305 +            pStackBase  = nullptr;
   1.306 +            pStackLimit = nullptr;
   1.307 +        }
   1.308 +
   1.309 +    #elif defined(OVR_OS_APPLE)
   1.310 +        if(!threadHandle)
   1.311 +            threadHandle = pthread_self();
   1.312 +    
   1.313 +        pStackBase = pthread_get_stackaddr_np((pthread_t)threadHandle);
   1.314 +        size_t stackSize = pthread_get_stacksize_np((pthread_t)threadHandle);
   1.315 +        pStackLimit = (void*)((size_t)pStackBase - stackSize);
   1.316 +    
   1.317 +    #elif defined(OVR_OS_UNIX)
   1.318 +        pStackBase  = nullptr;
   1.319 +        pStackLimit = nullptr;
   1.320 +
   1.321 +        pthread_attr_t threadAttr;
   1.322 +        pthread_attr_init(&threadAttr);
   1.323 +    
   1.324 +        #if defined(OVR_OS_LINUX)
   1.325 +            int result = pthread_getattr_np((pthread_t)threadHandle, &threadAttr);
   1.326 +        #else
   1.327 +            int result = pthread_attr_get_np((pthread_t)threadHandle, &threadAttr);
   1.328 +        #endif
   1.329 +    
   1.330 +        if(result == 0)
   1.331 +        {
   1.332 +            size_t stackSize = 0;
   1.333 +            result = pthread_attr_getstack(&threadAttr, &pStackLimit, &stackSize);
   1.334 +            
   1.335 +            if(result == 0)
   1.336 +                pStackBase = (void*)((uintptr_t)pStackLimit + stackSize); // We assume the stack grows downward.
   1.337 +        }
   1.338 +
   1.339 +    #endif
   1.340 +}
   1.341 +
   1.342 +
   1.343 +bool OVRIsDebuggerPresent()
   1.344 +{
   1.345 +    #if defined(OVR_OS_MS)
   1.346 +        return ::IsDebuggerPresent() != 0;
   1.347 +
   1.348 +    #elif defined(OVR_OS_APPLE)
   1.349 +        int               mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() };
   1.350 +        struct kinfo_proc info;
   1.351 +        size_t            size = sizeof(info);
   1.352 +
   1.353 +        info.kp_proc.p_flag = 0;
   1.354 +        sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0);
   1.355 +
   1.356 +        return ((info.kp_proc.p_flag & P_TRACED) != 0);
   1.357 +
   1.358 +    #elif (defined(OVR_OS_LINUX) || defined(OVR_OS_BSD)) && !defined(OVR_OS_ANDROID)
   1.359 +        // This works better than the PT_TRACE_ME approach.
   1.360 +        // However, it presents a problem: 
   1.361 +        //     http://pubs.opengroup.org/onlinepubs/009695399/functions/fork.html
   1.362 +        //     When the application calls fork() from a signal handler and any of the
   1.363 +        //     fork handlers registered by pthread_atfork() calls a function that is
   1.364 +        //     not asynch-signal-safe, the behavior is undefined.
   1.365 +        // We may need to provide two pathways within this function, one of which 
   1.366 +        // doesn't fork and instead uses PT_TRACE_ME.
   1.367 +        int  pid = fork();
   1.368 +        int  status;
   1.369 +        bool present = false;
   1.370 +
   1.371 +        if (pid == -1) // If fork failed...
   1.372 +        {
   1.373 +            // perror("fork");
   1.374 +        }
   1.375 +        else if (pid == 0) // If we are the child process...
   1.376 +        {
   1.377 +            int ppid = getppid();
   1.378 +
   1.379 +          #if defined(OVR_OS_LINUX)
   1.380 +            if (ptrace(PTRACE_ATTACH, ppid, nullptr, nullptr) == 0)
   1.381 +          #else
   1.382 +            if (ptrace(PT_ATTACH, ppid, nullptr, nullptr) == 0)
   1.383 +          #endif
   1.384 +            {
   1.385 +                waitpid(ppid, nullptr, 0);
   1.386 +
   1.387 +              #if defined(OVR_OS_LINUX)
   1.388 +                ptrace(PTRACE_CONT, getppid(), nullptr, nullptr);
   1.389 +                ptrace(PTRACE_DETACH, getppid(), nullptr, nullptr);
   1.390 +              #else
   1.391 +                ptrace(PT_CONTINUE, getppid(), nullptr, nullptr);
   1.392 +                ptrace(PT_DETACH, getppid(), nullptr, nullptr);
   1.393 +              #endif
   1.394 +            }
   1.395 +            else
   1.396 +            {
   1.397 +                // ptrace failed so the debugger is present.
   1.398 +                present = true;
   1.399 +            }
   1.400 +
   1.401 +            exit(present ? 1 : 0); // The WEXITSTATUS call below will read this exit value.
   1.402 +        }
   1.403 +        else // Else we are the original process.
   1.404 +        {
   1.405 +            waitpid(pid, &status, 0);
   1.406 +            present = WEXITSTATUS(status) ? true : false; // Read the exit value from the child's call to exit.
   1.407 +        }
   1.408 +
   1.409 +        return present;
   1.410 +
   1.411 +    #elif defined(PT_TRACE_ME) && !defined(OVR_OS_ANDROID)
   1.412 +        return (ptrace(PT_TRACE_ME, 0, 1, 0) < 0);
   1.413 +
   1.414 +    #else
   1.415 +        return false;
   1.416 +    #endif
   1.417 +}
   1.418 +
   1.419 +
   1.420 +// Exits the process with the given exit code.
   1.421 +void ExitProcess(intptr_t processReturnValue)
   1.422 +{
   1.423 +    exit((int)processReturnValue);
   1.424 +}
   1.425 +
   1.426 +
   1.427 +void* SafeMMapAlloc(size_t size)
   1.428 +{
   1.429 +    #if defined(OVR_OS_MS)
   1.430 +        return VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); // size is rounded up to a page. // Returned memory is 0-filled.
   1.431 +
   1.432 +    #elif defined(OVR_OS_MAC) || defined(OVR_OS_UNIX)
   1.433 +        #if !defined(MAP_FAILED)
   1.434 +            #define MAP_FAILED ((void*)-1)
   1.435 +        #endif
   1.436 +
   1.437 +        void* result = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); // Returned memory is 0-filled.
   1.438 +        if(result == MAP_FAILED) // mmap returns MAP_FAILED (-1) upon failure.
   1.439 +            result = nullptr;
   1.440 +        return result;
   1.441 +    #endif
   1.442 +}
   1.443 +
   1.444 +
   1.445 +void SafeMMapFree(const void* memory, size_t size)
   1.446 +{
   1.447 +    #if defined(OVR_OS_MS)
   1.448 +        OVR_UNUSED(size);
   1.449 +        VirtualFree(const_cast<void*>(memory), 0, MEM_RELEASE);
   1.450 +
   1.451 +    #elif defined(OVR_OS_MAC) || defined(OVR_OS_UNIX)
   1.452 +        size_t pageSize = getpagesize();
   1.453 +        size = (((size + (pageSize - 1)) / pageSize) * pageSize);
   1.454 +        munmap(const_cast<void*>(memory), size); // Must supply the size to munmap.
   1.455 +    #endif
   1.456 +}
   1.457 +
   1.458 +
   1.459 +// Note that we can't just return sizeof(void*) == 8, as we may have the case of a
   1.460 +// 32 bit app running on a 64 bit operating system.
   1.461 +static bool Is64BitOS()
   1.462 +{
   1.463 +    #if (OVR_PTR_SIZE >= 8)
   1.464 +        return true;
   1.465 +
   1.466 +    #elif defined(OVR_OS_WIN32) || defined(OVR_OS_WIN64)
   1.467 +        BOOL is64BitOS = FALSE;
   1.468 +        bool IsWow64ProcessPresent = (GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "IsWow64Process") != nullptr);
   1.469 +        return (IsWow64ProcessPresent && IsWow64Process(GetCurrentProcess(), &is64BitOS) && is64BitOS);
   1.470 +
   1.471 +    #elif defined(OVR_OS_MAC) || defined(OVR_OS_UNIX)
   1.472 +        utsname utsName;
   1.473 +        memset(&utsName, 0, sizeof(utsName));
   1.474 +        return (uname(&utsName) == 0) && (strcmp(utsName.machine, "x86_64") == 0);
   1.475 +
   1.476 +    #else
   1.477 +        return false;
   1.478 +    #endif
   1.479 +}
   1.480 +
   1.481 +
   1.482 +// The output will always be 0-terminated.
   1.483 +// Returns the required strlen of the output.
   1.484 +// Returns (size_t)-1 on failure.
   1.485 +size_t SpawnShellCommand(const char* shellCommand, char* output, size_t outputCapacity)
   1.486 +{
   1.487 +    #if defined(OVR_OS_UNIX) || defined(OVR_OS_APPLE)
   1.488 +        FILE* pFile = popen(shellCommand, "r");
   1.489 +    
   1.490 +        if(pFile)
   1.491 +        {
   1.492 +            size_t requiredLength = 0;
   1.493 +            char buffer[256];
   1.494 +        
   1.495 +            while(fgets(buffer, sizeof(buffer), pFile)) // fgets 0-terminates the buffer.
   1.496 +            {
   1.497 +                size_t length = OVR_strlen(buffer);
   1.498 +                requiredLength += length;
   1.499 +            
   1.500 +                if(outputCapacity)
   1.501 +                {
   1.502 +                    OVR_strlcpy(output, buffer, outputCapacity);
   1.503 +                    length = MIN(outputCapacity, length);
   1.504 +                }
   1.505 +
   1.506 +                output         += length;
   1.507 +                outputCapacity -= length;
   1.508 +            }
   1.509 +        
   1.510 +            pclose(pFile);
   1.511 +            return requiredLength;
   1.512 +        }
   1.513 +    #else
   1.514 +        // To do. Properly solving this on Windows requires a bit of code.
   1.515 +        OVR_UNUSED(shellCommand);
   1.516 +        OVR_UNUSED(output);
   1.517 +        OVR_UNUSED(outputCapacity);
   1.518 +    #endif
   1.519 +
   1.520 +    return (size_t)-1;
   1.521 +}
   1.522 +
   1.523 +
   1.524 +// Retrieves a directory path which ends with a path separator.
   1.525 +// Returns the required strlen of the path.
   1.526 +// Guarantees the presence of the directory upon returning true.
   1.527 +static size_t GetUserDocumentsDirectory(char* directoryPath, size_t directoryPathCapacity)
   1.528 +{
   1.529 +    #if defined(OVR_OS_MS)
   1.530 +        wchar_t pathW[MAX_PATH + 1]; // +1 because we append a path separator.
   1.531 +        HRESULT hr = SHGetFolderPathW(nullptr, CSIDL_APPDATA | CSIDL_FLAG_CREATE, nullptr, SHGFP_TYPE_CURRENT, pathW);
   1.532 +
   1.533 +        if(SUCCEEDED(hr))
   1.534 +        {
   1.535 +            OVR_UNUSED(directoryPathCapacity);
   1.536 +
   1.537 +            intptr_t requiredUTF8Length = OVR::UTF8Util::GetEncodeStringSize(pathW); // Returns required strlen.
   1.538 +            if(requiredUTF8Length < MAX_PATH) // We need space for a trailing path separator.
   1.539 +            {
   1.540 +                OVR::UTF8Util::EncodeString(directoryPath, pathW, -1);
   1.541 +                OVR::OVR_strlcat(directoryPath, "\\", directoryPathCapacity);
   1.542 +            }        
   1.543 +
   1.544 +            return (requiredUTF8Length + 1);
   1.545 +        }
   1.546 +
   1.547 +    #elif defined(OVR_OS_MAC)
   1.548 +        // This is the same location that Apple puts its OS-generated .crash files.
   1.549 +        const char* home = getenv("HOME");
   1.550 +        size_t requiredStrlen = OVR::OVR_snprintf(directoryPath, directoryPathCapacity, "%s/Library/Logs/DiagnosticReports/", home ? home : "/Users/Shared/Logs/DiagnosticReports/");
   1.551 +        // To do: create the directory if it doesn't already exist.
   1.552 +        return requiredStrlen;
   1.553 +    
   1.554 +    #elif defined(OVR_OS_UNIX) || defined(OVR_OS_MAC)
   1.555 +        const char* home = getenv("HOME");
   1.556 +        size_t requiredStrlen = OVR::OVR_snprintf(directoryPath, directoryPathCapacity, "%s/Library/", home ? home : "/Users/Shared/");
   1.557 +        // To do: create the directory if it doesn't already exist.
   1.558 +        return requiredStrlen;
   1.559 +    #endif
   1.560 +
   1.561 +    return 0;
   1.562 +}
   1.563 +
   1.564 +
   1.565 +// Retrieves the name of the given thread.
   1.566 +// To do: Move this to OVR_Threads.h
   1.567 +bool GetThreadName(OVR::ThreadHandle threadHandle, char* threadName, size_t threadNameCapacity)
   1.568 +{
   1.569 +    #if defined(OVR_OS_APPLE) || defined(OVR_OS_LINUX)
   1.570 +        int result = pthread_getname_np((pthread_t)threadHandle, threadName, threadNameCapacity);
   1.571 +        if(result == 0)
   1.572 +            return true;
   1.573 +    #else
   1.574 +        // This is not currently possible on Windows, as only the debugger stores the thread name. We could potentially use a vectored 
   1.575 +        // exception handler to catch all thread name exceptions (0x406d1388) and record them in a static list at runtime. To detect
   1.576 +        // thread exit we could use WMI Win32_ThreadStopTrace. Maintain a list of thread names between these two events.
   1.577 +        OVR_UNUSED(threadHandle);
   1.578 +        OVR_UNUSED(threadNameCapacity);
   1.579 +    #endif
   1.580 +
   1.581 +    if(threadNameCapacity)
   1.582 +        threadName[0] = 0;
   1.583 +
   1.584 +    return false;
   1.585 +}
   1.586 +
   1.587 +
   1.588 +OVR::ThreadSysId ConvertThreadHandleToThreadSysId(OVR::ThreadHandle threadHandle)
   1.589 +{
   1.590 +    #if defined(OVR_OS_WIN64)
   1.591 +        return (OVR::ThreadSysId)::GetThreadId(threadHandle); // Requires THREAD_QUERY_INFORMATION privileges.
   1.592 +
   1.593 +    #elif defined(OVR_OS_WIN32)
   1.594 +        typedef DWORD (WINAPI *GetThreadIdFunc)(HANDLE);
   1.595 +
   1.596 +        static volatile bool sInitialized = false;
   1.597 +        static GetThreadIdFunc spGetThreadIdFunc = nullptr;
   1.598 +        static NtQueryInformationThreadFunc spNtQueryInformationThread = nullptr;
   1.599 +
   1.600 +        if(!sInitialized)
   1.601 +        {
   1.602 +            HMODULE hKernel32 = GetModuleHandleA("kernel32.dll");
   1.603 +            if(hKernel32)
   1.604 +                spGetThreadIdFunc = (GetThreadIdFunc)(uintptr_t)GetProcAddress(hKernel32, "GetThreadId");
   1.605 +
   1.606 +            if(!spGetThreadIdFunc)
   1.607 +            {
   1.608 +                HMODULE hNTDLL = GetModuleHandleA("ntdll.dll");
   1.609 +
   1.610 +                if(hNTDLL)
   1.611 +                    spNtQueryInformationThread = (NtQueryInformationThreadFunc)(uintptr_t)GetProcAddress(hNTDLL, "NtQueryInformationThread");
   1.612 +            }
   1.613 +
   1.614 +            sInitialized = true;
   1.615 +        }
   1.616 +
   1.617 +        if(spGetThreadIdFunc)
   1.618 +            return (OVR::ThreadSysId)spGetThreadIdFunc(threadHandle);
   1.619 +
   1.620 +        if(spNtQueryInformationThread)
   1.621 +        {
   1.622 +            THREAD_BASIC_INFORMATION tbi;
   1.623 +
   1.624 +            if(spNtQueryInformationThread(threadHandle, 0, &tbi, sizeof(tbi), nullptr) == 0)
   1.625 +                return (OVR::ThreadSysId)tbi.UniqueThreadId;
   1.626 +        }
   1.627 +
   1.628 +        return OVR_THREADSYSID_INVALID;
   1.629 +    
   1.630 +    #elif defined(OVR_OS_APPLE)
   1.631 +        mach_port_t threadSysId = pthread_mach_thread_np((pthread_t)threadHandle);  // OS 10.4 and later.
   1.632 +        return (ThreadSysId)threadSysId;
   1.633 +
   1.634 +    #elif defined(OVR_OS_LINUX)
   1.635 +
   1.636 +        // 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.
   1.637 +        OVR_UNUSED(threadHandle);
   1.638 +        return OVR_THREADSYSID_INVALID;
   1.639 +
   1.640 +    #else
   1.641 +        OVR_UNUSED(threadHandle);
   1.642 +        return OVR_THREADSYSID_INVALID;
   1.643 +    #endif
   1.644 +}
   1.645 +
   1.646 +
   1.647 +OVR::ThreadHandle ConvertThreadSysIdToThreadHandle(OVR::ThreadSysId threadSysId)
   1.648 +{
   1.649 +    #if defined(OVR_OS_MS)
   1.650 +        // We currently request the given rights because that's what users of this function typically need it for. Ideally there would 
   1.651 +        // 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.
   1.652 +        // 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.
   1.653 +        OVR::ThreadHandle threadHandle = ::OpenThread(THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION, TRUE, (DWORD)threadSysId);
   1.654 +
   1.655 +        if(threadHandle == OVR_THREADHANDLE_INVALID)
   1.656 +        {
   1.657 +            threadHandle = ::OpenThread(THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION, TRUE, (DWORD)threadSysId);
   1.658 +
   1.659 +            if(threadHandle == OVR_THREADHANDLE_INVALID)
   1.660 +                threadHandle = ::OpenThread(THREAD_QUERY_INFORMATION, TRUE, (DWORD)threadSysId);
   1.661 +        }
   1.662 +
   1.663 +        return threadHandle;
   1.664 +    #elif defined(OVR_OS_MAC)
   1.665 +        return (ThreadHandle)pthread_from_mach_thread_np((mach_port_t)threadSysId);
   1.666 +    #else
   1.667 +        return (ThreadHandle)threadSysId;
   1.668 +    #endif
   1.669 +}
   1.670 +
   1.671 +
   1.672 +void FreeThreadHandle(OVR::ThreadHandle threadHandle)
   1.673 +{
   1.674 +    #if defined(OVR_OS_MS)
   1.675 +        if(threadHandle != OVR_THREADHANDLE_INVALID)
   1.676 +            ::CloseHandle(threadHandle);
   1.677 +    #else
   1.678 +        OVR_UNUSED(threadHandle);
   1.679 +    #endif
   1.680 +}
   1.681 +
   1.682 +
   1.683 +OVR::ThreadSysId GetCurrentThreadSysId()
   1.684 +{
   1.685 +    #if defined(OVR_OS_MS)
   1.686 +        return ::GetCurrentThreadId();
   1.687 +    #elif defined(OVR_OS_APPLE)
   1.688 +        return (ThreadSysId)mach_thread_self();
   1.689 +    #else
   1.690 +        return (ThreadSysId)pthread_self();
   1.691 +    #endif
   1.692 +}
   1.693 +
   1.694 +
   1.695 +
   1.696 +static void GetCurrentProcessFilePath(char* appPath, size_t appPathCapacity)
   1.697 +{
   1.698 +    appPath[0] = 0;
   1.699 +
   1.700 +    #if defined(OVR_OS_MS)
   1.701 +        wchar_t pathW[MAX_PATH];
   1.702 +        GetModuleFileNameW(0, pathW, (DWORD)OVR_ARRAY_COUNT(pathW));
   1.703 +    
   1.704 +        size_t requiredUTF8Length = (size_t)OVR::UTF8Util::GetEncodeStringSize(pathW); // Returns required strlen.
   1.705 +    
   1.706 +        if(requiredUTF8Length < appPathCapacity)
   1.707 +        {
   1.708 +            OVR::UTF8Util::EncodeString(appPath, pathW, -1);
   1.709 +        }
   1.710 +        else
   1.711 +        {
   1.712 +            appPath[0] = 0;
   1.713 +        }
   1.714 +    
   1.715 +    #elif defined(OVR_OS_APPLE)
   1.716 +        struct BunderFolder
   1.717 +        {
   1.718 +            // Returns true if pStr ends with pFind, case insensitively.
   1.719 +            // To do: Move OVR_striend to OVRKernel/Std.h
   1.720 +            static bool OVR_striend(const char* pStr, const char* pFind, size_t strLength = (size_t)-1, size_t findLength = (size_t)-1)
   1.721 +            {
   1.722 +                if(strLength == (size_t)-1)
   1.723 +                    strLength = OVR_strlen(pStr);
   1.724 +                if(findLength == (size_t)-1)
   1.725 +                    findLength = OVR_strlen(pFind);
   1.726 +                if(strLength >= findLength)
   1.727 +                    return (OVR_stricmp(pStr + strLength - findLength, pFind) == 0);
   1.728 +                return false;
   1.729 +            }
   1.730 +            
   1.731 +            static bool IsBundleFolder(const char* filePath)
   1.732 +            {
   1.733 +                // https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/AboutBundles/AboutBundles.html#//apple_ref/doc/uid/10000123i-CH100-SW1
   1.734 +                static const char* extensionArray[] = { ".app", ".bundle", ".framework", ".plugin", ".kext" };
   1.735 +                
   1.736 +                for(size_t i = 0; i < OVR_ARRAY_COUNT(extensionArray); i++)
   1.737 +                {
   1.738 +                    if(OVR_striend(filePath, extensionArray[i]))
   1.739 +                        return true;
   1.740 +                }
   1.741 +                
   1.742 +                return false;
   1.743 +            }
   1.744 +        };
   1.745 +    
   1.746 +        char     appPathTemp[PATH_MAX];
   1.747 +        uint32_t appPathTempCapacity32 = PATH_MAX;
   1.748 +        size_t   requiredStrlen = appPathCapacity;
   1.749 +    
   1.750 +        if(_NSGetExecutablePath(appPathTemp, &appPathTempCapacity32) == 0)
   1.751 +        {
   1.752 +            char appPathTempReal[PATH_MAX];
   1.753 +
   1.754 +            if(realpath(appPathTemp, appPathTempReal))  // If the path is a symbolic link, this converts it to the real path.
   1.755 +            {
   1.756 +                // To consider: Enable reading the internal bundle executable path. An application on Mac may in
   1.757 +                // fact be within a file bundle, which is an private file system within a file. With Objective C
   1.758 +                // we could use: [[NSWorkspace sharedWorkspace] isFilePackageAtPath:fullPath];
   1.759 +                bool shouldReadTheBunderPath = false;
   1.760 +                
   1.761 +                if(shouldReadTheBunderPath)
   1.762 +                {
   1.763 +                    // We recursively call dirname() until we find .app/.bundle/.plugin as a directory name.
   1.764 +                    OVR_strlcpy(appPathTemp, appPathTempReal, OVR_ARRAY_COUNT(appPathTemp));
   1.765 +                    bool found = BunderFolder::IsBundleFolder(appPathTemp);
   1.766 + 
   1.767 +                    while(!found && OVR_strcmp(appPathTemp, ".") && OVR_strcmp(appPathTemp, "/"))
   1.768 +                    {
   1.769 +                        OVR_strlcpy(appPathTemp, dirname(appPathTemp), OVR_ARRAY_COUNT(appPathTemp));
   1.770 +                        found = BunderFolder::IsBundleFolder(appPathTemp);
   1.771 +                    }
   1.772 +
   1.773 +                    if(found) // If somewhere above we found a parent bundle container...
   1.774 +                        requiredStrlen = OVR_strlcpy(appPath, appPathTemp, appPathCapacity);
   1.775 +                    else
   1.776 +                        requiredStrlen = OVR_strlcpy(appPath, appPathTempReal, appPathCapacity);
   1.777 +                }
   1.778 +                else
   1.779 +                {
   1.780 +                    requiredStrlen = OVR_strlcpy(appPath, appPathTempReal, appPathCapacity);
   1.781 +                }
   1.782 +            }
   1.783 +        }
   1.784 +    
   1.785 +        if(requiredStrlen >= appPathCapacity)
   1.786 +            appPath[0] = '\0';
   1.787 +    
   1.788 +    #elif defined(OVR_OS_LINUX)
   1.789 +        ssize_t length = readlink("/proc/self/exe", appPath, appPathCapacity);
   1.790 +    
   1.791 +        if((length != -1) && ((size_t)length < (appPathCapacity - 1)))
   1.792 +        {
   1.793 +            appPath[length] = '\0';
   1.794 +        }
   1.795 +    #endif
   1.796 +}
   1.797 +
   1.798 +
   1.799 +static const char* GetFileNameFromPath(const char* filePath)
   1.800 +{
   1.801 +    #if defined(OVR_OS_MS)
   1.802 +    const char* lastPathSeparator = max(strrchr(filePath, '\\'), strrchr(filePath, '/')); // Microsoft APIs are inconsistent with respect to allowing / as a path separator.
   1.803 +    #else
   1.804 +    const char* lastPathSeparator = strrchr(filePath, '/');
   1.805 +    #endif
   1.806 +    
   1.807 +    if(lastPathSeparator)
   1.808 +        return lastPathSeparator + 1;
   1.809 +    
   1.810 +    return filePath;
   1.811 +}
   1.812 +
   1.813 +
   1.814 +
   1.815 +static void FormatDateTime(char* buffer, size_t bufferCapacity, time_t timeValue, bool getDate, bool getTime, bool localDateTime, bool fileNameSafeCharacters = false)
   1.816 +{
   1.817 +    char      temp[128];
   1.818 +    const tm* pTime = localDateTime ? localtime(&timeValue) : gmtime(&timeValue);
   1.819 +    
   1.820 +    if(bufferCapacity)
   1.821 +        buffer[0] = 0;
   1.822 +
   1.823 +    if(getDate)
   1.824 +    {
   1.825 +        const char* format = fileNameSafeCharacters ? "%Y-%m-%d" : "%Y/%m/%d";
   1.826 +        strftime(temp, OVR_ARRAY_COUNT(temp), format, pTime);
   1.827 +        OVR::OVR_strlcpy(buffer, temp, bufferCapacity);
   1.828 +    }
   1.829 +
   1.830 +    if(getTime)
   1.831 +    {
   1.832 +        const char* format = fileNameSafeCharacters ? " %H.%M.%S" : " %H:%M:%S";
   1.833 +        strftime(temp, OVR_ARRAY_COUNT(temp), (getDate ? format : format + 1), pTime);
   1.834 +        OVR::OVR_strlcat(buffer, temp, bufferCapacity);
   1.835 +    }
   1.836 +}
   1.837 +
   1.838 +
   1.839 +static void GetOSVersionName(char* versionName, size_t versionNameCapacity)
   1.840 +{
   1.841 +    #if defined(OVR_OS_MS)
   1.842 +        const char* name = "unknown";
   1.843 +
   1.844 +        OSVERSIONINFOEXW vi;
   1.845 +        memset(&vi, 0, sizeof(vi));
   1.846 +        vi.dwOSVersionInfoSize = sizeof(vi);
   1.847 +
   1.848 +        if(GetVersionExW((LPOSVERSIONINFOW)&vi))
   1.849 +        {
   1.850 +            if(vi.dwMajorVersion >= 7)
   1.851 +            {
   1.852 +                // Unknown recent version.
   1.853 +            }
   1.854 +            if(vi.dwMajorVersion >= 6)
   1.855 +            {
   1.856 +                if(vi.dwMinorVersion >= 4)
   1.857 +                    name = "Windows 10";
   1.858 +                else if(vi.dwMinorVersion >= 3)
   1.859 +                {
   1.860 +                    if(vi.wProductType == VER_NT_WORKSTATION)
   1.861 +						name = "Windows 8.1";
   1.862 +					else
   1.863 +						name = "Windows Server 2012 R2";
   1.864 +                }
   1.865 +                else if(vi.dwMinorVersion >= 2)
   1.866 +                {
   1.867 +                    if(vi.wProductType == VER_NT_WORKSTATION)
   1.868 +						name = "Windows 8";
   1.869 +					else
   1.870 +						name = "Windows Server 2012";
   1.871 +                }
   1.872 +                else if(vi.dwMinorVersion >= 1)
   1.873 +                {
   1.874 +                    if(vi.wProductType == VER_NT_WORKSTATION)
   1.875 +                        name = "Windows 7";
   1.876 +                    else
   1.877 +                        name = "Windows Server 2008 R2";
   1.878 +                }
   1.879 +                else
   1.880 +                {
   1.881 +                    if(vi.wProductType == VER_NT_WORKSTATION)
   1.882 +                        name = "Windows Vista";
   1.883 +                    else
   1.884 +                        name = "Windows Server 2008";
   1.885 +                }
   1.886 +            }
   1.887 +            else if(vi.dwMajorVersion >= 5)
   1.888 +            {
   1.889 +                if(vi.dwMinorVersion == 0)
   1.890 +                    name = "Windows 2000";
   1.891 +                else if(vi.dwMinorVersion == 1)
   1.892 +                    name = "Windows XP";
   1.893 +                else // vi.dwMinorVersion == 2
   1.894 +                {
   1.895 +                    if(GetSystemMetrics(SM_SERVERR2) != 0)
   1.896 +                        name = "Windows Server 2003 R2";
   1.897 +                    else if(vi.wSuiteMask & VER_SUITE_WH_SERVER)
   1.898 +                        name = "Windows Home Server";
   1.899 +                    if(GetSystemMetrics(SM_SERVERR2) == 0)
   1.900 +                        name = "Windows Server 2003";
   1.901 +                    else
   1.902 +                        name = "Windows XP Professional x64 Edition";
   1.903 +                }
   1.904 +            }
   1.905 +            else
   1.906 +                name = "Windows 98 or earlier";
   1.907 +        }
   1.908 +
   1.909 +        OVR_strlcpy(versionName, name, versionNameCapacity);
   1.910 +
   1.911 +    #elif defined(OVR_OS_UNIX) || defined(OVR_OS_APPLE)
   1.912 +        utsname utsName;
   1.913 +        memset(&utsName, 0, sizeof(utsName));
   1.914 +
   1.915 +        if(uname(&utsName) == 0)
   1.916 +            OVR_snprintf(versionName, versionNameCapacity, "%s %s %s %s", utsName.sysname, utsName.release, utsName.version, utsName.machine);
   1.917 +        else
   1.918 +            OVR_snprintf(versionName, versionNameCapacity, "Unix");
   1.919 +    #endif
   1.920 +}
   1.921 +
   1.922 +
   1.923 +
   1.924 +
   1.925 +void CreateException(CreateExceptionType exceptionType)
   1.926 +{
   1.927 +    char buffer[1024] = {};
   1.928 +
   1.929 +    switch(exceptionType)
   1.930 +    {
   1.931 +        case kCETAccessViolation:
   1.932 +        {
   1.933 +            int* pNullPtr = reinterpret_cast<int*>((rand() / 2) / RAND_MAX);
   1.934 +            pNullPtr[0] = 0;    // This line should generate an exception.
   1.935 +            sprintf(buffer, "%p", pNullPtr);
   1.936 +            break;
   1.937 +        }
   1.938 +
   1.939 +        case kCETDivideByZero:
   1.940 +        {
   1.941 +            int smallValue = 1;
   1.942 +            int largeValue = (1000 * exceptionType);
   1.943 +            int divByZero = (smallValue / largeValue); // This line should generate a div/0 exception.
   1.944 +            sprintf(buffer, "%d", divByZero);
   1.945 +            break;
   1.946 +        }
   1.947 +
   1.948 +        case kCETIllegalInstruction:
   1.949 +        {
   1.950 +            #if defined(OVR_CPU_X86) || (defined(OVR_CPU_X86_64) && !defined(OVR_CC_MSVC)) // (if x86) or (if x64 and any computer but VC++)...
   1.951 +                #if defined(OVR_CC_MSVC)
   1.952 +                    __asm ud2
   1.953 +                #else // e.g. GCC
   1.954 +                    asm volatile("ud2");
   1.955 +                #endif
   1.956 +
   1.957 +            #elif defined(OVR_CPU_X86_64) && (defined(OVR_OS_MS) && defined(PAGE_EXECUTE_READWRITE))
   1.958 +                // VC++ for x64 doesn't support inline asm.
   1.959 +                void*   pVoid          = _AddressOfReturnAddress();
   1.960 +                void**  ppVoid         = reinterpret_cast<void**>(pVoid);
   1.961 +                void*   pReturnAddress = *ppVoid;
   1.962 +                DWORD   dwProtectPrev  = 0;
   1.963 +
   1.964 +                if(VirtualProtect(pReturnAddress, 2, PAGE_EXECUTE_READWRITE, &dwProtectPrev)) // If we can set the memory to be executable...
   1.965 +                {
   1.966 +                    // Modify the code we return to.
   1.967 +                    uint8_t asm_ud2[] = { 0x0f, 0x0b };
   1.968 +                    memcpy(pReturnAddress, asm_ud2, sizeof(asm_ud2));
   1.969 +                    VirtualProtect(pReturnAddress, 2, dwProtectPrev, &dwProtectPrev);
   1.970 +                }
   1.971 +                else
   1.972 +                {
   1.973 +                    // To do: Fix this.
   1.974 +                }
   1.975 +
   1.976 +            #else
   1.977 +                // To do: Fix this.
   1.978 +            #endif
   1.979 +
   1.980 +            break;
   1.981 +        }
   1.982 +
   1.983 +        case kCETStackCorruption:
   1.984 +        {
   1.985 +            size_t size = (sizeof(buffer) * 16) - (rand() % 16);
   1.986 +            char*  pOutsizeStack = buffer - ((sizeof(buffer) * 16) + (rand() % 16));
   1.987 +
   1.988 +            memset(buffer, 0, size); 
   1.989 +            memset(pOutsizeStack, 0, size); // This line should generate an exception, or an exception will be generated upon return from this function.
   1.990 +            break;
   1.991 +        }
   1.992 +
   1.993 +        case kCETStackOverflow:
   1.994 +        {
   1.995 +            CreateException(exceptionType);             // Call ourselves recursively. This line should generate a div/0 exception.
   1.996 +            sprintf(buffer, "%d", exceptionType);
   1.997 +            break;
   1.998 +        }
   1.999 +
  1.1000 +        case kCETAlignment:
  1.1001 +        {
  1.1002 +            // Not all platforms generate alignment exceptions. Some internally handle it.
  1.1003 +            void*     pAligned      = malloc(16);
  1.1004 +            char*     pMisaligned   = (char*)pAligned + 1;
  1.1005 +            uint64_t* pMisaligned64 = reinterpret_cast<uint64_t*>(pMisaligned);
  1.1006 +
  1.1007 +            *pMisaligned64 = 0; // This line should generate an exception.
  1.1008 +            free(pAligned);
  1.1009 +            break;
  1.1010 +        }
  1.1011 +
  1.1012 +        case kCETFPU:
  1.1013 +            // Platforms usually have FPU exceptions disabled. In order to test FPU exceptions we will need to at least
  1.1014 +            // temporarily disable them before executing code here to generate such exceptions.
  1.1015 +            // To do.
  1.1016 +            break;
  1.1017 +
  1.1018 +        case kCETTrap:
  1.1019 +            // To do. This is hardware-specific.
  1.1020 +            break;
  1.1021 +    }
  1.1022 +}
  1.1023 +
  1.1024 +
  1.1025 +
  1.1026 +
  1.1027 +#if defined(OVR_OS_MS)
  1.1028 +    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);
  1.1029 +    typedef PVOID   (WINAPI * SymFunctionTableAccess64Type)(HANDLE hProcess, DWORD64 dwAddr);
  1.1030 +    typedef DWORD64 (WINAPI * SymGetModuleBase64Type)(HANDLE hProcess, DWORD64 dwAddr);
  1.1031 +    typedef DWORD   (WINAPI * SymSetOptionsType)(DWORD SymOptions);
  1.1032 +    typedef BOOL    (WINAPI * SymInitializeWType)(HANDLE hProcess, PCWSTR UserSearchPath, BOOL fInvadeProcess);
  1.1033 +    typedef BOOL    (WINAPI * SymCleanupType)(HANDLE hProcess);
  1.1034 +    typedef DWORD64 (WINAPI * SymLoadModule64Type)(HANDLE hProcess, HANDLE hFile, PCSTR ImageName, PCSTR ModuleName, DWORD64 BaseOfDll, DWORD SizeOfDll);
  1.1035 +    typedef BOOL    (WINAPI * SymFromAddrType)(HANDLE hProcess, DWORD64 Address, PDWORD64 Displacement, PSYMBOL_INFO Symbol);
  1.1036 +    typedef BOOL    (WINAPI * SymGetLineFromAddr64Type)(HANDLE hProcess, DWORD64 qwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINE64 Line64);
  1.1037 +
  1.1038 +    StackWalk64Type              pStackWalk64;
  1.1039 +    SymFunctionTableAccess64Type pSymFunctionTableAccess64;
  1.1040 +    SymGetModuleBase64Type       pSymGetModuleBase64;
  1.1041 +    SymSetOptionsType            pSymSetOptions;
  1.1042 +    SymInitializeWType           pSymInitializeW;
  1.1043 +    SymCleanupType               pSymCleanup;
  1.1044 +    SymLoadModule64Type          pSymLoadModule64;
  1.1045 +    SymFromAddrType              pSymFromAddr;
  1.1046 +    SymGetLineFromAddr64Type     pSymGetLineFromAddr64;
  1.1047 +#endif
  1.1048 +
  1.1049 +
  1.1050 +
  1.1051 +SymbolLookup::SymbolLookup()
  1.1052 +  : initialized(false),
  1.1053 +    allowMemoryAllocation(true),
  1.1054 +    moduleListUpdated(false),
  1.1055 +    moduleInfoArray(),
  1.1056 +    moduleInfoArraySize(0)
  1.1057 +{
  1.1058 +}
  1.1059 +
  1.1060 +SymbolLookup::~SymbolLookup()
  1.1061 +{
  1.1062 +    Shutdown();
  1.1063 +}
  1.1064 +
  1.1065 +void SymbolLookup::AddSourceCodeDirectory(const char* pDirectory)
  1.1066 +{
  1.1067 +    OVR_UNUSED(pDirectory);
  1.1068 +}
  1.1069 +
  1.1070 +bool SymbolLookup::Initialize()
  1.1071 +{
  1.1072 +    if(!initialized)
  1.1073 +    {
  1.1074 +        #if defined(OVR_OS_MS)
  1.1075 +            // http://msdn.microsoft.com/en-us/library/windows/desktop/ms679294%28v=vs.85%29.aspx
  1.1076 +            HANDLE  hProcess = GetCurrentProcess();
  1.1077 +            HMODULE hDbgHelp = LoadLibraryW(L"DbgHelp.dll"); // It's best if the application supplies a recent version of this.
  1.1078 +
  1.1079 +            if(hDbgHelp)
  1.1080 +            {
  1.1081 +                pStackWalk64              = (StackWalk64Type)(uintptr_t)::GetProcAddress(hDbgHelp, "StackWalk64");
  1.1082 +                pSymFunctionTableAccess64 = (SymFunctionTableAccess64Type)(uintptr_t)::GetProcAddress(hDbgHelp, "SymFunctionTableAccess64");
  1.1083 +                pSymGetModuleBase64       = (SymGetModuleBase64Type)(uintptr_t)::GetProcAddress(hDbgHelp, "SymGetModuleBase64");
  1.1084 +                pSymSetOptions            = (SymSetOptionsType)(uintptr_t)::GetProcAddress(hDbgHelp, "SymSetOptions");
  1.1085 +                pSymInitializeW           = (SymInitializeWType)(uintptr_t)::GetProcAddress(hDbgHelp, "SymInitializeW");
  1.1086 +                pSymCleanup               = (SymCleanupType)(uintptr_t)::GetProcAddress(hDbgHelp, "SymCleanup");
  1.1087 +                pSymLoadModule64          = (SymLoadModule64Type)(uintptr_t)::GetProcAddress(hDbgHelp, "SymLoadModule64");
  1.1088 +                pSymFromAddr              = (SymFromAddrType)(uintptr_t)::GetProcAddress(hDbgHelp, "SymFromAddr");
  1.1089 +                pSymGetLineFromAddr64     = (SymGetLineFromAddr64Type)(uintptr_t)::GetProcAddress(hDbgHelp, "SymGetLineFromAddr64");
  1.1090 +            }
  1.1091 +
  1.1092 +            pSymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS);
  1.1093 +
  1.1094 +            // To consider: Use a manually created search path:
  1.1095 +            // wchar_t searchPathW[4096]; // Semicolon-separated strings.
  1.1096 +            //     The current working directory of the application.
  1.1097 +            //     The directory of the application itself (GetModuleFileName).
  1.1098 +            //     The _NT_SYMBOL_PATH environment variable.
  1.1099 +            //     The _NT_ALTERNATE_SYMBOL_PATH environment variable.
  1.1100 +
  1.1101 +            if(pSymInitializeW(hProcess, nullptr /*searchPathW*/, FALSE))
  1.1102 +            {
  1.1103 +                initialized = true;
  1.1104 +            }
  1.1105 +        #endif
  1.1106 +    }
  1.1107 +
  1.1108 +    return true;
  1.1109 +}
  1.1110 +
  1.1111 +void SymbolLookup::Shutdown()
  1.1112 +{
  1.1113 +    if(initialized)
  1.1114 +    {
  1.1115 +        initialized = false;
  1.1116 +
  1.1117 +        #if defined(OVR_OS_MS)
  1.1118 +            HANDLE hProcess = GetCurrentProcess();
  1.1119 +
  1.1120 +            // SymCleanup should handle this for us.
  1.1121 +            //if(moduleListUpdated)
  1.1122 +            //{
  1.1123 +            //    for(size_t i = 0; i < moduleInfoArraySize; i++)
  1.1124 +            //        pSymUnloadModule64(hProcess, moduleInfoArray[i].baseAddress);
  1.1125 +            //}
  1.1126 +
  1.1127 +            moduleInfoArraySize = 0;
  1.1128 +
  1.1129 +            pSymCleanup(hProcess);
  1.1130 +        #endif
  1.1131 +    }
  1.1132 +}
  1.1133 +
  1.1134 +
  1.1135 +void SymbolLookup::EnableMemoryAllocation(bool enabled)
  1.1136 +{
  1.1137 +    allowMemoryAllocation = enabled;
  1.1138 +}
  1.1139 +
  1.1140 +
  1.1141 +OVR_DISABLE_MSVC_WARNING(4740) // flow in or out of inline asm code suppresses global optimization
  1.1142 +OVR_DISABLE_MSVC_WARNING(4748) // /GS can not protect parameters and local variables from local buffer overrun because optimizations are disabled in function
  1.1143 +
  1.1144 +
  1.1145 +size_t SymbolLookup::GetBacktrace(void* addressArray[], size_t addressArrayCapacity, size_t skipCount, void* platformThreadContext, OVR::ThreadSysId threadSysIdHelp)
  1.1146 +{
  1.1147 +    #if defined(OVR_OS_WIN64) || (defined(OVR_OS_MS) && defined(OVR_OS_CONSOLE))
  1.1148 +        OVR_UNUSED(threadSysIdHelp);
  1.1149 +    
  1.1150 +        if(platformThreadContext == nullptr)
  1.1151 +            return RtlCaptureStackBackTrace(1, (ULONG)addressArrayCapacity, addressArray, nullptr);
  1.1152 +
  1.1153 +        // We need to get the call stack of another thread.
  1.1154 +        size_t            frameIndex = 0;
  1.1155 +        CONTEXT           context;
  1.1156 +        PRUNTIME_FUNCTION pRuntimeFunction;
  1.1157 +        ULONG64           imageBase = 0;
  1.1158 +        ULONG64           imageBasePrev = 0;
  1.1159 +
  1.1160 +        memcpy(&context, (CONTEXT*)platformThreadContext, sizeof(CONTEXT));
  1.1161 +        context.ContextFlags = CONTEXT_CONTROL;
  1.1162 +
  1.1163 +        if(context.Rip && (frameIndex < addressArrayCapacity))
  1.1164 +            addressArray[frameIndex++] = (void*)(uintptr_t)context.Rip;
  1.1165 +
  1.1166 +        while(context.Rip && (frameIndex < addressArrayCapacity))
  1.1167 +        {
  1.1168 +            imageBasePrev = imageBase;
  1.1169 +            pRuntimeFunction = (PRUNTIME_FUNCTION)RtlLookupFunctionEntry(context.Rip, &imageBase, nullptr);
  1.1170 +
  1.1171 +            if(pRuntimeFunction)
  1.1172 +            {
  1.1173 +                VOID*   handlerData = nullptr;
  1.1174 +                ULONG64 establisherFramePointers[2] = { 0, 0 };
  1.1175 +                RtlVirtualUnwind(UNW_FLAG_NHANDLER, imageBase, context.Rip, pRuntimeFunction, &context, &handlerData,  establisherFramePointers, nullptr);                        
  1.1176 +            }
  1.1177 +            else
  1.1178 +            {
  1.1179 +                context.Rip  = (ULONG64)(*(PULONG64)context.Rsp);
  1.1180 +                context.Rsp += 8;
  1.1181 +            }
  1.1182 +
  1.1183 +            if(context.Rip && (frameIndex < addressArrayCapacity))
  1.1184 +            {
  1.1185 +                if(skipCount)
  1.1186 +                    --skipCount;
  1.1187 +                else
  1.1188 +                    addressArray[frameIndex++] = (void*)(uintptr_t)context.Rip;
  1.1189 +            }
  1.1190 +        }
  1.1191 +
  1.1192 +        return frameIndex;
  1.1193 +
  1.1194 +    #elif defined(OVR_OS_WIN32)
  1.1195 +        OVR_UNUSED(threadSysIdHelp);
  1.1196 +    
  1.1197 +        size_t frameIndex = 0;
  1.1198 +
  1.1199 +        if(pStackWalk64)
  1.1200 +        {
  1.1201 +            CONTEXT context;
  1.1202 +
  1.1203 +            if(platformThreadContext)
  1.1204 +            {
  1.1205 +                memcpy(&context, platformThreadContext, sizeof(context));
  1.1206 +                context.ContextFlags = CONTEXT_CONTROL;
  1.1207 +            }
  1.1208 +            else
  1.1209 +            {
  1.1210 +                memset(&context, 0, sizeof(context));
  1.1211 +                context.ContextFlags = CONTEXT_CONTROL;
  1.1212 +
  1.1213 +                __asm {
  1.1214 +                    mov context.Ebp, EBP
  1.1215 +                    mov context.Esp, ESP
  1.1216 +                    call GetEIP
  1.1217 +                    GetEIP:
  1.1218 +                    pop context.Eip
  1.1219 +                }
  1.1220 +            }
  1.1221 +
  1.1222 +            STACKFRAME64 sf;
  1.1223 +            memset(&sf, 0, sizeof(sf));
  1.1224 +            sf.AddrPC.Offset     = context.Eip;
  1.1225 +            sf.AddrPC.Mode       = AddrModeFlat;
  1.1226 +            sf.AddrStack.Offset  = context.Esp;
  1.1227 +            sf.AddrStack.Mode    = AddrModeFlat;
  1.1228 +            sf.AddrFrame.Offset  = context.Ebp;
  1.1229 +            sf.AddrFrame.Mode    = AddrModeFlat;
  1.1230 +
  1.1231 +            const HANDLE hCurrentProcess = ::GetCurrentProcess();
  1.1232 +            const HANDLE hCurrentThread  = ::GetCurrentThread();
  1.1233 +
  1.1234 +            if(!platformThreadContext) // If we are reading the current thread's call stack then we ignore this current function.
  1.1235 +                skipCount++;
  1.1236 +
  1.1237 +            while(frameIndex < addressArrayCapacity)
  1.1238 +            {
  1.1239 +                if(!pStackWalk64(IMAGE_FILE_MACHINE_I386, hCurrentProcess, hCurrentThread, &sf, &context, nullptr, pSymFunctionTableAccess64, pSymGetModuleBase64, nullptr))
  1.1240 +                    break;
  1.1241 +
  1.1242 +                if(sf.AddrFrame.Offset == 0)
  1.1243 +                    break;
  1.1244 +
  1.1245 +                if(skipCount)
  1.1246 +                    --skipCount;
  1.1247 +                else
  1.1248 +                    addressArray[frameIndex++] = ((void*)(uintptr_t)sf.AddrPC.Offset);
  1.1249 +            }
  1.1250 +        }
  1.1251 +
  1.1252 +        return frameIndex;
  1.1253 +
  1.1254 +    #elif defined(OVR_OS_APPLE)
  1.1255 +        struct StackFrame
  1.1256 +        {
  1.1257 +            StackFrame* pParentStackFrame;
  1.1258 +            void*       pReturnPC;
  1.1259 +        };
  1.1260 +        
  1.1261 +        void*       pInstruction;
  1.1262 +        StackFrame* pStackFrame;
  1.1263 +        size_t      frameIndex = 0;
  1.1264 +
  1.1265 +        if(platformThreadContext)
  1.1266 +        {
  1.1267 +            #if defined(OVR_CPU_ARM)
  1.1268 +                arm_thread_state_t* pThreadState = (arm_thread_state_t*)platformThreadContext;
  1.1269 +                pStackFrame  = (StackFrame*)pThreadState->__fp;
  1.1270 +                pInstruction = (void*)      pThreadState->__pc;
  1.1271 +                #define FrameIsAligned(pStackFrame) ((((uintptr_t)pStackFrame) & 0x1) == 0)
  1.1272 +            #elif defined(OVR_CPU_X86_64)
  1.1273 +                x86_thread_state_t* pThreadState = (x86_thread_state_t*)platformThreadContext;
  1.1274 +                pInstruction = (void*)      pThreadState->uts.ts64.__rip;
  1.1275 +                pStackFrame  = (StackFrame*)pThreadState->uts.ts64.__rbp;
  1.1276 +                #define FrameIsAligned(pStackFrame) ((((uintptr_t)pStackFrame) & 0xf) == 0)
  1.1277 +            #elif defined(OVR_CPU_X86)
  1.1278 +                x86_thread_state_t* pThreadState = (x86_thread_state_t*)platformThreadContext;
  1.1279 +                pInstruction = (void*)      pThreadState->uts.ts32.__eip;
  1.1280 +                pStackFrame  = (StackFrame*)pThreadState->uts.ts32.__ebp;
  1.1281 +                #define FrameIsAligned(pStackFrame) ((((uintptr_t)pStackFrame) & 0xf) == 8)
  1.1282 +            #endif
  1.1283 +
  1.1284 +            if(frameIndex < addressArrayCapacity)
  1.1285 +                addressArray[frameIndex++] = pInstruction;
  1.1286 +        }
  1.1287 +        else // Else get the current values...
  1.1288 +        {
  1.1289 +            pStackFrame = (StackFrame*)__builtin_frame_address(0);
  1.1290 +            GetInstructionPointer(pInstruction);
  1.1291 +        }
  1.1292 +
  1.1293 +        pthread_t   threadSelf         = pthread_self();
  1.1294 +        void*       pCurrentStackBase  = pthread_get_stackaddr_np(threadSelf);
  1.1295 +        void*       pCurrentStackLimit = (void*)((uintptr_t)pCurrentStackBase - pthread_get_stacksize_np(threadSelf));
  1.1296 +        bool        threadIsCurrent    = (platformThreadContext == nullptr) || (((void*)pStackFrame > pCurrentStackLimit) && ((void*)pStackFrame <= pCurrentStackBase));
  1.1297 +        StackFrame* pStackBase;
  1.1298 +        StackFrame* pStackLimit;
  1.1299 +    
  1.1300 +        if(threadIsCurrent)
  1.1301 +        {
  1.1302 +            pStackBase  = (StackFrame*)pCurrentStackBase;
  1.1303 +            pStackLimit = (StackFrame*)pCurrentStackLimit;
  1.1304 +        }
  1.1305 +        else if(threadSysIdHelp)
  1.1306 +        {
  1.1307 +            pthread_t threadHandle = pthread_from_mach_thread_np((mach_port_t)threadSysIdHelp);
  1.1308 +            pStackBase  = (StackFrame*)pthread_get_stackaddr_np(threadHandle);
  1.1309 +            pStackLimit = (StackFrame*)((uintptr_t)pStackBase - pthread_get_stacksize_np(threadHandle));
  1.1310 +        }
  1.1311 +        else
  1.1312 +        {   // We guess what the limits are.
  1.1313 +            pStackBase  = pStackFrame + ((384 * 1024) / sizeof(StackFrame));
  1.1314 +            pStackLimit = pStackFrame - ((384 * 1024) / sizeof(StackFrame));
  1.1315 +        }
  1.1316 +
  1.1317 +        if((frameIndex < addressArrayCapacity) && pStackFrame && FrameIsAligned(pStackFrame))
  1.1318 +        {
  1.1319 +            addressArray[frameIndex++] = pStackFrame->pReturnPC;
  1.1320 +
  1.1321 +            while(pStackFrame && pStackFrame->pReturnPC && (frameIndex < addressArrayCapacity))
  1.1322 +            {
  1.1323 +                pStackFrame = pStackFrame->pParentStackFrame;
  1.1324 +
  1.1325 +                if(pStackFrame && FrameIsAligned(pStackFrame) && pStackFrame->pReturnPC && (pStackFrame > pStackLimit) && (pStackFrame < pStackBase))
  1.1326 +                {
  1.1327 +                    if(skipCount)
  1.1328 +                        --skipCount;
  1.1329 +                    else
  1.1330 +                        addressArray[frameIndex++] = pStackFrame->pReturnPC;
  1.1331 +                }
  1.1332 +                else
  1.1333 +                    break;
  1.1334 +            }
  1.1335 +        }
  1.1336 +
  1.1337 +        return frameIndex;
  1.1338 +
  1.1339 +    #elif defined(OVR_OS_LINUX) && (defined( __LIBUNWIND__) || defined(LIBUNWIND_AVAIL))
  1.1340 +        // Libunwind-based solution. Requires installation of libunwind package.
  1.1341 +        // Libunwind isn't always safe for threads that are in signal handlers.
  1.1342 +        // An approach to get the callstack of another thread is to use signal injection into the target thread.
  1.1343 +    
  1.1344 +        OVR_UNUSED(platformThreadContext);
  1.1345 +        OVR_UNUSED(threadSysIdHelp);
  1.1346 +    
  1.1347 +        size_t        frameIndex = 0;
  1.1348 +        unw_cursor_t  cursor;
  1.1349 +        unw_context_t uc;
  1.1350 +        unw_word_t    ip, sp;
  1.1351 +
  1.1352 +        unw_getcontext(&uc);            // This gets the current thread's context. We could alternatively initialize another thread's context with it.
  1.1353 +        unw_init_local(&cursor, &uc);
  1.1354 +    
  1.1355 +        while((unw_step(&cursor) > 0) && (frameIndex < addressArrayCapacity))
  1.1356 +        {
  1.1357 +            // We can get the function name here too on some platforms with unw_get_proc_info() and unw_get_proc_name().
  1.1358 +            
  1.1359 +            if(skipCount)
  1.1360 +                --skipCount;
  1.1361 +            else
  1.1362 +            {
  1.1363 +                unw_get_reg(&cursor, UNW_REG_IP, &ip);
  1.1364 +                addressArray[frameIndex++] = (void*)ip;
  1.1365 +            }
  1.1366 +        }
  1.1367 +    
  1.1368 +        return frameIndex;
  1.1369 +	#else
  1.1370 +        OVR_UNUSED(addressArray);
  1.1371 +        OVR_UNUSED(addressArrayCapacity);
  1.1372 +        OVR_UNUSED(skipCount);
  1.1373 +        OVR_UNUSED(platformThreadContext);
  1.1374 +        OVR_UNUSED(threadSysIdHelp);
  1.1375 +
  1.1376 +        return 0;
  1.1377 +    #endif
  1.1378 +}
  1.1379 +
  1.1380 +
  1.1381 +size_t SymbolLookup::GetBacktraceFromThreadHandle(void* addressArray[], size_t addressArrayCapacity, size_t skipCount, OVR::ThreadHandle threadHandle)
  1.1382 +{
  1.1383 +    #if defined(OVR_OS_MS)
  1.1384 +        size_t count = 0;
  1.1385 +        DWORD  threadSysId = (DWORD)ConvertThreadHandleToThreadSysId(threadHandle);
  1.1386 +
  1.1387 +        // Compare to 0, compare to the self 'pseudohandle' and compare to the self id.
  1.1388 +        if((threadHandle == OVR_THREADHANDLE_INVALID) || (threadHandle == ::GetCurrentThread()) || (threadSysId == ::GetCurrentThreadId())) // If threadSysId refers to the current thread...
  1.1389 +            return GetBacktrace(addressArray, addressArrayCapacity, skipCount, nullptr);
  1.1390 +
  1.1391 +        // We are working with another thread. We need to suspend it and get its CONTEXT.
  1.1392 +        // Suspending other threads is risky, as they may be in some state that cannot be safely blocked.
  1.1393 +        BOOL  result = false;
  1.1394 +        DWORD suspendResult = ::SuspendThread(threadHandle); // Requires that the handle have THREAD_SUSPEND_RESUME rights.
  1.1395 +
  1.1396 +        if(suspendResult != (DWORD)-1) // Returns previous suspend count, or -1 if failed.
  1.1397 +        {
  1.1398 +            CONTEXT context;
  1.1399 +            context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; // Requires that the handle have THREAD_GET_CONTEXT rights.
  1.1400 +            result = ::GetThreadContext(threadHandle, &context);
  1.1401 +            count = GetBacktrace(addressArray, addressArrayCapacity, skipCount, &context);
  1.1402 +            suspendResult = ::ResumeThread(threadHandle);
  1.1403 +            OVR_ASSERT_AND_UNUSED(suspendResult != (DWORD)-1, suspendResult);
  1.1404 +        }
  1.1405 +
  1.1406 +        return count;
  1.1407 +
  1.1408 +    #elif defined(OVR_OS_APPLE)
  1.1409 +        mach_port_t threadSysID = pthread_mach_thread_np((pthread_t)threadHandle); // Convert pthread_t to mach thread id.
  1.1410 +        return GetBacktraceFromThreadSysId(addressArray, addressArrayCapacity, skipCount, (OVR::ThreadSysId)threadSysID);
  1.1411 +        
  1.1412 +    #elif defined(OVR_OS_LINUX)
  1.1413 +        // To do.
  1.1414 +        OVR_UNUSED(addressArray);
  1.1415 +        OVR_UNUSED(addressArrayCapacity);
  1.1416 +        OVR_UNUSED(skipCount);
  1.1417 +        OVR_UNUSED(threadHandle);
  1.1418 +        return 0;
  1.1419 +    #endif
  1.1420 +}
  1.1421 +
  1.1422 +
  1.1423 +size_t SymbolLookup::GetBacktraceFromThreadSysId(void* addressArray[], size_t addressArrayCapacity, size_t skipCount, OVR::ThreadSysId threadSysId)
  1.1424 +{
  1.1425 +    #if defined(OVR_OS_MS)
  1.1426 +        OVR::ThreadHandle threadHandle = ConvertThreadSysIdToThreadHandle(threadSysId);
  1.1427 +        if(threadHandle)
  1.1428 +        {
  1.1429 +            size_t count = GetBacktraceFromThreadHandle(addressArray, addressArrayCapacity, skipCount, threadHandle);
  1.1430 +            FreeThreadHandle(threadHandle);
  1.1431 +            return count;
  1.1432 +        }
  1.1433 +        return 0;
  1.1434 +
  1.1435 +    #elif defined(OVR_OS_APPLE)
  1.1436 +        mach_port_t threadCurrent = pthread_mach_thread_np(pthread_self());
  1.1437 +        mach_port_t thread = (mach_port_t)threadSysId;
  1.1438 +    
  1.1439 +        if(thread == threadCurrent)
  1.1440 +        {
  1.1441 +            return GetBacktrace(addressArray, addressArrayCapacity, skipCount, nullptr);
  1.1442 +        }
  1.1443 +        else
  1.1444 +        {
  1.1445 +            kern_return_t result = thread_suspend(thread);    // Do we need to do this if it's an thread who exception is being handled?
  1.1446 +            size_t        count  = 0;
  1.1447 +
  1.1448 +            if(result == KERN_SUCCESS)
  1.1449 +            {
  1.1450 +                #if defined(OVR_CPU_X86) || defined(OVR_CPU_X86_64)
  1.1451 +                    x86_thread_state_t threadState;
  1.1452 +                #elif defined(OVR_CPU_ARM)
  1.1453 +                    arm_thread_state_t threadState;
  1.1454 +                #endif
  1.1455 +                mach_msg_type_number_t stateCount = MACHINE_THREAD_STATE_COUNT;
  1.1456 +
  1.1457 +                result = thread_get_state(thread, MACHINE_THREAD_STATE, (natural_t*)(uintptr_t)&threadState, &stateCount);
  1.1458 +
  1.1459 +                if(result == KERN_SUCCESS)
  1.1460 +                    count = GetBacktrace(addressArray, addressArrayCapacity, skipCount, &threadState, threadSysId);
  1.1461 +
  1.1462 +                thread_resume(thread);
  1.1463 +                
  1.1464 +                return count;
  1.1465 +            }
  1.1466 +        }
  1.1467 +    
  1.1468 +        return 0;
  1.1469 +    
  1.1470 +    #elif defined(OVR_OS_LINUX)
  1.1471 +        // To do.
  1.1472 +        OVR_UNUSED(addressArray);
  1.1473 +        OVR_UNUSED(addressArrayCapacity);
  1.1474 +        OVR_UNUSED(skipCount);
  1.1475 +        OVR_UNUSED(threadSysId);
  1.1476 +        return 0;
  1.1477 +    #endif
  1.1478 +}
  1.1479 +
  1.1480 +
  1.1481 +// We need to return the required moduleInfoArrayCapacity.
  1.1482 +size_t SymbolLookup::GetModuleInfoArray(ModuleInfo* pModuleInfoArray, size_t moduleInfoArrayCapacity)
  1.1483 +{
  1.1484 +    #if defined(OVR_OS_MS)
  1.1485 +        size_t       moduleCountRequired = 0;         // The count we would copy to pModuleInfoArray if moduleInfoArrayCapacity was enough.
  1.1486 +        size_t       moduleCount = 0;                 // The count we actually copy to pModuleInfoArray. Will be <= moduleInfoArrayCapacity.
  1.1487 +        HANDLE       hProcess = GetCurrentProcess();
  1.1488 +        HMODULE      hModuleArray[200];
  1.1489 +        DWORD        cbNeeded = 0;
  1.1490 +        MODULEINFO   mi;
  1.1491 +
  1.1492 +        if(EnumProcessModules(hProcess, hModuleArray, sizeof(hModuleArray), &cbNeeded))
  1.1493 +        {
  1.1494 +            moduleCountRequired = ((cbNeeded / sizeof(HMODULE)) < OVR_ARRAY_COUNT(hModuleArray)) ? (cbNeeded / sizeof(HMODULE)) : OVR_ARRAY_COUNT(hModuleArray);
  1.1495 +            moduleCount         = MIN(moduleCountRequired, OVR_ARRAY_COUNT(hModuleArray));
  1.1496 +            moduleCount         = MIN(moduleCount, moduleInfoArrayCapacity);
  1.1497 +
  1.1498 +            for(size_t i = 0; i < moduleCount; i++)
  1.1499 +            {
  1.1500 +                ModuleInfo& moduleInfo = pModuleInfoArray[i];
  1.1501 +
  1.1502 +                memset(&mi, 0, sizeof(mi));
  1.1503 +                BOOL result = GetModuleInformation(hProcess, hModuleArray[i], &mi, sizeof(mi));
  1.1504 +
  1.1505 +                if(result)
  1.1506 +                {
  1.1507 +                    wchar_t pathW[MAX_PATH];
  1.1508 +                    char    pathA[MAX_PATH * 3]; // *3 to handle UTF8 multibyte encoding.
  1.1509 +
  1.1510 +                    moduleInfo.handle      = hModuleArray[i];
  1.1511 +                    moduleInfo.baseAddress = (uintptr_t)mi.lpBaseOfDll;
  1.1512 +                    moduleInfo.size        = mi.SizeOfImage;
  1.1513 +
  1.1514 +                    GetModuleFileNameW(hModuleArray[i], pathW, OVR_ARRAY_COUNT(pathW));
  1.1515 +                    OVR::UTF8Util::EncodeString(pathA, pathW, -1); // Problem: DecodeString provides no way to specify the destination capacity.
  1.1516 +                    OVR::OVR_strlcpy(moduleInfo.filePath, pathA, OVR_ARRAY_COUNT(moduleInfo.filePath));
  1.1517 +
  1.1518 +                    const char* fileName = GetFileNameFromPath(pathA);
  1.1519 +                    OVR::OVR_strlcpy(moduleInfo.name, fileName, OVR_ARRAY_COUNT(moduleInfo.name));
  1.1520 +                }
  1.1521 +                else
  1.1522 +                {
  1.1523 +                    moduleInfo.handle = 0;
  1.1524 +                    moduleInfo.baseAddress = 0;
  1.1525 +                    moduleInfo.size = 0;
  1.1526 +                    moduleInfo.filePath[0] = 0;
  1.1527 +                    moduleInfo.name[0] = 0;
  1.1528 +                }
  1.1529 +            }
  1.1530 +        }
  1.1531 +
  1.1532 +        return moduleCountRequired;
  1.1533 +    
  1.1534 +    #elif defined(OVR_OS_MAC)
  1.1535 +        size_t moduleCountRequired = 0;
  1.1536 +        size_t moduleCount = 0;
  1.1537 +    
  1.1538 +        struct MacModuleInfo // This struct exists solely so we can have a local function within this function..
  1.1539 +        {
  1.1540 +            static void AddMacModuleInfo(ModuleInfo* pModuleInfoArrayL, size_t& moduleCountRequiredL, size_t& moduleCountL, size_t moduleInfoArrayCapacityL,
  1.1541 +                                        const char* pTypeFilterL, const char* pModulePath, uintptr_t currentSegmentPos, const MachHeader* pMachHeader, uint64_t offset)
  1.1542 +            {
  1.1543 +                for(size_t i = 0; i < pMachHeader->ncmds; i++)
  1.1544 +                {
  1.1545 +                    const SegmentCommand* pSegmentCommand = reinterpret_cast<const SegmentCommand*>(currentSegmentPos);
  1.1546 +                    
  1.1547 +                    if(pSegmentCommand->cmd == kLCSegment)
  1.1548 +                    {
  1.1549 +                        const size_t segnameSize = (sizeof(pSegmentCommand->segname) + 1); // +1 so we can have a trailing '\0'.
  1.1550 +                        char segname[segnameSize];
  1.1551 +                        
  1.1552 +                        memcpy(segname, pSegmentCommand->segname, sizeof(pSegmentCommand->segname));
  1.1553 +                        segname[segnameSize - 1] = '\0';
  1.1554 +                        
  1.1555 +                        if(!pTypeFilterL || OVR_strncmp(segname, pTypeFilterL, sizeof(segname)))
  1.1556 +                        {
  1.1557 +                            moduleCountRequiredL++;
  1.1558 +                            
  1.1559 +                            if(moduleCountL < moduleInfoArrayCapacityL)
  1.1560 +                            {
  1.1561 +                                ModuleInfo& info = pModuleInfoArrayL[moduleCountL++];
  1.1562 +                                
  1.1563 +                                info.baseAddress = (uint64_t)(pSegmentCommand->vmaddr + offset);
  1.1564 +                                info.handle      = reinterpret_cast<ModuleHandle>((uintptr_t)info.baseAddress);
  1.1565 +                                info.size        = (uint64_t)pSegmentCommand->vmsize;
  1.1566 +                                OVR_strlcpy(info.filePath, pModulePath, OVR_ARRAY_COUNT(info.filePath));
  1.1567 +                                OVR_strlcpy(info.name, GetFileNameFromPath(pModulePath), OVR_ARRAY_COUNT(info.name));
  1.1568 +                                
  1.1569 +                                info.permissions[0] = (pSegmentCommand->initprot & VM_PROT_READ)    ? 'r' : '-';
  1.1570 +                                info.permissions[1] = (pSegmentCommand->initprot & VM_PROT_WRITE)   ? 'w' : '-';
  1.1571 +                                info.permissions[2] = (pSegmentCommand->initprot & VM_PROT_EXECUTE) ? 'x' : '-';
  1.1572 +                                info.permissions[3] = '/';
  1.1573 +                                info.permissions[4] = (pSegmentCommand->maxprot  & VM_PROT_READ)    ? 'r' : '-';
  1.1574 +                                info.permissions[5] = (pSegmentCommand->maxprot  & VM_PROT_WRITE)   ? 'w' : '-';
  1.1575 +                                info.permissions[6] = (pSegmentCommand->maxprot  & VM_PROT_EXECUTE) ? 'x' : '-';
  1.1576 +                                info.permissions[7] = '\0';
  1.1577 +                                
  1.1578 +                                OVR_strlcpy(info.type, pSegmentCommand->segname, OVR_ARRAY_COUNT(info.type));
  1.1579 +                            }
  1.1580 +                        }
  1.1581 +                    }
  1.1582 +                    
  1.1583 +                    currentSegmentPos += pSegmentCommand->cmdsize;
  1.1584 +                }
  1.1585 +            }
  1.1586 +        };
  1.1587 +
  1.1588 +        // Iterate dyld_all_image_infos->infoArray
  1.1589 +        const struct dyld_all_image_infos* pAllImageInfos = _dyld_get_all_image_infos();
  1.1590 +        
  1.1591 +        for(uint32_t i = 0; i < pAllImageInfos->infoArrayCount; i++)
  1.1592 +        {
  1.1593 +            const char* pModulePath = pAllImageInfos->infoArray[i].imageFilePath;
  1.1594 +            
  1.1595 +            if(pModulePath && *pModulePath)
  1.1596 +            {
  1.1597 +                uintptr_t         currentSegmentPos = (uintptr_t)pAllImageInfos->infoArray[i].imageLoadAddress;
  1.1598 +                const MachHeader* pMachHeader       = reinterpret_cast<const MachHeader*>(currentSegmentPos);
  1.1599 +                uint64_t          offset            = (uint64_t)_dyld_get_image_vmaddr_slide(i);
  1.1600 +
  1.1601 +                currentSegmentPos += sizeof(*pMachHeader);
  1.1602 +
  1.1603 +                MacModuleInfo::AddMacModuleInfo(pModuleInfoArray, moduleCountRequired, moduleCount, moduleInfoArrayCapacity,
  1.1604 +                                                nullptr /*"__TEXT"*/, pModulePath, currentSegmentPos, pMachHeader, offset);
  1.1605 +            }
  1.1606 +        }
  1.1607 +
  1.1608 +        // In addition to iterating dyld_all_image_infos->infoArray we need to also iterate /usr/lib/dyld entries.
  1.1609 +        const MachHeader* pMachHeader = (const MachHeader*)pAllImageInfos->dyldImageLoadAddress;
  1.1610 +        uintptr_t         currentSegmentPos = (uintptr_t)pMachHeader + sizeof(*pMachHeader);
  1.1611 +        char              modulePath[OVR_MAX_PATH] = "";
  1.1612 +        pid_t             pid = getpid();
  1.1613 +        int               filenameLen = proc_regionfilename((int)pid, currentSegmentPos, modulePath, (uint32_t)sizeof(modulePath));
  1.1614 +    
  1.1615 +        if(filenameLen > 0)
  1.1616 +            MacModuleInfo::AddMacModuleInfo(pModuleInfoArray, moduleCountRequired, moduleCount, moduleInfoArrayCapacity,
  1.1617 +                                                "__TEXT", modulePath, currentSegmentPos, pMachHeader, 0);
  1.1618 +
  1.1619 +        return moduleCountRequired;
  1.1620 +    
  1.1621 +    #elif defined(EA_PLATFORM_LINUX)
  1.1622 +        // One approach is to read /proc/self/maps, which is supported by Linux (though not BSD).
  1.1623 +        // Linux glibc dladdr() can tell us what module an arbitrary function address comes from, but can't tell us the list of modules.
  1.1624 +        OVR_UNUSED(pModuleInfoArray);
  1.1625 +        OVR_UNUSED(moduleInfoArrayCapacity);
  1.1626 +        return 0;
  1.1627 +
  1.1628 +    #else
  1.1629 +        OVR_UNUSED(pModuleInfoArray);
  1.1630 +        OVR_UNUSED(moduleInfoArrayCapacity);
  1.1631 +        return 0;
  1.1632 +    #endif
  1.1633 +}
  1.1634 +
  1.1635 +
  1.1636 +size_t SymbolLookup::GetThreadList(ThreadHandle* threadHandleArray, ThreadSysId* threadSysIdArray, size_t threadArrayCapacity)
  1.1637 +{
  1.1638 +    size_t countRequired = 0;
  1.1639 +    size_t count = 0;
  1.1640 +
  1.1641 +    #if defined(OVR_OS_MS)
  1.1642 +        // Print a list of threads.
  1.1643 +        DWORD  currentProcessId = GetCurrentProcessId();
  1.1644 +        HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, currentProcessId); // ICreateToolhelp32Snapshot actually ignores currentProcessId.
  1.1645 +
  1.1646 +        if(hThreadSnap != INVALID_HANDLE_VALUE) 
  1.1647 +        {
  1.1648 +            THREADENTRY32 te32;
  1.1649 +            te32.dwSize = sizeof(THREADENTRY32); 
  1.1650 +
  1.1651 +            if(Thread32First(hThreadSnap, &te32)) 
  1.1652 +            {
  1.1653 +                do
  1.1654 +                {
  1.1655 +                    if(te32.th32OwnerProcessID == currentProcessId)
  1.1656 +                    {
  1.1657 +                        HANDLE hThread = ConvertThreadSysIdToThreadHandle(te32.th32ThreadID);
  1.1658 +
  1.1659 +                        if(hThread)
  1.1660 +                        {
  1.1661 +                            ++countRequired;
  1.1662 +
  1.1663 +                            if((threadHandleArray || threadSysIdArray) && (count < threadArrayCapacity))
  1.1664 +                            {
  1.1665 +                                if(threadHandleArray)
  1.1666 +                                    threadHandleArray[count] = hThread; // The caller must call CloseHandle on this thread, or call DoneThreadList on the returned array.
  1.1667 +                                if(threadSysIdArray)
  1.1668 +                                    threadSysIdArray[count] = ConvertThreadHandleToThreadSysId(hThread);
  1.1669 +                                ++count;
  1.1670 +                            }
  1.1671 +
  1.1672 +                            if(!threadHandleArray) // If we aren't giving this back to the user...
  1.1673 +                                FreeThreadHandle(hThread);
  1.1674 +                        }
  1.1675 +                    }
  1.1676 +                } while(Thread32Next(hThreadSnap, &te32));
  1.1677 +            }
  1.1678 +            
  1.1679 +            CloseHandle(hThreadSnap);
  1.1680 +        }
  1.1681 +
  1.1682 +    #elif defined(OVR_OS_APPLE)
  1.1683 +        mach_port_t             taskSelf   = mach_task_self();
  1.1684 +        thread_act_port_array_t threadArray;
  1.1685 +        mach_msg_type_number_t  threadCount;
  1.1686 +        
  1.1687 +        kern_return_t result = task_threads(taskSelf, &threadArray, &threadCount);
  1.1688 +        
  1.1689 +        if(result == KERN_SUCCESS)
  1.1690 +        {
  1.1691 +            for(mach_msg_type_number_t i = 0; i < threadCount; i++)
  1.1692 +            {
  1.1693 +                ++countRequired;
  1.1694 +
  1.1695 +                if((threadHandleArray || threadSysIdArray) && (count < threadArrayCapacity))
  1.1696 +                {
  1.1697 +                    if(threadHandleArray)
  1.1698 +                        threadHandleArray[count] = pthread_from_mach_thread_np(threadArray[i]);
  1.1699 +                    if(threadSysIdArray)
  1.1700 +                        threadSysIdArray[count] = threadArray[i];
  1.1701 +                    ++count;
  1.1702 +                }
  1.1703 +            }
  1.1704 +            
  1.1705 +            vm_deallocate(taskSelf, (vm_address_t)threadArray, threadCount * sizeof(thread_act_t));
  1.1706 +        }
  1.1707 +
  1.1708 +    #elif defined(OVR_OS_LINUX)
  1.1709 +        // To do.
  1.1710 +        OVR_UNUSED(count);
  1.1711 +        OVR_UNUSED(threadHandleArray);
  1.1712 +        OVR_UNUSED(threadSysIdArray);
  1.1713 +        OVR_UNUSED(threadArrayCapacity);
  1.1714 +    #endif
  1.1715 +
  1.1716 +    return countRequired;
  1.1717 +}
  1.1718 +
  1.1719 +
  1.1720 +void SymbolLookup::DoneThreadList(ThreadHandle* threadHandleArray, ThreadSysId* threadSysIdArray, size_t threadArrayCount)
  1.1721 +{
  1.1722 +    #if defined(OVR_OS_MS)
  1.1723 +        for(size_t i = 0; i != threadArrayCount; ++i)
  1.1724 +        {
  1.1725 +            if(threadHandleArray[i])
  1.1726 +            {
  1.1727 +                CloseHandle(threadHandleArray[i]);
  1.1728 +                threadHandleArray[i] = OVR_THREADHANDLE_INVALID;
  1.1729 +            }
  1.1730 +        }
  1.1731 +
  1.1732 +        OVR_UNUSED(threadSysIdArray);
  1.1733 +    #else
  1.1734 +        OVR_UNUSED(threadHandleArray);
  1.1735 +        OVR_UNUSED(threadSysIdArray);
  1.1736 +        OVR_UNUSED(threadArrayCount);
  1.1737 +    #endif
  1.1738 +}
  1.1739 +
  1.1740 +
  1.1741 +// Writes a given thread's callstack wity symbols to the given output.
  1.1742 +// It may not be safe to call this from an exception handler, as sOutput allocates memory.
  1.1743 +bool SymbolLookup::ReportThreadCallstack(OVR::String& sOutput, size_t skipCount, ThreadSysId threadSysId)
  1.1744 +{
  1.1745 +    if(!threadSysId)
  1.1746 +        threadSysId = GetCurrentThreadSysId();
  1.1747 +
  1.1748 +    void*  addressArray[64];
  1.1749 +    size_t addressCount = GetBacktraceFromThreadSysId(addressArray, OVR_ARRAY_COUNT(addressArray), skipCount, threadSysId);
  1.1750 +
  1.1751 +    // Print the header
  1.1752 +    char         headerBuffer[256];
  1.1753 +    char         threadName[32];
  1.1754 +    char         threadHandleStr[24];
  1.1755 +    char         threadSysIdStr[24];
  1.1756 +    char         stackBaseStr[24];
  1.1757 +    char         stackLimitStr[24];
  1.1758 +    void*        pStackBase;
  1.1759 +    void*        pStackLimit;
  1.1760 +  //void*        pStackCurrent;  // Current stack pointer. To do: support reporting this.
  1.1761 +    ThreadHandle threadHandle = ConvertThreadSysIdToThreadHandle(threadSysId);
  1.1762 +    OVR::GetThreadStackBounds(pStackBase, pStackLimit, threadHandle);
  1.1763 +
  1.1764 +    Thread::GetThreadName(threadName, OVR_ARRAY_COUNT(threadName), threadName);
  1.1765 +    SprintfThreadHandle(threadHandleStr, OVR_ARRAY_COUNT(threadHandleStr), threadHandle);
  1.1766 +    SprintfThreadSysId(threadSysIdStr, OVR_ARRAY_COUNT(threadSysIdStr), threadSysId);
  1.1767 +    SprintfAddress(stackBaseStr, OVR_ARRAY_COUNT(stackBaseStr), pStackBase);
  1.1768 +    SprintfAddress(stackLimitStr, OVR_ARRAY_COUNT(stackLimitStr), pStackLimit);
  1.1769 +
  1.1770 +    if(threadName[0])
  1.1771 +        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);
  1.1772 +    else
  1.1773 +        OVR_snprintf(headerBuffer, OVR_ARRAY_COUNT(headerBuffer), "Thread handle: %s, id: %s, stack base: %s, stack limit: %s\r\n", threadHandleStr, threadSysIdStr, stackBaseStr, stackLimitStr);
  1.1774 +
  1.1775 +    sOutput += headerBuffer;
  1.1776 +
  1.1777 +    // Print the backtrace info
  1.1778 +    char        backtraceBuffer[1024];  // Sometimes function symbol names are very long.
  1.1779 +    SymbolInfo  symbolInfo;
  1.1780 +    const char* pModuleName;
  1.1781 +
  1.1782 +    if(addressCount == 0)
  1.1783 +    {
  1.1784 +        sOutput += "<Unable to read backtrace>\r\n";
  1.1785 +    }
  1.1786 +    else
  1.1787 +    {
  1.1788 +        for(size_t i = 0; i < addressCount; ++i)
  1.1789 +        {
  1.1790 +            LookupSymbol((uint64_t)addressArray[i], symbolInfo);
  1.1791 +
  1.1792 +            if(symbolInfo.pModuleInfo && symbolInfo.pModuleInfo->name[0])
  1.1793 +                pModuleName = symbolInfo.pModuleInfo->name;
  1.1794 +            else
  1.1795 +                pModuleName = "(unknown module)";
  1.1796 +
  1.1797 +            char addressStr[24];
  1.1798 +            SprintfAddress(addressStr, OVR_ARRAY_COUNT(addressStr), addressArray[i]);
  1.1799 +
  1.1800 +            if(symbolInfo.filePath[0])
  1.1801 +                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);
  1.1802 +            else
  1.1803 +                OVR_snprintf(backtraceBuffer, OVR_ARRAY_COUNT(backtraceBuffer), "%-2u %-24s %s %s+%d\r\n", (unsigned)i, pModuleName, addressStr, symbolInfo.function, symbolInfo.functionOffset);
  1.1804 +
  1.1805 +            sOutput += backtraceBuffer;
  1.1806 +        }
  1.1807 +    }
  1.1808 +
  1.1809 +    FreeThreadHandle(threadHandle);
  1.1810 +
  1.1811 +    return (addressCount > 0);
  1.1812 +}
  1.1813 +
  1.1814 +
  1.1815 +// Writes all thread's callstacks with symbols to the given output.
  1.1816 +// It may not be safe to call this from an exception handler, as sOutput allocates memory.
  1.1817 +bool SymbolLookup::ReportThreadCallstacks(OVR::String& sOutput, size_t skipCount)
  1.1818 +{
  1.1819 +    ThreadSysId threadSysIdArray[64];
  1.1820 +    size_t      threadSysIdCount = GetThreadList(nullptr, threadSysIdArray, OVR_ARRAY_COUNT(threadSysIdArray));
  1.1821 +
  1.1822 +    if(threadSysIdCount > OVR_ARRAY_COUNT(threadSysIdArray))
  1.1823 +        threadSysIdCount = OVR_ARRAY_COUNT(threadSysIdArray);
  1.1824 +
  1.1825 +    for(size_t i = 0; i < threadSysIdCount; i++)
  1.1826 +    {
  1.1827 +        String sTemp;
  1.1828 +        ReportThreadCallstack(sTemp, skipCount, threadSysIdArray[i]);
  1.1829 +        if(i > 0)
  1.1830 +            sOutput += "\r\n";
  1.1831 +        sOutput += sTemp;
  1.1832 +    }
  1.1833 +
  1.1834 +    return (threadSysIdCount > 0);
  1.1835 +}
  1.1836 +
  1.1837 +
  1.1838 +bool SymbolLookup::RefreshModuleList()
  1.1839 +{
  1.1840 +    if(!moduleListUpdated)
  1.1841 +    {
  1.1842 +        #if defined(OVR_OS_MS)
  1.1843 +            // We can't rely on SymRefreshModuleList because it's present in DbgHelp 6.5, 
  1.1844 +            // which doesn't distribute with Windows 7.
  1.1845 +
  1.1846 +            // Currently we support only refreshing the list once ever. With a little effort we could revise this code to 
  1.1847 +            // support re-refreshing the list at runtime to account for the possibility that modules have recently been
  1.1848 +            // added or removed.
  1.1849 +            if(pSymLoadModule64)
  1.1850 +            {
  1.1851 +                const size_t requiredCount = GetModuleInfoArray(moduleInfoArray, OVR_ARRAY_COUNT(moduleInfoArray));
  1.1852 +                moduleInfoArraySize = MIN(requiredCount, OVR_ARRAY_COUNT(moduleInfoArray));
  1.1853 +
  1.1854 +                HANDLE hProcess = GetCurrentProcess();
  1.1855 +
  1.1856 +                for(size_t i = 0; i < moduleInfoArraySize; i++)
  1.1857 +                    pSymLoadModule64(hProcess, nullptr, moduleInfoArray[i].filePath, nullptr, moduleInfoArray[i].baseAddress, (DWORD)moduleInfoArray[i].size);
  1.1858 +
  1.1859 +                moduleListUpdated = true;
  1.1860 +            }
  1.1861 +        #else
  1.1862 +            const size_t requiredCount = GetModuleInfoArray(moduleInfoArray, OVR_ARRAY_COUNT(moduleInfoArray));
  1.1863 +            moduleInfoArraySize = MIN(requiredCount, OVR_ARRAY_COUNT(moduleInfoArray));
  1.1864 +            moduleListUpdated = true;
  1.1865 +        #endif
  1.1866 +    }
  1.1867 +    
  1.1868 +    return true;
  1.1869 +}
  1.1870 +
  1.1871 +
  1.1872 +bool SymbolLookup::LookupSymbol(uint64_t address, SymbolInfo& symbolInfo)
  1.1873 +{
  1.1874 +    return LookupSymbols(&address, &symbolInfo, 1);
  1.1875 +}
  1.1876 +
  1.1877 +
  1.1878 +bool SymbolLookup::LookupSymbols(uint64_t* addressArray, SymbolInfo* pSymbolInfoArray, size_t arraySize)
  1.1879 +{
  1.1880 +    if(!moduleListUpdated)
  1.1881 +    {
  1.1882 +        RefreshModuleList();
  1.1883 +    }
  1.1884 +
  1.1885 +    #if defined(OVR_OS_MS)
  1.1886 +        union SYMBOL_INFO_UNION
  1.1887 +        {
  1.1888 +            SYMBOL_INFO msSymbolInfo;
  1.1889 +            char        suffixPadding[sizeof(SYMBOL_INFO) + 1024];
  1.1890 +        };
  1.1891 +
  1.1892 +        for(size_t i = 0; i < arraySize; i++)
  1.1893 +        {
  1.1894 +            uint64_t&   address    = addressArray[i];
  1.1895 +            SymbolInfo& symbolInfo = pSymbolInfoArray[i];
  1.1896 +
  1.1897 +            // Copy the address and ModuleInfo
  1.1898 +            symbolInfo.address     = addressArray[i];
  1.1899 +            symbolInfo.pModuleInfo = GetModuleInfoForAddress(address); // We could also use siu.msSymbolInfo.ModBase to get the module slightly faster.
  1.1900 +
  1.1901 +            // Get the function/offset.
  1.1902 +            SYMBOL_INFO_UNION siu;
  1.1903 +            memset(&siu, 0, sizeof(siu));
  1.1904 +            siu.msSymbolInfo.SizeOfStruct = sizeof(siu.msSymbolInfo);
  1.1905 +            siu.msSymbolInfo.MaxNameLen   = sizeof(siu.suffixPadding) - sizeof(SYMBOL_INFO) + 1; // +1 because SYMBOL_INFO itself has Name[1].
  1.1906 +
  1.1907 +            HANDLE  hProcess = GetCurrentProcess();
  1.1908 +            DWORD64 displacement64 = 0;
  1.1909 +            bool    bResult = (pSymFromAddr != nullptr) && (pSymFromAddr(hProcess, address, &displacement64, &siu.msSymbolInfo) != FALSE);
  1.1910 +
  1.1911 +            if(bResult)
  1.1912 +            {
  1.1913 +                symbolInfo.size = siu.msSymbolInfo.Size;
  1.1914 +                OVR_strlcpy(symbolInfo.function, siu.msSymbolInfo.Name, OVR_ARRAY_COUNT(symbolInfo.function));
  1.1915 +                symbolInfo.functionOffset = (int32_t)displacement64;
  1.1916 +            }
  1.1917 +            else
  1.1918 +            {
  1.1919 +                symbolInfo.size = kMISizeInvalid;
  1.1920 +                symbolInfo.function[0] = 0;
  1.1921 +                symbolInfo.functionOffset = kMIFunctionOffsetInvalid;
  1.1922 +            }
  1.1923 +
  1.1924 +            // Get the file/line
  1.1925 +            IMAGEHLP_LINE64 iLine64;
  1.1926 +            DWORD displacement = 0;
  1.1927 +            memset(&iLine64, 0, sizeof(iLine64));
  1.1928 +            iLine64.SizeOfStruct = sizeof(iLine64);
  1.1929 +
  1.1930 +            bResult = (pSymGetLineFromAddr64 != nullptr) && (pSymGetLineFromAddr64(hProcess, address, &displacement, &iLine64) != FALSE);
  1.1931 +
  1.1932 +            if(bResult)
  1.1933 +            {
  1.1934 +                OVR_strlcpy(symbolInfo.filePath, iLine64.FileName, OVR_ARRAY_COUNT(symbolInfo.filePath));
  1.1935 +                symbolInfo.fileLineNumber = (int32_t)iLine64.LineNumber;
  1.1936 +            }
  1.1937 +            else
  1.1938 +            {
  1.1939 +                symbolInfo.filePath[0] = 0;
  1.1940 +                symbolInfo.fileLineNumber = kMILineNumberInvalid;
  1.1941 +            }
  1.1942 +
  1.1943 +            // To do: get the source code when possible. We need to use the user-registered directory paths and the symbolInfo.filePath
  1.1944 +            // and find the given file in the tree(s), then open the file and find the symbolInfo.fileLineNumber line (and surrounding lines).
  1.1945 +            // symbolInfo.sourceCode[1024]
  1.1946 +            symbolInfo.sourceCode[0] = '\0';
  1.1947 +        }
  1.1948 +    
  1.1949 +    #elif defined(OVR_OS_APPLE)
  1.1950 +        // Apple has an internal CoreSymbolication library which could help with this.
  1.1951 +        // Third party implementations of the CoreSymbolication header are available and could be used
  1.1952 +        // to get file/line info better than other means. It used Objective C, so we'll need a .m or .mm file.
  1.1953 +    
  1.1954 +        memset(pSymbolInfoArray, 0, arraySize * sizeof(SymbolInfo));
  1.1955 +
  1.1956 +        for(size_t i = 0; i < arraySize; i++)
  1.1957 +        {
  1.1958 +            pSymbolInfoArray[i].address = addressArray[i];
  1.1959 +            pSymbolInfoArray[i].pModuleInfo = GetModuleInfoForAddress(addressArray[i]);
  1.1960 +        }
  1.1961 +
  1.1962 +        // Problem: backtrace_symbols allocates memory from malloc. If you got into a SIGSEGV due to
  1.1963 +        // malloc arena corruption (quite common) you will likely fault in backtrace_symbols.
  1.1964 +        // To do: Use allowMemoryAllocation here.
  1.1965 +    
  1.1966 +        #if (OVR_PTR_SIZE == 4)
  1.1967 +            // backtrace_symbols takes a void* array, but we have a uint64_t array. So for 32 bit we
  1.1968 +            // need to convert the 64 bit array to 32 bit temporarily for the backtrace_symbols call.
  1.1969 +            void* ptr32Array[256]; // To do: Remove this limit.
  1.1970 +            for(size_t i = 0, iEnd = MIN(arraySize, OVR_ARRAY_COUNT(ptr32Array)); i < iEnd; i++)
  1.1971 +                ptr32Array[i] = reinterpret_cast<void*>(addressArray[i]);
  1.1972 +            char** symbolArray = backtrace_symbols(reinterpret_cast<void**>(ptr32Array), (int)arraySize);
  1.1973 +        #else
  1.1974 +            char** symbolArray = backtrace_symbols(reinterpret_cast<void**>(addressArray), (int)arraySize);
  1.1975 +        #endif
  1.1976 +
  1.1977 +        if(symbolArray)
  1.1978 +        {
  1.1979 +            for(size_t i = 0; i < arraySize; i++)
  1.1980 +            {
  1.1981 +
  1.1982 +                // Generates a string like this: "0 OculusWorldDemo 0x000000010000cfd5 _ZN18OculusWorldDemoApp9OnStartupEiPPKc + 213"
  1.1983 +                static_assert(OVR_ARRAY_COUNT(pSymbolInfoArray[i].function) == 128, "Need to change the string format size below");
  1.1984 +                
  1.1985 +                sscanf(symbolArray[i], "%*d %*s %*x %128s + %d", pSymbolInfoArray[i].function, &pSymbolInfoArray[i].functionOffset);
  1.1986 +                
  1.1987 +                if(allowMemoryAllocation)
  1.1988 +                {
  1.1989 +                    int   status = 0;
  1.1990 +                    char* strDemangled = abi::__cxa_demangle(pSymbolInfoArray[i].function, nullptr, nullptr, &status);
  1.1991 +                    
  1.1992 +                    if(strDemangled)
  1.1993 +                    {
  1.1994 +                        OVR_strlcpy(pSymbolInfoArray[i].function, strDemangled, OVR_ARRAY_COUNT(pSymbolInfoArray[i].function));
  1.1995 +                        free(strDemangled);
  1.1996 +                    }
  1.1997 +                }
  1.1998 +            }
  1.1999 +
  1.2000 +            free(symbolArray);
  1.2001 +        }
  1.2002 +
  1.2003 +        // To consider: use CoreSybolication to get file/line info instead. atos is a bit slow and cumbersome.
  1.2004 +        // https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/atos.1.html
  1.2005 +        // atos -p <pid> <addr> <addr> ...
  1.2006 +        // atos -o <binary image path> -l <load-address> <addr> <addr> ...
  1.2007 +        // Generates output like this: "OVR::CreateException(OVR::CreateExceptionType) (in OculusWorldDemo) (ExceptionHandler.cpp:598)"
  1.2008 +        for(size_t i = 0; i < arraySize; i++)
  1.2009 +        {
  1.2010 +            struct stat statStruct;
  1.2011 +
  1.2012 +            if(pSymbolInfoArray[i].pModuleInfo && pSymbolInfoArray[i].pModuleInfo->filePath[0] && (stat(pSymbolInfoArray[i].pModuleInfo->filePath, &statStruct) == 0))
  1.2013 +            {
  1.2014 +                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.
  1.2015 +                OVR_snprintf(command, OVR_ARRAY_COUNT(command), "atos -o %s -l 0x%llx 0x%llx",
  1.2016 +                            pSymbolInfoArray[i].pModuleInfo->filePath, (int64_t)pSymbolInfoArray[i].pModuleInfo->baseAddress, (int64_t)pSymbolInfoArray[i].address);
  1.2017 +
  1.2018 +                char output[512];
  1.2019 +                if(SpawnShellCommand(command, output, OVR_ARRAY_COUNT(output)) != (size_t)-1)
  1.2020 +                {
  1.2021 +                    char* pLastOpenParen = strrchr(output, '(');
  1.2022 +                    char* pColon = strrchr(output, ':');
  1.2023 +                    
  1.2024 +                    if(pLastOpenParen && (pColon > pLastOpenParen))
  1.2025 +                    {
  1.2026 +                        *pColon = '\0';
  1.2027 +                        OVR_strlcpy(pSymbolInfoArray[i].filePath, pLastOpenParen + 1, OVR_ARRAY_COUNT(pSymbolInfoArray[i].filePath));
  1.2028 +                    }
  1.2029 +                }
  1.2030 +            }
  1.2031 +        }
  1.2032 +
  1.2033 +    #elif defined(OVR_OS_LINUX)
  1.2034 +        // We can use libunwind's unw_get_proc_name to try to get function name info. It can work regardless of relocation.
  1.2035 +        // Use backtrace_symbols and addr2line. Need to watch out for module load-time relocation.
  1.2036 +        // Ned to pass the -rdynamic flag to the linker. It will cause the linker to out in the link
  1.2037 +        // tables the name of all the none static functions in your code, not just the exported ones.
  1.2038 +        OVR_UNUSED(addressArray);
  1.2039 +        OVR_UNUSED(pSymbolInfoArray);
  1.2040 +        OVR_UNUSED(arraySize);
  1.2041 +    #endif
  1.2042 +
  1.2043 +    return true; // To do: Return true only if something was found.
  1.2044 +}
  1.2045 +
  1.2046 +
  1.2047 +const ModuleInfo* SymbolLookup::GetModuleInfoForAddress(uint64_t address)
  1.2048 +{
  1.2049 +    // This is a linear seach. To consider: it would be significantly faster to search by 
  1.2050 +    // address if we ordered it by base address and did a binary search.
  1.2051 +    for(size_t i = 0; i < moduleInfoArraySize; ++i)
  1.2052 +    {
  1.2053 +        const ModuleInfo& mi = moduleInfoArray[i];
  1.2054 +
  1.2055 +        if((mi.baseAddress <= address) && (address < (mi.baseAddress + mi.size)))
  1.2056 +            return &mi;
  1.2057 +    }
  1.2058 +
  1.2059 +    return nullptr;
  1.2060 +}
  1.2061 +
  1.2062 +
  1.2063 +
  1.2064 +
  1.2065 +ExceptionInfo::ExceptionInfo()
  1.2066 +  : time()
  1.2067 +  , timeVal(0)
  1.2068 +  , backtrace()
  1.2069 +  , backtraceCount(0)
  1.2070 +  , threadHandle(OVR_THREADHANDLE_INVALID)
  1.2071 +  , threadSysId(OVR_THREADSYSID_INVALID)
  1.2072 +  , threadName()
  1.2073 +  , pExceptionInstructionAddress(nullptr)
  1.2074 +  , pExceptionMemoryAddress(nullptr)
  1.2075 +  , cpuContext()
  1.2076 +  , exceptionDescription()
  1.2077 +  , symbolInfo()
  1.2078 + #if defined(OVR_OS_MS)
  1.2079 +  , exceptionRecord()
  1.2080 + #elif defined(OVR_OS_APPLE)
  1.2081 +  , exceptionType(0)
  1.2082 +  , cpuExceptionId(0)
  1.2083 +  , cpuExceptionIdError(0)
  1.2084 +  , machExceptionDetail()
  1.2085 +  , machExceptionDetailCount(0)
  1.2086 + #endif
  1.2087 +{
  1.2088 +}
  1.2089 +
  1.2090 +
  1.2091 +
  1.2092 +ExceptionHandler::ExceptionHandler()
  1.2093 +  : enabled(false)
  1.2094 +  , reportPrivacyEnabled(true)
  1.2095 +  , exceptionResponse(kERHandle)
  1.2096 +  , exceptionListener(nullptr)
  1.2097 +  , exceptionListenerUserValue(0)
  1.2098 +  , appDescription()
  1.2099 +  , codeBasePathArray()
  1.2100 +  , reportFilePath()
  1.2101 +  , miniDumpFlags(0)
  1.2102 +  , miniDumpFilePath()
  1.2103 +  , file(nullptr)
  1.2104 +  , scratchBuffer()
  1.2105 +  , exceptionOccurred(false)
  1.2106 +  , handlingBusy(0)
  1.2107 +  , reportFilePathActual()
  1.2108 +  , minidumpFilePathActual()
  1.2109 +  , terminateReturnValue(0)
  1.2110 +  , exceptionInfo()
  1.2111 + #if defined(OVR_OS_MS)
  1.2112 +  , vectoredHandle(nullptr)
  1.2113 +  , previousFilter(nullptr)
  1.2114 +  , pExceptionPointers(nullptr)
  1.2115 + #elif defined(OVR_OS_MAC)
  1.2116 +  , machHandlerInitialized(false)
  1.2117 +  , machExceptionPort(0)
  1.2118 +  , machExceptionPortsSaved()
  1.2119 +  , machThreadShouldContinue(false)
  1.2120 +  , machThreadExecuting(false)
  1.2121 +  , machThread((pthread_t)OVR_THREADHANDLE_INVALID)
  1.2122 + #endif
  1.2123 +{
  1.2124 +	SetExceptionPaths("default", "default");
  1.2125 +}
  1.2126 +
  1.2127 +
  1.2128 +ExceptionHandler::~ExceptionHandler()
  1.2129 +{
  1.2130 +    if(enabled)
  1.2131 +    {
  1.2132 +        Enable(false);
  1.2133 +    } 
  1.2134 +}
  1.2135 +
  1.2136 +
  1.2137 +#if defined(OVR_OS_MS)
  1.2138 +    static ExceptionHandler* sExceptionHandler = nullptr;
  1.2139 +
  1.2140 +    LONG WINAPI Win32ExceptionFilter(LPEXCEPTION_POINTERS pExceptionPointers)
  1.2141 +    {
  1.2142 +        if(sExceptionHandler)
  1.2143 +            return (LONG)sExceptionHandler->ExceptionFilter(pExceptionPointers);
  1.2144 +        return EXCEPTION_CONTINUE_SEARCH;
  1.2145 +    }
  1.2146 +
  1.2147 +    LONG ExceptionHandler::ExceptionFilter(LPEXCEPTION_POINTERS pExceptionPointers)
  1.2148 +    {
  1.2149 +        // Exception codes < 0x80000000 are not true exceptions but rather are debugger notifications. They include DBG_TERMINATE_THREAD, 
  1.2150 +        // DBG_TERMINATE_PROCESS, DBG_CONTROL_BREAK, DBG_COMMAND_EXCEPTION, DBG_CONTROL_C, DBG_PRINTEXCEPTION_C, DBG_RIPEXCEPTION, 
  1.2151 +        // and 0x406d1388 (thread named, http://blogs.msdn.com/b/stevejs/archive/2005/12/19/505815.aspx).
  1.2152 +
  1.2153 +        if(pExceptionPointers->ExceptionRecord->ExceptionCode < 0x80000000)
  1.2154 +            return EXCEPTION_CONTINUE_SEARCH;
  1.2155 +
  1.2156 +        // VC++ C++ exceptions use code 0xe06d7363 ('Emsc')
  1.2157 +        // http://support.microsoft.com/kb/185294 
  1.2158 +        // http://blogs.msdn.com/b/oldnewthing/archive/2010/07/30/10044061.aspx
  1.2159 +        if(pExceptionPointers->ExceptionRecord->ExceptionCode == 0xe06d7363)
  1.2160 +            return EXCEPTION_CONTINUE_SEARCH;
  1.2161 +
  1.2162 +        if(handlingBusy.CompareAndSet_Acquire(0, 1)) // If we can successfully change it from 0 to 1.
  1.2163 +        {
  1.2164 +            exceptionOccurred = true;
  1.2165 +
  1.2166 +            this->pExceptionPointers = pExceptionPointers;
  1.2167 +
  1.2168 +            // Disable the handler while we do this processing.
  1.2169 +            ULONG result = RemoveVectoredExceptionHandler(vectoredHandle);
  1.2170 +            OVR_ASSERT_AND_UNUSED(result != 0, result);
  1.2171 +
  1.2172 +            // Time
  1.2173 +            exceptionInfo.timeVal = time(nullptr);
  1.2174 +            exceptionInfo.time = *gmtime(&exceptionInfo.timeVal);
  1.2175 +
  1.2176 +            // Thread id
  1.2177 +            // This is the thread id of the current thread and not the exception thread.
  1.2178 +            if(!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &exceptionInfo.threadHandle, 0, true, DUPLICATE_SAME_ACCESS))
  1.2179 +                exceptionInfo.threadHandle = 0;
  1.2180 +            exceptionInfo.threadSysId = ConvertThreadHandleToThreadSysId(exceptionInfo.threadHandle);
  1.2181 +
  1.2182 +            OVR::GetThreadName(exceptionInfo.threadHandle, exceptionInfo.threadName, OVR_ARRAY_COUNT(exceptionInfo.threadName));
  1.2183 +
  1.2184 +            // Backtraces
  1.2185 +            exceptionInfo.backtraceCount = symbolLookup.GetBacktrace(exceptionInfo.backtrace, OVR_ARRAY_COUNT(exceptionInfo.backtrace));
  1.2186 +
  1.2187 +            // Context
  1.2188 +            exceptionInfo.cpuContext = *pExceptionPointers->ContextRecord;
  1.2189 +            exceptionInfo.exceptionRecord = *pExceptionPointers->ExceptionRecord;
  1.2190 +            exceptionInfo.pExceptionInstructionAddress  = exceptionInfo.exceptionRecord.ExceptionAddress;
  1.2191 +            if((exceptionInfo.exceptionRecord.ExceptionCode == EXCEPTION_ACCESS_VIOLATION) || (exceptionInfo.exceptionRecord.ExceptionCode == EXCEPTION_IN_PAGE_ERROR))
  1.2192 +                exceptionInfo.pExceptionMemoryAddress = (void*)exceptionInfo.exceptionRecord.ExceptionInformation[1]; // ExceptionInformation[0] indicates if it was a read (0), write (1), or data execution attempt (8).
  1.2193 +            else
  1.2194 +                exceptionInfo.pExceptionMemoryAddress = pExceptionPointers->ExceptionRecord->ExceptionAddress;
  1.2195 +
  1.2196 +            WriteExceptionDescription();
  1.2197 +
  1.2198 +            if(miniDumpFilePath[0])
  1.2199 +                WriteMiniDump();
  1.2200 +
  1.2201 +            if(reportFilePath[0])
  1.2202 +                WriteReport();
  1.2203 +
  1.2204 +            if(exceptionListener)
  1.2205 +                exceptionListener->HandleException(exceptionListenerUserValue, this, &exceptionInfo, reportFilePathActual);
  1.2206 +
  1.2207 +            if(exceptionInfo.threadHandle)
  1.2208 +            {
  1.2209 +                CloseHandle(exceptionInfo.threadHandle);
  1.2210 +                exceptionInfo.threadHandle = 0;
  1.2211 +            }
  1.2212 +
  1.2213 +            // Restore the handler that we temporarily disabled above.
  1.2214 +            vectoredHandle = AddVectoredExceptionHandler(1, Win32ExceptionFilter);
  1.2215 +
  1.2216 +            handlingBusy.Store_Release(0);
  1.2217 +        }
  1.2218 +
  1.2219 +        if(exceptionResponse == ExceptionHandler::kERTerminate)
  1.2220 +        {
  1.2221 +            TerminateProcess(GetCurrentProcess(), (UINT)terminateReturnValue);
  1.2222 +            return terminateReturnValue;
  1.2223 +        }
  1.2224 +        else if(exceptionResponse == ExceptionHandler::kERThrow)
  1.2225 +            return EXCEPTION_CONTINUE_SEARCH;
  1.2226 +        else if(exceptionResponse == ExceptionHandler::kERContinue)
  1.2227 +            return EXCEPTION_CONTINUE_EXECUTION;
  1.2228 +        return EXCEPTION_EXECUTE_HANDLER;
  1.2229 +    }
  1.2230 +
  1.2231 +#endif // defined(OVR_OS_MS)
  1.2232 +
  1.2233 +
  1.2234 +#if defined(OVR_OS_APPLE)
  1.2235 +    // http://www.opensource.apple.com/source/xnu/xnu-2050.22.13/
  1.2236 +    // http://www.opensource.apple.com/source/xnu/xnu-2050.22.13/osfmk/man/
  1.2237 +    // http://www.opensource.apple.com/source/Libc/Libc-825.26/
  1.2238 +    // https://mikeash.com/pyblog/friday-qa-2013-01-11-mach-exception-handlers.html
  1.2239 +    
  1.2240 +    void* ExceptionHandler::MachHandlerThreadFunction()
  1.2241 +    {
  1.2242 +        __Request__mach_exception_raise_state_identity_t msg;
  1.2243 +        __Reply__mach_exception_raise_state_identity_t reply;
  1.2244 +        mach_msg_return_t result;
  1.2245 +
  1.2246 +        machThreadExecuting = true;
  1.2247 +        pthread_setname_np("ExceptionHandler");
  1.2248 +        
  1.2249 +        while(machThreadShouldContinue)
  1.2250 +        {
  1.2251 +            mach_msg_option_t options = MACH_RCV_MSG | MACH_RCV_LARGE;
  1.2252 +            natural_t         timeout = 0; // Would be better to support a non-zero time.
  1.2253 +
  1.2254 +            if(timeout)
  1.2255 +                options |= MACH_RCV_TIMEOUT;
  1.2256 +            
  1.2257 +            result = mach_msg(&msg.Head, options, 0, sizeof(msg), machExceptionPort, timeout, MACH_PORT_NULL);
  1.2258 +
  1.2259 +            if(msg.Head.msgh_id != sMachCancelMessageType)
  1.2260 +            {
  1.2261 +                if(result == MACH_MSG_SUCCESS)
  1.2262 +                {
  1.2263 +                    if(mach_exc_server_OVR(&msg.Head, &reply.Head) == 0)  //This will call our HandleMachException function.
  1.2264 +                        result = ~MACH_MSG_SUCCESS;
  1.2265 +                }
  1.2266 +
  1.2267 +                // Send the reply
  1.2268 +                if(result == MACH_MSG_SUCCESS)
  1.2269 +                {
  1.2270 +                    result = mach_msg(&reply.Head, MACH_SEND_MSG, reply.Head.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
  1.2271 +                    
  1.2272 +                    if(result != MACH_MSG_SUCCESS)
  1.2273 +                    {
  1.2274 +                        // Failure.
  1.2275 +                    }
  1.2276 +                }
  1.2277 +            }
  1.2278 +        }
  1.2279 +
  1.2280 +        machThreadExecuting = false;
  1.2281 +
  1.2282 +        return nullptr;
  1.2283 +    }
  1.2284 +
  1.2285 +    
  1.2286 +    kern_return_t ExceptionHandler::HandleMachException(mach_port_t /*machPort*/, mach_port_t threadSysId, mach_port_t machTask,
  1.2287 +                                            exception_type_t machExceptionType, mach_exception_data_type_t* pExceptionDetail,
  1.2288 +                                            mach_msg_type_number_t exceptionDetailCount, int* /*pMachExceptionFlavor*/, thread_state_t threadStatePrev,
  1.2289 +                                            mach_msg_type_number_t /*threadStatePrevCount*/, thread_state_t /*threadStateNew*/,
  1.2290 +                                            mach_msg_type_number_t* /*pThreadStateNewCount*/)
  1.2291 +    {
  1.2292 +        // We don't want to handle exceptions for other processes.
  1.2293 +        if(machTask != mach_task_self())
  1.2294 +            return ForwardMachException(threadSysId, machTask, machExceptionType, pExceptionDetail, exceptionDetailCount);
  1.2295 +
  1.2296 +        if(handlingBusy.CompareAndSet_Acquire(0, 1)) // If we can successfully change it from 0 to 1.
  1.2297 +        {
  1.2298 +            exceptionOccurred = true;
  1.2299 +
  1.2300 +            // Disable the handler while we do this processing.
  1.2301 +            // To do.
  1.2302 +
  1.2303 +            // Time
  1.2304 +            exceptionInfo.timeVal = time(nullptr);
  1.2305 +            exceptionInfo.time = *gmtime(&exceptionInfo.timeVal);
  1.2306 +
  1.2307 +            // Thread id
  1.2308 +            exceptionInfo.threadHandle = pthread_from_mach_thread_np(threadSysId);
  1.2309 +            exceptionInfo.threadSysId  = threadSysId;
  1.2310 +            pthread_getname_np((pthread_t)exceptionInfo.threadHandle, exceptionInfo.threadName, sizeof(exceptionInfo.threadName));
  1.2311 +
  1.2312 +            // Backtraces
  1.2313 +            exceptionInfo.backtraceCount = symbolLookup.GetBacktraceFromThreadSysId(exceptionInfo.backtrace, OVR_ARRAY_COUNT(exceptionInfo.backtrace), 0, threadSysId);
  1.2314 +
  1.2315 +            // Context
  1.2316 +            #if defined(OVR_CPU_X86) || defined(OVR_CPU_X86_64)
  1.2317 +                // We can read x86_THREAD_STATE directly fromk threadStatePrev.
  1.2318 +                exceptionInfo.cpuContext.threadState = *reinterpret_cast<x86_thread_state_t*>(threadStatePrev);
  1.2319 +            
  1.2320 +                mach_msg_type_number_t stateCount = x86_FLOAT_STATE_COUNT;
  1.2321 +                thread_get_state(threadSysId, x86_FLOAT_STATE, (natural_t*)&exceptionInfo.cpuContext.floatState, &stateCount);
  1.2322 +            
  1.2323 +                stateCount = x86_DEBUG_STATE_COUNT;
  1.2324 +                thread_get_state(threadSysId, x86_DEBUG_STATE, (natural_t*)&exceptionInfo.cpuContext.debugState, &stateCount);
  1.2325 +            
  1.2326 +                stateCount = x86_AVX_STATE_COUNT;
  1.2327 +                thread_get_state(threadSysId, x86_AVX_STATE, (natural_t*)&exceptionInfo.cpuContext.avxState, &stateCount);
  1.2328 +            
  1.2329 +                stateCount = x86_EXCEPTION_STATE_COUNT;
  1.2330 +                thread_get_state(threadSysId, x86_EXCEPTION_STATE, (natural_t*)&exceptionInfo.cpuContext.exceptionState, &stateCount);
  1.2331 +
  1.2332 +                #if defined(OVR_CPU_X86)
  1.2333 +                    exceptionInfo.pExceptionInstructionAddress = (void*)exceptionInfo.cpuContext.threadState.uts.ts32.__eip;
  1.2334 +                    exceptionInfo.pExceptionMemoryAddress      = (void*)exceptionInfo.cpuContext.exceptionState.ues.es32.__faultvaddr;
  1.2335 +                    exceptionInfo.cpuExceptionId               = exceptionInfo.cpuContext.exceptionState.ues.es32.__trapno;
  1.2336 +                    exceptionInfo.cpuExceptionIdError          = exceptionInfo.cpuContext.exceptionState.ues.es32.__err;
  1.2337 +                #else
  1.2338 +                    exceptionInfo.pExceptionInstructionAddress = (void*)exceptionInfo.cpuContext.threadState.uts.ts64.__rip;
  1.2339 +                    exceptionInfo.pExceptionMemoryAddress      = (void*)exceptionInfo.cpuContext.exceptionState.ues.es64.__faultvaddr;
  1.2340 +                    exceptionInfo.cpuExceptionId               = exceptionInfo.cpuContext.exceptionState.ues.es64.__trapno;
  1.2341 +                    exceptionInfo.cpuExceptionIdError          = exceptionInfo.cpuContext.exceptionState.ues.es64.__err;
  1.2342 +                #endif
  1.2343 +            #endif
  1.2344 +            
  1.2345 +            exceptionInfo.exceptionType = machExceptionType;
  1.2346 +
  1.2347 +            exceptionInfo.machExceptionDetailCount = MIN(exceptionDetailCount, OVR_ARRAY_COUNT(exceptionInfo.machExceptionDetail));
  1.2348 +            for(int i = 0; i < exceptionInfo.machExceptionDetailCount; i++)
  1.2349 +                exceptionInfo.machExceptionDetail[i] = pExceptionDetail[i];
  1.2350 +
  1.2351 +            WriteExceptionDescription();
  1.2352 +
  1.2353 +            if(reportFilePath[0])
  1.2354 +                WriteReport();
  1.2355 +
  1.2356 +            if(miniDumpFilePath[0])
  1.2357 +                WriteMiniDump();
  1.2358 + 
  1.2359 +            if(exceptionListener)
  1.2360 +                exceptionListener->HandleException(exceptionListenerUserValue, this, &exceptionInfo, reportFilePathActual);
  1.2361 +
  1.2362 +            // Re-restore the handler.
  1.2363 +            // To do.
  1.2364 +
  1.2365 +            handlingBusy.Store_Release(0);
  1.2366 +        }
  1.2367 +
  1.2368 +        kern_return_t result = KERN_FAILURE; // By default pass on the exception to another handler after we are done here.
  1.2369 +
  1.2370 +        if(exceptionResponse == ExceptionHandler::kERTerminate)
  1.2371 +            ::exit(terminateReturnValue);
  1.2372 +        else if(exceptionResponse == ExceptionHandler::kERThrow)
  1.2373 +            ForwardMachException(threadSysId, machTask, machExceptionType, pExceptionDetail, exceptionDetailCount);
  1.2374 +        else if(exceptionResponse == ExceptionHandler::kERDefault)
  1.2375 +            ::exit(terminateReturnValue);
  1.2376 +        else if(exceptionResponse == ExceptionHandler::kERContinue)
  1.2377 +            result = KERN_SUCCESS; // This will trigger a re-execution of the function.
  1.2378 +
  1.2379 +        return result;
  1.2380 +    }
  1.2381 +
  1.2382 +
  1.2383 +    bool ExceptionHandler::InitMachExceptionHandler()
  1.2384 +    {
  1.2385 +        if(!machHandlerInitialized)
  1.2386 +        {
  1.2387 +            mach_port_t      machTaskSelf = mach_task_self();
  1.2388 +            kern_return_t    result = MACH_MSG_SUCCESS;
  1.2389 +            exception_mask_t mask = EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_CRASH;
  1.2390 +            
  1.2391 +            if(machExceptionPort == MACH_PORT_NULL)
  1.2392 +            {
  1.2393 +                result = mach_port_allocate(machTaskSelf, MACH_PORT_RIGHT_RECEIVE, &machExceptionPort);
  1.2394 +                
  1.2395 +                if(result == MACH_MSG_SUCCESS)
  1.2396 +                {
  1.2397 +                    result = mach_port_insert_right(machTaskSelf, machExceptionPort, machExceptionPort, MACH_MSG_TYPE_MAKE_SEND);
  1.2398 +
  1.2399 +                    if(result == MACH_MSG_SUCCESS)
  1.2400 +                        result = task_get_exception_ports(machTaskSelf, mask, machExceptionPortsSaved.masks, &machExceptionPortsSaved.count,
  1.2401 +                                                          machExceptionPortsSaved.ports, machExceptionPortsSaved.behaviors, machExceptionPortsSaved.flavors);
  1.2402 +                }
  1.2403 +            }
  1.2404 +            
  1.2405 +            if(result == MACH_MSG_SUCCESS)
  1.2406 +            {
  1.2407 +                result = task_set_exception_ports(machTaskSelf, mask, machExceptionPort, EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, MACHINE_THREAD_STATE);
  1.2408 +
  1.2409 +                if(result == MACH_MSG_SUCCESS)
  1.2410 +                {
  1.2411 +                    machThreadShouldContinue = true;
  1.2412 +
  1.2413 +                    pthread_attr_t attr;
  1.2414 +                    pthread_attr_init(&attr);
  1.2415 +                    
  1.2416 +                    result = pthread_create(&machThread, &attr, MachHandlerThreadFunctionStatic, (void*)this);
  1.2417 +                    pthread_attr_destroy(&attr);
  1.2418 +                    
  1.2419 +                    machHandlerInitialized = (result == 0);
  1.2420 +                }
  1.2421 +            }
  1.2422 +            
  1.2423 +            if(!machHandlerInitialized)
  1.2424 +                ShutdownMachExceptionHandler();
  1.2425 +        }
  1.2426 +        
  1.2427 +        return machHandlerInitialized;
  1.2428 +    }
  1.2429 +
  1.2430 +
  1.2431 +    void ExceptionHandler::ShutdownMachExceptionHandler()
  1.2432 +    {
  1.2433 +        if(machThreadExecuting)
  1.2434 +        {
  1.2435 +            machThreadShouldContinue = false; // Tell it to stop.
  1.2436 +
  1.2437 +            // Cancel the current exception handler thread (which is probably blocking in a call to mach_msg) by sending it a cencel message.
  1.2438 +            struct CancelMessage
  1.2439 +            {
  1.2440 +                mach_msg_header_t msgHeader;
  1.2441 +            };
  1.2442 +            
  1.2443 +            CancelMessage msg;
  1.2444 +            memset(&msg.msgHeader, 0, sizeof(CancelMessage));
  1.2445 +            msg.msgHeader.msgh_id          = sMachCancelMessageType;
  1.2446 +            msg.msgHeader.msgh_size        = sizeof(CancelMessage);
  1.2447 +            msg.msgHeader.msgh_bits        = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MAKE_SEND);
  1.2448 +            msg.msgHeader.msgh_remote_port = machExceptionPort;
  1.2449 +            msg.msgHeader.msgh_local_port  = MACH_PORT_NULL;
  1.2450 +            
  1.2451 +            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);
  1.2452 +            
  1.2453 +            if(result == MACH_MSG_SUCCESS)
  1.2454 +            {
  1.2455 +                const double threeSecondsLater = ovr_GetTimeInSeconds() + 3.f;
  1.2456 +                
  1.2457 +                while(machThreadExecuting && (ovr_GetTimeInSeconds() < threeSecondsLater))
  1.2458 +                {
  1.2459 +                    timespec ts = { 0, 1000000000 };
  1.2460 +                    nanosleep(&ts, nullptr);
  1.2461 +                }
  1.2462 +            }
  1.2463 +
  1.2464 +            void* joinResult = nullptr;
  1.2465 +            pthread_join(machThread, &joinResult);
  1.2466 +            machThread = 0;
  1.2467 +        }
  1.2468 +
  1.2469 +        if(machExceptionPort != MACH_PORT_NULL)
  1.2470 +        {
  1.2471 +            // Restore the previous ports
  1.2472 +            kern_return_t result = KERN_SUCCESS;
  1.2473 +            mach_port_t   machTaskSelf = mach_task_self();
  1.2474 +            
  1.2475 +            for(unsigned i = 0; (i < machExceptionPortsSaved.count) && (result == KERN_SUCCESS); i++)
  1.2476 +            {
  1.2477 +                result = task_set_exception_ports(machTaskSelf, machExceptionPortsSaved.masks[i], machExceptionPortsSaved.ports[i],
  1.2478 +                              machExceptionPortsSaved.behaviors[i], machExceptionPortsSaved.flavors[i]);
  1.2479 +            }
  1.2480 +
  1.2481 +            mach_port_deallocate(machTaskSelf, machExceptionPort);
  1.2482 +            machExceptionPort = MACH_PORT_NULL;
  1.2483 +        }
  1.2484 +        
  1.2485 +        machHandlerInitialized = false;
  1.2486 +    }
  1.2487 +
  1.2488 +
  1.2489 +    kern_return_t ExceptionHandler::ForwardMachException(mach_port_t thread, mach_port_t task, exception_type_t exceptionType,
  1.2490 +                                                mach_exception_data_t pExceptionDetail, mach_msg_type_number_t exceptionDetailCount)
  1.2491 +    {
  1.2492 +        kern_return_t result = KERN_FAILURE;
  1.2493 +        mach_msg_type_number_t i;
  1.2494 +
  1.2495 +        for(i = 0; i < machExceptionPortsSaved.count; i++)
  1.2496 +        {
  1.2497 +            if(machExceptionPortsSaved.masks[i] & (1 << exceptionType))
  1.2498 +                break;
  1.2499 +        }
  1.2500 +        
  1.2501 +        if(i < machExceptionPortsSaved.count)
  1.2502 +        {
  1.2503 +            mach_port_t            port             = machExceptionPortsSaved.ports[i];
  1.2504 +            exception_behavior_t   behavior         = machExceptionPortsSaved.behaviors[i];
  1.2505 +            thread_state_flavor_t  flavor           = machExceptionPortsSaved.flavors[i];
  1.2506 +            mach_msg_type_number_t threadStateCount = THREAD_STATE_MAX;
  1.2507 +            thread_state_data_t    threadState;
  1.2508 +
  1.2509 +            if(behavior != EXCEPTION_DEFAULT)
  1.2510 +                thread_get_state(thread, flavor, threadState, &threadStateCount);
  1.2511 +            
  1.2512 +            switch(behavior)
  1.2513 +            {
  1.2514 +                case EXCEPTION_DEFAULT:
  1.2515 +                    result = mach_exception_raise_OVR(port, thread, task, exceptionType, pExceptionDetail, exceptionDetailCount);
  1.2516 +                    break;
  1.2517 +                    
  1.2518 +                case EXCEPTION_STATE:
  1.2519 +                    result = mach_exception_raise_state_OVR(port, exceptionType, pExceptionDetail, exceptionDetailCount,
  1.2520 +                                       &flavor, threadState, threadStateCount, threadState, &threadStateCount);
  1.2521 +                    break;
  1.2522 +
  1.2523 +                case EXCEPTION_STATE_IDENTITY:
  1.2524 +                    result = mach_exception_raise_state_identity_OVR(port, thread, task, exceptionType, pExceptionDetail,
  1.2525 +                                    exceptionDetailCount, &flavor, threadState, threadStateCount, threadState, &threadStateCount);
  1.2526 +                    break;
  1.2527 +                    
  1.2528 +                default:
  1.2529 +                    result = KERN_FAILURE;
  1.2530 +                    break;
  1.2531 +            }
  1.2532 +            
  1.2533 +            if(behavior != EXCEPTION_DEFAULT)
  1.2534 +                result = thread_set_state(thread, flavor, threadState, threadStateCount);
  1.2535 +        }
  1.2536 +        
  1.2537 +        return result;
  1.2538 +    }
  1.2539 +
  1.2540 +
  1.2541 +#endif // OVR_OS_APPLE
  1.2542 +
  1.2543 +
  1.2544 +bool ExceptionHandler::Enable(bool enable)
  1.2545 +{
  1.2546 +    #if defined(OVR_OS_MS)
  1.2547 +        if(enable && !enabled)
  1.2548 +        {
  1.2549 +            OVR_ASSERT(vectoredHandle == nullptr);
  1.2550 +            vectoredHandle = AddVectoredExceptionHandler(1, Win32ExceptionFilter); // Windows call.
  1.2551 +            enabled = (vectoredHandle != nullptr);
  1.2552 +            OVR_ASSERT(enabled);
  1.2553 +            sExceptionHandler = this;
  1.2554 +            return enabled;
  1.2555 +        }
  1.2556 +        else if(!enable && enabled)
  1.2557 +        {
  1.2558 +            if(sExceptionHandler == this)
  1.2559 +                sExceptionHandler = nullptr;
  1.2560 +            OVR_ASSERT(vectoredHandle != nullptr);
  1.2561 +            ULONG result = RemoveVectoredExceptionHandler(vectoredHandle); // Windows call.
  1.2562 +            OVR_ASSERT_AND_UNUSED(result != 0, result);
  1.2563 +            vectoredHandle = nullptr;
  1.2564 +            enabled = false;
  1.2565 +            return true;
  1.2566 +        }
  1.2567 +    
  1.2568 +    #elif defined(OVR_OS_APPLE)
  1.2569 +    
  1.2570 +        if(enable && !enabled)
  1.2571 +        {
  1.2572 +            enabled = InitMachExceptionHandler();
  1.2573 +            OVR_ASSERT(enabled);
  1.2574 +            sExceptionHandler = this;
  1.2575 +            return enabled;
  1.2576 +        }
  1.2577 +        else if(!enable && enabled)
  1.2578 +        {
  1.2579 +            if(sExceptionHandler == this)
  1.2580 +                sExceptionHandler = nullptr;
  1.2581 +            ShutdownMachExceptionHandler();
  1.2582 +            enabled = false;
  1.2583 +            return true;
  1.2584 +        }
  1.2585 +	#else
  1.2586 +        OVR_UNUSED(enable);
  1.2587 +    #endif
  1.2588 +
  1.2589 +    return true;
  1.2590 +}
  1.2591 +
  1.2592 +
  1.2593 +void ExceptionHandler::EnableReportPrivacy(bool enable)
  1.2594 +{
  1.2595 +    reportPrivacyEnabled = enable;
  1.2596 +}
  1.2597 +
  1.2598 +void ExceptionHandler::WriteExceptionDescription()
  1.2599 +{
  1.2600 +    #if defined(OVR_OS_MS)
  1.2601 +        // There is some extra information available for AV exception.
  1.2602 +        if(exceptionInfo.exceptionRecord.ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
  1.2603 +        {
  1.2604 +            const char* error = (exceptionInfo.exceptionRecord.ExceptionInformation[0] == 0) ? "reading" : 
  1.2605 +                               ((exceptionInfo.exceptionRecord.ExceptionInformation[0] == 1) ? "writing" : "executing");
  1.2606 +
  1.2607 +            char addressStr[24];
  1.2608 +            SprintfAddress(addressStr, OVR_ARRAY_COUNT(addressStr), exceptionInfo.pExceptionMemoryAddress);
  1.2609 +            OVR::OVR_snprintf(exceptionInfo.exceptionDescription, OVR_ARRAY_COUNT(exceptionInfo.exceptionDescription), "ACCESS_VIOLATION %s address %s", error, addressStr);
  1.2610 +        }
  1.2611 +        else
  1.2612 +        {
  1.2613 +            exceptionInfo.exceptionDescription[0] = 0;
  1.2614 +
  1.2615 +            // Process "standard" exceptions, other than 'access violation'
  1.2616 +            #define FORMAT_EXCEPTION(x)           \
  1.2617 +                case EXCEPTION_##x:               \
  1.2618 +                    OVR::OVR_strlcpy(exceptionInfo.exceptionDescription, #x, OVR_ARRAY_COUNT(exceptionInfo.exceptionDescription)); \
  1.2619 +                    break;
  1.2620 +
  1.2621 +            switch(exceptionInfo.exceptionRecord.ExceptionCode)
  1.2622 +            {
  1.2623 +              //FORMAT_EXCEPTION(ACCESS_VIOLATION) Already handled above.
  1.2624 +                FORMAT_EXCEPTION(DATATYPE_MISALIGNMENT)
  1.2625 +                FORMAT_EXCEPTION(BREAKPOINT)
  1.2626 +                FORMAT_EXCEPTION(SINGLE_STEP)
  1.2627 +                FORMAT_EXCEPTION(ARRAY_BOUNDS_EXCEEDED)
  1.2628 +                FORMAT_EXCEPTION(FLT_DENORMAL_OPERAND)
  1.2629 +                FORMAT_EXCEPTION(FLT_DIVIDE_BY_ZERO)
  1.2630 +                FORMAT_EXCEPTION(FLT_INEXACT_RESULT)
  1.2631 +                FORMAT_EXCEPTION(FLT_INVALID_OPERATION)
  1.2632 +                FORMAT_EXCEPTION(FLT_OVERFLOW)
  1.2633 +                FORMAT_EXCEPTION(FLT_STACK_CHECK)
  1.2634 +                FORMAT_EXCEPTION(FLT_UNDERFLOW)
  1.2635 +                FORMAT_EXCEPTION(INT_DIVIDE_BY_ZERO)
  1.2636 +                FORMAT_EXCEPTION(INT_OVERFLOW)
  1.2637 +                FORMAT_EXCEPTION(PRIV_INSTRUCTION)
  1.2638 +                FORMAT_EXCEPTION(IN_PAGE_ERROR)
  1.2639 +                FORMAT_EXCEPTION(ILLEGAL_INSTRUCTION)
  1.2640 +                FORMAT_EXCEPTION(NONCONTINUABLE_EXCEPTION)
  1.2641 +                FORMAT_EXCEPTION(STACK_OVERFLOW)
  1.2642 +                FORMAT_EXCEPTION(INVALID_DISPOSITION)
  1.2643 +                FORMAT_EXCEPTION(GUARD_PAGE)
  1.2644 +                FORMAT_EXCEPTION(INVALID_HANDLE)
  1.2645 +              #if defined(EXCEPTION_POSSIBLE_DEADLOCK) && defined(STATUS_POSSIBLE_DEADLOCK) // This type seems to be non-existant in practice.
  1.2646 +                FORMAT_EXCEPTION(POSSIBLE_DEADLOCK)
  1.2647 +              #endif
  1.2648 +            }
  1.2649 +
  1.2650 +            // If not one of the "known" exceptions, try to get the string from NTDLL.DLL's message table.
  1.2651 +            if(exceptionInfo.exceptionDescription[0] == 0)
  1.2652 +            {
  1.2653 +                char addressStr[24];
  1.2654 +                SprintfAddress(addressStr, OVR_ARRAY_COUNT(addressStr), exceptionInfo.pExceptionMemoryAddress);
  1.2655 +
  1.2656 +                #if !defined(OVR_OS_CONSOLE) // If FormatMessage is supported...
  1.2657 +                    char  buffer[384];
  1.2658 +                    DWORD capacity = OVR_ARRAY_COUNT(buffer);
  1.2659 +
  1.2660 +                    const size_t length = (size_t)FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE, 
  1.2661 +                                                       GetModuleHandleW(L"NTDLL.DLL"), exceptionInfo.exceptionRecord.ExceptionCode, 0, buffer, capacity, nullptr);
  1.2662 +                    if(length)
  1.2663 +                        OVR::OVR_snprintf(exceptionInfo.exceptionDescription, OVR_ARRAY_COUNT(exceptionInfo.exceptionDescription),
  1.2664 +                                          "%s at instruction %s", buffer, addressStr);
  1.2665 +                #endif
  1.2666 +
  1.2667 +                // If everything else failed just show the hex code.
  1.2668 +                if(exceptionInfo.exceptionDescription[0] == 0)
  1.2669 +                    OVR::OVR_snprintf(exceptionInfo.exceptionDescription, OVR_ARRAY_COUNT(exceptionInfo.exceptionDescription), 
  1.2670 +                                      "Unknown exception 0x%08x at instruction %s", exceptionInfo.exceptionRecord.ExceptionCode, addressStr);
  1.2671 +            }
  1.2672 +        }
  1.2673 +
  1.2674 +    #elif defined(OVR_OS_APPLE)
  1.2675 +        struct MachExceptionInfo
  1.2676 +        {
  1.2677 +            static const char* GetCPUExceptionIdString(uint32_t cpuExceptionId)
  1.2678 +            {
  1.2679 +                const char* id;
  1.2680 +
  1.2681 +                #if defined(OVR_CPU_X86) || defined(OVR_CPU_X86_64)
  1.2682 +                    switch (cpuExceptionId)
  1.2683 +                    {
  1.2684 +                        case  0: id = "integer div/0";               break;
  1.2685 +                        case  1: id = "breakpoint fault";            break;
  1.2686 +                        case  2: id = "non-maskable interrupt";      break;
  1.2687 +                        case  3: id = "int 3";                       break;
  1.2688 +                        case  4: id = "overflow";                    break;
  1.2689 +                        case  5: id = "bounds check failure";        break;
  1.2690 +                        case  6: id = "invalid instruction";         break;
  1.2691 +                        case  7: id = "coprocessor unavailable";     break;
  1.2692 +                        case  8: id = "exception within exception";  break;
  1.2693 +                        case  9: id = "coprocessor segment overrun"; break;
  1.2694 +                        case 10: id = "invalid task switch";         break;
  1.2695 +                        case 11: id = "segment not present";         break;
  1.2696 +                        case 12: id = "stack exception";             break;
  1.2697 +                        case 13: id = "general protection fault";    break;
  1.2698 +                        case 14: id = "page fault";                  break;
  1.2699 +                        case 16: id = "coprocessor error";           break;
  1.2700 +                        default: id = "<unknown>";                   break;
  1.2701 +                    }
  1.2702 +                #else
  1.2703 +                    // To do: Support ARM or others.
  1.2704 +                #endif
  1.2705 +                
  1.2706 +                return id;
  1.2707 +            }
  1.2708 +
  1.2709 +            static const char* GetMachExceptionTypeString(uint64_t exceptionCause)
  1.2710 +            {
  1.2711 +                switch (exceptionCause)
  1.2712 +                {
  1.2713 +                    case EXC_ARITHMETIC:      return "EXC_ARITHMETIC";
  1.2714 +                    case EXC_BAD_ACCESS:      return "EXC_BAD_ACCESS";
  1.2715 +                    case EXC_BAD_INSTRUCTION: return "EXC_BAD_INSTRUCTION";
  1.2716 +                    case EXC_BREAKPOINT:      return "EXC_BREAKPOINT";
  1.2717 +                    case EXC_CRASH:           return "EXC_CRASH";
  1.2718 +                    case EXC_EMULATION:       return "EXC_EMULATION";
  1.2719 +                    case EXC_MACH_SYSCALL:    return "EXC_MACH_SYSCALL";
  1.2720 +                    case EXC_RPC_ALERT:       return "EXC_RPC_ALERT";
  1.2721 +                    case EXC_SOFTWARE:        return "EXC_SOFTWARE";
  1.2722 +                    case EXC_SYSCALL:         return "EXC_SYSCALL";
  1.2723 +                };
  1.2724 +
  1.2725 +                return "EXC_<unknown>";
  1.2726 +            }
  1.2727 +            
  1.2728 +            static const char* GetMachExceptionIdString(uint64_t machExceptionId, uint64_t code0)
  1.2729 +            {
  1.2730 +                const char* id = "<unknown>";
  1.2731 +
  1.2732 +                #if defined(OVR_CPU_X86) || defined(OVR_CPU_X86_64)
  1.2733 +                    switch (machExceptionId)
  1.2734 +                    {
  1.2735 +                        case EXC_ARITHMETIC:
  1.2736 +                            switch (code0)
  1.2737 +                            {
  1.2738 +                                case EXC_I386_BOUND:     id = "EXC_I386_BOUND";     break;
  1.2739 +                                case EXC_I386_DIV:       id = "EXC_I386_DIV";       break;
  1.2740 +                                case EXC_I386_EMERR:     id = "EXC_I386_EMERR";     break;
  1.2741 +                                case EXC_I386_EXTERR:    id = "EXC_I386_EXTERR";    break;
  1.2742 +                                case EXC_I386_EXTOVR:    id = "EXC_I386_EXTOVR";    break;
  1.2743 +                                case EXC_I386_INTO:      id = "EXC_I386_INTO";      break;
  1.2744 +                                case EXC_I386_NOEXT:     id = "EXC_I386_NOEXT";     break;
  1.2745 +                                case EXC_I386_SSEEXTERR: id = "EXC_I386_SSEEXTERR"; break;
  1.2746 +                            }
  1.2747 +                            break;
  1.2748 +                            
  1.2749 +                        case EXC_BAD_INSTRUCTION:
  1.2750 +                            if(code0 == EXC_I386_INVOP)
  1.2751 +                                id = "EXC_I386_INVOP";
  1.2752 +                            break;
  1.2753 +                            
  1.2754 +                        case EXC_BREAKPOINT:
  1.2755 +                            if(code0 == EXC_I386_BPT)
  1.2756 +                                id = "EXC_I386_BPT";
  1.2757 +                            else if(code0 == EXC_I386_SGL)
  1.2758 +                                id = "EXC_I386_SGL";
  1.2759 +                            break;
  1.2760 +                    };
  1.2761 +                #else
  1.2762 +                    // To do.
  1.2763 +                #endif
  1.2764 +
  1.2765 +                return id;
  1.2766 +            }
  1.2767 +        };
  1.2768 +    
  1.2769 +        OVR::OVR_snprintf(exceptionInfo.exceptionDescription, OVR_ARRAY_COUNT(exceptionInfo.exceptionDescription), 
  1.2770 +                            "Mach exception type: %llu (%s)\r\n", exceptionInfo.exceptionType, MachExceptionInfo::GetMachExceptionTypeString(exceptionInfo.exceptionType));
  1.2771 +    
  1.2772 +        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",
  1.2773 +                            exceptionInfo.cpuExceptionId, MachExceptionInfo::GetCPUExceptionIdString(exceptionInfo.cpuExceptionId), exceptionInfo.cpuExceptionIdError, exceptionInfo.pExceptionMemoryAddress);
  1.2774 +        OVR::OVR_strlcat(exceptionInfo.exceptionDescription, scratchBuffer, OVR_ARRAY_COUNT(exceptionInfo.exceptionDescription));
  1.2775 +
  1.2776 +
  1.2777 +        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],
  1.2778 +                           MachExceptionInfo::GetMachExceptionIdString(exceptionInfo.exceptionType, exceptionInfo.machExceptionDetail[0]),
  1.2779 +                           (uint64_t)exceptionInfo.machExceptionDetail[1], (uint64_t)exceptionInfo.machExceptionDetail[1]);
  1.2780 +        OVR::OVR_strlcat(exceptionInfo.exceptionDescription, scratchBuffer, OVR_ARRAY_COUNT(exceptionInfo.exceptionDescription));
  1.2781 +    #else
  1.2782 +        // To do.
  1.2783 +        exceptionInfo.exceptionDescription[0] = 0;
  1.2784 +    #endif
  1.2785 +}
  1.2786 +
  1.2787 +
  1.2788 +void ExceptionHandler::WriteReportLine(const char* pLine)
  1.2789 +{
  1.2790 +    fwrite(pLine, strlen(pLine), 1, file);
  1.2791 +}
  1.2792 +
  1.2793 +
  1.2794 +void ExceptionHandler::WriteReportLineF(const char* format, ...)
  1.2795 +{
  1.2796 +    va_list args;
  1.2797 +    va_start(args, format);
  1.2798 +    int length = OVR_vsnprintf(scratchBuffer, OVR_ARRAY_COUNT(scratchBuffer), format, args);
  1.2799 +    if(length >= (int)OVR_ARRAY_COUNT(scratchBuffer))     // If we didn't have enough space...
  1.2800 +        length = (OVR_ARRAY_COUNT(scratchBuffer) - 1);    // ... use what we have.
  1.2801 +    va_end(args);
  1.2802 +    
  1.2803 +    fwrite(scratchBuffer, length, 1, file);
  1.2804 +}
  1.2805 +
  1.2806 +
  1.2807 +// Thread <name> <handle> <id>
  1.2808 +// 0   <module> <address> <function> <file>:<line>
  1.2809 +// 1   <module> <address> <function> <file>:<line>
  1.2810 +// . . .
  1.2811 +//
  1.2812 +void ExceptionHandler::WriteThreadCallstack(ThreadHandle threadHandle, ThreadSysId threadSysId, const char* additionalInfo)
  1.2813 +{
  1.2814 +    // We intentionally do not directly use the SymbolInfo::ReportThreadCallstack function because that function allocates memory, 
  1.2815 +    // which we cannot do due to possibly being within an exception handler.
  1.2816 +
  1.2817 +    // Print the header
  1.2818 +    char    threadName[32];
  1.2819 +    char    threadHandleStr[32];
  1.2820 +    char    threadSysIdStr[32];
  1.2821 +    char    stackBaseStr[24];
  1.2822 +    char    stackLimitStr[24];
  1.2823 +    char    stackCurrentStr[24];
  1.2824 +    void*   pStackBase;
  1.2825 +    void*   pStackLimit;
  1.2826 +    bool    isExceptionThread = (threadSysId == exceptionInfo.threadSysId);
  1.2827 +
  1.2828 +    #if defined(OVR_OS_MS) && (OVR_PTR_SIZE == 8)
  1.2829 +      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. 
  1.2830 +    #elif defined(OVR_OS_MS)
  1.2831 +      void* pStackCurrent = (threadSysId == exceptionInfo.threadSysId) ? (void*)exceptionInfo.cpuContext.Esp : nullptr;
  1.2832 +    #elif defined(OVR_OS_MAC) && (OVR_PTR_SIZE == 8)
  1.2833 +      void* pStackCurrent = (threadSysId == exceptionInfo.threadSysId) ? (void*)exceptionInfo.cpuContext.threadState.uts.ts64.__rsp : nullptr;
  1.2834 +    #elif defined(OVR_OS_MAC)
  1.2835 +      void* pStackCurrent = (threadSysId == exceptionInfo.threadSysId) ? (void*)exceptionInfo.cpuContext.threadState.uts.ts32.__esp : nullptr;
  1.2836 +    #elif defined(OVR_OS_LINUX)
  1.2837 +      void* pStackCurrent = nullptr; // To do.
  1.2838 +    #endif
  1.2839 +
  1.2840 +    OVR::GetThreadStackBounds(pStackBase, pStackLimit, threadHandle);
  1.2841 +
  1.2842 +    OVR::Thread::GetThreadName(threadName, OVR_ARRAY_COUNT(threadName), threadName);
  1.2843 +    SprintfThreadHandle(threadHandleStr,  OVR_ARRAY_COUNT(threadHandleStr), threadHandle);
  1.2844 +    SprintfThreadSysId(threadSysIdStr, OVR_ARRAY_COUNT(threadSysIdStr), threadSysId);
  1.2845 +    SprintfAddress(stackBaseStr, OVR_ARRAY_COUNT(stackBaseStr), pStackBase);
  1.2846 +    SprintfAddress(stackLimitStr, OVR_ARRAY_COUNT(stackLimitStr), pStackLimit);
  1.2847 +    SprintfAddress(stackCurrentStr, OVR_ARRAY_COUNT(stackCurrentStr), pStackCurrent);
  1.2848 +
  1.2849 +    if(threadName[0])
  1.2850 +        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 : "");
  1.2851 +    else
  1.2852 +        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 : "");
  1.2853 +
  1.2854 +    // Print the backtrace info
  1.2855 +    void*       addressArray[64];
  1.2856 +    size_t      addressCount = symbolLookup.GetBacktraceFromThreadSysId(addressArray, OVR_ARRAY_COUNT(addressArray), 0, threadSysId);
  1.2857 +    SymbolInfo  symbolInfo;
  1.2858 +    const char* pModuleName;
  1.2859 +    size_t      backtraceSkipCount = 0;
  1.2860 +
  1.2861 +    if(isExceptionThread)
  1.2862 +    {
  1.2863 +        // If this thread is the exception thread, skip some frames.
  1.2864 +        #if defined(OVR_OS_MS)
  1.2865 +            size_t i, iEnd = MIN(16, addressCount);
  1.2866 +
  1.2867 +            for(i = 0; i < iEnd; i++)
  1.2868 +            {
  1.2869 +                symbolLookup.LookupSymbol((uint64_t)addressArray[i], symbolInfo);
  1.2870 +                if(strstr(symbolInfo.function, "UserExceptionDispatcher") != nullptr)
  1.2871 +                    break;
  1.2872 +            }
  1.2873 +
  1.2874 +            if(i < iEnd) // If found...
  1.2875 +                backtraceSkipCount = i;
  1.2876 +            else if(addressCount >= 9)      // Else default to 9, which is coincidentally what works.
  1.2877 +                backtraceSkipCount = 9;
  1.2878 +            else
  1.2879 +                backtraceSkipCount = 0;
  1.2880 +
  1.2881 +            addressArray[backtraceSkipCount] = exceptionInfo.pExceptionInstructionAddress;
  1.2882 +        #endif
  1.2883 +    }
  1.2884 +
  1.2885 +    if(addressCount == 0)
  1.2886 +    {
  1.2887 +        WriteReportLine("<Unable to read backtrace>\r\n\r\n");
  1.2888 +    }
  1.2889 +    else
  1.2890 +    {
  1.2891 +        for(size_t i = backtraceSkipCount; i < addressCount; ++i)
  1.2892 +        {
  1.2893 +            symbolLookup.LookupSymbol((uint64_t)addressArray[i], symbolInfo);
  1.2894 +
  1.2895 +            if(symbolInfo.pModuleInfo && symbolInfo.pModuleInfo->name[0])
  1.2896 +                pModuleName = symbolInfo.pModuleInfo->name;
  1.2897 +            else
  1.2898 +                pModuleName = "(unknown module)";
  1.2899 +
  1.2900 +            char addressStr[24];
  1.2901 +            SprintfAddress(addressStr, OVR_ARRAY_COUNT(addressStr), addressArray[i]);
  1.2902 +
  1.2903 +            if(symbolInfo.filePath[0])
  1.2904 +                WriteReportLineF("%-2u %-24s %s %s+%d %s:%d\r\n%s", (unsigned)i, pModuleName, addressStr,
  1.2905 +                                    symbolInfo.function, symbolInfo.functionOffset, symbolInfo.filePath,
  1.2906 +                                    symbolInfo.fileLineNumber, (i + 1) == addressCount ? "\r\n" : "");
  1.2907 +            else
  1.2908 +                WriteReportLineF("%-2u %-24s %s %s+%d\r\n%s", (unsigned)i, pModuleName, addressStr,
  1.2909 +                                    symbolInfo.function, symbolInfo.functionOffset, (i + 1) == addressCount ? "\r\n" : ""); // If this is the last line, append another \r\n.
  1.2910 +        }
  1.2911 +    }
  1.2912 +}
  1.2913 +
  1.2914 +
  1.2915 +void ExceptionHandler::WriteReport()
  1.2916 +{
  1.2917 +    // It's important that we don't allocate any memory here if we can help it.
  1.2918 +    using namespace OVR;
  1.2919 +
  1.2920 +    if(strstr(reportFilePath, "%s")) // If the user-specified file path includes a date/time component...
  1.2921 +    {
  1.2922 +        char dateTimeBuffer[64];
  1.2923 +        FormatDateTime(dateTimeBuffer, OVR_ARRAY_COUNT(dateTimeBuffer), exceptionInfo.timeVal, true, true, false, true);
  1.2924 +        OVR_snprintf(reportFilePathActual, OVR_ARRAY_COUNT(reportFilePathActual), reportFilePath, dateTimeBuffer);
  1.2925 +    }
  1.2926 +    else
  1.2927 +    {
  1.2928 +        OVR_strlcpy(reportFilePathActual, reportFilePath, OVR_ARRAY_COUNT(reportFilePathActual));
  1.2929 +    }
  1.2930 +
  1.2931 +    file = fopen(reportFilePathActual, "w");
  1.2932 +    OVR_ASSERT(file != nullptr);
  1.2933 +    if(!file)
  1.2934 +        return;
  1.2935 +
  1.2936 +    symbolLookup.Initialize();
  1.2937 +
  1.2938 +    {
  1.2939 +        // Exception information
  1.2940 +        WriteReportLine("Exception Info\r\n");
  1.2941 +
  1.2942 +        WriteReportLineF("Exception report file: %s\r\n", reportFilePathActual);
  1.2943 +
  1.2944 +        #if defined(OVR_OS_MS)
  1.2945 +            if(miniDumpFilePath[0])
  1.2946 +                WriteReportLineF("Exception minidump file: %s\r\n", minidumpFilePathActual);
  1.2947 +        #endif
  1.2948 +
  1.2949 +        char dateTimeBuffer[64];
  1.2950 +        FormatDateTime(dateTimeBuffer, OVR_ARRAY_COUNT(dateTimeBuffer), exceptionInfo.timeVal, true, true, false, false);
  1.2951 +        WriteReportLineF("Time (GMT): %s\r\n", dateTimeBuffer);
  1.2952 +
  1.2953 +        FormatDateTime(dateTimeBuffer, OVR_ARRAY_COUNT(scratchBuffer), exceptionInfo.timeVal, true, true, true, false);
  1.2954 +        WriteReportLineF("Time (local): %s\r\n", dateTimeBuffer);
  1.2955 +        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.
  1.2956 +
  1.2957 +        SprintfThreadHandle(scratchBuffer, OVR_ARRAY_COUNT(scratchBuffer), exceptionInfo.threadHandle);
  1.2958 +        OVR_strlcat(scratchBuffer, "\r\n", OVR_ARRAY_COUNT(scratchBuffer));
  1.2959 +        WriteReportLine("Thread handle: ");
  1.2960 +        WriteReportLine(scratchBuffer);
  1.2961 +
  1.2962 +        SprintfThreadSysId(scratchBuffer, OVR_ARRAY_COUNT(scratchBuffer), exceptionInfo.threadSysId);
  1.2963 +        OVR_strlcat(scratchBuffer, "\r\n", OVR_ARRAY_COUNT(scratchBuffer));
  1.2964 +        WriteReportLine("Thread sys id: ");
  1.2965 +        WriteReportLine(scratchBuffer);
  1.2966 +
  1.2967 +        char addressStr[24];
  1.2968 +        SprintfAddress(addressStr, OVR_ARRAY_COUNT(addressStr), exceptionInfo.pExceptionInstructionAddress);
  1.2969 +        WriteReportLineF("Exception instruction address: %s (see callstack below)\r\n", addressStr);
  1.2970 +        WriteReportLineF("Exception description: %s\r\n", exceptionInfo.exceptionDescription);
  1.2971 +
  1.2972 +        if(symbolLookup.LookupSymbol((uint64_t)exceptionInfo.pExceptionInstructionAddress, exceptionInfo.symbolInfo))
  1.2973 +        {
  1.2974 +            if(exceptionInfo.symbolInfo.filePath[0])
  1.2975 +                WriteReportLineF("Exception location: %s (%d)\r\n", exceptionInfo.symbolInfo.filePath, exceptionInfo.symbolInfo.fileLineNumber);
  1.2976 +            else
  1.2977 +                WriteReportLineF("Exception location: %s (%d)\r\n", exceptionInfo.symbolInfo.function, exceptionInfo.symbolInfo.functionOffset);
  1.2978 +        }
  1.2979 +
  1.2980 +        // To consider: print exceptionInfo.cpuContext registers 
  1.2981 +    }
  1.2982 +
  1.2983 +    // OVR information
  1.2984 +    WriteReportLine("\r\nOVR Info\r\n");
  1.2985 +    WriteReportLineF("OVR time: %f\r\n", ovr_GetTimeInSeconds());
  1.2986 +    WriteReportLineF("OVR version: %s\r\n", ovr_GetVersionString());
  1.2987 +
  1.2988 +    // OVR util information
  1.2989 +    // The following would be useful to use if they didn't allocate memory, which we can't do.
  1.2990 +    // To do: see if we can have versions of the functions below which don't allocate memory
  1.2991 +    // or allocate it safely (e.g. use an alternative heap).
  1.2992 +    // String OVR::GetDisplayDriverVersion();
  1.2993 +    // String OVR::GetCameraDriverVersion();
  1.2994 +
  1.2995 +    // OVR HMD information
  1.2996 +    WriteReportLine("\r\nOVR HMD Info\r\n");
  1.2997 +
  1.2998 +    const OVR::List<OVR::CAPI::HMDState>& hmdStateList = OVR::CAPI::HMDState::GetHMDStateList();
  1.2999 +    const OVR::CAPI::HMDState* pHMDState = hmdStateList.GetFirst();
  1.3000 +    
  1.3001 +    if(hmdStateList.IsNull(pHMDState))
  1.3002 +    {
  1.3003 +        WriteReportLine("No HMDs found.\r\n");
  1.3004 +    }
  1.3005 +
  1.3006 +    while(!hmdStateList.IsNull(pHMDState))
  1.3007 +    {
  1.3008 +        if(pHMDState->pProfile)
  1.3009 +        {
  1.3010 +            const char* user = pHMDState->pProfile->GetValue(OVR_KEY_USER);
  1.3011 +            
  1.3012 +            if(user)
  1.3013 +                WriteReportLineF("Profile user: %s\r\n", reportPrivacyEnabled ? "<disabled by report privacy settings>" : user);
  1.3014 +            else
  1.3015 +                WriteReportLine("Null profile user\r\n");
  1.3016 +
  1.3017 +            float NeckEyeDistance[2];
  1.3018 +            float EyeToNoseDistance[2];
  1.3019 +            float MaxEyeToPlateDist[2];
  1.3020 +            pHMDState->pProfile->GetFloatValues(OVR_KEY_NECK_TO_EYE_DISTANCE, NeckEyeDistance, 2);
  1.3021 +            pHMDState->pProfile->GetFloatValues(OVR_KEY_EYE_TO_NOSE_DISTANCE, EyeToNoseDistance, 2);
  1.3022 +            pHMDState->pProfile->GetFloatValues(OVR_KEY_MAX_EYE_TO_PLATE_DISTANCE, MaxEyeToPlateDist, 2);
  1.3023 +            
  1.3024 +            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",
  1.3025 +                        pHMDState->pProfile->GetFloatValue(OVR_KEY_PLAYER_HEIGHT, 0.f),
  1.3026 +                        pHMDState->pProfile->GetFloatValue(OVR_KEY_EYE_HEIGHT, 0.f),
  1.3027 +                        pHMDState->pProfile->GetFloatValue(OVR_KEY_IPD, 0.f),
  1.3028 +                        NeckEyeDistance[0], NeckEyeDistance[1],
  1.3029 +                        pHMDState->pProfile->GetIntValue(OVR_KEY_EYE_RELIEF_DIAL, 0),
  1.3030 +                        EyeToNoseDistance[0], EyeToNoseDistance[1],
  1.3031 +                        MaxEyeToPlateDist[0], MaxEyeToPlateDist[1],
  1.3032 +                        pHMDState->pProfile->GetBoolValue(OVR_KEY_CUSTOM_EYE_RENDER, false) ? "yes" : "no");
  1.3033 +            
  1.3034 +            // Not currently used:
  1.3035 +            // OVR_KEY_NAME
  1.3036 +            // OVR_KEY_GENDER
  1.3037 +            // OVR_KEY_EYE_CUP
  1.3038 +            // OVR_KEY_CAMERA_POSITION
  1.3039 +        }
  1.3040 +        else
  1.3041 +        {
  1.3042 +            WriteReportLine("Null HMD profile\r\n");
  1.3043 +        }
  1.3044 +        
  1.3045 +        if(pHMDState->pHmdDesc) // This should usually be true.
  1.3046 +        {
  1.3047 +            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",
  1.3048 +                            0, (unsigned)pHMDState->pHmdDesc->Type, pHMDState->pHmdDesc->ProductName, pHMDState->pHmdDesc->Manufacturer, pHMDState->pHmdDesc->VendorId, 
  1.3049 +                            pHMDState->pHmdDesc->ProductId, pHMDState->pHmdDesc->SerialNumber, pHMDState->pHmdDesc->FirmwareMajor, pHMDState->pHmdDesc->FirmwareMinor, 
  1.3050 +                            pHMDState->pHmdDesc->Resolution.w, pHMDState->pHmdDesc->Resolution.h, pHMDState->pHmdDesc->DisplayDeviceName, pHMDState->pHmdDesc->DisplayId);
  1.3051 +
  1.3052 +            // HSW display state
  1.3053 +            ovrHSWDisplayState hswDS;
  1.3054 +            ovrHmd_GetHSWDisplayState(pHMDState->pHmdDesc, &hswDS);
  1.3055 +            WriteReportLineF("HSW displayed for hmd: %s\r\n", hswDS.Displayed ? "yes" : "no");
  1.3056 +        }
  1.3057 +
  1.3058 +        char threadIdStr[24];
  1.3059 +        SprintfAddress(threadIdStr, OVR_ARRAY_COUNT(threadIdStr), pHMDState->BeginFrameThreadId);
  1.3060 +
  1.3061 +        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",
  1.3062 +                    pHMDState->EnabledHmdCaps, pHMDState->EnabledServiceHmdCaps, pHMDState->LatencyTestActive ? "yes" : "no", pHMDState->LastFrameTimeSeconds, pHMDState->LastGetFrameTimeSeconds, pHMDState->RenderingConfigured ? "yes" : "no",
  1.3063 +                    pHMDState->BeginFrameCalled ? "yes" : "no", threadIdStr);
  1.3064 +
  1.3065 +        if(pHMDState->pLastError)
  1.3066 +        {
  1.3067 +            WriteReportLineF("OVR last error for hmd: %s\r\n", pHMDState->pLastError);
  1.3068 +        }
  1.3069 +
  1.3070 +        pHMDState = hmdStateList.GetNext(pHMDState);
  1.3071 +    }
  1.3072 +
  1.3073 +    #if defined(OVR_OS_WIN32)
  1.3074 +        {
  1.3075 +            WriteReportLine("\r\nApp Info\r\n");
  1.3076 +
  1.3077 +            // Print the app path.
  1.3078 +            char appPath[MAX_PATH];
  1.3079 +            GetCurrentProcessFilePath(appPath, OVR_ARRAY_COUNT(appPath));
  1.3080 +            WriteReportLineF("Process path: %s\r\n", appPath);
  1.3081 +
  1.3082 +            #if (OVR_PTR_SIZE == 4)
  1.3083 +                WriteReportLine("App format: 32 bit\r\n");
  1.3084 +            #else
  1.3085 +                WriteReportLine("App format: 64 bit\r\n");
  1.3086 +            #endif
  1.3087 +            
  1.3088 +            // Print the app version
  1.3089 +            wchar_t pathW[MAX_PATH] = {};
  1.3090 +            GetModuleFileNameW(0, pathW, (DWORD)OVR_ARRAY_COUNT(pathW));
  1.3091 +            DWORD dwUnused;
  1.3092 +            DWORD dwSize = GetFileVersionInfoSizeW(pathW, &dwUnused);
  1.3093 +            scratchBuffer[0] = 0;
  1.3094 +
  1.3095 +            if(dwSize > 0)
  1.3096 +            {
  1.3097 +                void* const pVersionData = SafeMMapAlloc(dwSize);
  1.3098 +
  1.3099 +                if(pVersionData)
  1.3100 +                {
  1.3101 +                    if(GetFileVersionInfoW(pathW, 0, dwSize, pVersionData))
  1.3102 +                    {
  1.3103 +                        VS_FIXEDFILEINFO* pFFI;
  1.3104 +                        UINT size;
  1.3105 +
  1.3106 +                        if(VerQueryValueA(pVersionData, "\\", (void**)&pFFI, &size))
  1.3107 +                        {
  1.3108 +                            WriteReportLineF("App version: %u.%u.%u.%u\r\n",
  1.3109 +                                            HIWORD(pFFI->dwFileVersionMS), LOWORD(pFFI->dwFileVersionMS), 
  1.3110 +                                            HIWORD(pFFI->dwFileVersionLS), LOWORD(pFFI->dwFileVersionLS));
  1.3111 +                        }
  1.3112 +                    }
  1.3113 +
  1.3114 +                    SafeMMapFree(pVersionData, dwSize);
  1.3115 +                }
  1.3116 +            }
  1.3117 +
  1.3118 +            if(!scratchBuffer[0]) // If version info couldn't be found or read...
  1.3119 +                WriteReportLine("App version info not present\r\n");
  1.3120 +        }
  1.3121 +
  1.3122 +        {
  1.3123 +            WriteReportLine("\r\nSystem Info\r\n");
  1.3124 +
  1.3125 +            OSVERSIONINFOEXW vi;
  1.3126 +            memset(&vi, 0, sizeof(vi));
  1.3127 +            vi.dwOSVersionInfoSize = sizeof(vi);
  1.3128 +            GetVersionExW((LPOSVERSIONINFOW)&vi); // Cast to the older type.
  1.3129 +
  1.3130 +            char osVersionName[256];
  1.3131 +            GetOSVersionName(osVersionName, OVR_ARRAY_COUNT(osVersionName));
  1.3132 +            WriteReportLineF("OS name: %s, version: %u.%u build %u, %s, platform id: %u, service pack: %ls\r\n",
  1.3133 +                            osVersionName, vi.dwMajorVersion, vi.dwMinorVersion, vi.dwBuildNumber, Is64BitOS() ? "64 bit" : "32 bit",
  1.3134 +                            vi.dwPlatformId, vi.szCSDVersion[0] ? vi.szCSDVersion : L"<none>");
  1.3135 +
  1.3136 +            WriteReportLineF("Debugger present: %s\r\n", OVRIsDebuggerPresent() ? "yes" : "no");
  1.3137 +
  1.3138 +            // System info
  1.3139 +            SYSTEM_INFO systemInfo;
  1.3140 +            GetNativeSystemInfo(&systemInfo);
  1.3141 +            
  1.3142 +            WriteReportLineF("Processor count: %u\r\n", systemInfo.dwNumberOfProcessors);
  1.3143 +
  1.3144 +            // Windows Vista and later:
  1.3145 +            // BOOL WINAPI GetLogicalProcessorInformation(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION Buffer, PDWORD ReturnLength);
  1.3146 +
  1.3147 +            if(systemInfo.wProcessorArchitecture == 0)
  1.3148 +                WriteReportLineF("Processor type: x86\r\n");
  1.3149 +            else if(systemInfo.wProcessorArchitecture == 9)
  1.3150 +                WriteReportLineF("Processor type: x86-64\r\n");
  1.3151 +            else if(systemInfo.wProcessorArchitecture == 10)
  1.3152 +                WriteReportLineF("Processor type: x86 on x86-64\r\n");
  1.3153 +
  1.3154 +            WriteReportLineF("Processor level: %u\r\n", systemInfo.wProcessorLevel);
  1.3155 +            WriteReportLineF("Processor revision: %u\r\n", systemInfo.wProcessorRevision);
  1.3156 +
  1.3157 +            // Memory information
  1.3158 +            MEMORYSTATUSEX memoryStatusEx;
  1.3159 +            memset(&memoryStatusEx, 0, sizeof(memoryStatusEx));
  1.3160 +            memoryStatusEx.dwLength = sizeof(memoryStatusEx);
  1.3161 +            GlobalMemoryStatusEx(&memoryStatusEx);
  1.3162 +
  1.3163 +            WriteReportLineF("Memory load: %d%%\r\n", memoryStatusEx.dwMemoryLoad);
  1.3164 +            WriteReportLineF("Total physical memory: %I64d MiB\r\n", memoryStatusEx.ullTotalPhys / (1024 * 1024)); // Or are Mebibytes equal to (1024 * 1000)
  1.3165 +            WriteReportLineF("Available physical memory: %I64d MiB\r\n", memoryStatusEx.ullAvailPhys / (1024 * 1024));
  1.3166 +            WriteReportLineF("Total page file memory: %I64d MiB\r\n", memoryStatusEx.ullTotalPageFile / (1024 * 1024));
  1.3167 +            WriteReportLineF("Available page file memory: %I64d MiB\r\n", memoryStatusEx.ullAvailPageFile / (1024 * 1024));
  1.3168 +            WriteReportLineF("Total virtual memory: %I64d MiB\r\n", memoryStatusEx.ullTotalVirtual / (1024 * 1024));
  1.3169 +            WriteReportLineF("Free virtual memory: %I64d MiB\r\n", memoryStatusEx.ullAvailVirtual / (1024 * 1024));
  1.3170 +
  1.3171 +            DISPLAY_DEVICE dd; 
  1.3172 +            memset(&dd, 0, sizeof(DISPLAY_DEVICE));
  1.3173 +            dd.cb = sizeof(DISPLAY_DEVICE);
  1.3174 +
  1.3175 +            for(int i = 0; EnumDisplayDevicesW(nullptr, (DWORD)i, &dd, EDD_GET_DEVICE_INTERFACE_NAME); ++i)
  1.3176 +            {
  1.3177 +                WriteReportLineF("Display Device %d name: %ls, context: %ls, primary: %s, mirroring: %s\r\n",
  1.3178 +                             i, dd.DeviceName, dd.DeviceString, (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) ? "yes" : "no", (dd.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) ? "yes" : "no");
  1.3179 +            }
  1.3180 +        }
  1.3181 +
  1.3182 +        // Print video card information
  1.3183 +        // http://msdn.microsoft.com/en-us/library/aa394512%28v=vs.85%29.aspx
  1.3184 +        {
  1.3185 +            IWbemLocator*         pIWbemLocator = nullptr;
  1.3186 +            BSTR                  bstrServer = nullptr;
  1.3187 +            IWbemServices*        pIWbemServices = nullptr;
  1.3188 +            BSTR                  bstrWQL  = nullptr;
  1.3189 +            BSTR                  bstrPath = nullptr;
  1.3190 +            IEnumWbemClassObject* pEnum = nullptr;
  1.3191 +
  1.3192 +            CoInitializeEx(nullptr, COINIT_MULTITHREADED);
  1.3193 +
  1.3194 +            HRESULT hr = CoCreateInstance(__uuidof(WbemLocator), nullptr, CLSCTX_INPROC_SERVER, __uuidof(IWbemLocator), (LPVOID*)&pIWbemLocator);
  1.3195 +            if(FAILED(hr))
  1.3196 +                goto End;
  1.3197 +
  1.3198 +            bstrServer = SysAllocString(L"\\\\.\\root\\cimv2");
  1.3199 +            hr = pIWbemLocator->ConnectServer(bstrServer, nullptr, nullptr, 0L, 0L, nullptr, nullptr, &pIWbemServices);
  1.3200 +            if(FAILED(hr))
  1.3201 +                goto End;
  1.3202 +
  1.3203 +            hr = CoSetProxyBlanket(pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr, RPC_C_AUTHN_LEVEL_CALL,
  1.3204 +                                    RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_DEFAULT);
  1.3205 +            if(FAILED(hr))
  1.3206 +                goto End;
  1.3207 +
  1.3208 +            bstrWQL  = SysAllocString(L"WQL");
  1.3209 +            bstrPath = SysAllocString(L"select * from Win32_VideoController");
  1.3210 +            hr = pIWbemServices->ExecQuery(bstrWQL, bstrPath, WBEM_FLAG_FORWARD_ONLY, nullptr, &pEnum);
  1.3211 +            if(FAILED(hr))
  1.3212 +                goto End;
  1.3213 +
  1.3214 +            ULONG uReturned;
  1.3215 +            IWbemClassObject* pObj = nullptr;
  1.3216 +            hr = pEnum->Next(WBEM_INFINITE, 1, &pObj, &uReturned);
  1.3217 +            if(FAILED(hr))
  1.3218 +                goto End;
  1.3219 +
  1.3220 +            WriteReportLine("\r\nDisplay adapter list\r\n");
  1.3221 +
  1.3222 +            for(unsigned i = 0; SUCCEEDED(hr) && uReturned; i++)
  1.3223 +            {
  1.3224 +                char    sString[256];
  1.3225 +                VARIANT var;
  1.3226 +
  1.3227 +                if(i > 0)
  1.3228 +                    WriteReportLine("\r\n");
  1.3229 +
  1.3230 +                WriteReportLineF("Info for display adapter %u\r\n", i);
  1.3231 +
  1.3232 +                hr = pObj->Get(L"Name", 0, &var, nullptr, nullptr);
  1.3233 +                if(SUCCEEDED(hr))
  1.3234 +                {
  1.3235 +                    WideCharToMultiByte(CP_ACP, 0, var.bstrVal, -1, sString, sizeof(sString), nullptr, nullptr);
  1.3236 +                    WriteReportLineF("Display Adapter Name: %s\r\n", sString);
  1.3237 +                }
  1.3238 +
  1.3239 +                hr = pObj->Get(L"AdapterRAM", 0, &var, nullptr, nullptr);
  1.3240 +                if(SUCCEEDED(hr))
  1.3241 +                {
  1.3242 +                    WriteReportLineF("Display Adapter RAM: %u %s\r\n",
  1.3243 +                            ((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"));
  1.3244 +                }
  1.3245 +
  1.3246 +                hr = pObj->Get(L"DeviceID", 0, &var, nullptr, nullptr);
  1.3247 +                if(SUCCEEDED(hr))
  1.3248 +                {
  1.3249 +                    WideCharToMultiByte(CP_ACP, 0, var.bstrVal, -1, sString, sizeof(sString), nullptr, nullptr);
  1.3250 +                    WriteReportLineF("Display Adapter DeviceID: %s\r\n", sString);
  1.3251 +                }
  1.3252 +
  1.3253 +                hr = pObj->Get(L"DriverVersion", 0, &var, nullptr, nullptr);
  1.3254 +                if(SUCCEEDED(hr))
  1.3255 +                {
  1.3256 +                    WideCharToMultiByte(CP_ACP, 0, var.bstrVal, -1, sString, sizeof(sString), nullptr, nullptr);
  1.3257 +                    WriteReportLineF("Display Adapter DriverVersion: %s\r\n", sString);
  1.3258 +                }
  1.3259 +
  1.3260 +                hr = pObj->Get(L"DriverDate", 0, &var, nullptr, nullptr);
  1.3261 +                if(SUCCEEDED(hr))
  1.3262 +                {
  1.3263 +                    // http://technet.microsoft.com/en-us/library/ee156576.aspx
  1.3264 +                    wchar_t year[5] = { var.bstrVal[0], var.bstrVal[1], var.bstrVal[2], var.bstrVal[3], 0 };
  1.3265 +                    wchar_t month[3] = { var.bstrVal[4], var.bstrVal[5], 0 };
  1.3266 +                    wchar_t monthDay[3] = { var.bstrVal[6], var.bstrVal[7], 0 };
  1.3267 +                    
  1.3268 +                    WriteReportLineF("Display Adapter DriverDate (US format): %ls/%ls/%ls\r\n", month, monthDay, year);
  1.3269 +                }
  1.3270 +
  1.3271 +                // VideoProcessor
  1.3272 +                hr = pObj->Get(L"VideoProcessor", 0, &var, nullptr, nullptr);
  1.3273 +                if(SUCCEEDED(hr))
  1.3274 +                {
  1.3275 +                    WideCharToMultiByte(CP_ACP, 0, var.bstrVal, -1, sString, sizeof(sString), nullptr, nullptr);
  1.3276 +                    WriteReportLineF("Display Adapter VideoProcessor %s\r\n", sString);
  1.3277 +                }
  1.3278 +
  1.3279 +                hr = pObj->Get(L"VideoModeDescription", 0, &var, nullptr, nullptr);
  1.3280 +                if(SUCCEEDED(hr))
  1.3281 +                {
  1.3282 +                    WideCharToMultiByte(CP_ACP, 0, var.bstrVal, -1, sString, sizeof(sString), nullptr, nullptr);
  1.3283 +                    WriteReportLineF("Display Adapter VideoModeDescription: %s\r\n", sString);
  1.3284 +                }
  1.3285 +
  1.3286 +                pObj->Release();
  1.3287 +
  1.3288 +                hr = pEnum->Next(WBEM_INFINITE, 1, &pObj, &uReturned);
  1.3289 +            }
  1.3290 +
  1.3291 +            End:
  1.3292 +            if(pEnum)
  1.3293 +                pEnum->Release();
  1.3294 +            if(bstrPath)
  1.3295 +                SysFreeString(bstrPath);
  1.3296 +            if(bstrWQL)
  1.3297 +                SysFreeString(bstrWQL);
  1.3298 +            if(pIWbemServices)
  1.3299 +                pIWbemServices->Release();
  1.3300 +            if(bstrServer)
  1.3301 +                SysFreeString(bstrServer);
  1.3302 +            if(pIWbemLocator)
  1.3303 +                pIWbemLocator->Release();
  1.3304 +
  1.3305 +            CoUninitialize();
  1.3306 +        }
  1.3307 +
  1.3308 +        {
  1.3309 +            // Print a list of threads.
  1.3310 +            DWORD  currentProcessId = GetCurrentProcessId();
  1.3311 +            HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, currentProcessId); // ICreateToolhelp32Snapshot actually ignores currentProcessId.
  1.3312 +        
  1.3313 +            if(hThreadSnap != INVALID_HANDLE_VALUE) 
  1.3314 +            {
  1.3315 +                THREADENTRY32 te32;
  1.3316 +                te32.dwSize = sizeof(THREADENTRY32); 
  1.3317 +
  1.3318 +                if(Thread32First(hThreadSnap, &te32)) 
  1.3319 +                {
  1.3320 +                    WriteReportLine("\r\nThread list\r\n");
  1.3321 +
  1.3322 +                    do {
  1.3323 +                        if(te32.th32OwnerProcessID == currentProcessId)
  1.3324 +                        {
  1.3325 +                            HANDLE hThread = ConvertThreadSysIdToThreadHandle(te32.th32ThreadID);
  1.3326 +
  1.3327 +                            if(hThread)
  1.3328 +                            {
  1.3329 +                                char buffer[96]; // Can't use scratchBuffer, because it's used by WriteThreadCallstack.
  1.3330 +                                OVR_snprintf(buffer, OVR_ARRAY_COUNT(buffer), "base priority: %ld, delta priority: %ld", te32.tpBasePri, te32.tpDeltaPri);
  1.3331 + 
  1.3332 +                                bool threadIsExceptionThread = (te32.th32ThreadID == (DWORD)exceptionInfo.threadSysId);
  1.3333 +                                if(threadIsExceptionThread)
  1.3334 +                                    OVR_strlcat(buffer, ", exception thread", OVR_ARRAY_COUNT(buffer));
  1.3335 +
  1.3336 +                                WriteThreadCallstack(hThread, (OVR::ThreadSysId)te32.th32ThreadID, buffer);
  1.3337 +                                FreeThreadHandle(hThread);
  1.3338 +                            }
  1.3339 +                        }
  1.3340 +                    } while(Thread32Next(hThreadSnap, &te32));
  1.3341 +                }
  1.3342 +            
  1.3343 +                CloseHandle(hThreadSnap);
  1.3344 +            }
  1.3345 +        }
  1.3346 +
  1.3347 +        {
  1.3348 +            // Print a list of the current modules within this process.
  1.3349 +            // DbgHelp.dll also provides a EnumerateLoadedModules64 function.
  1.3350 +            // To do: Convert the code below to use the GetModuleInfoArray function which we now have.
  1.3351 +            #if defined(OVR_OS_CONSOLE)
  1.3352 +                struct MODULEINFO {
  1.3353 +                    LPVOID lpBaseOfDll;
  1.3354 +                    DWORD  SizeOfImage;
  1.3355 +                    LPVOID EntryPoint;
  1.3356 +                };
  1.3357 +                HMODULE hModule = LoadLibraryW(L"toolhelpx.dll");
  1.3358 +            #else
  1.3359 +                HMODULE hModule = LoadLibraryW(L"psapi.dll");
  1.3360 +            #endif
  1.3361 +
  1.3362 +            if(hModule)
  1.3363 +            {
  1.3364 +                typedef BOOL  (WINAPI * ENUMPROCESSMODULES)  (HANDLE hProcess, HMODULE* phModule, DWORD cb, LPDWORD lpcbNeeded);
  1.3365 +                typedef DWORD (WINAPI * GETMODULEBASENAME)   (HANDLE hProcess, HMODULE hModule, LPWSTR lpFilename, DWORD nSize);
  1.3366 +                typedef DWORD (WINAPI * GETMODULEFILENAMEEX) (HANDLE hProcess, HMODULE hModule, LPWSTR lpFilename, DWORD nSize);
  1.3367 +                typedef BOOL  (WINAPI * GETMODULEINFORMATION)(HANDLE hProcess, HMODULE hModule, MODULEINFO* pmi, DWORD nSize);
  1.3368 +
  1.3369 +                #if defined(OVR_OS_CONSOLE)
  1.3370 +                    ENUMPROCESSMODULES   pEnumProcessModules   = (ENUMPROCESSMODULES)  (uintptr_t)GetProcAddress(hModule, "K32EnumProcessModules");
  1.3371 +                    GETMODULEBASENAME    pGetModuleBaseName    = (GETMODULEBASENAME)   (uintptr_t)GetProcAddress(hModule, "K32GetModuleBaseNameW");
  1.3372 +                    GETMODULEFILENAMEEX  pGetModuleFileNameEx  = (GETMODULEFILENAMEEX) (uintptr_t)GetProcAddress(hModule, "K32GetModuleFileNameExW");
  1.3373 +                    GETMODULEINFORMATION pGetModuleInformation = (GETMODULEINFORMATION)(uintptr_t)GetProcAddress(hModule, "K32GetModuleInformation");
  1.3374 +                #else
  1.3375 +                    ENUMPROCESSMODULES   pEnumProcessModules   = (ENUMPROCESSMODULES)  (uintptr_t)GetProcAddress(hModule, "EnumProcessModules");
  1.3376 +                    GETMODULEBASENAME    pGetModuleBaseName    = (GETMODULEBASENAME)   (uintptr_t)GetProcAddress(hModule, "GetModuleBaseNameW");
  1.3377 +                    GETMODULEFILENAMEEX  pGetModuleFileNameEx  = (GETMODULEFILENAMEEX) (uintptr_t)GetProcAddress(hModule, "GetModuleFileNameExW");
  1.3378 +                    GETMODULEINFORMATION pGetModuleInformation = (GETMODULEINFORMATION)(uintptr_t)GetProcAddress(hModule, "GetModuleInformation");
  1.3379 +                #endif
  1.3380 +
  1.3381 +                HANDLE  hProcess = GetCurrentProcess();
  1.3382 +                HMODULE hModuleArray[200];
  1.3383 +                DWORD   cbNeeded;
  1.3384 +
  1.3385 +                if(pEnumProcessModules(hProcess, hModuleArray, sizeof(hModuleArray), &cbNeeded))
  1.3386 +                {
  1.3387 +                    size_t actualModuleCount = (cbNeeded / sizeof(HMODULE));
  1.3388 +
  1.3389 +                    if(actualModuleCount > OVR_ARRAY_COUNT(hModuleArray))  //If hModuleArray's capacity was not enough...
  1.3390 +                        actualModuleCount = OVR_ARRAY_COUNT(hModuleArray);
  1.3391 +
  1.3392 +                    // Print a header
  1.3393 +                    WriteReportLine("\r\nModule list\r\n");
  1.3394 +
  1.3395 +                    #if (OVR_PTR_SIZE == 4)
  1.3396 +                        WriteReportLine("Base        Size       Entrypoint Name                     Path\r\n");
  1.3397 +                    #else
  1.3398 +                        WriteReportLine("Base                Size               Entrypoint         Name                     Path\r\n");
  1.3399 +                    #endif
  1.3400 +
  1.3401 +                    // And go through the list one by one
  1.3402 +                    for(size_t i = 0; i < actualModuleCount; i++)
  1.3403 +                    {
  1.3404 +                        MODULEINFO mi;
  1.3405 +                        size_t     length;
  1.3406 +
  1.3407 +                        if(!pGetModuleInformation(hProcess, hModuleArray[i], &mi, sizeof(mi)))
  1.3408 +                        {
  1.3409 +                            mi.EntryPoint  = nullptr;
  1.3410 +                            mi.lpBaseOfDll = nullptr;
  1.3411 +                            mi.SizeOfImage = 0;
  1.3412 +                        }
  1.3413 +
  1.3414 +                        // Write the base name.
  1.3415 +                        wchar_t name[MAX_PATH + 3];
  1.3416 +                        name[0] = '"';
  1.3417 +                        if(pGetModuleBaseName(hProcess, hModuleArray[i], name + 1, MAX_PATH))
  1.3418 +                            length = wcslen(name);
  1.3419 +                        else
  1.3420 +                        {
  1.3421 +                            wcscpy(name + 1, L"(unknown)");
  1.3422 +                            length = 10;
  1.3423 +                        }
  1.3424 +
  1.3425 +                        name[length] = '"';
  1.3426 +                        name[length + 1] = '\0';
  1.3427 +
  1.3428 +                        // Write the path
  1.3429 +                        wchar_t path[MAX_PATH + 3];
  1.3430 +                        path[0] = '"';
  1.3431 +                        if(pGetModuleFileNameEx(hProcess, hModuleArray[i], path + 1, MAX_PATH))
  1.3432 +                            length = wcslen(path);
  1.3433 +                        else
  1.3434 +                        {
  1.3435 +                            wcscpy(path + 1, L"(unknown)");
  1.3436 +                            length = 10;
  1.3437 +                        }
  1.3438 +                        path[length]     = '"';
  1.3439 +                        path[length + 1] = '\0';
  1.3440 +
  1.3441 +                        #if (OVR_PTR_SIZE == 4)
  1.3442 +                            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);
  1.3443 +                        #else
  1.3444 +                            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);
  1.3445 +                        #endif
  1.3446 +                    }
  1.3447 +                }
  1.3448 +            }
  1.3449 +        }
  1.3450 +
  1.3451 +        {
  1.3452 +            // Print a list of processes.
  1.3453 +            // DbgHelp.dll provides a SymEnumProcesses function, but it's available with DbgHelp.dll v6.2 which doesn't ship with Windows until Windows 8.
  1.3454 +            WriteReportLine("\r\nProcess list\r\n");
  1.3455 +            
  1.3456 +            if(reportPrivacyEnabled)
  1.3457 +                WriteReportLine("Disabled by report privacy settings\r\n");
  1.3458 +            else
  1.3459 +            {
  1.3460 +                HANDLE hProcessSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  1.3461 +
  1.3462 +                if(hProcessSnapshot != INVALID_HANDLE_VALUE)
  1.3463 +                {
  1.3464 +                    PROCESSENTRY32W pe32;
  1.3465 +                    memset(&pe32, 0, sizeof(pe32));
  1.3466 +                    pe32.dwSize = sizeof(pe32);
  1.3467 +
  1.3468 +                    if(Process32FirstW(hProcessSnapshot, &pe32))
  1.3469 +                    {
  1.3470 +                        WriteReportLine("Process Id File\r\n");
  1.3471 +
  1.3472 +                        do {
  1.3473 +                            // Try to get the full path to the process, as pe32.szExeFile holds only the process file name.
  1.3474 +                            // This will typically fail with a privilege error unless this process has debug privileges: http://support.microsoft.com/kb/131065/en-us
  1.3475 +                            wchar_t filePathW[MAX_PATH];
  1.3476 +                            const wchar_t* pFilePathW = pe32.szExeFile;
  1.3477 +                            HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pe32.th32ProcessID); // With Windows Vista+ we can use PROCESS_QUERY_LIMITED_INFORMATION.
  1.3478 +                            if(hProcess)
  1.3479 +                            {
  1.3480 +                                if(GetProcessImageFileName(hProcess, filePathW, (DWORD)OVR_ARRAY_COUNT(filePathW)))
  1.3481 +                                    pFilePathW = filePathW;
  1.3482 +                            }
  1.3483 +
  1.3484 +                            WriteReportLineF("0x%08x %ls\r\n", pe32.th32ProcessID, pFilePathW);
  1.3485 +                        } while(Process32NextW(hProcessSnapshot, &pe32));
  1.3486 +                    }
  1.3487 +
  1.3488 +                    CloseHandle(hProcessSnapshot);
  1.3489 +                }
  1.3490 +                else
  1.3491 +                {
  1.3492 +                    WriteReportLine("Unable to read process list\r\n");
  1.3493 +                }
  1.3494 +            }
  1.3495 +        }
  1.3496 +
  1.3497 +    #elif defined(OVR_OS_APPLE)    
  1.3498 +
  1.3499 +        WriteReportLine("\r\nApp Info\r\n");
  1.3500 +    
  1.3501 +        // App path
  1.3502 +        const pid_t processId = getpid();
  1.3503 +        WriteReportLineF("Process id: ", "%lld (0x%llx)\r\n", (int64_t)processId, (int64_t)processId);
  1.3504 +
  1.3505 +        char appPath[PATH_MAX];
  1.3506 +        GetCurrentProcessFilePath(appPath, OVR_ARRAY_COUNT(appPath));
  1.3507 +        WriteReportLineF("Process path: %s\r\n", appPath);
  1.3508 +
  1.3509 +        #if (OVR_PTR_SIZE == 4)
  1.3510 +            WriteReportLine("App format: 32 bit\r\n");
  1.3511 +        #else
  1.3512 +            WriteReportLine("App format: 64 bit\r\n");
  1.3513 +        #endif
  1.3514 +            
  1.3515 +        // App version
  1.3516 +        // To do.
  1.3517 +
  1.3518 +        // System Info
  1.3519 +        WriteReportLine("\r\nSystem Info\r\n");
  1.3520 +
  1.3521 +        char osVersionName[256];
  1.3522 +        GetOSVersionName(osVersionName, OVR_ARRAY_COUNT(osVersionName));
  1.3523 +        WriteReportLineF("OS name: %s, %s\r\n", osVersionName, Is64BitOS() ? "64 bit" : "32 bit");
  1.3524 +
  1.3525 +        int     name[2];
  1.3526 +        int     intValue;
  1.3527 +        size_t  length;
  1.3528 +        char    tempBuffer[256];
  1.3529 +
  1.3530 +        name[0] = CTL_KERN;
  1.3531 +        name[1] = KERN_OSTYPE;
  1.3532 +        length = sizeof(tempBuffer);
  1.3533 +        tempBuffer[0] = 0;
  1.3534 +        if(sysctl(name, 2, tempBuffer, &length, nullptr, 0) == 0)
  1.3535 +        {
  1.3536 +            WriteReportLineF("KERN_OSTYPE: %s\r\n", tempBuffer);
  1.3537 +        }
  1.3538 +    
  1.3539 +        name[0] = CTL_KERN;
  1.3540 +        name[1] = KERN_OSREV;
  1.3541 +        length = sizeof(intValue);
  1.3542 +        intValue = 0;
  1.3543 +        if(sysctl(name, 2, &intValue, &length, nullptr, 0) == 0)
  1.3544 +        {
  1.3545 +            WriteReportLineF("KERN_OSREV: %d\r\n", intValue);
  1.3546 +        }
  1.3547 +    
  1.3548 +        name[0] = CTL_KERN;
  1.3549 +        name[1] = KERN_OSRELEASE;
  1.3550 +        length = sizeof(tempBuffer);
  1.3551 +        tempBuffer[0] = 0;
  1.3552 +        if(sysctl(name, 2, tempBuffer, &length, nullptr, 0) == 0)
  1.3553 +            WriteReportLineF("KERN_OSRELEASE: %s\r\n", tempBuffer);
  1.3554 +
  1.3555 +        name[0] = CTL_HW;
  1.3556 +        name[1] = HW_MACHINE;
  1.3557 +        length = sizeof(tempBuffer);
  1.3558 +        tempBuffer[0] = 0;
  1.3559 +        if(sysctl(name, 2, tempBuffer, &length, nullptr, 0) == 0)
  1.3560 +            WriteReportLineF("HW_MACHINE: %s\r\n", tempBuffer);
  1.3561 +
  1.3562 +        name[0] = CTL_HW;
  1.3563 +        name[1] = HW_MODEL;
  1.3564 +        length = sizeof(tempBuffer);
  1.3565 +        tempBuffer[0] = 0;
  1.3566 +        if(sysctl(name, 2, tempBuffer, &length, nullptr, 0) == 0)
  1.3567 +            WriteReportLineF("sHW_MODEL: %s\r\n", tempBuffer);
  1.3568 +
  1.3569 +        name[0] = CTL_HW;
  1.3570 +        name[1] = HW_NCPU;
  1.3571 +        length = sizeof(intValue);
  1.3572 +        intValue = 0;
  1.3573 +        if(sysctl(name, 2, &intValue, &length, nullptr, 0) == 0)
  1.3574 +            WriteReportLineF("HW_NCPU: %d\r\n", intValue);
  1.3575 +
  1.3576 +        length = sizeof(tempBuffer);
  1.3577 +        tempBuffer[0] = 0;
  1.3578 +        if(sysctlbyname("machdep.cpu.brand_string", &tempBuffer, &length, nullptr, 0) == 0)
  1.3579 +            WriteReportLineF("machdep.cpu.brand_string: %s\r\n", tempBuffer);
  1.3580 +
  1.3581 +        length = sizeof(tempBuffer);
  1.3582 +        tempBuffer[0] = 0;
  1.3583 +        if(sysctlbyname("hw.acpi.thermal.tz0.temperature", &tempBuffer, &length, nullptr, 0) == 0)
  1.3584 +            WriteReportLineF("hw.acpi.thermal.tz0.temperature: %s\r\n", tempBuffer);
  1.3585 +
  1.3586 +        host_basic_info_data_t hostinfo;
  1.3587 +        mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
  1.3588 +        kern_return_t          kr = host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)&hostinfo, &count);
  1.3589 +
  1.3590 +        if(kr == KERN_SUCCESS)
  1.3591 +        {
  1.3592 +            const uint64_t memoryMib = (uint64_t)hostinfo.max_mem / (1024 * 1024);
  1.3593 +            WriteReportLineF("System memory: %lld Mib (%.1f Gib)\r\n", memoryMib, (double)memoryMib / 1024);
  1.3594 +        }
  1.3595 +    
  1.3596 +        // Video card info
  1.3597 +        // To do.
  1.3598 +    
  1.3599 +        // Thread list
  1.3600 +        mach_port_t             taskSelf   = mach_task_self();
  1.3601 +        thread_act_port_array_t threadArray;
  1.3602 +        mach_msg_type_number_t  threadCount;
  1.3603 +        
  1.3604 +        kern_return_t result = task_threads(taskSelf, &threadArray, &threadCount);
  1.3605 +        
  1.3606 +        if(result == KERN_SUCCESS)
  1.3607 +        {
  1.3608 +            WriteReportLine("\r\nThread list\r\n");
  1.3609 +
  1.3610 +            for(mach_msg_type_number_t i = 0; i < threadCount; i++)
  1.3611 +            {
  1.3612 +                union TBIUnion{
  1.3613 +                    natural_t words[THREAD_INFO_MAX];
  1.3614 +                    thread_basic_info tbi;
  1.3615 +                };
  1.3616 +
  1.3617 +                TBIUnion    tbiUnion;
  1.3618 +                mach_port_t thread = threadArray[i];
  1.3619 +                pthread_t   pthread = pthread_from_mach_thread_np(thread); // We assume the thread was created through pthreads.
  1.3620 +
  1.3621 +                char threadState[32] = "unknown";
  1.3622 +                mach_msg_type_number_t threadInfoCount = THREAD_INFO_MAX;
  1.3623 +                result = thread_info(thread, THREAD_BASIC_INFO, (thread_info_t)&tbiUnion, &threadInfoCount);
  1.3624 +
  1.3625 +                if(result == KERN_SUCCESS)
  1.3626 +                {
  1.3627 +                    const char* state;
  1.3628 +                    
  1.3629 +                    switch (tbiUnion.tbi.run_state)
  1.3630 +                    {
  1.3631 +                        case TH_STATE_HALTED:          state = "halted";          break;
  1.3632 +                        case TH_STATE_RUNNING:         state = "running";         break;
  1.3633 +                        case TH_STATE_STOPPED:         state = "stopped";         break;
  1.3634 +                        case TH_STATE_UNINTERRUPTIBLE: state = "uninterruptible"; break;
  1.3635 +                        case TH_STATE_WAITING:         state = "waiting";         break;
  1.3636 +                        default:                       state = "<unknown>";       break;
  1.3637 +                    }
  1.3638 +                    
  1.3639 +                    OVR_snprintf(threadState, OVR_ARRAY_COUNT(threadState), "%s", state);
  1.3640 +                    if(tbiUnion.tbi.flags & TH_FLAGS_IDLE)
  1.3641 +                        OVR_strlcat(threadState, ", idle", sizeof(threadState));
  1.3642 +                    if(tbiUnion.tbi.flags & TH_FLAGS_SWAPPED)
  1.3643 +                        OVR_strlcat(threadState, ", swapped", sizeof(threadState));
  1.3644 +                }
  1.3645 +
  1.3646 +                thread_identifier_info threadIdentifierInfo; 
  1.3647 +                memset(&threadIdentifierInfo, 0, sizeof(threadIdentifierInfo));
  1.3648 +
  1.3649 +                mach_msg_type_number_t threadIdentifierInfoCount = THREAD_IDENTIFIER_INFO_COUNT;
  1.3650 +                thread_info(thread, THREAD_IDENTIFIER_INFO, (thread_info_t)&threadIdentifierInfo, &threadIdentifierInfoCount);
  1.3651 +
  1.3652 +                proc_threadinfo procThreadInfo; 
  1.3653 +                memset(&procThreadInfo, 0, sizeof(procThreadInfo));
  1.3654 +                result = proc_pidinfo(processId, PROC_PIDTHREADINFO, threadIdentifierInfo.thread_handle, &procThreadInfo, sizeof(procThreadInfo));
  1.3655 +                OVR_UNUSED(result);
  1.3656 +                
  1.3657 +                char buffer[256]; // Can't use scratchBuffer, because it's used by WriteThreadCallstack.
  1.3658 +                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);
  1.3659 +                
  1.3660 +                bool threadIsExceptionThread = (thread == exceptionInfo.threadSysId);
  1.3661 +                if(threadIsExceptionThread)
  1.3662 +                    OVR_strlcat(buffer, ", exception thread", OVR_ARRAY_COUNT(buffer));
  1.3663 +                
  1.3664 +                WriteThreadCallstack(pthread, thread, buffer);
  1.3665 +            }
  1.3666 +            
  1.3667 +            vm_deallocate(taskSelf, (vm_address_t)threadArray, threadCount * sizeof(thread_act_t));
  1.3668 +        }
  1.3669 +    
  1.3670 +    
  1.3671 +        WriteReportLine("\r\nModule list\r\n");
  1.3672 +
  1.3673 +        const size_t mifCapacity  = 256;
  1.3674 +        const size_t mifAllocSize = mifCapacity * sizeof(ModuleInfo);
  1.3675 +        ModuleInfo* moduleInfoArray = (ModuleInfo*)SafeMMapAlloc(mifAllocSize);
  1.3676 +    
  1.3677 +        if(moduleInfoArray)
  1.3678 +        {
  1.3679 +            #if (OVR_PTR_SIZE == 4)
  1.3680 +                WriteReportLine("Base        Size       Name                     Path\r\n");
  1.3681 +            #else
  1.3682 +                WriteReportLine("Base                Size               Name                     Path\r\n");
  1.3683 +            #endif
  1.3684 +
  1.3685 +            size_t moduleCount = symbolLookup.GetModuleInfoArray(moduleInfoArray, mifCapacity);
  1.3686 +            if(moduleCount > mifCapacity)
  1.3687 +                moduleCount = mifCapacity;
  1.3688 +            
  1.3689 +            for(size_t i = 0; i < moduleCount; i++)
  1.3690 +            {
  1.3691 +                const ModuleInfo& mi = moduleInfoArray[i];
  1.3692 +                
  1.3693 +                #if (OVR_PTR_SIZE == 4)
  1.3694 +                    WriteReportLineF("0x%08x, 0x%08x %-24s %s\r\n", (uint32_t)mi.baseAddress, (uint32_t)mi.size, mi.name, mi.filePath);
  1.3695 +                #else
  1.3696 +                    WriteReportLineF("0x%016llx 0x%016llx %-24s %s\r\n", (uint64_t)mi.baseAddress, (uint64_t)mi.size, mi.name, mi.filePath);
  1.3697 +                #endif
  1.3698 +            }
  1.3699 +            
  1.3700 +            SafeMMapFree(moduleInfoArray, mifAllocSize);
  1.3701 +        }
  1.3702 +    
  1.3703 +    
  1.3704 +        WriteReportLine("\r\nProcess list\r\n");
  1.3705 +    
  1.3706 +        if(reportPrivacyEnabled)
  1.3707 +            WriteReportLine("Disabled by report privacy settings\r\n");
  1.3708 +        else
  1.3709 +        {
  1.3710 +            WriteReportLine("Process Id File\r\n");
  1.3711 +
  1.3712 +            pid_t pidArray[1024];
  1.3713 +            int   processCount = proc_listpids(PROC_ALL_PIDS, 0, pidArray, sizeof(pidArray)); // Important that we use sizeof not OVR_ARRAY_COUNT.
  1.3714 +            char  processFilePath[PATH_MAX];
  1.3715 +
  1.3716 +            for(int i = 0; i < processCount; i++)
  1.3717 +            {
  1.3718 +                if(proc_pidpath(pidArray[i], processFilePath, sizeof(processFilePath)) > 0)
  1.3719 +                    WriteReportLineF("%-10d %s\r\n", pidArray[i], processFilePath);
  1.3720 +            }
  1.3721 +            
  1.3722 +            if(!processCount)
  1.3723 +                WriteReportLine("Unable to read process list\r\n");
  1.3724 +        }
  1.3725 +
  1.3726 +	#elif defined(OVR_OS_UNIX)
  1.3727 +         Is64BitOS();
  1.3728 +         GetCurrentProcessFilePath(nullptr, 0);
  1.3729 +         GetFileNameFromPath(nullptr);
  1.3730 +         GetOSVersionName(nullptr, 0);
  1.3731 +
  1.3732 +    #endif // OVR_OS_MS
  1.3733 +
  1.3734 +    symbolLookup.Shutdown();
  1.3735 +
  1.3736 +    fclose(file);
  1.3737 +    file = nullptr;
  1.3738 +}
  1.3739 +
  1.3740 +
  1.3741 +void ExceptionHandler::WriteMiniDump()
  1.3742 +{
  1.3743 +    if(strstr(miniDumpFilePath, "%s")) // If the user-specified file path includes a date/time component...
  1.3744 +    {
  1.3745 +        char dateTimeBuffer[64];
  1.3746 +        FormatDateTime(dateTimeBuffer, OVR_ARRAY_COUNT(dateTimeBuffer), exceptionInfo.timeVal, true, true, false, true);
  1.3747 +        OVR_snprintf(minidumpFilePathActual, OVR_ARRAY_COUNT(minidumpFilePathActual), miniDumpFilePath, dateTimeBuffer);
  1.3748 +    }
  1.3749 +    else
  1.3750 +    {
  1.3751 +        OVR_strlcpy(minidumpFilePathActual, miniDumpFilePath, OVR_ARRAY_COUNT(minidumpFilePathActual));
  1.3752 +    }
  1.3753 +
  1.3754 +    #if defined(OVR_OS_WIN32) || defined(OVR_OS_WIN64) || (defined(OVR_OS_MS) && defined(OVR_OS_CONSOLE))
  1.3755 +        #if defined(OVR_OS_CONSOLE)
  1.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 PVOID                          CallbackParam);
  1.3757 +            HMODULE hModuleDbgHelp = LoadLibraryW(L"toolhelpx.dll");
  1.3758 +        #else
  1.3759 +            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);
  1.3760 +            HMODULE hModuleDbgHelp = LoadLibraryW(L"DbgHelp.dll");
  1.3761 +        #endif
  1.3762 +
  1.3763 +        MINIDUMPWRITEDUMP pMiniDumpWriteDump = hModuleDbgHelp ? (MINIDUMPWRITEDUMP)(void*)GetProcAddress(hModuleDbgHelp, "MiniDumpWriteDump") : nullptr;
  1.3764 +
  1.3765 +        if(pMiniDumpWriteDump)
  1.3766 +        {
  1.3767 +            wchar_t miniDumpFilePathW[OVR_MAX_PATH];
  1.3768 +            OVR::UTF8Util::DecodeString(miniDumpFilePathW, minidumpFilePathActual, -1); // Problem: DecodeString provides no way to specify the destination capacity.
  1.3769 +
  1.3770 +            HANDLE hFile = CreateFileW(miniDumpFilePathW, GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_FLAG_WRITE_THROUGH, 0);
  1.3771 +
  1.3772 +            if(hFile != INVALID_HANDLE_VALUE)
  1.3773 +            {
  1.3774 +                MINIDUMP_EXCEPTION_INFORMATION minidumpExceptionInfo = { ::GetCurrentThreadId(), pExceptionPointers, TRUE };
  1.3775 +
  1.3776 +                #if defined(OVR_OS_CONSOLE)
  1.3777 +                    BOOL result = pMiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile,
  1.3778 +                                                     (MINIDUMP_TYPE)miniDumpType, &exceptionInfo, 
  1.3779 +                                                     (CONST PMINIDUMP_USER_STREAM_INFORMATION)nullptr, nullptr);
  1.3780 +                #else
  1.3781 +                    BOOL result = pMiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile,
  1.3782 +                                                     (MINIDUMP_TYPE)miniDumpFlags, &minidumpExceptionInfo, 
  1.3783 +                                                     (CONST PMINIDUMP_USER_STREAM_INFORMATION)nullptr, (CONST PMINIDUMP_CALLBACK_INFORMATION)nullptr);
  1.3784 +                #endif
  1.3785 +
  1.3786 +                OVR_ASSERT_AND_UNUSED(result, result);
  1.3787 +                CloseHandle(hFile);
  1.3788 +                hFile = 0;
  1.3789 +            }
  1.3790 +            else
  1.3791 +            {
  1.3792 +                OVR_ASSERT(pMiniDumpWriteDump);  // OVR_FAIL_F(("ExceptionHandler::WriteMiniDump: Failed to create minidump file at %s", minidumpFilePathActual));
  1.3793 +            } 
  1.3794 +        }
  1.3795 +
  1.3796 +        FreeLibrary(hModuleDbgHelp);
  1.3797 +    #else
  1.3798 +        // Some platforms support various forms or exception reports and core dumps which are automatically generated upon us
  1.3799 +        // returning from our own exception handling. We might want to put something here if we are using a custom version of
  1.3800 +        // this, such as Google Breakpad.
  1.3801 +    #endif
  1.3802 +}
  1.3803 +
  1.3804 +
  1.3805 +void ExceptionHandler::SetExceptionListener(ExceptionListener* pExceptionListener, uintptr_t userValue)
  1.3806 +{
  1.3807 +    exceptionListener = pExceptionListener;
  1.3808 +    exceptionListenerUserValue = userValue;
  1.3809 +}
  1.3810 +
  1.3811 +
  1.3812 +void ExceptionHandler::SetAppDescription(const char* pAppDescription)
  1.3813 +{
  1.3814 +    appDescription = pAppDescription;
  1.3815 +}
  1.3816 +
  1.3817 +
  1.3818 +void ExceptionHandler::SetExceptionPaths(const char* exceptionReportPath, const char* exceptionMiniDumpFilePath)
  1.3819 +{
  1.3820 +    char tempPath[OVR_MAX_PATH];
  1.3821 +
  1.3822 +    if(exceptionReportPath)
  1.3823 +    {
  1.3824 +        if(OVR_stricmp(exceptionReportPath, "default") == 0)
  1.3825 +        {
  1.3826 +            GetUserDocumentsDirectory(tempPath, OVR_ARRAY_COUNT(tempPath));
  1.3827 +            OVR::OVR_strlcat(tempPath, "Exception Report (%s).txt", OVR_ARRAY_COUNT(tempPath));
  1.3828 +            exceptionReportPath = tempPath;
  1.3829 +        }
  1.3830 +
  1.3831 +        OVR_strlcpy(reportFilePath, exceptionReportPath, OVR_ARRAY_COUNT(reportFilePath));
  1.3832 +    }
  1.3833 +    else
  1.3834 +    {
  1.3835 +        reportFilePath[0] = '\0';
  1.3836 +    }
  1.3837 +    
  1.3838 +    if(exceptionMiniDumpFilePath)
  1.3839 +    {
  1.3840 +        if(OVR_stricmp(exceptionMiniDumpFilePath, "default") == 0)
  1.3841 +        {
  1.3842 +            GetUserDocumentsDirectory(tempPath, OVR_ARRAY_COUNT(tempPath));
  1.3843 +            OVR::OVR_strlcat(tempPath, "Exception Minidump (%s).mdmp", OVR_ARRAY_COUNT(tempPath));
  1.3844 +            exceptionMiniDumpFilePath = tempPath;
  1.3845 +        }
  1.3846 +
  1.3847 +        OVR_strlcpy(miniDumpFilePath, exceptionMiniDumpFilePath, OVR_ARRAY_COUNT(miniDumpFilePath));
  1.3848 +    }
  1.3849 +    else
  1.3850 +    {
  1.3851 +        miniDumpFilePath[0] = '\0';
  1.3852 +    }
  1.3853 +}
  1.3854 +
  1.3855 +
  1.3856 +void ExceptionHandler::SetCodeBaseDirectoryPaths(const char* codeBaseDirectoryPathArray[], size_t codeBaseDirectoryPathCount)
  1.3857 +{
  1.3858 +    for(size_t i = 0, iCount = OVR::Alg::Min<size_t>(codeBaseDirectoryPathCount, OVR_ARRAY_COUNT(codeBasePathArray)); i != iCount; ++i)
  1.3859 +    {
  1.3860 +        codeBasePathArray[i] = codeBaseDirectoryPathArray[i];
  1.3861 +    }
  1.3862 +}
  1.3863 +
  1.3864 +const char* ExceptionHandler::GetExceptionUIText(const char* exceptionReportPath)
  1.3865 +{
  1.3866 +	char* uiText = nullptr;
  1.3867 +    OVR::SysFile file(exceptionReportPath, SysFile::Open_Read, SysFile::Mode_ReadWrite);
  1.3868 +
  1.3869 +    if(file.IsValid())
  1.3870 +    {
  1.3871 +        size_t length = (size_t)file.GetLength();
  1.3872 +        uiText = (char*)OVR::SafeMMapAlloc(length + 1);
  1.3873 +
  1.3874 +        if(uiText)
  1.3875 +        {
  1.3876 +            file.Read((uint8_t*)uiText, (int)length);
  1.3877 +            uiText[length] = '\0';
  1.3878 +            file.Close();
  1.3879 +            
  1.3880 +            // Currently on Mac our message box implementation is unable to display arbitrarily large amounts of text.
  1.3881 +            // So we reduce its size to a more summary version before presenting.
  1.3882 +            #if defined(OVR_OS_MAC)
  1.3883 +                struct Find { static char* PreviousChar(char* p, char c){ while(*p != c) p--; return p; } }; // Assumes the given c is present prior to p.
  1.3884 +            
  1.3885 +                // Print that the full report is at <file path>
  1.3886 +                // Exception Info section
  1.3887 +                // Exception thread callstack.
  1.3888 +                char  empty[]               = "";
  1.3889 +                char* pExceptionInfoBegin   = strstr(uiText, "Exception Info") ? strstr(uiText, "Exception Info") : empty;
  1.3890 +                char* pExceptionInfoEnd     = (pExceptionInfoBegin == empty) ? (empty + 1) : strstr(uiText, "\r\n\r\n");
  1.3891 +                char* pExceptionThreadArea  = strstr(uiText, ", exception thread");
  1.3892 +                char* pExceptionThreadBegin = pExceptionThreadArea ? Find::PreviousChar(pExceptionThreadArea, '\n') + 1 : empty;
  1.3893 +                char* pExceptionThreadEnd   = (pExceptionThreadBegin == empty) ? (empty + 1) : strstr(pExceptionThreadArea, "\r\n\r\n");
  1.3894 +            
  1.3895 +                if(!pExceptionInfoEnd)
  1.3896 +                    pExceptionInfoEnd = pExceptionInfoBegin;
  1.3897 +                *pExceptionInfoEnd = '\0';
  1.3898 +
  1.3899 +                if(!pExceptionThreadEnd)
  1.3900 +                    pExceptionThreadEnd = pExceptionThreadBegin;
  1.3901 +                *pExceptionThreadEnd = '\0';
  1.3902 +            
  1.3903 +                size_t uiTextBriefLength = OVR_snprintf(nullptr, 0, "Full report:%s\n\nSummary report:\n%s\n\n%s", exceptionReportPath, pExceptionInfoBegin, pExceptionThreadBegin);
  1.3904 +                char* uiTextBrief = (char*)OVR::SafeMMapAlloc(uiTextBriefLength + 1);
  1.3905 +
  1.3906 +                if(uiTextBrief)
  1.3907 +                {
  1.3908 +                    OVR_snprintf(uiTextBrief, uiTextBriefLength + 1, "Full report:%s\n\nSummary report:\n%s\n\n%s", exceptionReportPath, pExceptionInfoBegin, pExceptionThreadBegin);
  1.3909 +                    OVR::SafeMMapFree(uiText, length);
  1.3910 +					uiText = uiTextBrief;
  1.3911 +                }
  1.3912 +           #endif
  1.3913 +        }
  1.3914 +    }
  1.3915 +
  1.3916 +    return uiText;
  1.3917 +}
  1.3918 +
  1.3919 +void ExceptionHandler::FreeExceptionUIText(const char* messageBoxText)
  1.3920 +{
  1.3921 +	OVR::SafeMMapFree(messageBoxText, OVR_strlen(messageBoxText));
  1.3922 +}
  1.3923 +
  1.3924 +
  1.3925 +} // namespace OVR
  1.3926 +
  1.3927 +
  1.3928 +OVR_RESTORE_MSVC_WARNING()
  1.3929 +