diff options
Diffstat (limited to 'LibOVR/Src/Kernel/OVR_DebugHelp.cpp')
-rw-r--r-- | LibOVR/Src/Kernel/OVR_DebugHelp.cpp | 3926 |
1 files changed, 0 insertions, 3926 deletions
diff --git a/LibOVR/Src/Kernel/OVR_DebugHelp.cpp b/LibOVR/Src/Kernel/OVR_DebugHelp.cpp deleted file mode 100644 index fe7b54f..0000000 --- a/LibOVR/Src/Kernel/OVR_DebugHelp.cpp +++ /dev/null @@ -1,3926 +0,0 @@ -/************************************************************************************ - -Filename : ExceptionHandler.cpp -Content : Platform-independent exception handling interface -Created : October 6, 2014 - -Copyright : Copyright 2014 Oculus VR, LLC. All Rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -************************************************************************************/ - - -#include "OVR_DebugHelp.h" -#include "OVR_Types.h" -#include "OVR_UTF8Util.h" -#include "../OVR_CAPI.h" -#include "../OVR_CAPI_Keys.h" -#include "../CAPI/CAPI_HMDState.h" -#include <stdlib.h> - -#if defined(OVR_OS_WIN32) || defined(OVR_OS_WIN64) - #pragma warning(push, 0) - #include <Windows.h> - #include <WinNT.h> - #include <DbgHelp.h> - #include <WinVer.h> - #include <share.h> - #include <Psapi.h> - #include <TlHelp32.h> - #include <comutil.h> - #include <Wbemcli.h> - #include <Wbemidl.h> - #include <ShlObj.h> - #include <ObjBase.h> - #pragma warning(pop) - - #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 - #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. - #pragma comment(lib, "shell32.lib") - - // NtQueryInformation and THREAD_BASIC_INFORMATION are undocumented but frequently needed for digging into thread information. - typedef LONG (WINAPI *NtQueryInformationThreadFunc)(HANDLE, int, PVOID, ULONG, PULONG); - - struct THREAD_BASIC_INFORMATION - { - LONG ExitStatus; - PVOID TebBaseAddress; - PVOID UniqueProcessId; - PVOID UniqueThreadId; - PVOID Priority; - PVOID BasePriority; - }; - - #ifndef UNW_FLAG_NHANDLER // Older Windows SDKs don't support this. - #define UNW_FLAG_NHANDLER 0 - #endif - -#elif defined(OVR_OS_MAC) - #include <unistd.h> - #include <sys/sysctl.h> - #include <sys/utsname.h> - #include <sys/types.h> - #include <sys/mman.h> - #include <stdlib.h> - #include <stdio.h> - #include <pthread.h> - #include <mach/mach.h> - #include <mach/mach_error.h> - #include <mach/thread_status.h> - #include <mach/exception.h> - #include <mach/task.h> - #include <mach/thread_act.h> - #include <mach-o/dyld.h> - #include <mach-o/dyld_images.h> - #include <libproc.h> - #include <libgen.h> - #include <execinfo.h> - #include <cxxabi.h> - #include "OVR_mach_exc_OSX.h" - - #if defined(__LP64__) - typedef struct mach_header_64 MachHeader; - typedef struct segment_command_64 SegmentCommand; - typedef struct section_64 Section; - #define kLCSegment LC_SEGMENT_64 - #else - typedef struct mach_header MachHeader; - typedef struct segment_command SegmentCommand; - typedef struct section Section; - #define kLCSegment LC_SEGMENT - #endif - - extern "C" const struct dyld_all_image_infos* _dyld_get_all_image_infos(); // From libdyld.dylib - -#elif defined(OVR_OS_UNIX) - #include <unistd.h> - #include <sys/sysctl.h> - #include <sys/utsname.h> - #include <sys/types.h> - #include <sys/ptrace.h> - #include <sys/wait.h> - #include <sys/mman.h> - #include <stdlib.h> - #include <stdio.h> - #include <pthread.h> - #include <libgen.h> - #include <execinfo.h> - #include <cxxabi.h> - //#include <libunwind.h> // Can't use this until we can ensure that we have an installed version of it. -#endif - -#if !defined(MIN) - #define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) - #define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) -#endif - - -OVR_DISABLE_MSVC_WARNING(4351) // new behavior: elements of array will be default initialized -OVR_DISABLE_MSVC_WARNING(4996) // This function or variable may be unsafe - - - - -#if defined(OVR_OS_APPLE) - static OVR::ExceptionHandler* sExceptionHandler = nullptr; - const uint32_t sMachCancelMessageType = 0x0ca9ce11; // This is a made-up value of our own choice. - - extern "C" - { - kern_return_t catch_mach_exception_raise_OVR(mach_port_t /*exceptionPort*/, mach_port_t /*threadSysId*/, - mach_port_t /*machTask*/, exception_type_t /*machExceptionType*/, - mach_exception_data_t /*machExceptionData*/, mach_msg_type_number_t /*machExceptionDataCount*/) - { - return KERN_FAILURE; - } - - kern_return_t catch_mach_exception_raise_state_OVR(mach_port_t /*exceptionPort*/, exception_type_t /*exceptionType*/, - const mach_exception_data_t /*machExceptionData*/, mach_msg_type_number_t /*machExceptionDataCount*/, - int* /*pMachExceptionFlavor*/, const thread_state_t /*threadStatePrev*/, mach_msg_type_number_t /*threaStatePrevCount*/, - thread_state_t /*threadStateNew*/, mach_msg_type_number_t* /*pThreadStateNewCount*/) - { - return KERN_FAILURE; - } - - kern_return_t catch_mach_exception_raise_state_identity_OVR(mach_port_t exceptionPort, mach_port_t threadSysId, mach_port_t machTask, - exception_type_t exceptionType, mach_exception_data_type_t* machExceptionData, - mach_msg_type_number_t machExceptionDataCount, int* pMachExceptionFlavor, - thread_state_t threadStatePrev, mach_msg_type_number_t threadStatePrevCount, - thread_state_t threadStateNew, mach_msg_type_number_t* pThreadStateNewCount) - { - return sExceptionHandler->HandleMachException(exceptionPort, threadSysId, machTask, exceptionType, machExceptionData, - machExceptionDataCount, pMachExceptionFlavor, threadStatePrev, threadStatePrevCount, - threadStateNew, pThreadStateNewCount); - } - - void* MachHandlerThreadFunctionStatic(void* pExceptionHandlerVoid) - { - return static_cast<OVR::ExceptionHandler*>(pExceptionHandlerVoid)->MachHandlerThreadFunction(); - } - - } // extern "C" -#endif - - - - -namespace OVR { - - -void GetInstructionPointer(void*& pInstruction) -{ - #if defined(OVR_CC_MSVC) - pInstruction = _ReturnAddress(); - #else // GCC, clang - pInstruction = __builtin_return_address(0); - #endif -} - - -static size_t SprintfAddress(char* threadHandleStr, size_t threadHandleStrCapacity, const void* pAddress) -{ - #if defined(OVR_CC_MSVC) - #if (OVR_PTR_SIZE >= 8) - return OVR_snprintf(threadHandleStr, threadHandleStrCapacity, "0x%016I64x", pAddress); // e.g. 0x0123456789abcdef - #else - return OVR_snprintf(threadHandleStr, threadHandleStrCapacity, "0x%08x", pAddress); // e.g. 0x89abcdef - #endif - #else - #if (OVR_PTR_SIZE >= 8) - return OVR_snprintf(threadHandleStr, threadHandleStrCapacity, "%016llx", pAddress); // e.g. 0x0123456789abcdef - #else - return OVR_snprintf(threadHandleStr, threadHandleStrCapacity, "%08x", pAddress); // e.g. 0x89abcdef - #endif - #endif -} - - -static size_t SprintfThreadHandle(char* threadHandleStr, size_t threadHandleStrCapacity, const ThreadHandle& threadHandle) -{ - return SprintfAddress(threadHandleStr, threadHandleStrCapacity, threadHandle); -} - - -static size_t SprintfThreadSysId(char* threadSysIdStr, size_t threadSysIdStrCapacity, const ThreadSysId& threadSysId) -{ - #if defined(OVR_CC_MSVC) // Somebody could conceivably use VC++ with a different standard library that supports %ll. And VS2012+ also support %ll. - return OVR_snprintf(threadSysIdStr, threadSysIdStrCapacity, "%I64u", (uint64_t)threadSysId); - #else - return OVR_snprintf(threadSysIdStr, threadSysIdStrCapacity, "%llu", (uint64_t)threadSysId); - #endif -} - - - - - -void GetThreadStackBounds(void*& pStackBase, void*& pStackLimit, ThreadHandle threadHandle) -{ - #if defined(OVR_OS_WIN64) || defined(OVR_OS_WIN32) || (defined(OVR_OS_MS) && defined(OVR_OS_CONSOLE)) - ThreadSysId threadSysIdCurrent = (ThreadSysId)GetCurrentThreadId(); - ThreadSysId threadSysId; - NT_TIB* pTIB = nullptr; - - if(threadHandle == OVR_THREADHANDLE_INVALID) - threadSysId = threadSysIdCurrent; - else - threadSysId = ConvertThreadHandleToThreadSysId(threadHandle); - - if(threadSysId == threadSysIdCurrent) - { - #if (OVR_PTR_SIZE == 4) - // Need to use __asm__("movl %%fs:0x18, %0" : "=r" (pTIB) : : ); under gcc/clang. - __asm { - mov eax, fs:[18h] - mov pTIB, eax - } - #else - pTIB = (NT_TIB*)NtCurrentTeb(); - #endif - } - else - { - #if (OVR_PTR_SIZE == 4) - // It turns out we don't need to suspend the thread when getting SegFs/SegGS, as that's - // constant per thread and doesn't require the thread to be suspended. - //SuspendThread((HANDLE)threadHandle); - CONTEXT context; - memset(&context, 0, sizeof(context)); - context.ContextFlags = CONTEXT_SEGMENTS; - GetThreadContext((HANDLE)threadHandle, &context); // Requires THREAD_QUERY_INFORMATION privileges. - - LDT_ENTRY ldtEntry; - if(GetThreadSelectorEntry(threadHandle, context.SegFs, &ldtEntry)) // Requires THREAD_QUERY_INFORMATION - pTIB = (NT_TIB*)((ldtEntry.HighWord.Bits.BaseHi << 24 ) | (ldtEntry.HighWord.Bits.BaseMid << 16) | ldtEntry.BaseLow); - - //ResumeThread((HANDLE)threadHandle); - #else - // We cannot use GetThreadSelectorEntry or Wow64GetThreadSelectorEntry on Win64. - // 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. - // mov rax, qword ptr gs:[30h] - // mov qword ptr [pTIB],rax - // In the meantime we rely on the NtQueryInformationThread function. - - static NtQueryInformationThreadFunc spNtQueryInformationThread = nullptr; - - if(!spNtQueryInformationThread) - { - HMODULE hNTDLL = GetModuleHandleA("ntdll.dll"); - spNtQueryInformationThread = (NtQueryInformationThreadFunc)(uintptr_t)GetProcAddress(hNTDLL, "NtQueryInformationThread"); - } - - if(spNtQueryInformationThread) - { - THREAD_BASIC_INFORMATION tbi; - - memset(&tbi, 0, sizeof(tbi)); - LONG result = spNtQueryInformationThread(threadHandle, 0, &tbi, sizeof(tbi), nullptr); // Requires THREAD_QUERY_INFORMATION privileges - if(result == 0) - pTIB = (NT_TIB*)tbi.TebBaseAddress; - } - #endif - } - - if(pTIB) - { - pStackBase = (void*)pTIB->StackBase; - pStackLimit = (void*)pTIB->StackLimit; - } - else - { - pStackBase = nullptr; - pStackLimit = nullptr; - } - - #elif defined(OVR_OS_APPLE) - if(!threadHandle) - threadHandle = pthread_self(); - - pStackBase = pthread_get_stackaddr_np((pthread_t)threadHandle); - size_t stackSize = pthread_get_stacksize_np((pthread_t)threadHandle); - pStackLimit = (void*)((size_t)pStackBase - stackSize); - - #elif defined(OVR_OS_UNIX) - pStackBase = nullptr; - pStackLimit = nullptr; - - pthread_attr_t threadAttr; - pthread_attr_init(&threadAttr); - - #if defined(OVR_OS_LINUX) - int result = pthread_getattr_np((pthread_t)threadHandle, &threadAttr); - #else - int result = pthread_attr_get_np((pthread_t)threadHandle, &threadAttr); - #endif - - if(result == 0) - { - size_t stackSize = 0; - result = pthread_attr_getstack(&threadAttr, &pStackLimit, &stackSize); - - if(result == 0) - pStackBase = (void*)((uintptr_t)pStackLimit + stackSize); // We assume the stack grows downward. - } - - #endif -} - - -bool OVRIsDebuggerPresent() -{ - #if defined(OVR_OS_MS) - return ::IsDebuggerPresent() != 0; - - #elif defined(OVR_OS_APPLE) - int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() }; - struct kinfo_proc info; - size_t size = sizeof(info); - - info.kp_proc.p_flag = 0; - sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0); - - return ((info.kp_proc.p_flag & P_TRACED) != 0); - - #elif (defined(OVR_OS_LINUX) || defined(OVR_OS_BSD)) && !defined(OVR_OS_ANDROID) - // This works better than the PT_TRACE_ME approach. - // However, it presents a problem: - // http://pubs.opengroup.org/onlinepubs/009695399/functions/fork.html - // When the application calls fork() from a signal handler and any of the - // fork handlers registered by pthread_atfork() calls a function that is - // not asynch-signal-safe, the behavior is undefined. - // We may need to provide two pathways within this function, one of which - // doesn't fork and instead uses PT_TRACE_ME. - int pid = fork(); - int status; - bool present = false; - - if (pid == -1) // If fork failed... - { - // perror("fork"); - } - else if (pid == 0) // If we are the child process... - { - int ppid = getppid(); - - #if defined(OVR_OS_LINUX) - if (ptrace(PTRACE_ATTACH, ppid, nullptr, nullptr) == 0) - #else - if (ptrace(PT_ATTACH, ppid, nullptr, nullptr) == 0) - #endif - { - waitpid(ppid, nullptr, 0); - - #if defined(OVR_OS_LINUX) - ptrace(PTRACE_CONT, getppid(), nullptr, nullptr); - ptrace(PTRACE_DETACH, getppid(), nullptr, nullptr); - #else - ptrace(PT_CONTINUE, getppid(), nullptr, nullptr); - ptrace(PT_DETACH, getppid(), nullptr, nullptr); - #endif - } - else - { - // ptrace failed so the debugger is present. - present = true; - } - - exit(present ? 1 : 0); // The WEXITSTATUS call below will read this exit value. - } - else // Else we are the original process. - { - waitpid(pid, &status, 0); - present = WEXITSTATUS(status) ? true : false; // Read the exit value from the child's call to exit. - } - - return present; - - #elif defined(PT_TRACE_ME) && !defined(OVR_OS_ANDROID) - return (ptrace(PT_TRACE_ME, 0, 1, 0) < 0); - - #else - return false; - #endif -} - - -// Exits the process with the given exit code. -void ExitProcess(intptr_t processReturnValue) -{ - exit((int)processReturnValue); -} - - -void* SafeMMapAlloc(size_t size) -{ - #if defined(OVR_OS_MS) - return VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); // size is rounded up to a page. // Returned memory is 0-filled. - - #elif defined(OVR_OS_MAC) || defined(OVR_OS_UNIX) - #if !defined(MAP_FAILED) - #define MAP_FAILED ((void*)-1) - #endif - - void* result = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); // Returned memory is 0-filled. - if(result == MAP_FAILED) // mmap returns MAP_FAILED (-1) upon failure. - result = nullptr; - return result; - #endif -} - - -void SafeMMapFree(const void* memory, size_t size) -{ - #if defined(OVR_OS_MS) - OVR_UNUSED(size); - VirtualFree(const_cast<void*>(memory), 0, MEM_RELEASE); - - #elif defined(OVR_OS_MAC) || defined(OVR_OS_UNIX) - size_t pageSize = getpagesize(); - size = (((size + (pageSize - 1)) / pageSize) * pageSize); - munmap(const_cast<void*>(memory), size); // Must supply the size to munmap. - #endif -} - - -// Note that we can't just return sizeof(void*) == 8, as we may have the case of a -// 32 bit app running on a 64 bit operating system. -static bool Is64BitOS() -{ - #if (OVR_PTR_SIZE >= 8) - return true; - - #elif defined(OVR_OS_WIN32) || defined(OVR_OS_WIN64) - BOOL is64BitOS = FALSE; - bool IsWow64ProcessPresent = (GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "IsWow64Process") != nullptr); - return (IsWow64ProcessPresent && IsWow64Process(GetCurrentProcess(), &is64BitOS) && is64BitOS); - - #elif defined(OVR_OS_MAC) || defined(OVR_OS_UNIX) - utsname utsName; - memset(&utsName, 0, sizeof(utsName)); - return (uname(&utsName) == 0) && (strcmp(utsName.machine, "x86_64") == 0); - - #else - return false; - #endif -} - - -// The output will always be 0-terminated. -// Returns the required strlen of the output. -// Returns (size_t)-1 on failure. -size_t SpawnShellCommand(const char* shellCommand, char* output, size_t outputCapacity) -{ - #if defined(OVR_OS_UNIX) || defined(OVR_OS_APPLE) - FILE* pFile = popen(shellCommand, "r"); - - if(pFile) - { - size_t requiredLength = 0; - char buffer[256]; - - while(fgets(buffer, sizeof(buffer), pFile)) // fgets 0-terminates the buffer. - { - size_t length = OVR_strlen(buffer); - requiredLength += length; - - if(outputCapacity) - { - OVR_strlcpy(output, buffer, outputCapacity); - length = MIN(outputCapacity, length); - } - - output += length; - outputCapacity -= length; - } - - pclose(pFile); - return requiredLength; - } - #else - // To do. Properly solving this on Windows requires a bit of code. - OVR_UNUSED(shellCommand); - OVR_UNUSED(output); - OVR_UNUSED(outputCapacity); - #endif - - return (size_t)-1; -} - - -// Retrieves a directory path which ends with a path separator. -// Returns the required strlen of the path. -// Guarantees the presence of the directory upon returning true. -static size_t GetUserDocumentsDirectory(char* directoryPath, size_t directoryPathCapacity) -{ - #if defined(OVR_OS_MS) - wchar_t pathW[MAX_PATH + 1]; // +1 because we append a path separator. - HRESULT hr = SHGetFolderPathW(nullptr, CSIDL_APPDATA | CSIDL_FLAG_CREATE, nullptr, SHGFP_TYPE_CURRENT, pathW); - - if(SUCCEEDED(hr)) - { - OVR_UNUSED(directoryPathCapacity); - - intptr_t requiredUTF8Length = OVR::UTF8Util::GetEncodeStringSize(pathW); // Returns required strlen. - if(requiredUTF8Length < MAX_PATH) // We need space for a trailing path separator. - { - OVR::UTF8Util::EncodeString(directoryPath, pathW, -1); - OVR::OVR_strlcat(directoryPath, "\\", directoryPathCapacity); - } - - return (requiredUTF8Length + 1); - } - - #elif defined(OVR_OS_MAC) - // This is the same location that Apple puts its OS-generated .crash files. - const char* home = getenv("HOME"); - size_t requiredStrlen = OVR::OVR_snprintf(directoryPath, directoryPathCapacity, "%s/Library/Logs/DiagnosticReports/", home ? home : "/Users/Shared/Logs/DiagnosticReports/"); - // To do: create the directory if it doesn't already exist. - return requiredStrlen; - - #elif defined(OVR_OS_UNIX) || defined(OVR_OS_MAC) - const char* home = getenv("HOME"); - size_t requiredStrlen = OVR::OVR_snprintf(directoryPath, directoryPathCapacity, "%s/Library/", home ? home : "/Users/Shared/"); - // To do: create the directory if it doesn't already exist. - return requiredStrlen; - #endif - - return 0; -} - - -// Retrieves the name of the given thread. -// To do: Move this to OVR_Threads.h -bool GetThreadName(OVR::ThreadHandle threadHandle, char* threadName, size_t threadNameCapacity) -{ - #if defined(OVR_OS_APPLE) || defined(OVR_OS_LINUX) - int result = pthread_getname_np((pthread_t)threadHandle, threadName, threadNameCapacity); - if(result == 0) - return true; - #else - // This is not currently possible on Windows, as only the debugger stores the thread name. We could potentially use a vectored - // exception handler to catch all thread name exceptions (0x406d1388) and record them in a static list at runtime. To detect - // thread exit we could use WMI Win32_ThreadStopTrace. Maintain a list of thread names between these two events. - OVR_UNUSED(threadHandle); - OVR_UNUSED(threadNameCapacity); - #endif - - if(threadNameCapacity) - threadName[0] = 0; - - return false; -} - - -OVR::ThreadSysId ConvertThreadHandleToThreadSysId(OVR::ThreadHandle threadHandle) -{ - #if defined(OVR_OS_WIN64) - return (OVR::ThreadSysId)::GetThreadId(threadHandle); // Requires THREAD_QUERY_INFORMATION privileges. - - #elif defined(OVR_OS_WIN32) - typedef DWORD (WINAPI *GetThreadIdFunc)(HANDLE); - - static volatile bool sInitialized = false; - static GetThreadIdFunc spGetThreadIdFunc = nullptr; - static NtQueryInformationThreadFunc spNtQueryInformationThread = nullptr; - - if(!sInitialized) - { - HMODULE hKernel32 = GetModuleHandleA("kernel32.dll"); - if(hKernel32) - spGetThreadIdFunc = (GetThreadIdFunc)(uintptr_t)GetProcAddress(hKernel32, "GetThreadId"); - - if(!spGetThreadIdFunc) - { - HMODULE hNTDLL = GetModuleHandleA("ntdll.dll"); - - if(hNTDLL) - spNtQueryInformationThread = (NtQueryInformationThreadFunc)(uintptr_t)GetProcAddress(hNTDLL, "NtQueryInformationThread"); - } - - sInitialized = true; - } - - if(spGetThreadIdFunc) - return (OVR::ThreadSysId)spGetThreadIdFunc(threadHandle); - - if(spNtQueryInformationThread) - { - THREAD_BASIC_INFORMATION tbi; - - if(spNtQueryInformationThread(threadHandle, 0, &tbi, sizeof(tbi), nullptr) == 0) - return (OVR::ThreadSysId)tbi.UniqueThreadId; - } - - return OVR_THREADSYSID_INVALID; - - #elif defined(OVR_OS_APPLE) - mach_port_t threadSysId = pthread_mach_thread_np((pthread_t)threadHandle); // OS 10.4 and later. - return (ThreadSysId)threadSysId; - - #elif defined(OVR_OS_LINUX) - - // 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. - OVR_UNUSED(threadHandle); - return OVR_THREADSYSID_INVALID; - - #else - OVR_UNUSED(threadHandle); - return OVR_THREADSYSID_INVALID; - #endif -} - - -OVR::ThreadHandle ConvertThreadSysIdToThreadHandle(OVR::ThreadSysId threadSysId) -{ - #if defined(OVR_OS_MS) - // We currently request the given rights because that's what users of this function typically need it for. Ideally there would - // 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. - // 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. - OVR::ThreadHandle threadHandle = ::OpenThread(THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION, TRUE, (DWORD)threadSysId); - - if(threadHandle == OVR_THREADHANDLE_INVALID) - { - threadHandle = ::OpenThread(THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION, TRUE, (DWORD)threadSysId); - - if(threadHandle == OVR_THREADHANDLE_INVALID) - threadHandle = ::OpenThread(THREAD_QUERY_INFORMATION, TRUE, (DWORD)threadSysId); - } - - return threadHandle; - #elif defined(OVR_OS_MAC) - return (ThreadHandle)pthread_from_mach_thread_np((mach_port_t)threadSysId); - #else - return (ThreadHandle)threadSysId; - #endif -} - - -void FreeThreadHandle(OVR::ThreadHandle threadHandle) -{ - #if defined(OVR_OS_MS) - if(threadHandle != OVR_THREADHANDLE_INVALID) - ::CloseHandle(threadHandle); - #else - OVR_UNUSED(threadHandle); - #endif -} - - -OVR::ThreadSysId GetCurrentThreadSysId() -{ - #if defined(OVR_OS_MS) - return ::GetCurrentThreadId(); - #elif defined(OVR_OS_APPLE) - return (ThreadSysId)mach_thread_self(); - #else - return (ThreadSysId)pthread_self(); - #endif -} - - - -static void GetCurrentProcessFilePath(char* appPath, size_t appPathCapacity) -{ - appPath[0] = 0; - - #if defined(OVR_OS_MS) - wchar_t pathW[MAX_PATH]; - GetModuleFileNameW(0, pathW, (DWORD)OVR_ARRAY_COUNT(pathW)); - - size_t requiredUTF8Length = (size_t)OVR::UTF8Util::GetEncodeStringSize(pathW); // Returns required strlen. - - if(requiredUTF8Length < appPathCapacity) - { - OVR::UTF8Util::EncodeString(appPath, pathW, -1); - } - else - { - appPath[0] = 0; - } - - #elif defined(OVR_OS_APPLE) - struct BunderFolder - { - // Returns true if pStr ends with pFind, case insensitively. - // To do: Move OVR_striend to OVRKernel/Std.h - static bool OVR_striend(const char* pStr, const char* pFind, size_t strLength = (size_t)-1, size_t findLength = (size_t)-1) - { - if(strLength == (size_t)-1) - strLength = OVR_strlen(pStr); - if(findLength == (size_t)-1) - findLength = OVR_strlen(pFind); - if(strLength >= findLength) - return (OVR_stricmp(pStr + strLength - findLength, pFind) == 0); - return false; - } - - static bool IsBundleFolder(const char* filePath) - { - // https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/AboutBundles/AboutBundles.html#//apple_ref/doc/uid/10000123i-CH100-SW1 - static const char* extensionArray[] = { ".app", ".bundle", ".framework", ".plugin", ".kext" }; - - for(size_t i = 0; i < OVR_ARRAY_COUNT(extensionArray); i++) - { - if(OVR_striend(filePath, extensionArray[i])) - return true; - } - - return false; - } - }; - - char appPathTemp[PATH_MAX]; - uint32_t appPathTempCapacity32 = PATH_MAX; - size_t requiredStrlen = appPathCapacity; - - if(_NSGetExecutablePath(appPathTemp, &appPathTempCapacity32) == 0) - { - char appPathTempReal[PATH_MAX]; - - if(realpath(appPathTemp, appPathTempReal)) // If the path is a symbolic link, this converts it to the real path. - { - // To consider: Enable reading the internal bundle executable path. An application on Mac may in - // fact be within a file bundle, which is an private file system within a file. With Objective C - // we could use: [[NSWorkspace sharedWorkspace] isFilePackageAtPath:fullPath]; - bool shouldReadTheBunderPath = false; - - if(shouldReadTheBunderPath) - { - // We recursively call dirname() until we find .app/.bundle/.plugin as a directory name. - OVR_strlcpy(appPathTemp, appPathTempReal, OVR_ARRAY_COUNT(appPathTemp)); - bool found = BunderFolder::IsBundleFolder(appPathTemp); - - while(!found && OVR_strcmp(appPathTemp, ".") && OVR_strcmp(appPathTemp, "/")) - { - OVR_strlcpy(appPathTemp, dirname(appPathTemp), OVR_ARRAY_COUNT(appPathTemp)); - found = BunderFolder::IsBundleFolder(appPathTemp); - } - - if(found) // If somewhere above we found a parent bundle container... - requiredStrlen = OVR_strlcpy(appPath, appPathTemp, appPathCapacity); - else - requiredStrlen = OVR_strlcpy(appPath, appPathTempReal, appPathCapacity); - } - else - { - requiredStrlen = OVR_strlcpy(appPath, appPathTempReal, appPathCapacity); - } - } - } - - if(requiredStrlen >= appPathCapacity) - appPath[0] = '\0'; - - #elif defined(OVR_OS_LINUX) - ssize_t length = readlink("/proc/self/exe", appPath, appPathCapacity); - - if((length != -1) && ((size_t)length < (appPathCapacity - 1))) - { - appPath[length] = '\0'; - } - #endif -} - - -static const char* GetFileNameFromPath(const char* filePath) -{ - #if defined(OVR_OS_MS) - const char* lastPathSeparator = max(strrchr(filePath, '\\'), strrchr(filePath, '/')); // Microsoft APIs are inconsistent with respect to allowing / as a path separator. - #else - const char* lastPathSeparator = strrchr(filePath, '/'); - #endif - - if(lastPathSeparator) - return lastPathSeparator + 1; - - return filePath; -} - - - -static void FormatDateTime(char* buffer, size_t bufferCapacity, time_t timeValue, bool getDate, bool getTime, bool localDateTime, bool fileNameSafeCharacters = false) -{ - char temp[128]; - const tm* pTime = localDateTime ? localtime(&timeValue) : gmtime(&timeValue); - - if(bufferCapacity) - buffer[0] = 0; - - if(getDate) - { - const char* format = fileNameSafeCharacters ? "%Y-%m-%d" : "%Y/%m/%d"; - strftime(temp, OVR_ARRAY_COUNT(temp), format, pTime); - OVR::OVR_strlcpy(buffer, temp, bufferCapacity); - } - - if(getTime) - { - const char* format = fileNameSafeCharacters ? " %H.%M.%S" : " %H:%M:%S"; - strftime(temp, OVR_ARRAY_COUNT(temp), (getDate ? format : format + 1), pTime); - OVR::OVR_strlcat(buffer, temp, bufferCapacity); - } -} - - -static void GetOSVersionName(char* versionName, size_t versionNameCapacity) -{ - #if defined(OVR_OS_MS) - const char* name = "unknown"; - - OSVERSIONINFOEXW vi; - memset(&vi, 0, sizeof(vi)); - vi.dwOSVersionInfoSize = sizeof(vi); - - if(GetVersionExW((LPOSVERSIONINFOW)&vi)) - { - if(vi.dwMajorVersion >= 7) - { - // Unknown recent version. - } - if(vi.dwMajorVersion >= 6) - { - if(vi.dwMinorVersion >= 4) - name = "Windows 10"; - else if(vi.dwMinorVersion >= 3) - { - if(vi.wProductType == VER_NT_WORKSTATION) - name = "Windows 8.1"; - else - name = "Windows Server 2012 R2"; - } - else if(vi.dwMinorVersion >= 2) - { - if(vi.wProductType == VER_NT_WORKSTATION) - name = "Windows 8"; - else - name = "Windows Server 2012"; - } - else if(vi.dwMinorVersion >= 1) - { - if(vi.wProductType == VER_NT_WORKSTATION) - name = "Windows 7"; - else - name = "Windows Server 2008 R2"; - } - else - { - if(vi.wProductType == VER_NT_WORKSTATION) - name = "Windows Vista"; - else - name = "Windows Server 2008"; - } - } - else if(vi.dwMajorVersion >= 5) - { - if(vi.dwMinorVersion == 0) - name = "Windows 2000"; - else if(vi.dwMinorVersion == 1) - name = "Windows XP"; - else // vi.dwMinorVersion == 2 - { - if(GetSystemMetrics(SM_SERVERR2) != 0) - name = "Windows Server 2003 R2"; - else if(vi.wSuiteMask & VER_SUITE_WH_SERVER) - name = "Windows Home Server"; - if(GetSystemMetrics(SM_SERVERR2) == 0) - name = "Windows Server 2003"; - else - name = "Windows XP Professional x64 Edition"; - } - } - else - name = "Windows 98 or earlier"; - } - - OVR_strlcpy(versionName, name, versionNameCapacity); - - #elif defined(OVR_OS_UNIX) || defined(OVR_OS_APPLE) - utsname utsName; - memset(&utsName, 0, sizeof(utsName)); - - if(uname(&utsName) == 0) - OVR_snprintf(versionName, versionNameCapacity, "%s %s %s %s", utsName.sysname, utsName.release, utsName.version, utsName.machine); - else - OVR_snprintf(versionName, versionNameCapacity, "Unix"); - #endif -} - - - - -void CreateException(CreateExceptionType exceptionType) -{ - char buffer[1024] = {}; - - switch(exceptionType) - { - case kCETAccessViolation: - { - int* pNullPtr = reinterpret_cast<int*>((rand() / 2) / RAND_MAX); - pNullPtr[0] = 0; // This line should generate an exception. - sprintf(buffer, "%p", pNullPtr); - break; - } - - case kCETDivideByZero: - { - int smallValue = 1; - int largeValue = (1000 * exceptionType); - int divByZero = (smallValue / largeValue); // This line should generate a div/0 exception. - sprintf(buffer, "%d", divByZero); - break; - } - - case kCETIllegalInstruction: - { - #if defined(OVR_CPU_X86) || (defined(OVR_CPU_X86_64) && !defined(OVR_CC_MSVC)) // (if x86) or (if x64 and any computer but VC++)... - #if defined(OVR_CC_MSVC) - __asm ud2 - #else // e.g. GCC - asm volatile("ud2"); - #endif - - #elif defined(OVR_CPU_X86_64) && (defined(OVR_OS_MS) && defined(PAGE_EXECUTE_READWRITE)) - // VC++ for x64 doesn't support inline asm. - void* pVoid = _AddressOfReturnAddress(); - void** ppVoid = reinterpret_cast<void**>(pVoid); - void* pReturnAddress = *ppVoid; - DWORD dwProtectPrev = 0; - - if(VirtualProtect(pReturnAddress, 2, PAGE_EXECUTE_READWRITE, &dwProtectPrev)) // If we can set the memory to be executable... - { - // Modify the code we return to. - uint8_t asm_ud2[] = { 0x0f, 0x0b }; - memcpy(pReturnAddress, asm_ud2, sizeof(asm_ud2)); - VirtualProtect(pReturnAddress, 2, dwProtectPrev, &dwProtectPrev); - } - else - { - // To do: Fix this. - } - - #else - // To do: Fix this. - #endif - - break; - } - - case kCETStackCorruption: - { - size_t size = (sizeof(buffer) * 16) - (rand() % 16); - char* pOutsizeStack = buffer - ((sizeof(buffer) * 16) + (rand() % 16)); - - memset(buffer, 0, size); - memset(pOutsizeStack, 0, size); // This line should generate an exception, or an exception will be generated upon return from this function. - break; - } - - case kCETStackOverflow: - { - CreateException(exceptionType); // Call ourselves recursively. This line should generate a div/0 exception. - sprintf(buffer, "%d", exceptionType); - break; - } - - case kCETAlignment: - { - // Not all platforms generate alignment exceptions. Some internally handle it. - void* pAligned = malloc(16); - char* pMisaligned = (char*)pAligned + 1; - uint64_t* pMisaligned64 = reinterpret_cast<uint64_t*>(pMisaligned); - - *pMisaligned64 = 0; // This line should generate an exception. - free(pAligned); - break; - } - - case kCETFPU: - // Platforms usually have FPU exceptions disabled. In order to test FPU exceptions we will need to at least - // temporarily disable them before executing code here to generate such exceptions. - // To do. - break; - - case kCETTrap: - // To do. This is hardware-specific. - break; - } -} - - - - -#if defined(OVR_OS_MS) - 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); - typedef PVOID (WINAPI * SymFunctionTableAccess64Type)(HANDLE hProcess, DWORD64 dwAddr); - typedef DWORD64 (WINAPI * SymGetModuleBase64Type)(HANDLE hProcess, DWORD64 dwAddr); - typedef DWORD (WINAPI * SymSetOptionsType)(DWORD SymOptions); - typedef BOOL (WINAPI * SymInitializeWType)(HANDLE hProcess, PCWSTR UserSearchPath, BOOL fInvadeProcess); - typedef BOOL (WINAPI * SymCleanupType)(HANDLE hProcess); - typedef DWORD64 (WINAPI * SymLoadModule64Type)(HANDLE hProcess, HANDLE hFile, PCSTR ImageName, PCSTR ModuleName, DWORD64 BaseOfDll, DWORD SizeOfDll); - typedef BOOL (WINAPI * SymFromAddrType)(HANDLE hProcess, DWORD64 Address, PDWORD64 Displacement, PSYMBOL_INFO Symbol); - typedef BOOL (WINAPI * SymGetLineFromAddr64Type)(HANDLE hProcess, DWORD64 qwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINE64 Line64); - - StackWalk64Type pStackWalk64; - SymFunctionTableAccess64Type pSymFunctionTableAccess64; - SymGetModuleBase64Type pSymGetModuleBase64; - SymSetOptionsType pSymSetOptions; - SymInitializeWType pSymInitializeW; - SymCleanupType pSymCleanup; - SymLoadModule64Type pSymLoadModule64; - SymFromAddrType pSymFromAddr; - SymGetLineFromAddr64Type pSymGetLineFromAddr64; -#endif - - - -SymbolLookup::SymbolLookup() - : initialized(false), - allowMemoryAllocation(true), - moduleListUpdated(false), - moduleInfoArray(), - moduleInfoArraySize(0) -{ -} - -SymbolLookup::~SymbolLookup() -{ - Shutdown(); -} - -void SymbolLookup::AddSourceCodeDirectory(const char* pDirectory) -{ - OVR_UNUSED(pDirectory); -} - -bool SymbolLookup::Initialize() -{ - if(!initialized) - { - #if defined(OVR_OS_MS) - // http://msdn.microsoft.com/en-us/library/windows/desktop/ms679294%28v=vs.85%29.aspx - HANDLE hProcess = GetCurrentProcess(); - HMODULE hDbgHelp = LoadLibraryW(L"DbgHelp.dll"); // It's best if the application supplies a recent version of this. - - if(hDbgHelp) - { - pStackWalk64 = (StackWalk64Type)(uintptr_t)::GetProcAddress(hDbgHelp, "StackWalk64"); - pSymFunctionTableAccess64 = (SymFunctionTableAccess64Type)(uintptr_t)::GetProcAddress(hDbgHelp, "SymFunctionTableAccess64"); - pSymGetModuleBase64 = (SymGetModuleBase64Type)(uintptr_t)::GetProcAddress(hDbgHelp, "SymGetModuleBase64"); - pSymSetOptions = (SymSetOptionsType)(uintptr_t)::GetProcAddress(hDbgHelp, "SymSetOptions"); - pSymInitializeW = (SymInitializeWType)(uintptr_t)::GetProcAddress(hDbgHelp, "SymInitializeW"); - pSymCleanup = (SymCleanupType)(uintptr_t)::GetProcAddress(hDbgHelp, "SymCleanup"); - pSymLoadModule64 = (SymLoadModule64Type)(uintptr_t)::GetProcAddress(hDbgHelp, "SymLoadModule64"); - pSymFromAddr = (SymFromAddrType)(uintptr_t)::GetProcAddress(hDbgHelp, "SymFromAddr"); - pSymGetLineFromAddr64 = (SymGetLineFromAddr64Type)(uintptr_t)::GetProcAddress(hDbgHelp, "SymGetLineFromAddr64"); - } - - pSymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); - - // To consider: Use a manually created search path: - // wchar_t searchPathW[4096]; // Semicolon-separated strings. - // The current working directory of the application. - // The directory of the application itself (GetModuleFileName). - // The _NT_SYMBOL_PATH environment variable. - // The _NT_ALTERNATE_SYMBOL_PATH environment variable. - - if(pSymInitializeW(hProcess, nullptr /*searchPathW*/, FALSE)) - { - initialized = true; - } - #endif - } - - return true; -} - -void SymbolLookup::Shutdown() -{ - if(initialized) - { - initialized = false; - - #if defined(OVR_OS_MS) - HANDLE hProcess = GetCurrentProcess(); - - // SymCleanup should handle this for us. - //if(moduleListUpdated) - //{ - // for(size_t i = 0; i < moduleInfoArraySize; i++) - // pSymUnloadModule64(hProcess, moduleInfoArray[i].baseAddress); - //} - - moduleInfoArraySize = 0; - - pSymCleanup(hProcess); - #endif - } -} - - -void SymbolLookup::EnableMemoryAllocation(bool enabled) -{ - allowMemoryAllocation = enabled; -} - - -OVR_DISABLE_MSVC_WARNING(4740) // flow in or out of inline asm code suppresses global optimization -OVR_DISABLE_MSVC_WARNING(4748) // /GS can not protect parameters and local variables from local buffer overrun because optimizations are disabled in function - - -size_t SymbolLookup::GetBacktrace(void* addressArray[], size_t addressArrayCapacity, size_t skipCount, void* platformThreadContext, OVR::ThreadSysId threadSysIdHelp) -{ - #if defined(OVR_OS_WIN64) || (defined(OVR_OS_MS) && defined(OVR_OS_CONSOLE)) - OVR_UNUSED(threadSysIdHelp); - - if(platformThreadContext == nullptr) - return RtlCaptureStackBackTrace(1, (ULONG)addressArrayCapacity, addressArray, nullptr); - - // We need to get the call stack of another thread. - size_t frameIndex = 0; - CONTEXT context; - PRUNTIME_FUNCTION pRuntimeFunction; - ULONG64 imageBase = 0; - ULONG64 imageBasePrev = 0; - - memcpy(&context, (CONTEXT*)platformThreadContext, sizeof(CONTEXT)); - context.ContextFlags = CONTEXT_CONTROL; - - if(context.Rip && (frameIndex < addressArrayCapacity)) - addressArray[frameIndex++] = (void*)(uintptr_t)context.Rip; - - while(context.Rip && (frameIndex < addressArrayCapacity)) - { - imageBasePrev = imageBase; - pRuntimeFunction = (PRUNTIME_FUNCTION)RtlLookupFunctionEntry(context.Rip, &imageBase, nullptr); - - if(pRuntimeFunction) - { - VOID* handlerData = nullptr; - ULONG64 establisherFramePointers[2] = { 0, 0 }; - RtlVirtualUnwind(UNW_FLAG_NHANDLER, imageBase, context.Rip, pRuntimeFunction, &context, &handlerData, establisherFramePointers, nullptr); - } - else - { - context.Rip = (ULONG64)(*(PULONG64)context.Rsp); - context.Rsp += 8; - } - - if(context.Rip && (frameIndex < addressArrayCapacity)) - { - if(skipCount) - --skipCount; - else - addressArray[frameIndex++] = (void*)(uintptr_t)context.Rip; - } - } - - return frameIndex; - - #elif defined(OVR_OS_WIN32) - OVR_UNUSED(threadSysIdHelp); - - size_t frameIndex = 0; - - if(pStackWalk64) - { - CONTEXT context; - - if(platformThreadContext) - { - memcpy(&context, platformThreadContext, sizeof(context)); - context.ContextFlags = CONTEXT_CONTROL; - } - else - { - memset(&context, 0, sizeof(context)); - context.ContextFlags = CONTEXT_CONTROL; - - __asm { - mov context.Ebp, EBP - mov context.Esp, ESP - call GetEIP - GetEIP: - pop context.Eip - } - } - - STACKFRAME64 sf; - memset(&sf, 0, sizeof(sf)); - sf.AddrPC.Offset = context.Eip; - sf.AddrPC.Mode = AddrModeFlat; - sf.AddrStack.Offset = context.Esp; - sf.AddrStack.Mode = AddrModeFlat; - sf.AddrFrame.Offset = context.Ebp; - sf.AddrFrame.Mode = AddrModeFlat; - - const HANDLE hCurrentProcess = ::GetCurrentProcess(); - const HANDLE hCurrentThread = ::GetCurrentThread(); - - if(!platformThreadContext) // If we are reading the current thread's call stack then we ignore this current function. - skipCount++; - - while(frameIndex < addressArrayCapacity) - { - if(!pStackWalk64(IMAGE_FILE_MACHINE_I386, hCurrentProcess, hCurrentThread, &sf, &context, nullptr, pSymFunctionTableAccess64, pSymGetModuleBase64, nullptr)) - break; - - if(sf.AddrFrame.Offset == 0) - break; - - if(skipCount) - --skipCount; - else - addressArray[frameIndex++] = ((void*)(uintptr_t)sf.AddrPC.Offset); - } - } - - return frameIndex; - - #elif defined(OVR_OS_APPLE) - struct StackFrame - { - StackFrame* pParentStackFrame; - void* pReturnPC; - }; - - void* pInstruction; - StackFrame* pStackFrame; - size_t frameIndex = 0; - - if(platformThreadContext) - { - #if defined(OVR_CPU_ARM) - arm_thread_state_t* pThreadState = (arm_thread_state_t*)platformThreadContext; - pStackFrame = (StackFrame*)pThreadState->__fp; - pInstruction = (void*) pThreadState->__pc; - #define FrameIsAligned(pStackFrame) ((((uintptr_t)pStackFrame) & 0x1) == 0) - #elif defined(OVR_CPU_X86_64) - x86_thread_state_t* pThreadState = (x86_thread_state_t*)platformThreadContext; - pInstruction = (void*) pThreadState->uts.ts64.__rip; - pStackFrame = (StackFrame*)pThreadState->uts.ts64.__rbp; - #define FrameIsAligned(pStackFrame) ((((uintptr_t)pStackFrame) & 0xf) == 0) - #elif defined(OVR_CPU_X86) - x86_thread_state_t* pThreadState = (x86_thread_state_t*)platformThreadContext; - pInstruction = (void*) pThreadState->uts.ts32.__eip; - pStackFrame = (StackFrame*)pThreadState->uts.ts32.__ebp; - #define FrameIsAligned(pStackFrame) ((((uintptr_t)pStackFrame) & 0xf) == 8) - #endif - - if(frameIndex < addressArrayCapacity) - addressArray[frameIndex++] = pInstruction; - } - else // Else get the current values... - { - pStackFrame = (StackFrame*)__builtin_frame_address(0); - GetInstructionPointer(pInstruction); - } - - pthread_t threadSelf = pthread_self(); - void* pCurrentStackBase = pthread_get_stackaddr_np(threadSelf); - void* pCurrentStackLimit = (void*)((uintptr_t)pCurrentStackBase - pthread_get_stacksize_np(threadSelf)); - bool threadIsCurrent = (platformThreadContext == nullptr) || (((void*)pStackFrame > pCurrentStackLimit) && ((void*)pStackFrame <= pCurrentStackBase)); - StackFrame* pStackBase; - StackFrame* pStackLimit; - - if(threadIsCurrent) - { - pStackBase = (StackFrame*)pCurrentStackBase; - pStackLimit = (StackFrame*)pCurrentStackLimit; - } - else if(threadSysIdHelp) - { - pthread_t threadHandle = pthread_from_mach_thread_np((mach_port_t)threadSysIdHelp); - pStackBase = (StackFrame*)pthread_get_stackaddr_np(threadHandle); - pStackLimit = (StackFrame*)((uintptr_t)pStackBase - pthread_get_stacksize_np(threadHandle)); - } - else - { // We guess what the limits are. - pStackBase = pStackFrame + ((384 * 1024) / sizeof(StackFrame)); - pStackLimit = pStackFrame - ((384 * 1024) / sizeof(StackFrame)); - } - - if((frameIndex < addressArrayCapacity) && pStackFrame && FrameIsAligned(pStackFrame)) - { - addressArray[frameIndex++] = pStackFrame->pReturnPC; - - while(pStackFrame && pStackFrame->pReturnPC && (frameIndex < addressArrayCapacity)) - { - pStackFrame = pStackFrame->pParentStackFrame; - - if(pStackFrame && FrameIsAligned(pStackFrame) && pStackFrame->pReturnPC && (pStackFrame > pStackLimit) && (pStackFrame < pStackBase)) - { - if(skipCount) - --skipCount; - else - addressArray[frameIndex++] = pStackFrame->pReturnPC; - } - else - break; - } - } - - return frameIndex; - - #elif defined(OVR_OS_LINUX) && (defined( __LIBUNWIND__) || defined(LIBUNWIND_AVAIL)) - // Libunwind-based solution. Requires installation of libunwind package. - // Libunwind isn't always safe for threads that are in signal handlers. - // An approach to get the callstack of another thread is to use signal injection into the target thread. - - OVR_UNUSED(platformThreadContext); - OVR_UNUSED(threadSysIdHelp); - - size_t frameIndex = 0; - unw_cursor_t cursor; - unw_context_t uc; - unw_word_t ip, sp; - - unw_getcontext(&uc); // This gets the current thread's context. We could alternatively initialize another thread's context with it. - unw_init_local(&cursor, &uc); - - while((unw_step(&cursor) > 0) && (frameIndex < addressArrayCapacity)) - { - // We can get the function name here too on some platforms with unw_get_proc_info() and unw_get_proc_name(). - - if(skipCount) - --skipCount; - else - { - unw_get_reg(&cursor, UNW_REG_IP, &ip); - addressArray[frameIndex++] = (void*)ip; - } - } - - return frameIndex; - #else - OVR_UNUSED(addressArray); - OVR_UNUSED(addressArrayCapacity); - OVR_UNUSED(skipCount); - OVR_UNUSED(platformThreadContext); - OVR_UNUSED(threadSysIdHelp); - - return 0; - #endif -} - - -size_t SymbolLookup::GetBacktraceFromThreadHandle(void* addressArray[], size_t addressArrayCapacity, size_t skipCount, OVR::ThreadHandle threadHandle) -{ - #if defined(OVR_OS_MS) - size_t count = 0; - DWORD threadSysId = (DWORD)ConvertThreadHandleToThreadSysId(threadHandle); - - // Compare to 0, compare to the self 'pseudohandle' and compare to the self id. - if((threadHandle == OVR_THREADHANDLE_INVALID) || (threadHandle == ::GetCurrentThread()) || (threadSysId == ::GetCurrentThreadId())) // If threadSysId refers to the current thread... - return GetBacktrace(addressArray, addressArrayCapacity, skipCount, nullptr); - - // We are working with another thread. We need to suspend it and get its CONTEXT. - // Suspending other threads is risky, as they may be in some state that cannot be safely blocked. - BOOL result = false; - DWORD suspendResult = ::SuspendThread(threadHandle); // Requires that the handle have THREAD_SUSPEND_RESUME rights. - - if(suspendResult != (DWORD)-1) // Returns previous suspend count, or -1 if failed. - { - CONTEXT context; - context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; // Requires that the handle have THREAD_GET_CONTEXT rights. - result = ::GetThreadContext(threadHandle, &context); - count = GetBacktrace(addressArray, addressArrayCapacity, skipCount, &context); - suspendResult = ::ResumeThread(threadHandle); - OVR_ASSERT_AND_UNUSED(suspendResult != (DWORD)-1, suspendResult); - } - - return count; - - #elif defined(OVR_OS_APPLE) - mach_port_t threadSysID = pthread_mach_thread_np((pthread_t)threadHandle); // Convert pthread_t to mach thread id. - return GetBacktraceFromThreadSysId(addressArray, addressArrayCapacity, skipCount, (OVR::ThreadSysId)threadSysID); - - #elif defined(OVR_OS_LINUX) - // To do. - OVR_UNUSED(addressArray); - OVR_UNUSED(addressArrayCapacity); - OVR_UNUSED(skipCount); - OVR_UNUSED(threadHandle); - return 0; - #endif -} - - -size_t SymbolLookup::GetBacktraceFromThreadSysId(void* addressArray[], size_t addressArrayCapacity, size_t skipCount, OVR::ThreadSysId threadSysId) -{ - #if defined(OVR_OS_MS) - OVR::ThreadHandle threadHandle = ConvertThreadSysIdToThreadHandle(threadSysId); - if(threadHandle) - { - size_t count = GetBacktraceFromThreadHandle(addressArray, addressArrayCapacity, skipCount, threadHandle); - FreeThreadHandle(threadHandle); - return count; - } - return 0; - - #elif defined(OVR_OS_APPLE) - mach_port_t threadCurrent = pthread_mach_thread_np(pthread_self()); - mach_port_t thread = (mach_port_t)threadSysId; - - if(thread == threadCurrent) - { - return GetBacktrace(addressArray, addressArrayCapacity, skipCount, nullptr); - } - else - { - kern_return_t result = thread_suspend(thread); // Do we need to do this if it's an thread who exception is being handled? - size_t count = 0; - - if(result == KERN_SUCCESS) - { - #if defined(OVR_CPU_X86) || defined(OVR_CPU_X86_64) - x86_thread_state_t threadState; - #elif defined(OVR_CPU_ARM) - arm_thread_state_t threadState; - #endif - mach_msg_type_number_t stateCount = MACHINE_THREAD_STATE_COUNT; - - result = thread_get_state(thread, MACHINE_THREAD_STATE, (natural_t*)(uintptr_t)&threadState, &stateCount); - - if(result == KERN_SUCCESS) - count = GetBacktrace(addressArray, addressArrayCapacity, skipCount, &threadState, threadSysId); - - thread_resume(thread); - - return count; - } - } - - return 0; - - #elif defined(OVR_OS_LINUX) - // To do. - OVR_UNUSED(addressArray); - OVR_UNUSED(addressArrayCapacity); - OVR_UNUSED(skipCount); - OVR_UNUSED(threadSysId); - return 0; - #endif -} - - -// We need to return the required moduleInfoArrayCapacity. -size_t SymbolLookup::GetModuleInfoArray(ModuleInfo* pModuleInfoArray, size_t moduleInfoArrayCapacity) -{ - #if defined(OVR_OS_MS) - size_t moduleCountRequired = 0; // The count we would copy to pModuleInfoArray if moduleInfoArrayCapacity was enough. - size_t moduleCount = 0; // The count we actually copy to pModuleInfoArray. Will be <= moduleInfoArrayCapacity. - HANDLE hProcess = GetCurrentProcess(); - HMODULE hModuleArray[200]; - DWORD cbNeeded = 0; - MODULEINFO mi; - - if(EnumProcessModules(hProcess, hModuleArray, sizeof(hModuleArray), &cbNeeded)) - { - moduleCountRequired = ((cbNeeded / sizeof(HMODULE)) < OVR_ARRAY_COUNT(hModuleArray)) ? (cbNeeded / sizeof(HMODULE)) : OVR_ARRAY_COUNT(hModuleArray); - moduleCount = MIN(moduleCountRequired, OVR_ARRAY_COUNT(hModuleArray)); - moduleCount = MIN(moduleCount, moduleInfoArrayCapacity); - - for(size_t i = 0; i < moduleCount; i++) - { - ModuleInfo& moduleInfo = pModuleInfoArray[i]; - - memset(&mi, 0, sizeof(mi)); - BOOL result = GetModuleInformation(hProcess, hModuleArray[i], &mi, sizeof(mi)); - - if(result) - { - wchar_t pathW[MAX_PATH]; - char pathA[MAX_PATH * 3]; // *3 to handle UTF8 multibyte encoding. - - moduleInfo.handle = hModuleArray[i]; - moduleInfo.baseAddress = (uintptr_t)mi.lpBaseOfDll; - moduleInfo.size = mi.SizeOfImage; - - GetModuleFileNameW(hModuleArray[i], pathW, OVR_ARRAY_COUNT(pathW)); - OVR::UTF8Util::EncodeString(pathA, pathW, -1); // Problem: DecodeString provides no way to specify the destination capacity. - OVR::OVR_strlcpy(moduleInfo.filePath, pathA, OVR_ARRAY_COUNT(moduleInfo.filePath)); - - const char* fileName = GetFileNameFromPath(pathA); - OVR::OVR_strlcpy(moduleInfo.name, fileName, OVR_ARRAY_COUNT(moduleInfo.name)); - } - else - { - moduleInfo.handle = 0; - moduleInfo.baseAddress = 0; - moduleInfo.size = 0; - moduleInfo.filePath[0] = 0; - moduleInfo.name[0] = 0; - } - } - } - - return moduleCountRequired; - - #elif defined(OVR_OS_MAC) - size_t moduleCountRequired = 0; - size_t moduleCount = 0; - - struct MacModuleInfo // This struct exists solely so we can have a local function within this function.. - { - static void AddMacModuleInfo(ModuleInfo* pModuleInfoArrayL, size_t& moduleCountRequiredL, size_t& moduleCountL, size_t moduleInfoArrayCapacityL, - const char* pTypeFilterL, const char* pModulePath, uintptr_t currentSegmentPos, const MachHeader* pMachHeader, uint64_t offset) - { - for(size_t i = 0; i < pMachHeader->ncmds; i++) - { - const SegmentCommand* pSegmentCommand = reinterpret_cast<const SegmentCommand*>(currentSegmentPos); - - if(pSegmentCommand->cmd == kLCSegment) - { - const size_t segnameSize = (sizeof(pSegmentCommand->segname) + 1); // +1 so we can have a trailing '\0'. - char segname[segnameSize]; - - memcpy(segname, pSegmentCommand->segname, sizeof(pSegmentCommand->segname)); - segname[segnameSize - 1] = '\0'; - - if(!pTypeFilterL || OVR_strncmp(segname, pTypeFilterL, sizeof(segname))) - { - moduleCountRequiredL++; - - if(moduleCountL < moduleInfoArrayCapacityL) - { - ModuleInfo& info = pModuleInfoArrayL[moduleCountL++]; - - info.baseAddress = (uint64_t)(pSegmentCommand->vmaddr + offset); - info.handle = reinterpret_cast<ModuleHandle>((uintptr_t)info.baseAddress); - info.size = (uint64_t)pSegmentCommand->vmsize; - OVR_strlcpy(info.filePath, pModulePath, OVR_ARRAY_COUNT(info.filePath)); - OVR_strlcpy(info.name, GetFileNameFromPath(pModulePath), OVR_ARRAY_COUNT(info.name)); - - info.permissions[0] = (pSegmentCommand->initprot & VM_PROT_READ) ? 'r' : '-'; - info.permissions[1] = (pSegmentCommand->initprot & VM_PROT_WRITE) ? 'w' : '-'; - info.permissions[2] = (pSegmentCommand->initprot & VM_PROT_EXECUTE) ? 'x' : '-'; - info.permissions[3] = '/'; - info.permissions[4] = (pSegmentCommand->maxprot & VM_PROT_READ) ? 'r' : '-'; - info.permissions[5] = (pSegmentCommand->maxprot & VM_PROT_WRITE) ? 'w' : '-'; - info.permissions[6] = (pSegmentCommand->maxprot & VM_PROT_EXECUTE) ? 'x' : '-'; - info.permissions[7] = '\0'; - - OVR_strlcpy(info.type, pSegmentCommand->segname, OVR_ARRAY_COUNT(info.type)); - } - } - } - - currentSegmentPos += pSegmentCommand->cmdsize; - } - } - }; - - // Iterate dyld_all_image_infos->infoArray - const struct dyld_all_image_infos* pAllImageInfos = _dyld_get_all_image_infos(); - - for(uint32_t i = 0; i < pAllImageInfos->infoArrayCount; i++) - { - const char* pModulePath = pAllImageInfos->infoArray[i].imageFilePath; - - if(pModulePath && *pModulePath) - { - uintptr_t currentSegmentPos = (uintptr_t)pAllImageInfos->infoArray[i].imageLoadAddress; - const MachHeader* pMachHeader = reinterpret_cast<const MachHeader*>(currentSegmentPos); - uint64_t offset = (uint64_t)_dyld_get_image_vmaddr_slide(i); - - currentSegmentPos += sizeof(*pMachHeader); - - MacModuleInfo::AddMacModuleInfo(pModuleInfoArray, moduleCountRequired, moduleCount, moduleInfoArrayCapacity, - nullptr /*"__TEXT"*/, pModulePath, currentSegmentPos, pMachHeader, offset); - } - } - - // In addition to iterating dyld_all_image_infos->infoArray we need to also iterate /usr/lib/dyld entries. - const MachHeader* pMachHeader = (const MachHeader*)pAllImageInfos->dyldImageLoadAddress; - uintptr_t currentSegmentPos = (uintptr_t)pMachHeader + sizeof(*pMachHeader); - char modulePath[OVR_MAX_PATH] = ""; - pid_t pid = getpid(); - int filenameLen = proc_regionfilename((int)pid, currentSegmentPos, modulePath, (uint32_t)sizeof(modulePath)); - - if(filenameLen > 0) - MacModuleInfo::AddMacModuleInfo(pModuleInfoArray, moduleCountRequired, moduleCount, moduleInfoArrayCapacity, - "__TEXT", modulePath, currentSegmentPos, pMachHeader, 0); - - return moduleCountRequired; - - #elif defined(EA_PLATFORM_LINUX) - // One approach is to read /proc/self/maps, which is supported by Linux (though not BSD). - // Linux glibc dladdr() can tell us what module an arbitrary function address comes from, but can't tell us the list of modules. - OVR_UNUSED(pModuleInfoArray); - OVR_UNUSED(moduleInfoArrayCapacity); - return 0; - - #else - OVR_UNUSED(pModuleInfoArray); - OVR_UNUSED(moduleInfoArrayCapacity); - return 0; - #endif -} - - -size_t SymbolLookup::GetThreadList(ThreadHandle* threadHandleArray, ThreadSysId* threadSysIdArray, size_t threadArrayCapacity) -{ - size_t countRequired = 0; - size_t count = 0; - - #if defined(OVR_OS_MS) - // Print a list of threads. - DWORD currentProcessId = GetCurrentProcessId(); - HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, currentProcessId); // ICreateToolhelp32Snapshot actually ignores currentProcessId. - - if(hThreadSnap != INVALID_HANDLE_VALUE) - { - THREADENTRY32 te32; - te32.dwSize = sizeof(THREADENTRY32); - - if(Thread32First(hThreadSnap, &te32)) - { - do - { - if(te32.th32OwnerProcessID == currentProcessId) - { - HANDLE hThread = ConvertThreadSysIdToThreadHandle(te32.th32ThreadID); - - if(hThread) - { - ++countRequired; - - if((threadHandleArray || threadSysIdArray) && (count < threadArrayCapacity)) - { - if(threadHandleArray) - threadHandleArray[count] = hThread; // The caller must call CloseHandle on this thread, or call DoneThreadList on the returned array. - if(threadSysIdArray) - threadSysIdArray[count] = ConvertThreadHandleToThreadSysId(hThread); - ++count; - } - - if(!threadHandleArray) // If we aren't giving this back to the user... - FreeThreadHandle(hThread); - } - } - } while(Thread32Next(hThreadSnap, &te32)); - } - - CloseHandle(hThreadSnap); - } - - #elif defined(OVR_OS_APPLE) - mach_port_t taskSelf = mach_task_self(); - thread_act_port_array_t threadArray; - mach_msg_type_number_t threadCount; - - kern_return_t result = task_threads(taskSelf, &threadArray, &threadCount); - - if(result == KERN_SUCCESS) - { - for(mach_msg_type_number_t i = 0; i < threadCount; i++) - { - ++countRequired; - - if((threadHandleArray || threadSysIdArray) && (count < threadArrayCapacity)) - { - if(threadHandleArray) - threadHandleArray[count] = pthread_from_mach_thread_np(threadArray[i]); - if(threadSysIdArray) - threadSysIdArray[count] = threadArray[i]; - ++count; - } - } - - vm_deallocate(taskSelf, (vm_address_t)threadArray, threadCount * sizeof(thread_act_t)); - } - - #elif defined(OVR_OS_LINUX) - // To do. - OVR_UNUSED(count); - OVR_UNUSED(threadHandleArray); - OVR_UNUSED(threadSysIdArray); - OVR_UNUSED(threadArrayCapacity); - #endif - - return countRequired; -} - - -void SymbolLookup::DoneThreadList(ThreadHandle* threadHandleArray, ThreadSysId* threadSysIdArray, size_t threadArrayCount) -{ - #if defined(OVR_OS_MS) - for(size_t i = 0; i != threadArrayCount; ++i) - { - if(threadHandleArray[i]) - { - CloseHandle(threadHandleArray[i]); - threadHandleArray[i] = OVR_THREADHANDLE_INVALID; - } - } - - OVR_UNUSED(threadSysIdArray); - #else - OVR_UNUSED(threadHandleArray); - OVR_UNUSED(threadSysIdArray); - OVR_UNUSED(threadArrayCount); - #endif -} - - -// Writes a given thread's callstack wity symbols to the given output. -// It may not be safe to call this from an exception handler, as sOutput allocates memory. -bool SymbolLookup::ReportThreadCallstack(OVR::String& sOutput, size_t skipCount, ThreadSysId threadSysId) -{ - if(!threadSysId) - threadSysId = GetCurrentThreadSysId(); - - void* addressArray[64]; - size_t addressCount = GetBacktraceFromThreadSysId(addressArray, OVR_ARRAY_COUNT(addressArray), skipCount, threadSysId); - - // Print the header - char headerBuffer[256]; - char threadName[32]; - char threadHandleStr[24]; - char threadSysIdStr[24]; - char stackBaseStr[24]; - char stackLimitStr[24]; - void* pStackBase; - void* pStackLimit; - //void* pStackCurrent; // Current stack pointer. To do: support reporting this. - ThreadHandle threadHandle = ConvertThreadSysIdToThreadHandle(threadSysId); - OVR::GetThreadStackBounds(pStackBase, pStackLimit, threadHandle); - - Thread::GetThreadName(threadName, OVR_ARRAY_COUNT(threadName), threadName); - SprintfThreadHandle(threadHandleStr, OVR_ARRAY_COUNT(threadHandleStr), threadHandle); - SprintfThreadSysId(threadSysIdStr, OVR_ARRAY_COUNT(threadSysIdStr), threadSysId); - SprintfAddress(stackBaseStr, OVR_ARRAY_COUNT(stackBaseStr), pStackBase); - SprintfAddress(stackLimitStr, OVR_ARRAY_COUNT(stackLimitStr), pStackLimit); - - if(threadName[0]) - 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); - else - OVR_snprintf(headerBuffer, OVR_ARRAY_COUNT(headerBuffer), "Thread handle: %s, id: %s, stack base: %s, stack limit: %s\r\n", threadHandleStr, threadSysIdStr, stackBaseStr, stackLimitStr); - - sOutput += headerBuffer; - - // Print the backtrace info - char backtraceBuffer[1024]; // Sometimes function symbol names are very long. - SymbolInfo symbolInfo; - const char* pModuleName; - - if(addressCount == 0) - { - sOutput += "<Unable to read backtrace>\r\n"; - } - else - { - for(size_t i = 0; i < addressCount; ++i) - { - LookupSymbol((uint64_t)addressArray[i], symbolInfo); - - if(symbolInfo.pModuleInfo && symbolInfo.pModuleInfo->name[0]) - pModuleName = symbolInfo.pModuleInfo->name; - else - pModuleName = "(unknown module)"; - - char addressStr[24]; - SprintfAddress(addressStr, OVR_ARRAY_COUNT(addressStr), addressArray[i]); - - if(symbolInfo.filePath[0]) - 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); - else - OVR_snprintf(backtraceBuffer, OVR_ARRAY_COUNT(backtraceBuffer), "%-2u %-24s %s %s+%d\r\n", (unsigned)i, pModuleName, addressStr, symbolInfo.function, symbolInfo.functionOffset); - - sOutput += backtraceBuffer; - } - } - - FreeThreadHandle(threadHandle); - - return (addressCount > 0); -} - - -// Writes all thread's callstacks with symbols to the given output. -// It may not be safe to call this from an exception handler, as sOutput allocates memory. -bool SymbolLookup::ReportThreadCallstacks(OVR::String& sOutput, size_t skipCount) -{ - ThreadSysId threadSysIdArray[64]; - size_t threadSysIdCount = GetThreadList(nullptr, threadSysIdArray, OVR_ARRAY_COUNT(threadSysIdArray)); - - if(threadSysIdCount > OVR_ARRAY_COUNT(threadSysIdArray)) - threadSysIdCount = OVR_ARRAY_COUNT(threadSysIdArray); - - for(size_t i = 0; i < threadSysIdCount; i++) - { - String sTemp; - ReportThreadCallstack(sTemp, skipCount, threadSysIdArray[i]); - if(i > 0) - sOutput += "\r\n"; - sOutput += sTemp; - } - - return (threadSysIdCount > 0); -} - - -bool SymbolLookup::RefreshModuleList() -{ - if(!moduleListUpdated) - { - #if defined(OVR_OS_MS) - // We can't rely on SymRefreshModuleList because it's present in DbgHelp 6.5, - // which doesn't distribute with Windows 7. - - // Currently we support only refreshing the list once ever. With a little effort we could revise this code to - // support re-refreshing the list at runtime to account for the possibility that modules have recently been - // added or removed. - if(pSymLoadModule64) - { - const size_t requiredCount = GetModuleInfoArray(moduleInfoArray, OVR_ARRAY_COUNT(moduleInfoArray)); - moduleInfoArraySize = MIN(requiredCount, OVR_ARRAY_COUNT(moduleInfoArray)); - - HANDLE hProcess = GetCurrentProcess(); - - for(size_t i = 0; i < moduleInfoArraySize; i++) - pSymLoadModule64(hProcess, nullptr, moduleInfoArray[i].filePath, nullptr, moduleInfoArray[i].baseAddress, (DWORD)moduleInfoArray[i].size); - - moduleListUpdated = true; - } - #else - const size_t requiredCount = GetModuleInfoArray(moduleInfoArray, OVR_ARRAY_COUNT(moduleInfoArray)); - moduleInfoArraySize = MIN(requiredCount, OVR_ARRAY_COUNT(moduleInfoArray)); - moduleListUpdated = true; - #endif - } - - return true; -} - - -bool SymbolLookup::LookupSymbol(uint64_t address, SymbolInfo& symbolInfo) -{ - return LookupSymbols(&address, &symbolInfo, 1); -} - - -bool SymbolLookup::LookupSymbols(uint64_t* addressArray, SymbolInfo* pSymbolInfoArray, size_t arraySize) -{ - if(!moduleListUpdated) - { - RefreshModuleList(); - } - - #if defined(OVR_OS_MS) - union SYMBOL_INFO_UNION - { - SYMBOL_INFO msSymbolInfo; - char suffixPadding[sizeof(SYMBOL_INFO) + 1024]; - }; - - for(size_t i = 0; i < arraySize; i++) - { - uint64_t& address = addressArray[i]; - SymbolInfo& symbolInfo = pSymbolInfoArray[i]; - - // Copy the address and ModuleInfo - symbolInfo.address = addressArray[i]; - symbolInfo.pModuleInfo = GetModuleInfoForAddress(address); // We could also use siu.msSymbolInfo.ModBase to get the module slightly faster. - - // Get the function/offset. - SYMBOL_INFO_UNION siu; - memset(&siu, 0, sizeof(siu)); - siu.msSymbolInfo.SizeOfStruct = sizeof(siu.msSymbolInfo); - siu.msSymbolInfo.MaxNameLen = sizeof(siu.suffixPadding) - sizeof(SYMBOL_INFO) + 1; // +1 because SYMBOL_INFO itself has Name[1]. - - HANDLE hProcess = GetCurrentProcess(); - DWORD64 displacement64 = 0; - bool bResult = (pSymFromAddr != nullptr) && (pSymFromAddr(hProcess, address, &displacement64, &siu.msSymbolInfo) != FALSE); - - if(bResult) - { - symbolInfo.size = siu.msSymbolInfo.Size; - OVR_strlcpy(symbolInfo.function, siu.msSymbolInfo.Name, OVR_ARRAY_COUNT(symbolInfo.function)); - symbolInfo.functionOffset = (int32_t)displacement64; - } - else - { - symbolInfo.size = kMISizeInvalid; - symbolInfo.function[0] = 0; - symbolInfo.functionOffset = kMIFunctionOffsetInvalid; - } - - // Get the file/line - IMAGEHLP_LINE64 iLine64; - DWORD displacement = 0; - memset(&iLine64, 0, sizeof(iLine64)); - iLine64.SizeOfStruct = sizeof(iLine64); - - bResult = (pSymGetLineFromAddr64 != nullptr) && (pSymGetLineFromAddr64(hProcess, address, &displacement, &iLine64) != FALSE); - - if(bResult) - { - OVR_strlcpy(symbolInfo.filePath, iLine64.FileName, OVR_ARRAY_COUNT(symbolInfo.filePath)); - symbolInfo.fileLineNumber = (int32_t)iLine64.LineNumber; - } - else - { - symbolInfo.filePath[0] = 0; - symbolInfo.fileLineNumber = kMILineNumberInvalid; - } - - // To do: get the source code when possible. We need to use the user-registered directory paths and the symbolInfo.filePath - // and find the given file in the tree(s), then open the file and find the symbolInfo.fileLineNumber line (and surrounding lines). - // symbolInfo.sourceCode[1024] - symbolInfo.sourceCode[0] = '\0'; - } - - #elif defined(OVR_OS_APPLE) - // Apple has an internal CoreSymbolication library which could help with this. - // Third party implementations of the CoreSymbolication header are available and could be used - // to get file/line info better than other means. It used Objective C, so we'll need a .m or .mm file. - - memset(pSymbolInfoArray, 0, arraySize * sizeof(SymbolInfo)); - - for(size_t i = 0; i < arraySize; i++) - { - pSymbolInfoArray[i].address = addressArray[i]; - pSymbolInfoArray[i].pModuleInfo = GetModuleInfoForAddress(addressArray[i]); - } - - // Problem: backtrace_symbols allocates memory from malloc. If you got into a SIGSEGV due to - // malloc arena corruption (quite common) you will likely fault in backtrace_symbols. - // To do: Use allowMemoryAllocation here. - - #if (OVR_PTR_SIZE == 4) - // backtrace_symbols takes a void* array, but we have a uint64_t array. So for 32 bit we - // need to convert the 64 bit array to 32 bit temporarily for the backtrace_symbols call. - void* ptr32Array[256]; // To do: Remove this limit. - for(size_t i = 0, iEnd = MIN(arraySize, OVR_ARRAY_COUNT(ptr32Array)); i < iEnd; i++) - ptr32Array[i] = reinterpret_cast<void*>(addressArray[i]); - char** symbolArray = backtrace_symbols(reinterpret_cast<void**>(ptr32Array), (int)arraySize); - #else - char** symbolArray = backtrace_symbols(reinterpret_cast<void**>(addressArray), (int)arraySize); - #endif - - if(symbolArray) - { - for(size_t i = 0; i < arraySize; i++) - { - - // Generates a string like this: "0 OculusWorldDemo 0x000000010000cfd5 _ZN18OculusWorldDemoApp9OnStartupEiPPKc + 213" - static_assert(OVR_ARRAY_COUNT(pSymbolInfoArray[i].function) == 128, "Need to change the string format size below"); - - sscanf(symbolArray[i], "%*d %*s %*x %128s + %d", pSymbolInfoArray[i].function, &pSymbolInfoArray[i].functionOffset); - - if(allowMemoryAllocation) - { - int status = 0; - char* strDemangled = abi::__cxa_demangle(pSymbolInfoArray[i].function, nullptr, nullptr, &status); - - if(strDemangled) - { - OVR_strlcpy(pSymbolInfoArray[i].function, strDemangled, OVR_ARRAY_COUNT(pSymbolInfoArray[i].function)); - free(strDemangled); - } - } - } - - free(symbolArray); - } - - // To consider: use CoreSybolication to get file/line info instead. atos is a bit slow and cumbersome. - // https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/atos.1.html - // atos -p <pid> <addr> <addr> ... - // atos -o <binary image path> -l <load-address> <addr> <addr> ... - // Generates output like this: "OVR::CreateException(OVR::CreateExceptionType) (in OculusWorldDemo) (ExceptionHandler.cpp:598)" - for(size_t i = 0; i < arraySize; i++) - { - struct stat statStruct; - - if(pSymbolInfoArray[i].pModuleInfo && pSymbolInfoArray[i].pModuleInfo->filePath[0] && (stat(pSymbolInfoArray[i].pModuleInfo->filePath, &statStruct) == 0)) - { - 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. - OVR_snprintf(command, OVR_ARRAY_COUNT(command), "atos -o %s -l 0x%llx 0x%llx", - pSymbolInfoArray[i].pModuleInfo->filePath, (int64_t)pSymbolInfoArray[i].pModuleInfo->baseAddress, (int64_t)pSymbolInfoArray[i].address); - - char output[512]; - if(SpawnShellCommand(command, output, OVR_ARRAY_COUNT(output)) != (size_t)-1) - { - char* pLastOpenParen = strrchr(output, '('); - char* pColon = strrchr(output, ':'); - - if(pLastOpenParen && (pColon > pLastOpenParen)) - { - *pColon = '\0'; - OVR_strlcpy(pSymbolInfoArray[i].filePath, pLastOpenParen + 1, OVR_ARRAY_COUNT(pSymbolInfoArray[i].filePath)); - } - } - } - } - - #elif defined(OVR_OS_LINUX) - // We can use libunwind's unw_get_proc_name to try to get function name info. It can work regardless of relocation. - // Use backtrace_symbols and addr2line. Need to watch out for module load-time relocation. - // Ned to pass the -rdynamic flag to the linker. It will cause the linker to out in the link - // tables the name of all the none static functions in your code, not just the exported ones. - OVR_UNUSED(addressArray); - OVR_UNUSED(pSymbolInfoArray); - OVR_UNUSED(arraySize); - #endif - - return true; // To do: Return true only if something was found. -} - - -const ModuleInfo* SymbolLookup::GetModuleInfoForAddress(uint64_t address) -{ - // This is a linear seach. To consider: it would be significantly faster to search by - // address if we ordered it by base address and did a binary search. - for(size_t i = 0; i < moduleInfoArraySize; ++i) - { - const ModuleInfo& mi = moduleInfoArray[i]; - - if((mi.baseAddress <= address) && (address < (mi.baseAddress + mi.size))) - return &mi; - } - - return nullptr; -} - - - - -ExceptionInfo::ExceptionInfo() - : time() - , timeVal(0) - , backtrace() - , backtraceCount(0) - , threadHandle(OVR_THREADHANDLE_INVALID) - , threadSysId(OVR_THREADSYSID_INVALID) - , threadName() - , pExceptionInstructionAddress(nullptr) - , pExceptionMemoryAddress(nullptr) - , cpuContext() - , exceptionDescription() - , symbolInfo() - #if defined(OVR_OS_MS) - , exceptionRecord() - #elif defined(OVR_OS_APPLE) - , exceptionType(0) - , cpuExceptionId(0) - , cpuExceptionIdError(0) - , machExceptionDetail() - , machExceptionDetailCount(0) - #endif -{ -} - - - -ExceptionHandler::ExceptionHandler() - : enabled(false) - , reportPrivacyEnabled(true) - , exceptionResponse(kERHandle) - , exceptionListener(nullptr) - , exceptionListenerUserValue(0) - , appDescription() - , codeBasePathArray() - , reportFilePath() - , miniDumpFlags(0) - , miniDumpFilePath() - , file(nullptr) - , scratchBuffer() - , exceptionOccurred(false) - , handlingBusy(0) - , reportFilePathActual() - , minidumpFilePathActual() - , terminateReturnValue(0) - , exceptionInfo() - #if defined(OVR_OS_MS) - , vectoredHandle(nullptr) - , previousFilter(nullptr) - , pExceptionPointers(nullptr) - #elif defined(OVR_OS_MAC) - , machHandlerInitialized(false) - , machExceptionPort(0) - , machExceptionPortsSaved() - , machThreadShouldContinue(false) - , machThreadExecuting(false) - , machThread((pthread_t)OVR_THREADHANDLE_INVALID) - #endif -{ - SetExceptionPaths("default", "default"); -} - - -ExceptionHandler::~ExceptionHandler() -{ - if(enabled) - { - Enable(false); - } -} - - -#if defined(OVR_OS_MS) - static ExceptionHandler* sExceptionHandler = nullptr; - - LONG WINAPI Win32ExceptionFilter(LPEXCEPTION_POINTERS pExceptionPointers) - { - if(sExceptionHandler) - return (LONG)sExceptionHandler->ExceptionFilter(pExceptionPointers); - return EXCEPTION_CONTINUE_SEARCH; - } - - LONG ExceptionHandler::ExceptionFilter(LPEXCEPTION_POINTERS pExceptionPointers) - { - // Exception codes < 0x80000000 are not true exceptions but rather are debugger notifications. They include DBG_TERMINATE_THREAD, - // DBG_TERMINATE_PROCESS, DBG_CONTROL_BREAK, DBG_COMMAND_EXCEPTION, DBG_CONTROL_C, DBG_PRINTEXCEPTION_C, DBG_RIPEXCEPTION, - // and 0x406d1388 (thread named, http://blogs.msdn.com/b/stevejs/archive/2005/12/19/505815.aspx). - - if(pExceptionPointers->ExceptionRecord->ExceptionCode < 0x80000000) - return EXCEPTION_CONTINUE_SEARCH; - - // VC++ C++ exceptions use code 0xe06d7363 ('Emsc') - // http://support.microsoft.com/kb/185294 - // http://blogs.msdn.com/b/oldnewthing/archive/2010/07/30/10044061.aspx - if(pExceptionPointers->ExceptionRecord->ExceptionCode == 0xe06d7363) - return EXCEPTION_CONTINUE_SEARCH; - - if(handlingBusy.CompareAndSet_Acquire(0, 1)) // If we can successfully change it from 0 to 1. - { - exceptionOccurred = true; - - this->pExceptionPointers = pExceptionPointers; - - // Disable the handler while we do this processing. - ULONG result = RemoveVectoredExceptionHandler(vectoredHandle); - OVR_ASSERT_AND_UNUSED(result != 0, result); - - // Time - exceptionInfo.timeVal = time(nullptr); - exceptionInfo.time = *gmtime(&exceptionInfo.timeVal); - - // Thread id - // This is the thread id of the current thread and not the exception thread. - if(!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &exceptionInfo.threadHandle, 0, true, DUPLICATE_SAME_ACCESS)) - exceptionInfo.threadHandle = 0; - exceptionInfo.threadSysId = ConvertThreadHandleToThreadSysId(exceptionInfo.threadHandle); - - OVR::GetThreadName(exceptionInfo.threadHandle, exceptionInfo.threadName, OVR_ARRAY_COUNT(exceptionInfo.threadName)); - - // Backtraces - exceptionInfo.backtraceCount = symbolLookup.GetBacktrace(exceptionInfo.backtrace, OVR_ARRAY_COUNT(exceptionInfo.backtrace)); - - // Context - exceptionInfo.cpuContext = *pExceptionPointers->ContextRecord; - exceptionInfo.exceptionRecord = *pExceptionPointers->ExceptionRecord; - exceptionInfo.pExceptionInstructionAddress = exceptionInfo.exceptionRecord.ExceptionAddress; - if((exceptionInfo.exceptionRecord.ExceptionCode == EXCEPTION_ACCESS_VIOLATION) || (exceptionInfo.exceptionRecord.ExceptionCode == EXCEPTION_IN_PAGE_ERROR)) - exceptionInfo.pExceptionMemoryAddress = (void*)exceptionInfo.exceptionRecord.ExceptionInformation[1]; // ExceptionInformation[0] indicates if it was a read (0), write (1), or data execution attempt (8). - else - exceptionInfo.pExceptionMemoryAddress = pExceptionPointers->ExceptionRecord->ExceptionAddress; - - WriteExceptionDescription(); - - if(miniDumpFilePath[0]) - WriteMiniDump(); - - if(reportFilePath[0]) - WriteReport(); - - if(exceptionListener) - exceptionListener->HandleException(exceptionListenerUserValue, this, &exceptionInfo, reportFilePathActual); - - if(exceptionInfo.threadHandle) - { - CloseHandle(exceptionInfo.threadHandle); - exceptionInfo.threadHandle = 0; - } - - // Restore the handler that we temporarily disabled above. - vectoredHandle = AddVectoredExceptionHandler(1, Win32ExceptionFilter); - - handlingBusy.Store_Release(0); - } - - if(exceptionResponse == ExceptionHandler::kERTerminate) - { - TerminateProcess(GetCurrentProcess(), (UINT)terminateReturnValue); - return terminateReturnValue; - } - else if(exceptionResponse == ExceptionHandler::kERThrow) - return EXCEPTION_CONTINUE_SEARCH; - else if(exceptionResponse == ExceptionHandler::kERContinue) - return EXCEPTION_CONTINUE_EXECUTION; - return EXCEPTION_EXECUTE_HANDLER; - } - -#endif // defined(OVR_OS_MS) - - -#if defined(OVR_OS_APPLE) - // http://www.opensource.apple.com/source/xnu/xnu-2050.22.13/ - // http://www.opensource.apple.com/source/xnu/xnu-2050.22.13/osfmk/man/ - // http://www.opensource.apple.com/source/Libc/Libc-825.26/ - // https://mikeash.com/pyblog/friday-qa-2013-01-11-mach-exception-handlers.html - - void* ExceptionHandler::MachHandlerThreadFunction() - { - __Request__mach_exception_raise_state_identity_t msg; - __Reply__mach_exception_raise_state_identity_t reply; - mach_msg_return_t result; - - machThreadExecuting = true; - pthread_setname_np("ExceptionHandler"); - - while(machThreadShouldContinue) - { - mach_msg_option_t options = MACH_RCV_MSG | MACH_RCV_LARGE; - natural_t timeout = 0; // Would be better to support a non-zero time. - - if(timeout) - options |= MACH_RCV_TIMEOUT; - - result = mach_msg(&msg.Head, options, 0, sizeof(msg), machExceptionPort, timeout, MACH_PORT_NULL); - - if(msg.Head.msgh_id != sMachCancelMessageType) - { - if(result == MACH_MSG_SUCCESS) - { - if(mach_exc_server_OVR(&msg.Head, &reply.Head) == 0) //This will call our HandleMachException function. - result = ~MACH_MSG_SUCCESS; - } - - // Send the reply - if(result == MACH_MSG_SUCCESS) - { - result = mach_msg(&reply.Head, MACH_SEND_MSG, reply.Head.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); - - if(result != MACH_MSG_SUCCESS) - { - // Failure. - } - } - } - } - - machThreadExecuting = false; - - return nullptr; - } - - - kern_return_t ExceptionHandler::HandleMachException(mach_port_t /*machPort*/, mach_port_t threadSysId, mach_port_t machTask, - exception_type_t machExceptionType, mach_exception_data_type_t* pExceptionDetail, - mach_msg_type_number_t exceptionDetailCount, int* /*pMachExceptionFlavor*/, thread_state_t threadStatePrev, - mach_msg_type_number_t /*threadStatePrevCount*/, thread_state_t /*threadStateNew*/, - mach_msg_type_number_t* /*pThreadStateNewCount*/) - { - // We don't want to handle exceptions for other processes. - if(machTask != mach_task_self()) - return ForwardMachException(threadSysId, machTask, machExceptionType, pExceptionDetail, exceptionDetailCount); - - if(handlingBusy.CompareAndSet_Acquire(0, 1)) // If we can successfully change it from 0 to 1. - { - exceptionOccurred = true; - - // Disable the handler while we do this processing. - // To do. - - // Time - exceptionInfo.timeVal = time(nullptr); - exceptionInfo.time = *gmtime(&exceptionInfo.timeVal); - - // Thread id - exceptionInfo.threadHandle = pthread_from_mach_thread_np(threadSysId); - exceptionInfo.threadSysId = threadSysId; - pthread_getname_np((pthread_t)exceptionInfo.threadHandle, exceptionInfo.threadName, sizeof(exceptionInfo.threadName)); - - // Backtraces - exceptionInfo.backtraceCount = symbolLookup.GetBacktraceFromThreadSysId(exceptionInfo.backtrace, OVR_ARRAY_COUNT(exceptionInfo.backtrace), 0, threadSysId); - - // Context - #if defined(OVR_CPU_X86) || defined(OVR_CPU_X86_64) - // We can read x86_THREAD_STATE directly fromk threadStatePrev. - exceptionInfo.cpuContext.threadState = *reinterpret_cast<x86_thread_state_t*>(threadStatePrev); - - mach_msg_type_number_t stateCount = x86_FLOAT_STATE_COUNT; - thread_get_state(threadSysId, x86_FLOAT_STATE, (natural_t*)&exceptionInfo.cpuContext.floatState, &stateCount); - - stateCount = x86_DEBUG_STATE_COUNT; - thread_get_state(threadSysId, x86_DEBUG_STATE, (natural_t*)&exceptionInfo.cpuContext.debugState, &stateCount); - - stateCount = x86_AVX_STATE_COUNT; - thread_get_state(threadSysId, x86_AVX_STATE, (natural_t*)&exceptionInfo.cpuContext.avxState, &stateCount); - - stateCount = x86_EXCEPTION_STATE_COUNT; - thread_get_state(threadSysId, x86_EXCEPTION_STATE, (natural_t*)&exceptionInfo.cpuContext.exceptionState, &stateCount); - - #if defined(OVR_CPU_X86) - exceptionInfo.pExceptionInstructionAddress = (void*)exceptionInfo.cpuContext.threadState.uts.ts32.__eip; - exceptionInfo.pExceptionMemoryAddress = (void*)exceptionInfo.cpuContext.exceptionState.ues.es32.__faultvaddr; - exceptionInfo.cpuExceptionId = exceptionInfo.cpuContext.exceptionState.ues.es32.__trapno; - exceptionInfo.cpuExceptionIdError = exceptionInfo.cpuContext.exceptionState.ues.es32.__err; - #else - exceptionInfo.pExceptionInstructionAddress = (void*)exceptionInfo.cpuContext.threadState.uts.ts64.__rip; - exceptionInfo.pExceptionMemoryAddress = (void*)exceptionInfo.cpuContext.exceptionState.ues.es64.__faultvaddr; - exceptionInfo.cpuExceptionId = exceptionInfo.cpuContext.exceptionState.ues.es64.__trapno; - exceptionInfo.cpuExceptionIdError = exceptionInfo.cpuContext.exceptionState.ues.es64.__err; - #endif - #endif - - exceptionInfo.exceptionType = machExceptionType; - - exceptionInfo.machExceptionDetailCount = MIN(exceptionDetailCount, OVR_ARRAY_COUNT(exceptionInfo.machExceptionDetail)); - for(int i = 0; i < exceptionInfo.machExceptionDetailCount; i++) - exceptionInfo.machExceptionDetail[i] = pExceptionDetail[i]; - - WriteExceptionDescription(); - - if(reportFilePath[0]) - WriteReport(); - - if(miniDumpFilePath[0]) - WriteMiniDump(); - - if(exceptionListener) - exceptionListener->HandleException(exceptionListenerUserValue, this, &exceptionInfo, reportFilePathActual); - - // Re-restore the handler. - // To do. - - handlingBusy.Store_Release(0); - } - - kern_return_t result = KERN_FAILURE; // By default pass on the exception to another handler after we are done here. - - if(exceptionResponse == ExceptionHandler::kERTerminate) - ::exit(terminateReturnValue); - else if(exceptionResponse == ExceptionHandler::kERThrow) - ForwardMachException(threadSysId, machTask, machExceptionType, pExceptionDetail, exceptionDetailCount); - else if(exceptionResponse == ExceptionHandler::kERDefault) - ::exit(terminateReturnValue); - else if(exceptionResponse == ExceptionHandler::kERContinue) - result = KERN_SUCCESS; // This will trigger a re-execution of the function. - - return result; - } - - - bool ExceptionHandler::InitMachExceptionHandler() - { - if(!machHandlerInitialized) - { - mach_port_t machTaskSelf = mach_task_self(); - kern_return_t result = MACH_MSG_SUCCESS; - exception_mask_t mask = EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_CRASH; - - if(machExceptionPort == MACH_PORT_NULL) - { - result = mach_port_allocate(machTaskSelf, MACH_PORT_RIGHT_RECEIVE, &machExceptionPort); - - if(result == MACH_MSG_SUCCESS) - { - result = mach_port_insert_right(machTaskSelf, machExceptionPort, machExceptionPort, MACH_MSG_TYPE_MAKE_SEND); - - if(result == MACH_MSG_SUCCESS) - result = task_get_exception_ports(machTaskSelf, mask, machExceptionPortsSaved.masks, &machExceptionPortsSaved.count, - machExceptionPortsSaved.ports, machExceptionPortsSaved.behaviors, machExceptionPortsSaved.flavors); - } - } - - if(result == MACH_MSG_SUCCESS) - { - result = task_set_exception_ports(machTaskSelf, mask, machExceptionPort, EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, MACHINE_THREAD_STATE); - - if(result == MACH_MSG_SUCCESS) - { - machThreadShouldContinue = true; - - pthread_attr_t attr; - pthread_attr_init(&attr); - - result = pthread_create(&machThread, &attr, MachHandlerThreadFunctionStatic, (void*)this); - pthread_attr_destroy(&attr); - - machHandlerInitialized = (result == 0); - } - } - - if(!machHandlerInitialized) - ShutdownMachExceptionHandler(); - } - - return machHandlerInitialized; - } - - - void ExceptionHandler::ShutdownMachExceptionHandler() - { - if(machThreadExecuting) - { - machThreadShouldContinue = false; // Tell it to stop. - - // Cancel the current exception handler thread (which is probably blocking in a call to mach_msg) by sending it a cencel message. - struct CancelMessage - { - mach_msg_header_t msgHeader; - }; - - CancelMessage msg; - memset(&msg.msgHeader, 0, sizeof(CancelMessage)); - msg.msgHeader.msgh_id = sMachCancelMessageType; - msg.msgHeader.msgh_size = sizeof(CancelMessage); - msg.msgHeader.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MAKE_SEND); - msg.msgHeader.msgh_remote_port = machExceptionPort; - msg.msgHeader.msgh_local_port = MACH_PORT_NULL; - - 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); - - if(result == MACH_MSG_SUCCESS) - { - const double threeSecondsLater = ovr_GetTimeInSeconds() + 3.f; - - while(machThreadExecuting && (ovr_GetTimeInSeconds() < threeSecondsLater)) - { - timespec ts = { 0, 1000000000 }; - nanosleep(&ts, nullptr); - } - } - - void* joinResult = nullptr; - pthread_join(machThread, &joinResult); - machThread = 0; - } - - if(machExceptionPort != MACH_PORT_NULL) - { - // Restore the previous ports - kern_return_t result = KERN_SUCCESS; - mach_port_t machTaskSelf = mach_task_self(); - - for(unsigned i = 0; (i < machExceptionPortsSaved.count) && (result == KERN_SUCCESS); i++) - { - result = task_set_exception_ports(machTaskSelf, machExceptionPortsSaved.masks[i], machExceptionPortsSaved.ports[i], - machExceptionPortsSaved.behaviors[i], machExceptionPortsSaved.flavors[i]); - } - - mach_port_deallocate(machTaskSelf, machExceptionPort); - machExceptionPort = MACH_PORT_NULL; - } - - machHandlerInitialized = false; - } - - - kern_return_t ExceptionHandler::ForwardMachException(mach_port_t thread, mach_port_t task, exception_type_t exceptionType, - mach_exception_data_t pExceptionDetail, mach_msg_type_number_t exceptionDetailCount) - { - kern_return_t result = KERN_FAILURE; - mach_msg_type_number_t i; - - for(i = 0; i < machExceptionPortsSaved.count; i++) - { - if(machExceptionPortsSaved.masks[i] & (1 << exceptionType)) - break; - } - - if(i < machExceptionPortsSaved.count) - { - mach_port_t port = machExceptionPortsSaved.ports[i]; - exception_behavior_t behavior = machExceptionPortsSaved.behaviors[i]; - thread_state_flavor_t flavor = machExceptionPortsSaved.flavors[i]; - mach_msg_type_number_t threadStateCount = THREAD_STATE_MAX; - thread_state_data_t threadState; - - if(behavior != EXCEPTION_DEFAULT) - thread_get_state(thread, flavor, threadState, &threadStateCount); - - switch(behavior) - { - case EXCEPTION_DEFAULT: - result = mach_exception_raise_OVR(port, thread, task, exceptionType, pExceptionDetail, exceptionDetailCount); - break; - - case EXCEPTION_STATE: - result = mach_exception_raise_state_OVR(port, exceptionType, pExceptionDetail, exceptionDetailCount, - &flavor, threadState, threadStateCount, threadState, &threadStateCount); - break; - - case EXCEPTION_STATE_IDENTITY: - result = mach_exception_raise_state_identity_OVR(port, thread, task, exceptionType, pExceptionDetail, - exceptionDetailCount, &flavor, threadState, threadStateCount, threadState, &threadStateCount); - break; - - default: - result = KERN_FAILURE; - break; - } - - if(behavior != EXCEPTION_DEFAULT) - result = thread_set_state(thread, flavor, threadState, threadStateCount); - } - - return result; - } - - -#endif // OVR_OS_APPLE - - -bool ExceptionHandler::Enable(bool enable) -{ - #if defined(OVR_OS_MS) - if(enable && !enabled) - { - OVR_ASSERT(vectoredHandle == nullptr); - vectoredHandle = AddVectoredExceptionHandler(1, Win32ExceptionFilter); // Windows call. - enabled = (vectoredHandle != nullptr); - OVR_ASSERT(enabled); - sExceptionHandler = this; - return enabled; - } - else if(!enable && enabled) - { - if(sExceptionHandler == this) - sExceptionHandler = nullptr; - OVR_ASSERT(vectoredHandle != nullptr); - ULONG result = RemoveVectoredExceptionHandler(vectoredHandle); // Windows call. - OVR_ASSERT_AND_UNUSED(result != 0, result); - vectoredHandle = nullptr; - enabled = false; - return true; - } - - #elif defined(OVR_OS_APPLE) - - if(enable && !enabled) - { - enabled = InitMachExceptionHandler(); - OVR_ASSERT(enabled); - sExceptionHandler = this; - return enabled; - } - else if(!enable && enabled) - { - if(sExceptionHandler == this) - sExceptionHandler = nullptr; - ShutdownMachExceptionHandler(); - enabled = false; - return true; - } - #else - OVR_UNUSED(enable); - #endif - - return true; -} - - -void ExceptionHandler::EnableReportPrivacy(bool enable) -{ - reportPrivacyEnabled = enable; -} - -void ExceptionHandler::WriteExceptionDescription() -{ - #if defined(OVR_OS_MS) - // There is some extra information available for AV exception. - if(exceptionInfo.exceptionRecord.ExceptionCode == EXCEPTION_ACCESS_VIOLATION) - { - const char* error = (exceptionInfo.exceptionRecord.ExceptionInformation[0] == 0) ? "reading" : - ((exceptionInfo.exceptionRecord.ExceptionInformation[0] == 1) ? "writing" : "executing"); - - char addressStr[24]; - SprintfAddress(addressStr, OVR_ARRAY_COUNT(addressStr), exceptionInfo.pExceptionMemoryAddress); - OVR::OVR_snprintf(exceptionInfo.exceptionDescription, OVR_ARRAY_COUNT(exceptionInfo.exceptionDescription), "ACCESS_VIOLATION %s address %s", error, addressStr); - } - else - { - exceptionInfo.exceptionDescription[0] = 0; - - // Process "standard" exceptions, other than 'access violation' - #define FORMAT_EXCEPTION(x) \ - case EXCEPTION_##x: \ - OVR::OVR_strlcpy(exceptionInfo.exceptionDescription, #x, OVR_ARRAY_COUNT(exceptionInfo.exceptionDescription)); \ - break; - - switch(exceptionInfo.exceptionRecord.ExceptionCode) - { - //FORMAT_EXCEPTION(ACCESS_VIOLATION) Already handled above. - FORMAT_EXCEPTION(DATATYPE_MISALIGNMENT) - FORMAT_EXCEPTION(BREAKPOINT) - FORMAT_EXCEPTION(SINGLE_STEP) - FORMAT_EXCEPTION(ARRAY_BOUNDS_EXCEEDED) - FORMAT_EXCEPTION(FLT_DENORMAL_OPERAND) - FORMAT_EXCEPTION(FLT_DIVIDE_BY_ZERO) - FORMAT_EXCEPTION(FLT_INEXACT_RESULT) - FORMAT_EXCEPTION(FLT_INVALID_OPERATION) - FORMAT_EXCEPTION(FLT_OVERFLOW) - FORMAT_EXCEPTION(FLT_STACK_CHECK) - FORMAT_EXCEPTION(FLT_UNDERFLOW) - FORMAT_EXCEPTION(INT_DIVIDE_BY_ZERO) - FORMAT_EXCEPTION(INT_OVERFLOW) - FORMAT_EXCEPTION(PRIV_INSTRUCTION) - FORMAT_EXCEPTION(IN_PAGE_ERROR) - FORMAT_EXCEPTION(ILLEGAL_INSTRUCTION) - FORMAT_EXCEPTION(NONCONTINUABLE_EXCEPTION) - FORMAT_EXCEPTION(STACK_OVERFLOW) - FORMAT_EXCEPTION(INVALID_DISPOSITION) - FORMAT_EXCEPTION(GUARD_PAGE) - FORMAT_EXCEPTION(INVALID_HANDLE) - #if defined(EXCEPTION_POSSIBLE_DEADLOCK) && defined(STATUS_POSSIBLE_DEADLOCK) // This type seems to be non-existant in practice. - FORMAT_EXCEPTION(POSSIBLE_DEADLOCK) - #endif - } - - // If not one of the "known" exceptions, try to get the string from NTDLL.DLL's message table. - if(exceptionInfo.exceptionDescription[0] == 0) - { - char addressStr[24]; - SprintfAddress(addressStr, OVR_ARRAY_COUNT(addressStr), exceptionInfo.pExceptionMemoryAddress); - - #if !defined(OVR_OS_CONSOLE) // If FormatMessage is supported... - char buffer[384]; - DWORD capacity = OVR_ARRAY_COUNT(buffer); - - const size_t length = (size_t)FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE, - GetModuleHandleW(L"NTDLL.DLL"), exceptionInfo.exceptionRecord.ExceptionCode, 0, buffer, capacity, nullptr); - if(length) - OVR::OVR_snprintf(exceptionInfo.exceptionDescription, OVR_ARRAY_COUNT(exceptionInfo.exceptionDescription), - "%s at instruction %s", buffer, addressStr); - #endif - - // If everything else failed just show the hex code. - if(exceptionInfo.exceptionDescription[0] == 0) - OVR::OVR_snprintf(exceptionInfo.exceptionDescription, OVR_ARRAY_COUNT(exceptionInfo.exceptionDescription), - "Unknown exception 0x%08x at instruction %s", exceptionInfo.exceptionRecord.ExceptionCode, addressStr); - } - } - - #elif defined(OVR_OS_APPLE) - struct MachExceptionInfo - { - static const char* GetCPUExceptionIdString(uint32_t cpuExceptionId) - { - const char* id; - - #if defined(OVR_CPU_X86) || defined(OVR_CPU_X86_64) - switch (cpuExceptionId) - { - case 0: id = "integer div/0"; break; - case 1: id = "breakpoint fault"; break; - case 2: id = "non-maskable interrupt"; break; - case 3: id = "int 3"; break; - case 4: id = "overflow"; break; - case 5: id = "bounds check failure"; break; - case 6: id = "invalid instruction"; break; - case 7: id = "coprocessor unavailable"; break; - case 8: id = "exception within exception"; break; - case 9: id = "coprocessor segment overrun"; break; - case 10: id = "invalid task switch"; break; - case 11: id = "segment not present"; break; - case 12: id = "stack exception"; break; - case 13: id = "general protection fault"; break; - case 14: id = "page fault"; break; - case 16: id = "coprocessor error"; break; - default: id = "<unknown>"; break; - } - #else - // To do: Support ARM or others. - #endif - - return id; - } - - static const char* GetMachExceptionTypeString(uint64_t exceptionCause) - { - switch (exceptionCause) - { - case EXC_ARITHMETIC: return "EXC_ARITHMETIC"; - case EXC_BAD_ACCESS: return "EXC_BAD_ACCESS"; - case EXC_BAD_INSTRUCTION: return "EXC_BAD_INSTRUCTION"; - case EXC_BREAKPOINT: return "EXC_BREAKPOINT"; - case EXC_CRASH: return "EXC_CRASH"; - case EXC_EMULATION: return "EXC_EMULATION"; - case EXC_MACH_SYSCALL: return "EXC_MACH_SYSCALL"; - case EXC_RPC_ALERT: return "EXC_RPC_ALERT"; - case EXC_SOFTWARE: return "EXC_SOFTWARE"; - case EXC_SYSCALL: return "EXC_SYSCALL"; - }; - - return "EXC_<unknown>"; - } - - static const char* GetMachExceptionIdString(uint64_t machExceptionId, uint64_t code0) - { - const char* id = "<unknown>"; - - #if defined(OVR_CPU_X86) || defined(OVR_CPU_X86_64) - switch (machExceptionId) - { - case EXC_ARITHMETIC: - switch (code0) - { - case EXC_I386_BOUND: id = "EXC_I386_BOUND"; break; - case EXC_I386_DIV: id = "EXC_I386_DIV"; break; - case EXC_I386_EMERR: id = "EXC_I386_EMERR"; break; - case EXC_I386_EXTERR: id = "EXC_I386_EXTERR"; break; - case EXC_I386_EXTOVR: id = "EXC_I386_EXTOVR"; break; - case EXC_I386_INTO: id = "EXC_I386_INTO"; break; - case EXC_I386_NOEXT: id = "EXC_I386_NOEXT"; break; - case EXC_I386_SSEEXTERR: id = "EXC_I386_SSEEXTERR"; break; - } - break; - - case EXC_BAD_INSTRUCTION: - if(code0 == EXC_I386_INVOP) - id = "EXC_I386_INVOP"; - break; - - case EXC_BREAKPOINT: - if(code0 == EXC_I386_BPT) - id = "EXC_I386_BPT"; - else if(code0 == EXC_I386_SGL) - id = "EXC_I386_SGL"; - break; - }; - #else - // To do. - #endif - - return id; - } - }; - - OVR::OVR_snprintf(exceptionInfo.exceptionDescription, OVR_ARRAY_COUNT(exceptionInfo.exceptionDescription), - "Mach exception type: %llu (%s)\r\n", exceptionInfo.exceptionType, MachExceptionInfo::GetMachExceptionTypeString(exceptionInfo.exceptionType)); - - 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", - exceptionInfo.cpuExceptionId, MachExceptionInfo::GetCPUExceptionIdString(exceptionInfo.cpuExceptionId), exceptionInfo.cpuExceptionIdError, exceptionInfo.pExceptionMemoryAddress); - OVR::OVR_strlcat(exceptionInfo.exceptionDescription, scratchBuffer, OVR_ARRAY_COUNT(exceptionInfo.exceptionDescription)); - - - 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], - MachExceptionInfo::GetMachExceptionIdString(exceptionInfo.exceptionType, exceptionInfo.machExceptionDetail[0]), - (uint64_t)exceptionInfo.machExceptionDetail[1], (uint64_t)exceptionInfo.machExceptionDetail[1]); - OVR::OVR_strlcat(exceptionInfo.exceptionDescription, scratchBuffer, OVR_ARRAY_COUNT(exceptionInfo.exceptionDescription)); - #else - // To do. - exceptionInfo.exceptionDescription[0] = 0; - #endif -} - - -void ExceptionHandler::WriteReportLine(const char* pLine) -{ - fwrite(pLine, strlen(pLine), 1, file); -} - - -void ExceptionHandler::WriteReportLineF(const char* format, ...) -{ - va_list args; - va_start(args, format); - int length = OVR_vsnprintf(scratchBuffer, OVR_ARRAY_COUNT(scratchBuffer), format, args); - if(length >= (int)OVR_ARRAY_COUNT(scratchBuffer)) // If we didn't have enough space... - length = (OVR_ARRAY_COUNT(scratchBuffer) - 1); // ... use what we have. - va_end(args); - - fwrite(scratchBuffer, length, 1, file); -} - - -// Thread <name> <handle> <id> -// 0 <module> <address> <function> <file>:<line> -// 1 <module> <address> <function> <file>:<line> -// . . . -// -void ExceptionHandler::WriteThreadCallstack(ThreadHandle threadHandle, ThreadSysId threadSysId, const char* additionalInfo) -{ - // We intentionally do not directly use the SymbolInfo::ReportThreadCallstack function because that function allocates memory, - // which we cannot do due to possibly being within an exception handler. - - // Print the header - char threadName[32]; - char threadHandleStr[32]; - char threadSysIdStr[32]; - char stackBaseStr[24]; - char stackLimitStr[24]; - char stackCurrentStr[24]; - void* pStackBase; - void* pStackLimit; - bool isExceptionThread = (threadSysId == exceptionInfo.threadSysId); - - #if defined(OVR_OS_MS) && (OVR_PTR_SIZE == 8) - 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. - #elif defined(OVR_OS_MS) - void* pStackCurrent = (threadSysId == exceptionInfo.threadSysId) ? (void*)exceptionInfo.cpuContext.Esp : nullptr; - #elif defined(OVR_OS_MAC) && (OVR_PTR_SIZE == 8) - void* pStackCurrent = (threadSysId == exceptionInfo.threadSysId) ? (void*)exceptionInfo.cpuContext.threadState.uts.ts64.__rsp : nullptr; - #elif defined(OVR_OS_MAC) - void* pStackCurrent = (threadSysId == exceptionInfo.threadSysId) ? (void*)exceptionInfo.cpuContext.threadState.uts.ts32.__esp : nullptr; - #elif defined(OVR_OS_LINUX) - void* pStackCurrent = nullptr; // To do. - #endif - - OVR::GetThreadStackBounds(pStackBase, pStackLimit, threadHandle); - - OVR::Thread::GetThreadName(threadName, OVR_ARRAY_COUNT(threadName), threadName); - SprintfThreadHandle(threadHandleStr, OVR_ARRAY_COUNT(threadHandleStr), threadHandle); - SprintfThreadSysId(threadSysIdStr, OVR_ARRAY_COUNT(threadSysIdStr), threadSysId); - SprintfAddress(stackBaseStr, OVR_ARRAY_COUNT(stackBaseStr), pStackBase); - SprintfAddress(stackLimitStr, OVR_ARRAY_COUNT(stackLimitStr), pStackLimit); - SprintfAddress(stackCurrentStr, OVR_ARRAY_COUNT(stackCurrentStr), pStackCurrent); - - if(threadName[0]) - 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 : ""); - else - 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 : ""); - - // Print the backtrace info - void* addressArray[64]; - size_t addressCount = symbolLookup.GetBacktraceFromThreadSysId(addressArray, OVR_ARRAY_COUNT(addressArray), 0, threadSysId); - SymbolInfo symbolInfo; - const char* pModuleName; - size_t backtraceSkipCount = 0; - - if(isExceptionThread) - { - // If this thread is the exception thread, skip some frames. - #if defined(OVR_OS_MS) - size_t i, iEnd = MIN(16, addressCount); - - for(i = 0; i < iEnd; i++) - { - symbolLookup.LookupSymbol((uint64_t)addressArray[i], symbolInfo); - if(strstr(symbolInfo.function, "UserExceptionDispatcher") != nullptr) - break; - } - - if(i < iEnd) // If found... - backtraceSkipCount = i; - else if(addressCount >= 9) // Else default to 9, which is coincidentally what works. - backtraceSkipCount = 9; - else - backtraceSkipCount = 0; - - addressArray[backtraceSkipCount] = exceptionInfo.pExceptionInstructionAddress; - #endif - } - - if(addressCount == 0) - { - WriteReportLine("<Unable to read backtrace>\r\n\r\n"); - } - else - { - for(size_t i = backtraceSkipCount; i < addressCount; ++i) - { - symbolLookup.LookupSymbol((uint64_t)addressArray[i], symbolInfo); - - if(symbolInfo.pModuleInfo && symbolInfo.pModuleInfo->name[0]) - pModuleName = symbolInfo.pModuleInfo->name; - else - pModuleName = "(unknown module)"; - - char addressStr[24]; - SprintfAddress(addressStr, OVR_ARRAY_COUNT(addressStr), addressArray[i]); - - if(symbolInfo.filePath[0]) - WriteReportLineF("%-2u %-24s %s %s+%d %s:%d\r\n%s", (unsigned)i, pModuleName, addressStr, - symbolInfo.function, symbolInfo.functionOffset, symbolInfo.filePath, - symbolInfo.fileLineNumber, (i + 1) == addressCount ? "\r\n" : ""); - else - WriteReportLineF("%-2u %-24s %s %s+%d\r\n%s", (unsigned)i, pModuleName, addressStr, - symbolInfo.function, symbolInfo.functionOffset, (i + 1) == addressCount ? "\r\n" : ""); // If this is the last line, append another \r\n. - } - } -} - - -void ExceptionHandler::WriteReport() -{ - // It's important that we don't allocate any memory here if we can help it. - using namespace OVR; - - if(strstr(reportFilePath, "%s")) // If the user-specified file path includes a date/time component... - { - char dateTimeBuffer[64]; - FormatDateTime(dateTimeBuffer, OVR_ARRAY_COUNT(dateTimeBuffer), exceptionInfo.timeVal, true, true, false, true); - OVR_snprintf(reportFilePathActual, OVR_ARRAY_COUNT(reportFilePathActual), reportFilePath, dateTimeBuffer); - } - else - { - OVR_strlcpy(reportFilePathActual, reportFilePath, OVR_ARRAY_COUNT(reportFilePathActual)); - } - - file = fopen(reportFilePathActual, "w"); - OVR_ASSERT(file != nullptr); - if(!file) - return; - - symbolLookup.Initialize(); - - { - // Exception information - WriteReportLine("Exception Info\r\n"); - - WriteReportLineF("Exception report file: %s\r\n", reportFilePathActual); - - #if defined(OVR_OS_MS) - if(miniDumpFilePath[0]) - WriteReportLineF("Exception minidump file: %s\r\n", minidumpFilePathActual); - #endif - - char dateTimeBuffer[64]; - FormatDateTime(dateTimeBuffer, OVR_ARRAY_COUNT(dateTimeBuffer), exceptionInfo.timeVal, true, true, false, false); - WriteReportLineF("Time (GMT): %s\r\n", dateTimeBuffer); - - FormatDateTime(dateTimeBuffer, OVR_ARRAY_COUNT(scratchBuffer), exceptionInfo.timeVal, true, true, true, false); - WriteReportLineF("Time (local): %s\r\n", dateTimeBuffer); - 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. - - SprintfThreadHandle(scratchBuffer, OVR_ARRAY_COUNT(scratchBuffer), exceptionInfo.threadHandle); - OVR_strlcat(scratchBuffer, "\r\n", OVR_ARRAY_COUNT(scratchBuffer)); - WriteReportLine("Thread handle: "); - WriteReportLine(scratchBuffer); - - SprintfThreadSysId(scratchBuffer, OVR_ARRAY_COUNT(scratchBuffer), exceptionInfo.threadSysId); - OVR_strlcat(scratchBuffer, "\r\n", OVR_ARRAY_COUNT(scratchBuffer)); - WriteReportLine("Thread sys id: "); - WriteReportLine(scratchBuffer); - - char addressStr[24]; - SprintfAddress(addressStr, OVR_ARRAY_COUNT(addressStr), exceptionInfo.pExceptionInstructionAddress); - WriteReportLineF("Exception instruction address: %s (see callstack below)\r\n", addressStr); - WriteReportLineF("Exception description: %s\r\n", exceptionInfo.exceptionDescription); - - if(symbolLookup.LookupSymbol((uint64_t)exceptionInfo.pExceptionInstructionAddress, exceptionInfo.symbolInfo)) - { - if(exceptionInfo.symbolInfo.filePath[0]) - WriteReportLineF("Exception location: %s (%d)\r\n", exceptionInfo.symbolInfo.filePath, exceptionInfo.symbolInfo.fileLineNumber); - else - WriteReportLineF("Exception location: %s (%d)\r\n", exceptionInfo.symbolInfo.function, exceptionInfo.symbolInfo.functionOffset); - } - - // To consider: print exceptionInfo.cpuContext registers - } - - // OVR information - WriteReportLine("\r\nOVR Info\r\n"); - WriteReportLineF("OVR time: %f\r\n", ovr_GetTimeInSeconds()); - WriteReportLineF("OVR version: %s\r\n", ovr_GetVersionString()); - - // OVR util information - // The following would be useful to use if they didn't allocate memory, which we can't do. - // To do: see if we can have versions of the functions below which don't allocate memory - // or allocate it safely (e.g. use an alternative heap). - // String OVR::GetDisplayDriverVersion(); - // String OVR::GetCameraDriverVersion(); - - // OVR HMD information - WriteReportLine("\r\nOVR HMD Info\r\n"); - - const OVR::List<OVR::CAPI::HMDState>& hmdStateList = OVR::CAPI::HMDState::GetHMDStateList(); - const OVR::CAPI::HMDState* pHMDState = hmdStateList.GetFirst(); - - if(hmdStateList.IsNull(pHMDState)) - { - WriteReportLine("No HMDs found.\r\n"); - } - - while(!hmdStateList.IsNull(pHMDState)) - { - if(pHMDState->pProfile) - { - const char* user = pHMDState->pProfile->GetValue(OVR_KEY_USER); - - if(user) - WriteReportLineF("Profile user: %s\r\n", reportPrivacyEnabled ? "<disabled by report privacy settings>" : user); - else - WriteReportLine("Null profile user\r\n"); - - float NeckEyeDistance[2]; - float EyeToNoseDistance[2]; - float MaxEyeToPlateDist[2]; - pHMDState->pProfile->GetFloatValues(OVR_KEY_NECK_TO_EYE_DISTANCE, NeckEyeDistance, 2); - pHMDState->pProfile->GetFloatValues(OVR_KEY_EYE_TO_NOSE_DISTANCE, EyeToNoseDistance, 2); - pHMDState->pProfile->GetFloatValues(OVR_KEY_MAX_EYE_TO_PLATE_DISTANCE, MaxEyeToPlateDist, 2); - - 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", - pHMDState->pProfile->GetFloatValue(OVR_KEY_PLAYER_HEIGHT, 0.f), - pHMDState->pProfile->GetFloatValue(OVR_KEY_EYE_HEIGHT, 0.f), - pHMDState->pProfile->GetFloatValue(OVR_KEY_IPD, 0.f), - NeckEyeDistance[0], NeckEyeDistance[1], - pHMDState->pProfile->GetIntValue(OVR_KEY_EYE_RELIEF_DIAL, 0), - EyeToNoseDistance[0], EyeToNoseDistance[1], - MaxEyeToPlateDist[0], MaxEyeToPlateDist[1], - pHMDState->pProfile->GetBoolValue(OVR_KEY_CUSTOM_EYE_RENDER, false) ? "yes" : "no"); - - // Not currently used: - // OVR_KEY_NAME - // OVR_KEY_GENDER - // OVR_KEY_EYE_CUP - // OVR_KEY_CAMERA_POSITION - } - else - { - WriteReportLine("Null HMD profile\r\n"); - } - - if(pHMDState->pHmdDesc) // This should usually be true. - { - 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", - 0, (unsigned)pHMDState->pHmdDesc->Type, pHMDState->pHmdDesc->ProductName, pHMDState->pHmdDesc->Manufacturer, pHMDState->pHmdDesc->VendorId, - pHMDState->pHmdDesc->ProductId, pHMDState->pHmdDesc->SerialNumber, pHMDState->pHmdDesc->FirmwareMajor, pHMDState->pHmdDesc->FirmwareMinor, - pHMDState->pHmdDesc->Resolution.w, pHMDState->pHmdDesc->Resolution.h, pHMDState->pHmdDesc->DisplayDeviceName, pHMDState->pHmdDesc->DisplayId); - - // HSW display state - ovrHSWDisplayState hswDS; - ovrHmd_GetHSWDisplayState(pHMDState->pHmdDesc, &hswDS); - WriteReportLineF("HSW displayed for hmd: %s\r\n", hswDS.Displayed ? "yes" : "no"); - } - - char threadIdStr[24]; - SprintfAddress(threadIdStr, OVR_ARRAY_COUNT(threadIdStr), pHMDState->BeginFrameThreadId); - - 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", - pHMDState->EnabledHmdCaps, pHMDState->EnabledServiceHmdCaps, pHMDState->LatencyTestActive ? "yes" : "no", pHMDState->LastFrameTimeSeconds, pHMDState->LastGetFrameTimeSeconds, pHMDState->RenderingConfigured ? "yes" : "no", - pHMDState->BeginFrameCalled ? "yes" : "no", threadIdStr); - - if(pHMDState->pLastError) - { - WriteReportLineF("OVR last error for hmd: %s\r\n", pHMDState->pLastError); - } - - pHMDState = hmdStateList.GetNext(pHMDState); - } - - #if defined(OVR_OS_WIN32) - { - WriteReportLine("\r\nApp Info\r\n"); - - // Print the app path. - char appPath[MAX_PATH]; - GetCurrentProcessFilePath(appPath, OVR_ARRAY_COUNT(appPath)); - WriteReportLineF("Process path: %s\r\n", appPath); - - #if (OVR_PTR_SIZE == 4) - WriteReportLine("App format: 32 bit\r\n"); - #else - WriteReportLine("App format: 64 bit\r\n"); - #endif - - // Print the app version - wchar_t pathW[MAX_PATH] = {}; - GetModuleFileNameW(0, pathW, (DWORD)OVR_ARRAY_COUNT(pathW)); - DWORD dwUnused; - DWORD dwSize = GetFileVersionInfoSizeW(pathW, &dwUnused); - scratchBuffer[0] = 0; - - if(dwSize > 0) - { - void* const pVersionData = SafeMMapAlloc(dwSize); - - if(pVersionData) - { - if(GetFileVersionInfoW(pathW, 0, dwSize, pVersionData)) - { - VS_FIXEDFILEINFO* pFFI; - UINT size; - - if(VerQueryValueA(pVersionData, "\\", (void**)&pFFI, &size)) - { - WriteReportLineF("App version: %u.%u.%u.%u\r\n", - HIWORD(pFFI->dwFileVersionMS), LOWORD(pFFI->dwFileVersionMS), - HIWORD(pFFI->dwFileVersionLS), LOWORD(pFFI->dwFileVersionLS)); - } - } - - SafeMMapFree(pVersionData, dwSize); - } - } - - if(!scratchBuffer[0]) // If version info couldn't be found or read... - WriteReportLine("App version info not present\r\n"); - } - - { - WriteReportLine("\r\nSystem Info\r\n"); - - OSVERSIONINFOEXW vi; - memset(&vi, 0, sizeof(vi)); - vi.dwOSVersionInfoSize = sizeof(vi); - GetVersionExW((LPOSVERSIONINFOW)&vi); // Cast to the older type. - - char osVersionName[256]; - GetOSVersionName(osVersionName, OVR_ARRAY_COUNT(osVersionName)); - WriteReportLineF("OS name: %s, version: %u.%u build %u, %s, platform id: %u, service pack: %ls\r\n", - osVersionName, vi.dwMajorVersion, vi.dwMinorVersion, vi.dwBuildNumber, Is64BitOS() ? "64 bit" : "32 bit", - vi.dwPlatformId, vi.szCSDVersion[0] ? vi.szCSDVersion : L"<none>"); - - WriteReportLineF("Debugger present: %s\r\n", OVRIsDebuggerPresent() ? "yes" : "no"); - - // System info - SYSTEM_INFO systemInfo; - GetNativeSystemInfo(&systemInfo); - - WriteReportLineF("Processor count: %u\r\n", systemInfo.dwNumberOfProcessors); - - // Windows Vista and later: - // BOOL WINAPI GetLogicalProcessorInformation(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION Buffer, PDWORD ReturnLength); - - if(systemInfo.wProcessorArchitecture == 0) - WriteReportLineF("Processor type: x86\r\n"); - else if(systemInfo.wProcessorArchitecture == 9) - WriteReportLineF("Processor type: x86-64\r\n"); - else if(systemInfo.wProcessorArchitecture == 10) - WriteReportLineF("Processor type: x86 on x86-64\r\n"); - - WriteReportLineF("Processor level: %u\r\n", systemInfo.wProcessorLevel); - WriteReportLineF("Processor revision: %u\r\n", systemInfo.wProcessorRevision); - - // Memory information - MEMORYSTATUSEX memoryStatusEx; - memset(&memoryStatusEx, 0, sizeof(memoryStatusEx)); - memoryStatusEx.dwLength = sizeof(memoryStatusEx); - GlobalMemoryStatusEx(&memoryStatusEx); - - WriteReportLineF("Memory load: %d%%\r\n", memoryStatusEx.dwMemoryLoad); - WriteReportLineF("Total physical memory: %I64d MiB\r\n", memoryStatusEx.ullTotalPhys / (1024 * 1024)); // Or are Mebibytes equal to (1024 * 1000) - WriteReportLineF("Available physical memory: %I64d MiB\r\n", memoryStatusEx.ullAvailPhys / (1024 * 1024)); - WriteReportLineF("Total page file memory: %I64d MiB\r\n", memoryStatusEx.ullTotalPageFile / (1024 * 1024)); - WriteReportLineF("Available page file memory: %I64d MiB\r\n", memoryStatusEx.ullAvailPageFile / (1024 * 1024)); - WriteReportLineF("Total virtual memory: %I64d MiB\r\n", memoryStatusEx.ullTotalVirtual / (1024 * 1024)); - WriteReportLineF("Free virtual memory: %I64d MiB\r\n", memoryStatusEx.ullAvailVirtual / (1024 * 1024)); - - DISPLAY_DEVICE dd; - memset(&dd, 0, sizeof(DISPLAY_DEVICE)); - dd.cb = sizeof(DISPLAY_DEVICE); - - for(int i = 0; EnumDisplayDevicesW(nullptr, (DWORD)i, &dd, EDD_GET_DEVICE_INTERFACE_NAME); ++i) - { - WriteReportLineF("Display Device %d name: %ls, context: %ls, primary: %s, mirroring: %s\r\n", - i, dd.DeviceName, dd.DeviceString, (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) ? "yes" : "no", (dd.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) ? "yes" : "no"); - } - } - - // Print video card information - // http://msdn.microsoft.com/en-us/library/aa394512%28v=vs.85%29.aspx - { - IWbemLocator* pIWbemLocator = nullptr; - BSTR bstrServer = nullptr; - IWbemServices* pIWbemServices = nullptr; - BSTR bstrWQL = nullptr; - BSTR bstrPath = nullptr; - IEnumWbemClassObject* pEnum = nullptr; - - CoInitializeEx(nullptr, COINIT_MULTITHREADED); - - HRESULT hr = CoCreateInstance(__uuidof(WbemLocator), nullptr, CLSCTX_INPROC_SERVER, __uuidof(IWbemLocator), (LPVOID*)&pIWbemLocator); - if(FAILED(hr)) - goto End; - - bstrServer = SysAllocString(L"\\\\.\\root\\cimv2"); - hr = pIWbemLocator->ConnectServer(bstrServer, nullptr, nullptr, 0L, 0L, nullptr, nullptr, &pIWbemServices); - if(FAILED(hr)) - goto End; - - hr = CoSetProxyBlanket(pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr, RPC_C_AUTHN_LEVEL_CALL, - RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_DEFAULT); - if(FAILED(hr)) - goto End; - - bstrWQL = SysAllocString(L"WQL"); - bstrPath = SysAllocString(L"select * from Win32_VideoController"); - hr = pIWbemServices->ExecQuery(bstrWQL, bstrPath, WBEM_FLAG_FORWARD_ONLY, nullptr, &pEnum); - if(FAILED(hr)) - goto End; - - ULONG uReturned; - IWbemClassObject* pObj = nullptr; - hr = pEnum->Next(WBEM_INFINITE, 1, &pObj, &uReturned); - if(FAILED(hr)) - goto End; - - WriteReportLine("\r\nDisplay adapter list\r\n"); - - for(unsigned i = 0; SUCCEEDED(hr) && uReturned; i++) - { - char sString[256]; - VARIANT var; - - if(i > 0) - WriteReportLine("\r\n"); - - WriteReportLineF("Info for display adapter %u\r\n", i); - - hr = pObj->Get(L"Name", 0, &var, nullptr, nullptr); - if(SUCCEEDED(hr)) - { - WideCharToMultiByte(CP_ACP, 0, var.bstrVal, -1, sString, sizeof(sString), nullptr, nullptr); - WriteReportLineF("Display Adapter Name: %s\r\n", sString); - } - - hr = pObj->Get(L"AdapterRAM", 0, &var, nullptr, nullptr); - if(SUCCEEDED(hr)) - { - WriteReportLineF("Display Adapter RAM: %u %s\r\n", - ((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")); - } - - hr = pObj->Get(L"DeviceID", 0, &var, nullptr, nullptr); - if(SUCCEEDED(hr)) - { - WideCharToMultiByte(CP_ACP, 0, var.bstrVal, -1, sString, sizeof(sString), nullptr, nullptr); - WriteReportLineF("Display Adapter DeviceID: %s\r\n", sString); - } - - hr = pObj->Get(L"DriverVersion", 0, &var, nullptr, nullptr); - if(SUCCEEDED(hr)) - { - WideCharToMultiByte(CP_ACP, 0, var.bstrVal, -1, sString, sizeof(sString), nullptr, nullptr); - WriteReportLineF("Display Adapter DriverVersion: %s\r\n", sString); - } - - hr = pObj->Get(L"DriverDate", 0, &var, nullptr, nullptr); - if(SUCCEEDED(hr)) - { - // http://technet.microsoft.com/en-us/library/ee156576.aspx - wchar_t year[5] = { var.bstrVal[0], var.bstrVal[1], var.bstrVal[2], var.bstrVal[3], 0 }; - wchar_t month[3] = { var.bstrVal[4], var.bstrVal[5], 0 }; - wchar_t monthDay[3] = { var.bstrVal[6], var.bstrVal[7], 0 }; - - WriteReportLineF("Display Adapter DriverDate (US format): %ls/%ls/%ls\r\n", month, monthDay, year); - } - - // VideoProcessor - hr = pObj->Get(L"VideoProcessor", 0, &var, nullptr, nullptr); - if(SUCCEEDED(hr)) - { - WideCharToMultiByte(CP_ACP, 0, var.bstrVal, -1, sString, sizeof(sString), nullptr, nullptr); - WriteReportLineF("Display Adapter VideoProcessor %s\r\n", sString); - } - - hr = pObj->Get(L"VideoModeDescription", 0, &var, nullptr, nullptr); - if(SUCCEEDED(hr)) - { - WideCharToMultiByte(CP_ACP, 0, var.bstrVal, -1, sString, sizeof(sString), nullptr, nullptr); - WriteReportLineF("Display Adapter VideoModeDescription: %s\r\n", sString); - } - - pObj->Release(); - - hr = pEnum->Next(WBEM_INFINITE, 1, &pObj, &uReturned); - } - - End: - if(pEnum) - pEnum->Release(); - if(bstrPath) - SysFreeString(bstrPath); - if(bstrWQL) - SysFreeString(bstrWQL); - if(pIWbemServices) - pIWbemServices->Release(); - if(bstrServer) - SysFreeString(bstrServer); - if(pIWbemLocator) - pIWbemLocator->Release(); - - CoUninitialize(); - } - - { - // Print a list of threads. - DWORD currentProcessId = GetCurrentProcessId(); - HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, currentProcessId); // ICreateToolhelp32Snapshot actually ignores currentProcessId. - - if(hThreadSnap != INVALID_HANDLE_VALUE) - { - THREADENTRY32 te32; - te32.dwSize = sizeof(THREADENTRY32); - - if(Thread32First(hThreadSnap, &te32)) - { - WriteReportLine("\r\nThread list\r\n"); - - do { - if(te32.th32OwnerProcessID == currentProcessId) - { - HANDLE hThread = ConvertThreadSysIdToThreadHandle(te32.th32ThreadID); - - if(hThread) - { - char buffer[96]; // Can't use scratchBuffer, because it's used by WriteThreadCallstack. - OVR_snprintf(buffer, OVR_ARRAY_COUNT(buffer), "base priority: %ld, delta priority: %ld", te32.tpBasePri, te32.tpDeltaPri); - - bool threadIsExceptionThread = (te32.th32ThreadID == (DWORD)exceptionInfo.threadSysId); - if(threadIsExceptionThread) - OVR_strlcat(buffer, ", exception thread", OVR_ARRAY_COUNT(buffer)); - - WriteThreadCallstack(hThread, (OVR::ThreadSysId)te32.th32ThreadID, buffer); - FreeThreadHandle(hThread); - } - } - } while(Thread32Next(hThreadSnap, &te32)); - } - - CloseHandle(hThreadSnap); - } - } - - { - // Print a list of the current modules within this process. - // DbgHelp.dll also provides a EnumerateLoadedModules64 function. - // To do: Convert the code below to use the GetModuleInfoArray function which we now have. - #if defined(OVR_OS_CONSOLE) - struct MODULEINFO { - LPVOID lpBaseOfDll; - DWORD SizeOfImage; - LPVOID EntryPoint; - }; - HMODULE hModule = LoadLibraryW(L"toolhelpx.dll"); - #else - HMODULE hModule = LoadLibraryW(L"psapi.dll"); - #endif - - if(hModule) - { - typedef BOOL (WINAPI * ENUMPROCESSMODULES) (HANDLE hProcess, HMODULE* phModule, DWORD cb, LPDWORD lpcbNeeded); - typedef DWORD (WINAPI * GETMODULEBASENAME) (HANDLE hProcess, HMODULE hModule, LPWSTR lpFilename, DWORD nSize); - typedef DWORD (WINAPI * GETMODULEFILENAMEEX) (HANDLE hProcess, HMODULE hModule, LPWSTR lpFilename, DWORD nSize); - typedef BOOL (WINAPI * GETMODULEINFORMATION)(HANDLE hProcess, HMODULE hModule, MODULEINFO* pmi, DWORD nSize); - - #if defined(OVR_OS_CONSOLE) - ENUMPROCESSMODULES pEnumProcessModules = (ENUMPROCESSMODULES) (uintptr_t)GetProcAddress(hModule, "K32EnumProcessModules"); - GETMODULEBASENAME pGetModuleBaseName = (GETMODULEBASENAME) (uintptr_t)GetProcAddress(hModule, "K32GetModuleBaseNameW"); - GETMODULEFILENAMEEX pGetModuleFileNameEx = (GETMODULEFILENAMEEX) (uintptr_t)GetProcAddress(hModule, "K32GetModuleFileNameExW"); - GETMODULEINFORMATION pGetModuleInformation = (GETMODULEINFORMATION)(uintptr_t)GetProcAddress(hModule, "K32GetModuleInformation"); - #else - ENUMPROCESSMODULES pEnumProcessModules = (ENUMPROCESSMODULES) (uintptr_t)GetProcAddress(hModule, "EnumProcessModules"); - GETMODULEBASENAME pGetModuleBaseName = (GETMODULEBASENAME) (uintptr_t)GetProcAddress(hModule, "GetModuleBaseNameW"); - GETMODULEFILENAMEEX pGetModuleFileNameEx = (GETMODULEFILENAMEEX) (uintptr_t)GetProcAddress(hModule, "GetModuleFileNameExW"); - GETMODULEINFORMATION pGetModuleInformation = (GETMODULEINFORMATION)(uintptr_t)GetProcAddress(hModule, "GetModuleInformation"); - #endif - - HANDLE hProcess = GetCurrentProcess(); - HMODULE hModuleArray[200]; - DWORD cbNeeded; - - if(pEnumProcessModules(hProcess, hModuleArray, sizeof(hModuleArray), &cbNeeded)) - { - size_t actualModuleCount = (cbNeeded / sizeof(HMODULE)); - - if(actualModuleCount > OVR_ARRAY_COUNT(hModuleArray)) //If hModuleArray's capacity was not enough... - actualModuleCount = OVR_ARRAY_COUNT(hModuleArray); - - // Print a header - WriteReportLine("\r\nModule list\r\n"); - - #if (OVR_PTR_SIZE == 4) - WriteReportLine("Base Size Entrypoint Name Path\r\n"); - #else - WriteReportLine("Base Size Entrypoint Name Path\r\n"); - #endif - - // And go through the list one by one - for(size_t i = 0; i < actualModuleCount; i++) - { - MODULEINFO mi; - size_t length; - - if(!pGetModuleInformation(hProcess, hModuleArray[i], &mi, sizeof(mi))) - { - mi.EntryPoint = nullptr; - mi.lpBaseOfDll = nullptr; - mi.SizeOfImage = 0; - } - - // Write the base name. - wchar_t name[MAX_PATH + 3]; - name[0] = '"'; - if(pGetModuleBaseName(hProcess, hModuleArray[i], name + 1, MAX_PATH)) - length = wcslen(name); - else - { - wcscpy(name + 1, L"(unknown)"); - length = 10; - } - - name[length] = '"'; - name[length + 1] = '\0'; - - // Write the path - wchar_t path[MAX_PATH + 3]; - path[0] = '"'; - if(pGetModuleFileNameEx(hProcess, hModuleArray[i], path + 1, MAX_PATH)) - length = wcslen(path); - else - { - wcscpy(path + 1, L"(unknown)"); - length = 10; - } - path[length] = '"'; - path[length + 1] = '\0'; - - #if (OVR_PTR_SIZE == 4) - 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); - #else - 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); - #endif - } - } - } - } - - { - // Print a list of processes. - // DbgHelp.dll provides a SymEnumProcesses function, but it's available with DbgHelp.dll v6.2 which doesn't ship with Windows until Windows 8. - WriteReportLine("\r\nProcess list\r\n"); - - if(reportPrivacyEnabled) - WriteReportLine("Disabled by report privacy settings\r\n"); - else - { - HANDLE hProcessSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - - if(hProcessSnapshot != INVALID_HANDLE_VALUE) - { - PROCESSENTRY32W pe32; - memset(&pe32, 0, sizeof(pe32)); - pe32.dwSize = sizeof(pe32); - - if(Process32FirstW(hProcessSnapshot, &pe32)) - { - WriteReportLine("Process Id File\r\n"); - - do { - // Try to get the full path to the process, as pe32.szExeFile holds only the process file name. - // This will typically fail with a privilege error unless this process has debug privileges: http://support.microsoft.com/kb/131065/en-us - wchar_t filePathW[MAX_PATH]; - const wchar_t* pFilePathW = pe32.szExeFile; - HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pe32.th32ProcessID); // With Windows Vista+ we can use PROCESS_QUERY_LIMITED_INFORMATION. - if(hProcess) - { - if(GetProcessImageFileName(hProcess, filePathW, (DWORD)OVR_ARRAY_COUNT(filePathW))) - pFilePathW = filePathW; - } - - WriteReportLineF("0x%08x %ls\r\n", pe32.th32ProcessID, pFilePathW); - } while(Process32NextW(hProcessSnapshot, &pe32)); - } - - CloseHandle(hProcessSnapshot); - } - else - { - WriteReportLine("Unable to read process list\r\n"); - } - } - } - - #elif defined(OVR_OS_APPLE) - - WriteReportLine("\r\nApp Info\r\n"); - - // App path - const pid_t processId = getpid(); - WriteReportLineF("Process id: ", "%lld (0x%llx)\r\n", (int64_t)processId, (int64_t)processId); - - char appPath[PATH_MAX]; - GetCurrentProcessFilePath(appPath, OVR_ARRAY_COUNT(appPath)); - WriteReportLineF("Process path: %s\r\n", appPath); - - #if (OVR_PTR_SIZE == 4) - WriteReportLine("App format: 32 bit\r\n"); - #else - WriteReportLine("App format: 64 bit\r\n"); - #endif - - // App version - // To do. - - // System Info - WriteReportLine("\r\nSystem Info\r\n"); - - char osVersionName[256]; - GetOSVersionName(osVersionName, OVR_ARRAY_COUNT(osVersionName)); - WriteReportLineF("OS name: %s, %s\r\n", osVersionName, Is64BitOS() ? "64 bit" : "32 bit"); - - int name[2]; - int intValue; - size_t length; - char tempBuffer[256]; - - name[0] = CTL_KERN; - name[1] = KERN_OSTYPE; - length = sizeof(tempBuffer); - tempBuffer[0] = 0; - if(sysctl(name, 2, tempBuffer, &length, nullptr, 0) == 0) - { - WriteReportLineF("KERN_OSTYPE: %s\r\n", tempBuffer); - } - - name[0] = CTL_KERN; - name[1] = KERN_OSREV; - length = sizeof(intValue); - intValue = 0; - if(sysctl(name, 2, &intValue, &length, nullptr, 0) == 0) - { - WriteReportLineF("KERN_OSREV: %d\r\n", intValue); - } - - name[0] = CTL_KERN; - name[1] = KERN_OSRELEASE; - length = sizeof(tempBuffer); - tempBuffer[0] = 0; - if(sysctl(name, 2, tempBuffer, &length, nullptr, 0) == 0) - WriteReportLineF("KERN_OSRELEASE: %s\r\n", tempBuffer); - - name[0] = CTL_HW; - name[1] = HW_MACHINE; - length = sizeof(tempBuffer); - tempBuffer[0] = 0; - if(sysctl(name, 2, tempBuffer, &length, nullptr, 0) == 0) - WriteReportLineF("HW_MACHINE: %s\r\n", tempBuffer); - - name[0] = CTL_HW; - name[1] = HW_MODEL; - length = sizeof(tempBuffer); - tempBuffer[0] = 0; - if(sysctl(name, 2, tempBuffer, &length, nullptr, 0) == 0) - WriteReportLineF("sHW_MODEL: %s\r\n", tempBuffer); - - name[0] = CTL_HW; - name[1] = HW_NCPU; - length = sizeof(intValue); - intValue = 0; - if(sysctl(name, 2, &intValue, &length, nullptr, 0) == 0) - WriteReportLineF("HW_NCPU: %d\r\n", intValue); - - length = sizeof(tempBuffer); - tempBuffer[0] = 0; - if(sysctlbyname("machdep.cpu.brand_string", &tempBuffer, &length, nullptr, 0) == 0) - WriteReportLineF("machdep.cpu.brand_string: %s\r\n", tempBuffer); - - length = sizeof(tempBuffer); - tempBuffer[0] = 0; - if(sysctlbyname("hw.acpi.thermal.tz0.temperature", &tempBuffer, &length, nullptr, 0) == 0) - WriteReportLineF("hw.acpi.thermal.tz0.temperature: %s\r\n", tempBuffer); - - host_basic_info_data_t hostinfo; - mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT; - kern_return_t kr = host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)&hostinfo, &count); - - if(kr == KERN_SUCCESS) - { - const uint64_t memoryMib = (uint64_t)hostinfo.max_mem / (1024 * 1024); - WriteReportLineF("System memory: %lld Mib (%.1f Gib)\r\n", memoryMib, (double)memoryMib / 1024); - } - - // Video card info - // To do. - - // Thread list - mach_port_t taskSelf = mach_task_self(); - thread_act_port_array_t threadArray; - mach_msg_type_number_t threadCount; - - kern_return_t result = task_threads(taskSelf, &threadArray, &threadCount); - - if(result == KERN_SUCCESS) - { - WriteReportLine("\r\nThread list\r\n"); - - for(mach_msg_type_number_t i = 0; i < threadCount; i++) - { - union TBIUnion{ - natural_t words[THREAD_INFO_MAX]; - thread_basic_info tbi; - }; - - TBIUnion tbiUnion; - mach_port_t thread = threadArray[i]; - pthread_t pthread = pthread_from_mach_thread_np(thread); // We assume the thread was created through pthreads. - - char threadState[32] = "unknown"; - mach_msg_type_number_t threadInfoCount = THREAD_INFO_MAX; - result = thread_info(thread, THREAD_BASIC_INFO, (thread_info_t)&tbiUnion, &threadInfoCount); - - if(result == KERN_SUCCESS) - { - const char* state; - - switch (tbiUnion.tbi.run_state) - { - case TH_STATE_HALTED: state = "halted"; break; - case TH_STATE_RUNNING: state = "running"; break; - case TH_STATE_STOPPED: state = "stopped"; break; - case TH_STATE_UNINTERRUPTIBLE: state = "uninterruptible"; break; - case TH_STATE_WAITING: state = "waiting"; break; - default: state = "<unknown>"; break; - } - - OVR_snprintf(threadState, OVR_ARRAY_COUNT(threadState), "%s", state); - if(tbiUnion.tbi.flags & TH_FLAGS_IDLE) - OVR_strlcat(threadState, ", idle", sizeof(threadState)); - if(tbiUnion.tbi.flags & TH_FLAGS_SWAPPED) - OVR_strlcat(threadState, ", swapped", sizeof(threadState)); - } - - thread_identifier_info threadIdentifierInfo; - memset(&threadIdentifierInfo, 0, sizeof(threadIdentifierInfo)); - - mach_msg_type_number_t threadIdentifierInfoCount = THREAD_IDENTIFIER_INFO_COUNT; - thread_info(thread, THREAD_IDENTIFIER_INFO, (thread_info_t)&threadIdentifierInfo, &threadIdentifierInfoCount); - - proc_threadinfo procThreadInfo; - memset(&procThreadInfo, 0, sizeof(procThreadInfo)); - result = proc_pidinfo(processId, PROC_PIDTHREADINFO, threadIdentifierInfo.thread_handle, &procThreadInfo, sizeof(procThreadInfo)); - OVR_UNUSED(result); - - char buffer[256]; // Can't use scratchBuffer, because it's used by WriteThreadCallstack. - 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); - - bool threadIsExceptionThread = (thread == exceptionInfo.threadSysId); - if(threadIsExceptionThread) - OVR_strlcat(buffer, ", exception thread", OVR_ARRAY_COUNT(buffer)); - - WriteThreadCallstack(pthread, thread, buffer); - } - - vm_deallocate(taskSelf, (vm_address_t)threadArray, threadCount * sizeof(thread_act_t)); - } - - - WriteReportLine("\r\nModule list\r\n"); - - const size_t mifCapacity = 256; - const size_t mifAllocSize = mifCapacity * sizeof(ModuleInfo); - ModuleInfo* moduleInfoArray = (ModuleInfo*)SafeMMapAlloc(mifAllocSize); - - if(moduleInfoArray) - { - #if (OVR_PTR_SIZE == 4) - WriteReportLine("Base Size Name Path\r\n"); - #else - WriteReportLine("Base Size Name Path\r\n"); - #endif - - size_t moduleCount = symbolLookup.GetModuleInfoArray(moduleInfoArray, mifCapacity); - if(moduleCount > mifCapacity) - moduleCount = mifCapacity; - - for(size_t i = 0; i < moduleCount; i++) - { - const ModuleInfo& mi = moduleInfoArray[i]; - - #if (OVR_PTR_SIZE == 4) - WriteReportLineF("0x%08x, 0x%08x %-24s %s\r\n", (uint32_t)mi.baseAddress, (uint32_t)mi.size, mi.name, mi.filePath); - #else - WriteReportLineF("0x%016llx 0x%016llx %-24s %s\r\n", (uint64_t)mi.baseAddress, (uint64_t)mi.size, mi.name, mi.filePath); - #endif - } - - SafeMMapFree(moduleInfoArray, mifAllocSize); - } - - - WriteReportLine("\r\nProcess list\r\n"); - - if(reportPrivacyEnabled) - WriteReportLine("Disabled by report privacy settings\r\n"); - else - { - WriteReportLine("Process Id File\r\n"); - - pid_t pidArray[1024]; - int processCount = proc_listpids(PROC_ALL_PIDS, 0, pidArray, sizeof(pidArray)); // Important that we use sizeof not OVR_ARRAY_COUNT. - char processFilePath[PATH_MAX]; - - for(int i = 0; i < processCount; i++) - { - if(proc_pidpath(pidArray[i], processFilePath, sizeof(processFilePath)) > 0) - WriteReportLineF("%-10d %s\r\n", pidArray[i], processFilePath); - } - - if(!processCount) - WriteReportLine("Unable to read process list\r\n"); - } - - #elif defined(OVR_OS_UNIX) - Is64BitOS(); - GetCurrentProcessFilePath(nullptr, 0); - GetFileNameFromPath(nullptr); - GetOSVersionName(nullptr, 0); - - #endif // OVR_OS_MS - - symbolLookup.Shutdown(); - - fclose(file); - file = nullptr; -} - - -void ExceptionHandler::WriteMiniDump() -{ - if(strstr(miniDumpFilePath, "%s")) // If the user-specified file path includes a date/time component... - { - char dateTimeBuffer[64]; - FormatDateTime(dateTimeBuffer, OVR_ARRAY_COUNT(dateTimeBuffer), exceptionInfo.timeVal, true, true, false, true); - OVR_snprintf(minidumpFilePathActual, OVR_ARRAY_COUNT(minidumpFilePathActual), miniDumpFilePath, dateTimeBuffer); - } - else - { - OVR_strlcpy(minidumpFilePathActual, miniDumpFilePath, OVR_ARRAY_COUNT(minidumpFilePathActual)); - } - - #if defined(OVR_OS_WIN32) || defined(OVR_OS_WIN64) || (defined(OVR_OS_MS) && defined(OVR_OS_CONSOLE)) - #if defined(OVR_OS_CONSOLE) - 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); - HMODULE hModuleDbgHelp = LoadLibraryW(L"toolhelpx.dll"); - #else - 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); - HMODULE hModuleDbgHelp = LoadLibraryW(L"DbgHelp.dll"); - #endif - - MINIDUMPWRITEDUMP pMiniDumpWriteDump = hModuleDbgHelp ? (MINIDUMPWRITEDUMP)(void*)GetProcAddress(hModuleDbgHelp, "MiniDumpWriteDump") : nullptr; - - if(pMiniDumpWriteDump) - { - wchar_t miniDumpFilePathW[OVR_MAX_PATH]; - OVR::UTF8Util::DecodeString(miniDumpFilePathW, minidumpFilePathActual, -1); // Problem: DecodeString provides no way to specify the destination capacity. - - HANDLE hFile = CreateFileW(miniDumpFilePathW, GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_FLAG_WRITE_THROUGH, 0); - - if(hFile != INVALID_HANDLE_VALUE) - { - MINIDUMP_EXCEPTION_INFORMATION minidumpExceptionInfo = { ::GetCurrentThreadId(), pExceptionPointers, TRUE }; - - #if defined(OVR_OS_CONSOLE) - BOOL result = pMiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, - (MINIDUMP_TYPE)miniDumpType, &exceptionInfo, - (CONST PMINIDUMP_USER_STREAM_INFORMATION)nullptr, nullptr); - #else - BOOL result = pMiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, - (MINIDUMP_TYPE)miniDumpFlags, &minidumpExceptionInfo, - (CONST PMINIDUMP_USER_STREAM_INFORMATION)nullptr, (CONST PMINIDUMP_CALLBACK_INFORMATION)nullptr); - #endif - - OVR_ASSERT_AND_UNUSED(result, result); - CloseHandle(hFile); - hFile = 0; - } - else - { - OVR_ASSERT(pMiniDumpWriteDump); // OVR_FAIL_F(("ExceptionHandler::WriteMiniDump: Failed to create minidump file at %s", minidumpFilePathActual)); - } - } - - FreeLibrary(hModuleDbgHelp); - #else - // Some platforms support various forms or exception reports and core dumps which are automatically generated upon us - // returning from our own exception handling. We might want to put something here if we are using a custom version of - // this, such as Google Breakpad. - #endif -} - - -void ExceptionHandler::SetExceptionListener(ExceptionListener* pExceptionListener, uintptr_t userValue) -{ - exceptionListener = pExceptionListener; - exceptionListenerUserValue = userValue; -} - - -void ExceptionHandler::SetAppDescription(const char* pAppDescription) -{ - appDescription = pAppDescription; -} - - -void ExceptionHandler::SetExceptionPaths(const char* exceptionReportPath, const char* exceptionMiniDumpFilePath) -{ - char tempPath[OVR_MAX_PATH]; - - if(exceptionReportPath) - { - if(OVR_stricmp(exceptionReportPath, "default") == 0) - { - GetUserDocumentsDirectory(tempPath, OVR_ARRAY_COUNT(tempPath)); - OVR::OVR_strlcat(tempPath, "Exception Report (%s).txt", OVR_ARRAY_COUNT(tempPath)); - exceptionReportPath = tempPath; - } - - OVR_strlcpy(reportFilePath, exceptionReportPath, OVR_ARRAY_COUNT(reportFilePath)); - } - else - { - reportFilePath[0] = '\0'; - } - - if(exceptionMiniDumpFilePath) - { - if(OVR_stricmp(exceptionMiniDumpFilePath, "default") == 0) - { - GetUserDocumentsDirectory(tempPath, OVR_ARRAY_COUNT(tempPath)); - OVR::OVR_strlcat(tempPath, "Exception Minidump (%s).mdmp", OVR_ARRAY_COUNT(tempPath)); - exceptionMiniDumpFilePath = tempPath; - } - - OVR_strlcpy(miniDumpFilePath, exceptionMiniDumpFilePath, OVR_ARRAY_COUNT(miniDumpFilePath)); - } - else - { - miniDumpFilePath[0] = '\0'; - } -} - - -void ExceptionHandler::SetCodeBaseDirectoryPaths(const char* codeBaseDirectoryPathArray[], size_t codeBaseDirectoryPathCount) -{ - for(size_t i = 0, iCount = OVR::Alg::Min<size_t>(codeBaseDirectoryPathCount, OVR_ARRAY_COUNT(codeBasePathArray)); i != iCount; ++i) - { - codeBasePathArray[i] = codeBaseDirectoryPathArray[i]; - } -} - -const char* ExceptionHandler::GetExceptionUIText(const char* exceptionReportPath) -{ - char* uiText = nullptr; - OVR::SysFile file(exceptionReportPath, SysFile::Open_Read, SysFile::Mode_ReadWrite); - - if(file.IsValid()) - { - size_t length = (size_t)file.GetLength(); - uiText = (char*)OVR::SafeMMapAlloc(length + 1); - - if(uiText) - { - file.Read((uint8_t*)uiText, (int)length); - uiText[length] = '\0'; - file.Close(); - - // Currently on Mac our message box implementation is unable to display arbitrarily large amounts of text. - // So we reduce its size to a more summary version before presenting. - #if defined(OVR_OS_MAC) - struct Find { static char* PreviousChar(char* p, char c){ while(*p != c) p--; return p; } }; // Assumes the given c is present prior to p. - - // Print that the full report is at <file path> - // Exception Info section - // Exception thread callstack. - char empty[] = ""; - char* pExceptionInfoBegin = strstr(uiText, "Exception Info") ? strstr(uiText, "Exception Info") : empty; - char* pExceptionInfoEnd = (pExceptionInfoBegin == empty) ? (empty + 1) : strstr(uiText, "\r\n\r\n"); - char* pExceptionThreadArea = strstr(uiText, ", exception thread"); - char* pExceptionThreadBegin = pExceptionThreadArea ? Find::PreviousChar(pExceptionThreadArea, '\n') + 1 : empty; - char* pExceptionThreadEnd = (pExceptionThreadBegin == empty) ? (empty + 1) : strstr(pExceptionThreadArea, "\r\n\r\n"); - - if(!pExceptionInfoEnd) - pExceptionInfoEnd = pExceptionInfoBegin; - *pExceptionInfoEnd = '\0'; - - if(!pExceptionThreadEnd) - pExceptionThreadEnd = pExceptionThreadBegin; - *pExceptionThreadEnd = '\0'; - - size_t uiTextBriefLength = OVR_snprintf(nullptr, 0, "Full report:%s\n\nSummary report:\n%s\n\n%s", exceptionReportPath, pExceptionInfoBegin, pExceptionThreadBegin); - char* uiTextBrief = (char*)OVR::SafeMMapAlloc(uiTextBriefLength + 1); - - if(uiTextBrief) - { - OVR_snprintf(uiTextBrief, uiTextBriefLength + 1, "Full report:%s\n\nSummary report:\n%s\n\n%s", exceptionReportPath, pExceptionInfoBegin, pExceptionThreadBegin); - OVR::SafeMMapFree(uiText, length); - uiText = uiTextBrief; - } - #endif - } - } - - return uiText; -} - -void ExceptionHandler::FreeExceptionUIText(const char* messageBoxText) -{ - OVR::SafeMMapFree(messageBoxText, OVR_strlen(messageBoxText)); -} - - -} // namespace OVR - - -OVR_RESTORE_MSVC_WARNING() - |