ovr_sdk

view LibOVR/Src/Kernel/OVR_DebugHelp.h @ 0:1b39a1b46319

initial 0.4.4
author John Tsiombikas <nuclear@member.fsf.org>
date Wed, 14 Jan 2015 06:51:16 +0200
parents
children
line source
1 /************************************************************************************
3 Filename : OVR_DebugHelp.h
4 Content : Platform-independent exception handling interface
5 Created : October 6, 2014
7 Copyright : Copyright 2014 Oculus VR, LLC. All Rights reserved.
9 Licensed under the Apache License, Version 2.0 (the "License");
10 you may not use this file except in compliance with the License.
11 You may obtain a copy of the License at
13 http://www.apache.org/licenses/LICENSE-2.0
15 Unless required by applicable law or agreed to in writing, software
16 distributed under the License is distributed on an "AS IS" BASIS,
17 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 See the License for the specific language governing permissions and
19 limitations under the License.
21 ************************************************************************************/
23 #ifndef OVR_ExceptionHandler_h
24 #define OVR_ExceptionHandler_h
27 #include "OVR_Types.h"
28 #include "OVR_String.h"
29 #include "OVR_Threads.h"
30 #include "OVR_Atomic.h"
31 #include "OVR_Nullptr.h"
32 #include <stdio.h>
33 #include <time.h>
35 #if defined(OVR_OS_WIN32) || defined(OVR_OS_WIN64)
36 #include <Windows.h>
38 #elif defined(OVR_OS_APPLE)
39 #include <pthread.h>
40 #include <mach/thread_status.h>
41 #include <mach/mach_types.h>
43 extern "C" void* MachHandlerThreadFunctionStatic(void*);
44 extern "C" int catch_mach_exception_raise_state_identity_OVR(mach_port_t, mach_port_t, mach_port_t, exception_type_t, mach_exception_data_type_t*,
45 mach_msg_type_number_t, int*, thread_state_t, mach_msg_type_number_t, thread_state_t, mach_msg_type_number_t*);
46 #elif defined(OVR_OS_LINUX)
47 #include <pthread.h>
48 #endif
51 OVR_DISABLE_MSVC_WARNING(4351) // new behavior: elements of array will be default initialized
54 namespace OVR {
56 // Thread identifiers
57 //typedef void* ThreadHandle; // Already defined by OVR Threads. Same as Windows thread handle, Unix pthread_t.
58 //typedef void* ThreadId; // Already defined by OVR Threads. Used by Windows as DWORD thread id, by Unix as pthread_t.
59 typedef uintptr_t ThreadSysId; // System thread identifier. Used by Windows the same as ThreadId (DWORD), thread_act_t on Mac/BSD, lwp id on Linux.
61 // Thread constants
62 // To do: Move to OVR Threads
63 #define OVR_THREADHANDLE_INVALID ((ThreadHandle*)nullptr)
64 #define OVR_THREADID_INVALID ((ThreadId*)nullptr)
65 #define OVR_THREADSYSID_INVALID ((uintptr_t)0)
67 OVR::ThreadSysId ConvertThreadHandleToThreadSysId(OVR::ThreadHandle threadHandle);
68 OVR::ThreadHandle ConvertThreadSysIdToThreadHandle(OVR::ThreadSysId threadSysId); // The returned handle must be freed with FreeThreadHandle.
69 void FreeThreadHandle(OVR::ThreadHandle threadHandle); // Frees the handle returned by ConvertThreadSysIdToThreadHandle.
70 OVR::ThreadSysId GetCurrentThreadSysId();
72 // CPUContext
73 #if defined(OVR_OS_MS)
74 typedef CONTEXT CPUContext;
75 #elif defined(OVR_OS_MAC)
76 struct CPUContext
77 {
78 x86_thread_state_t threadState; // This works for both x86 and x64.
79 x86_float_state_t floatState;
80 x86_debug_state_t debugState;
81 x86_avx_state_t avxState;
82 x86_exception_state exceptionState;
84 CPUContext() { memset(this, 0, sizeof(CPUContext)); }
85 };
86 #elif defined(OVR_OS_LINUX)
87 typedef int CPUContext; // To do.
88 #endif
91 // Tells if the current process appears to be running under a debugger. Does not attempt to
92 // detect the case of sleath debuggers (malware-related for example).
93 bool OVRIsDebuggerPresent();
95 // Exits the process with the given exit code.
96 #if !defined(OVR_NORETURN)
97 #if defined(OVR_CC_MSVC)
98 #define OVR_NORETURN __declspec(noreturn)
99 #else
100 #define OVR_NORETURN __attribute__((noreturn))
101 #endif
102 #endif
103 OVR_NORETURN void ExitProcess(intptr_t processReturnValue);
105 // Returns the instruction pointer of the caller for the position right after the call.
106 OVR_NO_INLINE void GetInstructionPointer(void*& pInstruction);
108 // Returns the stack base and limit addresses for the given thread, or for the current thread if the threadHandle is default.
109 // The stack limit is a value less than the stack base on most platforms, as stacks usually grow downward.
110 // Some platforms (e.g. Microsoft) have dynamically resizing stacks, in which case the stack limit reflects the current limit.
111 void GetThreadStackBounds(void*& pStackBase, void*& pStackLimit, ThreadHandle threadHandle = OVR_THREADHANDLE_INVALID);
114 // Equates to VirtualAlloc/VirtualFree on Windows, mmap/munmap on Unix.
115 // These are useful for when you need system-supplied memory pages.
116 // These are also useful for when you need to allocate memory in a way
117 // that doesn't affect the application heap.
118 void* SafeMMapAlloc(size_t size);
119 void SafeMMapFree(const void* memory, size_t size);
122 // OVR_MAX_PATH
123 // Max file path length (for most uses).
124 // To do: move this to OVR_File.
125 #if defined(OVR_OS_MS) // http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
126 #define OVR_MAX_PATH 260 // Windows can use paths longer than this in some cases (network paths, UNC paths).
127 #else
128 #define OVR_MAX_PATH 1024 // This isn't a strict limit on all Unix-based platforms.
129 #endif
132 // ModuleHandle
133 #if defined(OVR_OS_MS)
134 typedef void* ModuleHandle; // from LoadLibrary()
135 #elif defined(OVR_OS_APPLE) || defined(OVR_OS_UNIX)
136 typedef void* ModuleHandle; // from dlopen()
137 #endif
139 #define OVR_MODULEHANDLE_INVALID ((ModuleHandle*)nullptr)
143 // Module info constants
144 static const ModuleHandle kMIHandleInvalid = OVR_MODULEHANDLE_INVALID;
145 static const uint64_t kMIAddressInvalid = 0xffffffffffffffffull;
146 static const uint64_t kMISizeInvalid = 0xffffffffffffffffull;
147 static const int32_t kMILineNumberInvalid = -1;
148 static const int32_t kMIFunctionOffsetInvalid = -1;
149 static const uint64_t kMIBaseAddressInvalid = 0xffffffffffffffffull;
150 static const uint64_t kMIBaseAddressUnspecified = 0xffffffffffffffffull;
152 struct ModuleInfo
153 {
154 ModuleHandle handle;
155 uint64_t baseAddress; // The actual runtime base address of the module. May be different from the base address specified in the debug symbol file.
156 uint64_t size;
157 char filePath[OVR_MAX_PATH];
158 char name[32];
159 char type[8]; // Unix-specific. e.g. __TEXT
160 char permissions[8]; // Unix specific. e.g. "drwxr-xr-x"
162 ModuleInfo() : handle(kMIHandleInvalid), baseAddress(kMIBaseAddressInvalid), size(0), filePath(), name(){}
163 };
166 // Refers to symbol info for an instruction address.
167 // Info includes function name, source code file/line, and source code itself.
168 struct SymbolInfo
169 {
170 uint64_t address;
171 uint64_t size;
172 const ModuleInfo* pModuleInfo;
173 char filePath[OVR_MAX_PATH];
174 int32_t fileLineNumber;
175 char function[128]; // This is a fixed size because we need to use it during application exceptions.
176 int32_t functionOffset;
177 char sourceCode[1024]; // This is a string representing the code itself and not a file path to the code.
179 SymbolInfo() : address(kMIAddressInvalid), size(kMISizeInvalid), pModuleInfo(nullptr), filePath(),
180 fileLineNumber(kMILineNumberInvalid), function(), functionOffset(kMIFunctionOffsetInvalid), sourceCode() {}
181 };
184 // Implements support for reading thread lists, module lists, backtraces, and backtrace symbols.
185 class SymbolLookup
186 {
187 public:
188 SymbolLookup();
189 ~SymbolLookup();
191 void AddSourceCodeDirectory(const char* pDirectory);
193 bool Initialize();
194 void Shutdown();
196 // Should be disabled when within an exception handler.
197 void EnableMemoryAllocation(bool enabled);
199 // Retrieves the backtrace (call stack) of the given thread. There may be some per-platform restrictions on this.
200 // Returns the number written, which will be <= addressArrayCapacity.
201 // This may not work on some platforms unless stack frames are enabled.
202 // For Microsoft platforms the platformThreadContext is CONTEXT*.
203 // For Apple platforms the platformThreadContext is x86_thread_state_t* or arm_thread_state_t*.
204 // If threadSysIdHelp is non-zero, it may be used by the implementation to help produce a better backtrace.
205 size_t GetBacktrace(void* addressArray[], size_t addressArrayCapacity, size_t skipCount = 0, void* platformThreadContext = nullptr, OVR::ThreadSysId threadSysIdHelp = OVR_THREADSYSID_INVALID);
207 // Retrieves the backtrace for the given ThreadHandle.
208 // Returns the number written, which will be <= addressArrayCapacity.
209 size_t GetBacktraceFromThreadHandle(void* addressArray[], size_t addressArrayCapacity, size_t skipCount = 0, OVR::ThreadHandle threadHandle = OVR_THREADHANDLE_INVALID);
211 // Retrieves the backtrace for the given ThreadSysId.
212 // Returns the number written, which will be <= addressArrayCapacity.
213 size_t GetBacktraceFromThreadSysId(void* addressArray[], size_t addressArrayCapacity, size_t skipCount = 0, OVR::ThreadSysId threadSysId = OVR_THREADSYSID_INVALID);
215 // Gets a list of the modules (e.g. DLLs) present in the current process.
216 // Writes as many ModuleInfos as possible to pModuleInfoArray.
217 // Returns the required count of ModuleInfos, which will be > moduleInfoArrayCapacity if the capacity needs to be larger.
218 size_t GetModuleInfoArray(ModuleInfo* pModuleInfoArray, size_t moduleInfoArrayCapacity);
220 // Retrieves a list of the current threads. Unless the process is paused the list is volatile.
221 // Returns the required capacity, which may be larger than threadArrayCapacity.
222 // Either array can be NULL to specify that it's not written to.
223 // For Windows the caller needs to CloseHandle the returned ThreadHandles. This can be done by calling DoneThreadList.
224 size_t GetThreadList(ThreadHandle* threadHandleArray, ThreadSysId* threadSysIdArray, size_t threadArrayCapacity);
226 // Frees any references to thread handles or ids returned by GetThreadList;
227 void DoneThreadList(ThreadHandle* threadHandleArray, ThreadSysId* threadSysIdArray, size_t threadArrayCount);
229 // Writes a given thread's callstack with symbols to the given output.
230 // It may not be safe to call this from an exception handler, as sOutput allocates memory.
231 bool ReportThreadCallstack(OVR::String& sOutput, size_t skipCount = 0, ThreadSysId threadSysId = OVR_THREADSYSID_INVALID);
233 // Writes all thread's callstacks with symbols to the given output.
234 // It may not be safe to call this from an exception handler, as sOutput allocates memory.
235 bool ReportThreadCallstacks(OVR::String& sOutput, size_t skipCount = 0);
237 // Retrieves symbol info for the given address.
238 bool LookupSymbol(uint64_t address, SymbolInfo& symbolInfo);
239 bool LookupSymbols(uint64_t* addressArray, SymbolInfo* pSymbolInfoArray, size_t arraySize);
241 const ModuleInfo* GetModuleInfoForAddress(uint64_t address); // The returned ModuleInfo points to an internal structure.
243 protected:
244 bool RefreshModuleList();
246 protected:
247 bool initialized;
248 bool allowMemoryAllocation; // True by default. If true then we allow allocating memory (and as a result provide less information). This is useful for when in an exception handler.
249 bool moduleListUpdated;
250 ModuleInfo moduleInfoArray[96]; // Cached list of modules we use. This is a fixed size because we need to use it during application exceptions.
251 size_t moduleInfoArraySize;
252 };
256 // ExceptionInfo
257 // We need to be careful to avoid data types that can allocate memory while we are
258 // handling an exception, as the memory system may be corrupted at that point in time.
259 struct ExceptionInfo
260 {
261 tm time; // GM time.
262 time_t timeVal; // GM time_t (seconds since 1970).
263 void* backtrace[64];
264 size_t backtraceCount;
265 ThreadHandle threadHandle; //
266 ThreadSysId threadSysId; //
267 char threadName[32]; // Cannot be an allocating String object.
268 void* pExceptionInstructionAddress;
269 void* pExceptionMemoryAddress;
270 CPUContext cpuContext;
271 char exceptionDescription[1024]; // Cannot be an allocating String object.
272 SymbolInfo symbolInfo; // SymbolInfo for the exception location.
274 #if defined(OVR_OS_MS)
275 EXCEPTION_RECORD exceptionRecord; // This is a Windows SDK struct.
276 #elif defined(OVR_OS_APPLE)
277 uint64_t exceptionType; // e.g. EXC_BAD_INSTRUCTION, EXC_BAD_ACCESS, etc.
278 uint32_t cpuExceptionId; // The x86/x64 CPU trap id.
279 uint32_t cpuExceptionIdError; // The x86/x64 CPU trap id extra info.
280 int64_t machExceptionDetail[4]; // Kernel exception code info.
281 int machExceptionDetailCount; // Count of valid entries.
282 #endif
284 ExceptionInfo();
285 };
288 // Implments support for asynchronous exception handling and basic exception report generation.
289 // If you are implementing exception handling for a commercial application and want auto-uploading
290 // functionality you may want to consider using something like Google Breakpad. This exception handler
291 // is for in-application debug/diagnostic services, though it can write a report that has similar
292 // information to Breakpad or OS-provided reports such as Apple .crash files.
293 //
294 // Example usage:
295 // ExceptionHandler exceptionHandler;
296 //
297 // int main(int, char**)
298 // {
299 // exceptionHandler.Enable(true);
300 // exceptionHandler.SetExceptionListener(pSomeListener, 0); // Optional listener hook.
301 // }
302 //
303 class ExceptionHandler
304 {
305 public:
306 ExceptionHandler();
307 ~ExceptionHandler();
309 bool Enable(bool enable);
311 // Some report info can be considered private information of the user, such as the current process list,
312 // computer name, IP address or other identifying info, etc. We should not report this information for
313 // external users unless they agree to this.
314 void EnableReportPrivacy(bool enable);
316 struct ExceptionListener
317 {
318 virtual ~ExceptionListener(){}
319 virtual int HandleException(uintptr_t userValue, ExceptionHandler* pExceptionHandler, ExceptionInfo* pExceptionInfo, const char* reportFilePath) = 0;
320 };
322 void SetExceptionListener(ExceptionListener* pExceptionListener, uintptr_t userValue);
324 // What we do after handling the exception.
325 enum ExceptionResponse
326 {
327 kERContinue, // Continue execution. Will result in the exception being re-generated unless the application has fixed the cause. Similar to Windows EXCEPTION_CONTINUE_EXECUTION.
328 kERHandle, // Causes the OS to handle the exception as it normally would. Similar to Windows EXCEPTION_EXECUTE_HANDLER.
329 kERTerminate, // Exit the application.
330 kERThrow, // Re-throw the exception. Other handlers may catch it. Similar to Windows EXCEPTION_CONTINUE_SEARCH.
331 kERDefault // Usually set to kERTerminate.
332 };
334 void SetExceptionResponse(ExceptionResponse er)
335 { exceptionResponse = er; }
337 // Allws you to add an arbitrary description of the current application, which will be added to exception reports.
338 void SetAppDescription(const char* appDescription);
340 // If the report path has a "%s" in its name, then assume the path is a sprintf format and write it
341 // with the %s specified as a date/time string.
342 // The report path can be "default" to signify that you want to use the default user location.
343 // Example usage:
344 // handler.SetExceptionPaths("/Users/Current/Exceptions/Exception %s.txt");
345 void SetExceptionPaths(const char* exceptionReportPath, const char* exceptionMinidumpPath = nullptr);
347 // Allows you to specify base directories for code paths, which can be used to associate exception addresses to lines
348 // of code as opposed to just file names and line numbers, or function names plus binary offsets.
349 void SetCodeBaseDirectoryPaths(const char* codeBaseDirectoryPathArray[], size_t codeBaseDirectoryPathCount);
351 // Given an exception report at a given file path, returns a string suitable for displaying in a message
352 // box or similar user interface during the handling of an exception. The returned string must be passed
353 // to FreeMessageBoxText when complete.
354 static const char* GetExceptionUIText(const char* exceptionReportPath);
355 static void FreeExceptionUIText(const char* messageBoxText);
357 protected:
358 void WriteExceptionDescription();
359 void WriteReport();
360 void WriteReportLine(const char* pLine);
361 void WriteReportLineF(const char* format, ...);
362 void WriteThreadCallstack(ThreadHandle threadHandle, ThreadSysId threadSysId, const char* additionalInfo);
363 void WriteMiniDump();
365 // Runtime constants
366 bool enabled;
367 bool reportPrivacyEnabled; // Defaults to true.
368 ExceptionResponse exceptionResponse; // Defaults to kERHandle
369 ExceptionListener* exceptionListener;
370 uintptr_t exceptionListenerUserValue;
371 String appDescription;
372 String codeBasePathArray[6]; // 6 is arbitrary.
373 char reportFilePath[OVR_MAX_PATH];// May be an encoded path, in that it has "%s" in it or is named "default". See reporFiletPathActual for the runtime actual report path.
374 int miniDumpFlags;
375 char miniDumpFilePath[OVR_MAX_PATH];
376 FILE* file; // Can/should we use OVR Files for this?
377 char scratchBuffer[4096];
378 SymbolLookup symbolLookup;
380 // Runtime variables
381 bool exceptionOccurred;
382 OVR::AtomicInt<uint32_t> handlingBusy;
383 char reportFilePathActual[OVR_MAX_PATH];
384 char minidumpFilePathActual[OVR_MAX_PATH];
385 int terminateReturnValue;
386 ExceptionInfo exceptionInfo;
388 #if defined(OVR_OS_MS)
389 void* vectoredHandle;
390 LPTOP_LEVEL_EXCEPTION_FILTER previousFilter;
391 LPEXCEPTION_POINTERS pExceptionPointers;
393 friend LONG WINAPI Win32ExceptionFilter(LPEXCEPTION_POINTERS pExceptionPointers);
394 LONG ExceptionFilter(LPEXCEPTION_POINTERS pExceptionPointers);
396 #elif defined(OVR_OS_APPLE)
397 struct SavedExceptionPorts
398 {
399 SavedExceptionPorts() : count(0) { memset(this, 0, sizeof(SavedExceptionPorts)); }
401 mach_msg_type_number_t count;
402 exception_mask_t masks[6];
403 exception_handler_t ports[6];
404 exception_behavior_t behaviors[6];
405 thread_state_flavor_t flavors[6];
406 };
408 friend void* ::MachHandlerThreadFunctionStatic(void*);
409 friend int ::catch_mach_exception_raise_state_identity_OVR(mach_port_t, mach_port_t, mach_port_t, exception_type_t,
410 mach_exception_data_type_t*, mach_msg_type_number_t, int*, thread_state_t,
411 mach_msg_type_number_t, thread_state_t, mach_msg_type_number_t*);
413 bool InitMachExceptionHandler();
414 void ShutdownMachExceptionHandler();
415 void* MachHandlerThreadFunction();
416 kern_return_t HandleMachException(mach_port_t port, mach_port_t thread, mach_port_t task, exception_type_t exceptionType,
417 mach_exception_data_type_t* pExceptionDetail, mach_msg_type_number_t exceptionDetailCount,
418 int* pFlavor, thread_state_t pOldState, mach_msg_type_number_t oldStateCount, thread_state_t pNewState,
419 mach_msg_type_number_t* pNewStateCount);
420 kern_return_t ForwardMachException(mach_port_t thread, mach_port_t task, exception_type_t exceptionType,
421 mach_exception_data_t pExceptionDetail, mach_msg_type_number_t exceptionDetailCount);
423 bool machHandlerInitialized;
424 mach_port_t machExceptionPort;
425 SavedExceptionPorts machExceptionPortsSaved;
426 volatile bool machThreadShouldContinue;
427 volatile bool machThreadExecuting;
428 pthread_t machThread;
430 #elif defined(OVR_OS_LINUX)
431 // To do.
432 #endif
433 };
436 // Identifies basic exception types for the CreateException function.
437 enum CreateExceptionType
438 {
439 kCETAccessViolation, // Read or write to inaccessable memory.
440 kCETAlignment, // Misaligned read or write.
441 kCETDivideByZero, // Integer divide by zero.
442 kCETFPU, // Floating point / VPU exception.
443 kCETIllegalInstruction, // Illegal opcode.
444 kCETStackCorruption, // Stack frame was corrupted.
445 kCETStackOverflow, // Stack ran out of space, often due to infinite recursion.
446 kCETTrap // System/OS trap (system call).
447 };
450 // Creates an exception of the given type, primarily for testing.
451 void CreateException(CreateExceptionType exceptionType);
455 } // namespace OVR
458 OVR_RESTORE_MSVC_WARNING()
461 #endif // Header include guard