Debug.cpp 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277
  1. // This code is in the public domain -- Ignacio Castaño <[email protected]>
  2. #include "Debug.h"
  3. #include "Array.inl"
  4. #include "StrLib.h" // StringBuilder
  5. #include "StdStream.h" // fileOpen
  6. #include <stdlib.h>
  7. // Extern
  8. #if NV_OS_WIN32 //&& NV_CC_MSVC
  9. # define WIN32_LEAN_AND_MEAN
  10. # define VC_EXTRALEAN
  11. # include <windows.h>
  12. # include <direct.h>
  13. # if NV_CC_MSVC
  14. # include <crtdbg.h>
  15. # if _MSC_VER < 1300
  16. # define DECLSPEC_DEPRECATED
  17. // VC6: change this path to your Platform SDK headers
  18. # include <dbghelp.h> // must be XP version of file
  19. // include "M:\\dev7\\vs\\devtools\\common\\win32sdk\\include\\dbghelp.h"
  20. # else
  21. // VC7: ships with updated headers
  22. # include <dbghelp.h>
  23. # endif
  24. # endif
  25. # pragma comment(lib,"dbghelp.lib")
  26. #endif
  27. #if NV_OS_XBOX
  28. # include <Xtl.h>
  29. # ifdef _DEBUG
  30. # include <xbdm.h>
  31. # endif //_DEBUG
  32. #endif //NV_OS_XBOX
  33. #if !NV_OS_WIN32 && defined(HAVE_SIGNAL_H)
  34. # include <signal.h>
  35. #endif
  36. #if NV_OS_UNIX
  37. # include <unistd.h> // getpid
  38. #endif
  39. #if NV_OS_LINUX && defined(HAVE_EXECINFO_H)
  40. # include <execinfo.h> // backtrace
  41. # if NV_CC_GNUC // defined(HAVE_CXXABI_H)
  42. # include <cxxabi.h>
  43. # endif
  44. #endif
  45. #if NV_OS_DARWIN || NV_OS_FREEBSD || NV_OS_OPENBSD
  46. # include <sys/types.h>
  47. # include <sys/param.h>
  48. # include <sys/sysctl.h> // sysctl
  49. # if !defined(NV_OS_OPENBSD)
  50. # include <sys/ucontext.h>
  51. # endif
  52. # if defined(HAVE_EXECINFO_H) // only after OSX 10.5
  53. # include <execinfo.h> // backtrace
  54. # if NV_CC_GNUC // defined(HAVE_CXXABI_H)
  55. # include <cxxabi.h>
  56. # endif
  57. # endif
  58. # define NV_USE_SEPARATE_THREAD 0
  59. #else
  60. # define NV_USE_SEPARATE_THREAD 1
  61. #endif
  62. #if NV_OS_ORBIS
  63. #include <libdbg.h>
  64. #endif
  65. using namespace nv;
  66. namespace
  67. {
  68. static MessageHandler * s_message_handler = NULL;
  69. static AssertHandler * s_assert_handler = NULL;
  70. static bool s_sig_handler_enabled = false;
  71. static bool s_interactive = true;
  72. #if NV_OS_WIN32 && NV_CC_MSVC
  73. // Old exception filter.
  74. static LPTOP_LEVEL_EXCEPTION_FILTER s_old_exception_filter = NULL;
  75. #elif !NV_OS_WIN32 && defined(HAVE_SIGNAL_H)
  76. // Old signal handlers.
  77. struct sigaction s_old_sigsegv;
  78. struct sigaction s_old_sigtrap;
  79. struct sigaction s_old_sigfpe;
  80. struct sigaction s_old_sigbus;
  81. #endif
  82. #if NV_OS_WIN32 && NV_CC_MSVC
  83. // We should try to simplify the top level filter as much as possible.
  84. // http://www.nynaeve.net/?p=128
  85. #if NV_USE_SEPARATE_THREAD
  86. // The critical section enforcing the requirement that only one exception be
  87. // handled by a handler at a time.
  88. static CRITICAL_SECTION s_handler_critical_section;
  89. // Semaphores used to move exception handling between the exception thread
  90. // and the handler thread. handler_start_semaphore_ is signalled by the
  91. // exception thread to wake up the handler thread when an exception occurs.
  92. // handler_finish_semaphore_ is signalled by the handler thread to wake up
  93. // the exception thread when handling is complete.
  94. static HANDLE s_handler_start_semaphore = NULL;
  95. static HANDLE s_handler_finish_semaphore = NULL;
  96. // The exception handler thread.
  97. static HANDLE s_handler_thread = NULL;
  98. static DWORD s_requesting_thread_id = 0;
  99. static EXCEPTION_POINTERS * s_exception_info = NULL;
  100. #endif // NV_USE_SEPARATE_THREAD
  101. struct MinidumpCallbackContext {
  102. ULONG64 memory_base;
  103. ULONG memory_size;
  104. bool finished;
  105. };
  106. // static
  107. static BOOL CALLBACK miniDumpWriteDumpCallback(PVOID context, const PMINIDUMP_CALLBACK_INPUT callback_input, PMINIDUMP_CALLBACK_OUTPUT callback_output)
  108. {
  109. switch (callback_input->CallbackType)
  110. {
  111. case MemoryCallback: {
  112. MinidumpCallbackContext* callback_context = reinterpret_cast<MinidumpCallbackContext*>(context);
  113. if (callback_context->finished)
  114. return FALSE;
  115. // Include the specified memory region.
  116. callback_output->MemoryBase = callback_context->memory_base;
  117. callback_output->MemorySize = callback_context->memory_size;
  118. callback_context->finished = true;
  119. return TRUE;
  120. }
  121. // Include all modules.
  122. case IncludeModuleCallback:
  123. case ModuleCallback:
  124. return TRUE;
  125. // Include all threads.
  126. case IncludeThreadCallback:
  127. case ThreadCallback:
  128. return TRUE;
  129. // Stop receiving cancel callbacks.
  130. case CancelCallback:
  131. callback_output->CheckCancel = FALSE;
  132. callback_output->Cancel = FALSE;
  133. return TRUE;
  134. }
  135. // Ignore other callback types.
  136. return FALSE;
  137. }
  138. static bool writeMiniDump(EXCEPTION_POINTERS * pExceptionInfo)
  139. {
  140. // create the file
  141. HANDLE hFile = CreateFileA("crash.dmp", GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  142. if (hFile == INVALID_HANDLE_VALUE) {
  143. //nvDebug("*** Failed to create dump file.\n");
  144. return false;
  145. }
  146. MINIDUMP_EXCEPTION_INFORMATION * pExInfo = NULL;
  147. MINIDUMP_CALLBACK_INFORMATION * pCallback = NULL;
  148. if (pExceptionInfo != NULL) {
  149. MINIDUMP_EXCEPTION_INFORMATION ExInfo;
  150. ExInfo.ThreadId = ::GetCurrentThreadId();
  151. ExInfo.ExceptionPointers = pExceptionInfo;
  152. ExInfo.ClientPointers = NULL;
  153. pExInfo = &ExInfo;
  154. MINIDUMP_CALLBACK_INFORMATION callback;
  155. MinidumpCallbackContext context;
  156. // Find a memory region of 256 bytes centered on the
  157. // faulting instruction pointer.
  158. const ULONG64 instruction_pointer =
  159. #if defined(_M_IX86)
  160. pExceptionInfo->ContextRecord->Eip;
  161. #elif defined(_M_AMD64)
  162. pExceptionInfo->ContextRecord->Rip;
  163. #else
  164. #error Unsupported platform
  165. #endif
  166. MEMORY_BASIC_INFORMATION info;
  167. if (VirtualQuery(reinterpret_cast<LPCVOID>(instruction_pointer), &info, sizeof(MEMORY_BASIC_INFORMATION)) != 0 && info.State == MEM_COMMIT)
  168. {
  169. // Attempt to get 128 bytes before and after the instruction
  170. // pointer, but settle for whatever's available up to the
  171. // boundaries of the memory region.
  172. const ULONG64 kIPMemorySize = 256;
  173. context.memory_base = max(reinterpret_cast<ULONG64>(info.BaseAddress), instruction_pointer - (kIPMemorySize / 2));
  174. ULONG64 end_of_range = min(instruction_pointer + (kIPMemorySize / 2), reinterpret_cast<ULONG64>(info.BaseAddress) + info.RegionSize);
  175. context.memory_size = static_cast<ULONG>(end_of_range - context.memory_base);
  176. context.finished = false;
  177. callback.CallbackRoutine = miniDumpWriteDumpCallback;
  178. callback.CallbackParam = reinterpret_cast<void*>(&context);
  179. pCallback = &callback;
  180. }
  181. }
  182. MINIDUMP_TYPE miniDumpType = (MINIDUMP_TYPE)(MiniDumpNormal|MiniDumpWithHandleData|MiniDumpWithThreadInfo);
  183. // write the dump
  184. BOOL ok = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, miniDumpType, pExInfo, NULL, pCallback) != 0;
  185. CloseHandle(hFile);
  186. if (ok == FALSE) {
  187. //nvDebug("*** Failed to save dump file.\n");
  188. return false;
  189. }
  190. //nvDebug("\nDump file saved.\n");
  191. return true;
  192. }
  193. #if NV_USE_SEPARATE_THREAD
  194. static DWORD WINAPI ExceptionHandlerThreadMain(void* lpParameter) {
  195. nvDebugCheck(s_handler_start_semaphore != NULL);
  196. nvDebugCheck(s_handler_finish_semaphore != NULL);
  197. while (true) {
  198. if (WaitForSingleObject(s_handler_start_semaphore, INFINITE) == WAIT_OBJECT_0) {
  199. writeMiniDump(s_exception_info);
  200. // Allow the requesting thread to proceed.
  201. ReleaseSemaphore(s_handler_finish_semaphore, 1, NULL);
  202. }
  203. }
  204. // This statement is not reached when the thread is unconditionally
  205. // terminated by the ExceptionHandler destructor.
  206. return 0;
  207. }
  208. #endif // NV_USE_SEPARATE_THREAD
  209. static bool hasStackTrace() {
  210. return true;
  211. }
  212. /*static NV_NOINLINE int backtrace(void * trace[], int maxcount) {
  213. // In Windows XP and Windows Server 2003, the sum of the FramesToSkip and FramesToCapture parameters must be less than 63.
  214. int xp_maxcount = min(63-1, maxcount);
  215. int count = RtlCaptureStackBackTrace(1, xp_maxcount, trace, NULL);
  216. nvDebugCheck(count <= maxcount);
  217. return count;
  218. }*/
  219. static NV_NOINLINE int backtraceWithSymbols(CONTEXT * ctx, void * trace[], int maxcount, int skip = 0) {
  220. // Init the stack frame for this function
  221. STACKFRAME64 stackFrame = { 0 };
  222. #if NV_CPU_X86_64
  223. DWORD dwMachineType = IMAGE_FILE_MACHINE_AMD64;
  224. stackFrame.AddrPC.Offset = ctx->Rip;
  225. stackFrame.AddrFrame.Offset = ctx->Rbp;
  226. stackFrame.AddrStack.Offset = ctx->Rsp;
  227. #elif NV_CPU_X86
  228. DWORD dwMachineType = IMAGE_FILE_MACHINE_I386;
  229. stackFrame.AddrPC.Offset = ctx->Eip;
  230. stackFrame.AddrFrame.Offset = ctx->Ebp;
  231. stackFrame.AddrStack.Offset = ctx->Esp;
  232. #else
  233. #error "Platform not supported!"
  234. #endif
  235. stackFrame.AddrPC.Mode = AddrModeFlat;
  236. stackFrame.AddrFrame.Mode = AddrModeFlat;
  237. stackFrame.AddrStack.Mode = AddrModeFlat;
  238. // Walk up the stack
  239. const HANDLE hThread = GetCurrentThread();
  240. const HANDLE hProcess = GetCurrentProcess();
  241. int i;
  242. for (i = 0; i < maxcount; i++)
  243. {
  244. // walking once first makes us skip self
  245. if (!StackWalk64(dwMachineType, hProcess, hThread, &stackFrame, ctx, NULL, &SymFunctionTableAccess64, &SymGetModuleBase64, NULL)) {
  246. break;
  247. }
  248. /*if (stackFrame.AddrPC.Offset == stackFrame.AddrReturn.Offset || stackFrame.AddrPC.Offset == 0) {
  249. break;
  250. }*/
  251. if (i >= skip) {
  252. trace[i - skip] = (PVOID)stackFrame.AddrPC.Offset;
  253. }
  254. }
  255. return i - skip;
  256. }
  257. #pragma warning(push)
  258. #pragma warning(disable:4748)
  259. static NV_NOINLINE int backtrace(void * trace[], int maxcount) {
  260. CONTEXT ctx = { 0 };
  261. #if NV_CPU_X86 && !NV_CPU_X86_64
  262. ctx.ContextFlags = CONTEXT_CONTROL;
  263. _asm {
  264. call x
  265. x: pop eax
  266. mov ctx.Eip, eax
  267. mov ctx.Ebp, ebp
  268. mov ctx.Esp, esp
  269. }
  270. #else
  271. RtlCaptureContext(&ctx); // Not implemented correctly in x86.
  272. #endif
  273. return backtraceWithSymbols(&ctx, trace, maxcount, 1);
  274. }
  275. #pragma warning(pop)
  276. static NV_NOINLINE void writeStackTrace(void * trace[], int size, int start, Array<const char *> & lines)
  277. {
  278. StringBuilder builder(512);
  279. HANDLE hProcess = GetCurrentProcess();
  280. // Resolve PC to function names
  281. for (int i = start; i < size; i++)
  282. {
  283. // Check for end of stack walk
  284. DWORD64 ip = (DWORD64)trace[i];
  285. if (ip == NULL)
  286. break;
  287. // Get function name
  288. #define MAX_STRING_LEN (512)
  289. unsigned char byBuffer[sizeof(IMAGEHLP_SYMBOL64) + MAX_STRING_LEN] = { 0 };
  290. IMAGEHLP_SYMBOL64 * pSymbol = (IMAGEHLP_SYMBOL64*)byBuffer;
  291. pSymbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
  292. pSymbol->MaxNameLength = MAX_STRING_LEN;
  293. DWORD64 dwDisplacement;
  294. if (SymGetSymFromAddr64(hProcess, ip, &dwDisplacement, pSymbol))
  295. {
  296. pSymbol->Name[MAX_STRING_LEN-1] = 0;
  297. /*
  298. // Make the symbol readable for humans
  299. UnDecorateSymbolName( pSym->Name, lpszNonUnicodeUnDSymbol, BUFFERSIZE,
  300. UNDNAME_COMPLETE |
  301. UNDNAME_NO_THISTYPE |
  302. UNDNAME_NO_SPECIAL_SYMS |
  303. UNDNAME_NO_MEMBER_TYPE |
  304. UNDNAME_NO_MS_KEYWORDS |
  305. UNDNAME_NO_ACCESS_SPECIFIERS );
  306. */
  307. // pSymbol->Name
  308. const char * pFunc = pSymbol->Name;
  309. // Get file/line number
  310. IMAGEHLP_LINE64 theLine = { 0 };
  311. theLine.SizeOfStruct = sizeof(theLine);
  312. DWORD dwDisplacement;
  313. if (!SymGetLineFromAddr64(hProcess, ip, &dwDisplacement, &theLine))
  314. {
  315. // Do not print unknown symbols anymore.
  316. break;
  317. //builder.format("unknown(%08X) : %s\n", (uint32)ip, pFunc);
  318. }
  319. else
  320. {
  321. /*
  322. const char* pFile = strrchr(theLine.FileName, '\\');
  323. if ( pFile == NULL ) pFile = theLine.FileName;
  324. else pFile++;
  325. */
  326. const char * pFile = theLine.FileName;
  327. int line = theLine.LineNumber;
  328. builder.format("%s(%d) : %s\n", pFile, line, pFunc);
  329. }
  330. lines.append(builder.release());
  331. if (pFunc != NULL && strcmp(pFunc, "WinMain") == 0) {
  332. break;
  333. }
  334. }
  335. }
  336. }
  337. // Write mini dump and print stack trace.
  338. static LONG WINAPI handleException(EXCEPTION_POINTERS * pExceptionInfo)
  339. {
  340. EnterCriticalSection(&s_handler_critical_section);
  341. #if NV_USE_SEPARATE_THREAD
  342. s_requesting_thread_id = GetCurrentThreadId();
  343. s_exception_info = pExceptionInfo;
  344. // This causes the handler thread to call writeMiniDump.
  345. ReleaseSemaphore(s_handler_start_semaphore, 1, NULL);
  346. // Wait until WriteMinidumpWithException is done and collect its return value.
  347. WaitForSingleObject(s_handler_finish_semaphore, INFINITE);
  348. //bool status = s_handler_return_value;
  349. // Clean up.
  350. s_requesting_thread_id = 0;
  351. s_exception_info = NULL;
  352. #else
  353. // First of all, write mini dump.
  354. writeMiniDump(pExceptionInfo);
  355. #endif
  356. LeaveCriticalSection(&s_handler_critical_section);
  357. nvDebug("\nDump file saved.\n");
  358. // Try to attach to debugger.
  359. if (s_interactive && debug::attachToDebugger()) {
  360. nvDebugBreak();
  361. return EXCEPTION_CONTINUE_EXECUTION;
  362. }
  363. // If that fails, then try to pretty print a stack trace and terminate.
  364. void * trace[64];
  365. int size = backtraceWithSymbols(pExceptionInfo->ContextRecord, trace, 64);
  366. // @@ Use win32's CreateFile?
  367. FILE * fp = fileOpen("crash.txt", "wb");
  368. if (fp != NULL) {
  369. Array<const char *> lines;
  370. writeStackTrace(trace, size, 0, lines);
  371. for (uint i = 0; i < lines.count(); i++) {
  372. fputs(lines[i], fp);
  373. delete lines[i];
  374. }
  375. // @@ Add more info to crash.txt?
  376. fclose(fp);
  377. }
  378. // This should terminate the process and set the error exit code.
  379. TerminateProcess(GetCurrentProcess(), EXIT_FAILURE + 2);
  380. return EXCEPTION_EXECUTE_HANDLER; // Terminate app. In case terminate process did not succeed.
  381. }
  382. static void handlePureVirtualCall() {
  383. nvDebugBreak();
  384. TerminateProcess(GetCurrentProcess(), EXIT_FAILURE + 8);
  385. }
  386. static void handleInvalidParameter(const wchar_t * expresion, const wchar_t * function, const wchar_t * file, unsigned int line, uintptr_t reserved) {
  387. size_t convertedCharCount = 0;
  388. StringBuilder tmp;
  389. if (expresion != NULL) {
  390. uint size = toU32(wcslen(expresion) + 1);
  391. tmp.reserve(size);
  392. wcstombs_s(&convertedCharCount, tmp.str(), size, expresion, _TRUNCATE);
  393. nvDebug("*** Invalid parameter: %s\n", tmp.str());
  394. if (file != NULL) {
  395. size = toU32(wcslen(file) + 1);
  396. tmp.reserve(size);
  397. wcstombs_s(&convertedCharCount, tmp.str(), size, file, _TRUNCATE);
  398. nvDebug(" On file: %s\n", tmp.str());
  399. if (function != NULL) {
  400. size = toU32(wcslen(function) + 1);
  401. tmp.reserve(size);
  402. wcstombs_s(&convertedCharCount, tmp.str(), size, function, _TRUNCATE);
  403. nvDebug(" On function: %s\n", tmp.str());
  404. }
  405. nvDebug(" On line: %u\n", line);
  406. }
  407. }
  408. nvDebugBreak();
  409. TerminateProcess(GetCurrentProcess(), EXIT_FAILURE + 8);
  410. }
  411. #elif !NV_OS_WIN32 && defined(HAVE_SIGNAL_H) // NV_OS_LINUX || NV_OS_DARWIN
  412. #if defined(HAVE_EXECINFO_H)
  413. static bool hasStackTrace() {
  414. #if NV_OS_DARWIN
  415. return backtrace != NULL;
  416. #else
  417. return true;
  418. #endif
  419. }
  420. static void writeStackTrace(void * trace[], int size, int start, Array<const char *> & lines) {
  421. StringBuilder builder(512);
  422. char ** string_array = backtrace_symbols(trace, size);
  423. for(int i = start; i < size-1; i++ ) {
  424. # if NV_CC_GNUC // defined(HAVE_CXXABI_H)
  425. // @@ Write a better parser for the possible formats.
  426. char * begin = strchr(string_array[i], '(');
  427. char * end = strrchr(string_array[i], '+');
  428. char * module = string_array[i];
  429. if (begin == 0 && end != 0) {
  430. *(end - 1) = '\0';
  431. begin = strrchr(string_array[i], ' ');
  432. module = NULL; // Ignore module.
  433. }
  434. if (begin != 0 && begin < end) {
  435. int stat;
  436. *end = '\0';
  437. *begin = '\0';
  438. char * name = abi::__cxa_demangle(begin+1, 0, 0, &stat);
  439. if (module == NULL) {
  440. if (name == NULL || stat != 0) {
  441. builder.format(" In: '%s'\n", begin+1);
  442. }
  443. else {
  444. builder.format(" In: '%s'\n", name);
  445. }
  446. }
  447. else {
  448. if (name == NULL || stat != 0) {
  449. builder.format(" In: [%s] '%s'\n", module, begin+1);
  450. }
  451. else {
  452. builder.format(" In: [%s] '%s'\n", module, name);
  453. }
  454. }
  455. free(name);
  456. }
  457. else {
  458. builder.format(" In: '%s'\n", string_array[i]);
  459. }
  460. # else
  461. builder.format(" In: '%s'\n", string_array[i]);
  462. # endif
  463. lines.append(builder.release());
  464. }
  465. free(string_array);
  466. }
  467. static void printStackTrace(void * trace[], int size, int start=0) {
  468. nvDebug( "\nDumping stacktrace:\n" );
  469. Array<const char *> lines;
  470. writeStackTrace(trace, size, 1, lines);
  471. for (uint i = 0; i < lines.count(); i++) {
  472. nvDebug(lines[i]);
  473. delete lines[i];
  474. }
  475. nvDebug("\n");
  476. }
  477. #endif // defined(HAVE_EXECINFO_H)
  478. static void * callerAddress(void * secret)
  479. {
  480. #if NV_OS_DARWIN
  481. # if defined(_STRUCT_MCONTEXT)
  482. # if NV_CPU_PPC
  483. ucontext_t * ucp = (ucontext_t *)secret;
  484. return (void *) ucp->uc_mcontext->__ss.__srr0;
  485. # elif NV_CPU_X86_64
  486. ucontext_t * ucp = (ucontext_t *)secret;
  487. return (void *) ucp->uc_mcontext->__ss.__rip;
  488. # elif NV_CPU_X86
  489. ucontext_t * ucp = (ucontext_t *)secret;
  490. return (void *) ucp->uc_mcontext->__ss.__eip;
  491. # elif NV_CPU_ARM
  492. ucontext_t * ucp = (ucontext_t *)secret;
  493. return (void *) ucp->uc_mcontext->__ss.__pc;
  494. # else
  495. # error "Unknown CPU"
  496. # endif
  497. # else
  498. # if NV_CPU_PPC
  499. ucontext_t * ucp = (ucontext_t *)secret;
  500. return (void *) ucp->uc_mcontext->ss.srr0;
  501. # elif NV_CPU_X86
  502. ucontext_t * ucp = (ucontext_t *)secret;
  503. return (void *) ucp->uc_mcontext->ss.eip;
  504. # else
  505. # error "Unknown CPU"
  506. # endif
  507. # endif
  508. #elif NV_OS_FREEBSD
  509. # if NV_CPU_X86_64
  510. ucontext_t * ucp = (ucontext_t *)secret;
  511. return (void *)ucp->uc_mcontext.mc_rip;
  512. # elif NV_CPU_X86
  513. ucontext_t * ucp = (ucontext_t *)secret;
  514. return (void *)ucp->uc_mcontext.mc_eip;
  515. # else
  516. # error "Unknown CPU"
  517. # endif
  518. #elif NV_OS_OPENBSD
  519. # if NV_CPU_X86_64
  520. ucontext_t * ucp = (ucontext_t *)secret;
  521. return (void *)ucp->sc_rip;
  522. # elif NV_CPU_X86
  523. ucontext_t * ucp = (ucontext_t *)secret;
  524. return (void *)ucp->sc_eip;
  525. # else
  526. # error "Unknown CPU"
  527. # endif
  528. #else
  529. # if NV_CPU_X86_64
  530. // #define REG_RIP REG_INDEX(rip) // seems to be 16
  531. ucontext_t * ucp = (ucontext_t *)secret;
  532. return (void *)ucp->uc_mcontext.gregs[REG_RIP];
  533. # elif NV_CPU_X86
  534. ucontext_t * ucp = (ucontext_t *)secret;
  535. return (void *)ucp->uc_mcontext.gregs[14/*REG_EIP*/];
  536. # elif NV_CPU_PPC
  537. ucontext_t * ucp = (ucontext_t *)secret;
  538. return (void *) ucp->uc_mcontext.regs->nip;
  539. # else
  540. # error "Unknown CPU"
  541. # endif
  542. #endif
  543. // How to obtain the instruction pointers in different platforms, from mlton's source code.
  544. // http://mlton.org/
  545. // OpenBSD && NetBSD
  546. // ucp->sc_eip
  547. // FreeBSD:
  548. // ucp->uc_mcontext.mc_eip
  549. // HPUX:
  550. // ucp->uc_link
  551. // Solaris:
  552. // ucp->uc_mcontext.gregs[REG_PC]
  553. // Linux hppa:
  554. // uc->uc_mcontext.sc_iaoq[0] & ~0x3UL
  555. // Linux sparc:
  556. // ((struct sigcontext*) secret)->sigc_regs.tpc
  557. // Linux sparc64:
  558. // ((struct sigcontext*) secret)->si_regs.pc
  559. // potentially correct for other archs:
  560. // Linux alpha: ucp->m_context.sc_pc
  561. // Linux arm: ucp->m_context.ctx.arm_pc
  562. // Linux ia64: ucp->m_context.sc_ip & ~0x3UL
  563. // Linux mips: ucp->m_context.sc_pc
  564. // Linux s390: ucp->m_context.sregs->regs.psw.addr
  565. }
  566. static void nvSigHandler(int sig, siginfo_t *info, void *secret)
  567. {
  568. void * pnt = callerAddress(secret);
  569. // Do something useful with siginfo_t
  570. if (sig == SIGSEGV) {
  571. if (pnt != NULL) nvDebug("Got signal %d, faulty address is %p, from %p\n", sig, info->si_addr, pnt);
  572. else nvDebug("Got signal %d, faulty address is %p\n", sig, info->si_addr);
  573. }
  574. else if(sig == SIGTRAP) {
  575. nvDebug("Breakpoint hit.\n");
  576. }
  577. else {
  578. nvDebug("Got signal %d\n", sig);
  579. }
  580. #if defined(HAVE_EXECINFO_H)
  581. if (hasStackTrace()) // in case of weak linking
  582. {
  583. void * trace[64];
  584. int size = backtrace(trace, 64);
  585. if (pnt != NULL) {
  586. // Overwrite sigaction with caller's address.
  587. trace[1] = pnt;
  588. }
  589. printStackTrace(trace, size, 1);
  590. }
  591. #endif // defined(HAVE_EXECINFO_H)
  592. exit(0);
  593. }
  594. #endif // defined(HAVE_SIGNAL_H)
  595. #if NV_OS_WIN32 //&& NV_CC_MSVC
  596. /** Win32 assert handler. */
  597. struct Win32AssertHandler : public AssertHandler
  598. {
  599. // Flush the message queue. This is necessary for the message box to show up.
  600. static void flushMessageQueue()
  601. {
  602. MSG msg;
  603. while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) {
  604. //if( msg.message == WM_QUIT ) break;
  605. TranslateMessage( &msg );
  606. DispatchMessage( &msg );
  607. }
  608. }
  609. // Assert handler method.
  610. virtual int assertion(const char * exp, const char * file, int line, const char * func, const char * msg, va_list arg)
  611. {
  612. int ret = NV_ABORT_EXIT;
  613. StringBuilder error_string;
  614. error_string.format("*** Assertion failed: %s\n On file: %s\n On line: %d\n", exp, file, line );
  615. if (func != NULL) {
  616. error_string.appendFormat(" On function: %s\n", func);
  617. }
  618. if (msg != NULL) {
  619. error_string.append(" Message: ");
  620. va_list tmp;
  621. va_copy(tmp, arg);
  622. error_string.appendFormatList(msg, tmp);
  623. va_end(tmp);
  624. error_string.append("\n");
  625. }
  626. nvDebug( error_string.str() );
  627. // Print stack trace:
  628. debug::dumpInfo();
  629. if (debug::isDebuggerPresent()) {
  630. return NV_ABORT_DEBUG;
  631. }
  632. if (s_interactive) {
  633. flushMessageQueue();
  634. int action = MessageBoxA(NULL, error_string.str(), "Assertion failed", MB_ABORTRETRYIGNORE|MB_ICONERROR);
  635. switch( action ) {
  636. case IDRETRY:
  637. ret = NV_ABORT_DEBUG;
  638. break;
  639. case IDIGNORE:
  640. ret = NV_ABORT_IGNORE;
  641. break;
  642. case IDABORT:
  643. default:
  644. ret = NV_ABORT_EXIT;
  645. break;
  646. }
  647. /*if( _CrtDbgReport( _CRT_ASSERT, file, line, module, exp ) == 1 ) {
  648. return NV_ABORT_DEBUG;
  649. }*/
  650. }
  651. if (ret == NV_ABORT_EXIT) {
  652. // Exit cleanly.
  653. exit(EXIT_FAILURE + 1);
  654. }
  655. return ret;
  656. }
  657. };
  658. #elif NV_OS_XBOX
  659. /** Xbox360 assert handler. */
  660. struct Xbox360AssertHandler : public AssertHandler
  661. {
  662. // Assert handler method.
  663. virtual int assertion(const char * exp, const char * file, int line, const char * func, const char * msg, va_list arg)
  664. {
  665. int ret = NV_ABORT_EXIT;
  666. StringBuilder error_string;
  667. if( func != NULL ) {
  668. error_string.format( "*** Assertion failed: %s\n On file: %s\n On function: %s\n On line: %d\n ", exp, file, func, line );
  669. nvDebug( error_string.str() );
  670. }
  671. else {
  672. error_string.format( "*** Assertion failed: %s\n On file: %s\n On line: %d\n ", exp, file, line );
  673. nvDebug( error_string.str() );
  674. }
  675. if (debug::isDebuggerPresent()) {
  676. return NV_ABORT_DEBUG;
  677. }
  678. if( ret == NV_ABORT_EXIT ) {
  679. // Exit cleanly.
  680. exit(EXIT_FAILURE + 1);
  681. }
  682. return ret;
  683. }
  684. };
  685. #elif NV_OS_ORBIS
  686. /** Orbis assert handler. */
  687. struct OrbisAssertHandler : public AssertHandler
  688. {
  689. // Assert handler method.
  690. virtual int assertion(const char * exp, const char * file, int line, const char * func, const char * msg, va_list arg)
  691. {
  692. if( func != NULL ) {
  693. nvDebug( "*** Assertion failed: %s\n On file: %s\n On function: %s\n On line: %d\n ", exp, file, func, line );
  694. }
  695. else {
  696. nvDebug( "*** Assertion failed: %s\n On file: %s\n On line: %d\n ", exp, file, line );
  697. }
  698. //SBtodoORBIS print stack trace
  699. /*if (hasStackTrace())
  700. {
  701. void * trace[64];
  702. int size = backtrace(trace, 64);
  703. printStackTrace(trace, size, 2);
  704. }*/
  705. if (debug::isDebuggerPresent())
  706. return NV_ABORT_DEBUG;
  707. return NV_ABORT_IGNORE;
  708. }
  709. };
  710. #else
  711. /** Unix assert handler. */
  712. struct UnixAssertHandler : public AssertHandler
  713. {
  714. // Assert handler method.
  715. virtual int assertion(const char * exp, const char * file, int line, const char * func, const char * msg, va_list arg)
  716. {
  717. int ret = NV_ABORT_EXIT;
  718. if( func != NULL ) {
  719. nvDebug( "*** Assertion failed: %s\n On file: %s\n On function: %s\n On line: %d\n ", exp, file, func, line );
  720. }
  721. else {
  722. nvDebug( "*** Assertion failed: %s\n On file: %s\n On line: %d\n ", exp, file, line );
  723. }
  724. #if _DEBUG
  725. if (debug::isDebuggerPresent()) {
  726. return NV_ABORT_DEBUG;
  727. }
  728. #endif
  729. #if defined(HAVE_EXECINFO_H)
  730. if (hasStackTrace())
  731. {
  732. void * trace[64];
  733. int size = backtrace(trace, 64);
  734. printStackTrace(trace, size, 2);
  735. }
  736. #endif
  737. if( ret == NV_ABORT_EXIT ) {
  738. // Exit cleanly.
  739. exit(EXIT_FAILURE + 1);
  740. }
  741. return ret;
  742. }
  743. };
  744. #endif
  745. } // namespace
  746. /// Handle assertion through the assert handler.
  747. int nvAbort(const char * exp, const char * file, int line, const char * func/*=NULL*/, const char * msg/*= NULL*/, ...)
  748. {
  749. #if NV_OS_WIN32 //&& NV_CC_MSVC
  750. static Win32AssertHandler s_default_assert_handler;
  751. #elif NV_OS_XBOX
  752. static Xbox360AssertHandler s_default_assert_handler;
  753. #elif NV_OS_ORBIS
  754. static OrbisAssertHandler s_default_assert_handler;
  755. #else
  756. static UnixAssertHandler s_default_assert_handler;
  757. #endif
  758. va_list arg;
  759. va_start(arg,msg);
  760. AssertHandler * handler = s_assert_handler != NULL ? s_assert_handler : &s_default_assert_handler;
  761. int result = handler->assertion(exp, file, line, func, msg, arg);
  762. va_end(arg);
  763. return result;
  764. }
  765. // Abnormal termination. Create mini dump and output call stack.
  766. void debug::terminate(int code)
  767. {
  768. #if NV_OS_IOS
  769. //ACStodoIOS
  770. #elif NV_OS_ORBIS
  771. //SBtodoORBIS
  772. #elif NV_OS_LINUX
  773. // TODO(casey): If anyone ever implements the iOS/Orbis
  774. // versions, then we should probably implement a Linux one too :)
  775. #elif NV_OS_DARWIN
  776. #else
  777. EnterCriticalSection(&s_handler_critical_section);
  778. writeMiniDump(NULL);
  779. const int max_stack_size = 64;
  780. void * trace[max_stack_size];
  781. int size = backtrace(trace, max_stack_size);
  782. // @@ Use win32's CreateFile?
  783. FILE * fp = fileOpen("crash.txt", "wb");
  784. if (fp != NULL) {
  785. Array<const char *> lines;
  786. writeStackTrace(trace, size, 0, lines);
  787. for (uint i = 0; i < lines.count(); i++) {
  788. fputs(lines[i], fp);
  789. delete lines[i];
  790. }
  791. // @@ Add more info to crash.txt?
  792. fclose(fp);
  793. }
  794. LeaveCriticalSection(&s_handler_critical_section);
  795. #endif
  796. exit(code);
  797. }
  798. /// Shows a message through the message handler.
  799. void NV_CDECL nvDebugPrint(const char *msg, ...)
  800. {
  801. va_list arg;
  802. va_start(arg,msg);
  803. if (s_message_handler != NULL) {
  804. s_message_handler->log( msg, arg );
  805. } else {
  806. vprintf(msg, arg);
  807. }
  808. va_end(arg);
  809. }
  810. /// Dump debug info.
  811. void debug::dumpInfo()
  812. {
  813. #if (NV_OS_WIN32 && NV_CC_MSVC) || (defined(HAVE_SIGNAL_H) && defined(HAVE_EXECINFO_H))
  814. if (hasStackTrace())
  815. {
  816. void * trace[64];
  817. int size = backtrace(trace, 64);
  818. nvDebug( "\nDumping stacktrace:\n" );
  819. Array<const char *> lines;
  820. writeStackTrace(trace, size, 1, lines);
  821. for (uint i = 0; i < lines.count(); i++) {
  822. nvDebug(lines[i]);
  823. delete lines[i];
  824. }
  825. }
  826. #endif
  827. }
  828. /// Dump callstack using the specified handler.
  829. void debug::dumpCallstack(MessageHandler *messageHandler, int callstackLevelsToSkip /*= 0*/)
  830. {
  831. #if (NV_OS_WIN32 && NV_CC_MSVC) || (defined(HAVE_SIGNAL_H) && defined(HAVE_EXECINFO_H))
  832. if (hasStackTrace())
  833. {
  834. void * trace[64];
  835. int size = backtrace(trace, 64);
  836. Array<const char *> lines;
  837. writeStackTrace(trace, size, callstackLevelsToSkip + 1, lines); // + 1 to skip the call to dumpCallstack
  838. for (uint i = 0; i < lines.count(); i++) {
  839. messageHandler->log(lines[i], NULL);
  840. delete lines[i];
  841. }
  842. }
  843. #endif
  844. }
  845. /// Set the debug message handler.
  846. void debug::setMessageHandler(MessageHandler * message_handler)
  847. {
  848. s_message_handler = message_handler;
  849. }
  850. /// Reset the debug message handler.
  851. void debug::resetMessageHandler()
  852. {
  853. s_message_handler = NULL;
  854. }
  855. /// Set the assert handler.
  856. void debug::setAssertHandler(AssertHandler * assert_handler)
  857. {
  858. s_assert_handler = assert_handler;
  859. }
  860. /// Reset the assert handler.
  861. void debug::resetAssertHandler()
  862. {
  863. s_assert_handler = NULL;
  864. }
  865. #if NV_USE_SEPARATE_THREAD
  866. static void initHandlerThread()
  867. {
  868. #if NV_OS_IOS
  869. //ACStodoIOS
  870. #elif NV_OS_ORBIS
  871. //SBtodoORBIS
  872. #elif NV_OS_LINUX
  873. // TODO(casey): If anyone ever implements the iOS/Orbis
  874. // versions, then we should probably implement a Linux one too :)
  875. #else
  876. static const int kExceptionHandlerThreadInitialStackSize = 64 * 1024;
  877. // Set synchronization primitives and the handler thread. Each
  878. // ExceptionHandler object gets its own handler thread because that's the
  879. // only way to reliably guarantee sufficient stack space in an exception,
  880. // and it allows an easy way to get a snapshot of the requesting thread's
  881. // context outside of an exception.
  882. InitializeCriticalSection(&s_handler_critical_section);
  883. s_handler_start_semaphore = CreateSemaphore(NULL, 0, 1, NULL);
  884. nvDebugCheck(s_handler_start_semaphore != NULL);
  885. s_handler_finish_semaphore = CreateSemaphore(NULL, 0, 1, NULL);
  886. nvDebugCheck(s_handler_finish_semaphore != NULL);
  887. // Don't attempt to create the thread if we could not create the semaphores.
  888. if (s_handler_finish_semaphore != NULL && s_handler_start_semaphore != NULL) {
  889. DWORD thread_id;
  890. s_handler_thread = CreateThread(NULL, // lpThreadAttributes
  891. kExceptionHandlerThreadInitialStackSize,
  892. ExceptionHandlerThreadMain,
  893. NULL, // lpParameter
  894. 0, // dwCreationFlags
  895. &thread_id);
  896. nvDebugCheck(s_handler_thread != NULL);
  897. }
  898. /* @@ We should avoid loading modules in the exception handler!
  899. dbghelp_module_ = LoadLibrary(L"dbghelp.dll");
  900. if (dbghelp_module_) {
  901. minidump_write_dump_ = reinterpret_cast<MiniDumpWriteDump_type>(GetProcAddress(dbghelp_module_, "MiniDumpWriteDump"));
  902. }
  903. */
  904. #endif
  905. }
  906. static void shutHandlerThread() {
  907. // @@ Free stuff. Terminate thread.
  908. }
  909. #endif // NV_USE_SEPARATE_THREAD
  910. // Enable signal handler.
  911. void debug::enableSigHandler(bool interactive)
  912. {
  913. nvCheck(s_sig_handler_enabled != true);
  914. s_sig_handler_enabled = true;
  915. s_interactive = interactive;
  916. #if NV_OS_WIN32 && NV_CC_MSVC
  917. if (interactive) {
  918. // Do not display message boxes on error.
  919. // http://msdn.microsoft.com/en-us/library/windows/desktop/ms680621(v=vs.85).aspx
  920. SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX|SEM_NOOPENFILEERRORBOX);
  921. // CRT reports errors to debug output only.
  922. // http://msdn.microsoft.com/en-us/library/1y71x448(v=vs.80).aspx
  923. _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG);
  924. _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG);
  925. _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG);
  926. }
  927. #if NV_USE_SEPARATE_THREAD
  928. initHandlerThread();
  929. #endif
  930. s_old_exception_filter = ::SetUnhandledExceptionFilter( handleException );
  931. #if _MSC_VER >= 1400 // MSVC 2005/8
  932. _set_invalid_parameter_handler(handleInvalidParameter);
  933. #endif // _MSC_VER >= 1400
  934. _set_purecall_handler(handlePureVirtualCall);
  935. // SYMOPT_DEFERRED_LOADS make us not take a ton of time unless we actual log traces
  936. SymSetOptions(SYMOPT_DEFERRED_LOADS|SYMOPT_FAIL_CRITICAL_ERRORS|SYMOPT_LOAD_LINES|SYMOPT_UNDNAME);
  937. if (!SymInitialize(GetCurrentProcess(), NULL, TRUE)) {
  938. DWORD error = GetLastError();
  939. nvDebug("SymInitialize returned error : %d\n", error);
  940. }
  941. #elif !NV_OS_WIN32 && defined(HAVE_SIGNAL_H)
  942. // Install our signal handler
  943. struct sigaction sa;
  944. sa.sa_sigaction = nvSigHandler;
  945. sigemptyset (&sa.sa_mask);
  946. sa.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO;
  947. sigaction(SIGSEGV, &sa, &s_old_sigsegv);
  948. sigaction(SIGTRAP, &sa, &s_old_sigtrap);
  949. sigaction(SIGFPE, &sa, &s_old_sigfpe);
  950. sigaction(SIGBUS, &sa, &s_old_sigbus);
  951. #endif
  952. }
  953. /// Disable signal handler.
  954. void debug::disableSigHandler()
  955. {
  956. nvCheck(s_sig_handler_enabled == true);
  957. s_sig_handler_enabled = false;
  958. #if NV_OS_WIN32 && NV_CC_MSVC
  959. ::SetUnhandledExceptionFilter( s_old_exception_filter );
  960. s_old_exception_filter = NULL;
  961. SymCleanup(GetCurrentProcess());
  962. #elif !NV_OS_WIN32 && defined(HAVE_SIGNAL_H)
  963. sigaction(SIGSEGV, &s_old_sigsegv, NULL);
  964. sigaction(SIGTRAP, &s_old_sigtrap, NULL);
  965. sigaction(SIGFPE, &s_old_sigfpe, NULL);
  966. sigaction(SIGBUS, &s_old_sigbus, NULL);
  967. #endif
  968. }
  969. bool debug::isDebuggerPresent()
  970. {
  971. #if NV_OS_WIN32
  972. HINSTANCE kernel32 = GetModuleHandleA("kernel32.dll");
  973. if (kernel32) {
  974. FARPROC IsDebuggerPresent = GetProcAddress(kernel32, "IsDebuggerPresent");
  975. if (IsDebuggerPresent != NULL && IsDebuggerPresent()) {
  976. return true;
  977. }
  978. }
  979. return false;
  980. #elif NV_OS_XBOX
  981. #ifdef _DEBUG
  982. return DmIsDebuggerPresent() == TRUE;
  983. #else
  984. return false;
  985. #endif
  986. #elif NV_OS_ORBIS
  987. return sceDbgIsDebuggerAttached() == 1;
  988. #elif NV_OS_DARWIN
  989. int mib[4];
  990. struct kinfo_proc info;
  991. size_t size;
  992. mib[0] = CTL_KERN;
  993. mib[1] = KERN_PROC;
  994. mib[2] = KERN_PROC_PID;
  995. mib[3] = getpid();
  996. size = sizeof(info);
  997. info.kp_proc.p_flag = 0;
  998. sysctl(mib,4,&info,&size,NULL,0);
  999. return ((info.kp_proc.p_flag & P_TRACED) == P_TRACED);
  1000. #else
  1001. // if ppid != sid, some process spawned our app, probably a debugger.
  1002. return getsid(getpid()) != getppid();
  1003. #endif
  1004. }
  1005. bool debug::attachToDebugger()
  1006. {
  1007. #if NV_OS_WIN32
  1008. if (isDebuggerPresent() == FALSE) {
  1009. Path process(1024);
  1010. process.copy("\"");
  1011. GetSystemDirectoryA(process.str() + 1, 1024 - 1);
  1012. process.appendSeparator();
  1013. process.appendFormat("VSJitDebugger.exe\" -p %lu", ::GetCurrentProcessId());
  1014. STARTUPINFOA sSi;
  1015. memset(&sSi, 0, sizeof(sSi));
  1016. PROCESS_INFORMATION sPi;
  1017. memset(&sPi, 0, sizeof(sPi));
  1018. BOOL b = CreateProcessA(NULL, process.str(), NULL, NULL, FALSE, 0, NULL, NULL, &sSi, &sPi);
  1019. if (b != FALSE) {
  1020. ::WaitForSingleObject(sPi.hProcess, INFINITE);
  1021. DWORD dwExitCode;
  1022. ::GetExitCodeProcess(sPi.hProcess, &dwExitCode);
  1023. if (dwExitCode != 0) //if exit code is zero, a debugger was selected
  1024. b = FALSE;
  1025. }
  1026. if (sPi.hThread != NULL) ::CloseHandle(sPi.hThread);
  1027. if (sPi.hProcess != NULL) ::CloseHandle(sPi.hProcess);
  1028. if (b == FALSE)
  1029. return false;
  1030. for (int i = 0; i < 5*60; i++) {
  1031. if (isDebuggerPresent())
  1032. break;
  1033. ::Sleep(200);
  1034. }
  1035. }
  1036. #endif // NV_OS_WIN32
  1037. return true;
  1038. }