Merge branch '138-toggle-free-look-with-hotkey' into 'main/atys-live'
[ryzomcore.git] / nel / src / misc / debug.cpp
blobf821ac620147b44c55104624283594321c1a3bdc
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2014-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 // Copyright (C) 2015 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
7 //
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "stdmisc.h"
23 #include "nel/misc/types_nl.h"
25 #ifdef NL_OS_WINDOWS
26 # include <direct.h>
27 # include <tchar.h>
28 # include <imagehlp.h>
29 # pragma comment(lib, "imagehlp.lib")
30 # ifndef getcwd
31 # define getcwd(_a, _b) (_getcwd(_a,_b))
32 # endif
33 # ifdef NL_OS_WIN64
34 # define DWORD_TYPE DWORD64
35 # else
36 # define DWORD_TYPE DWORD
37 # endif // NL_OS_WIN64
38 #elif defined NL_OS_UNIX
39 # include <unistd.h>
40 # define IsDebuggerPresent() false
41 # ifndef NL_OS_MAC
42 # include <execinfo.h>
43 # endif
44 //# include <malloc.h>
45 # include <errno.h>
46 #endif
48 #include "nel/misc/debug.h"
50 #ifdef HAVE_NELCONFIG_H
51 # include "nelconfig.h"
52 #endif // HAVE_NELCONFIG_H
54 #include "nel/misc/log.h"
55 #include "nel/misc/displayer.h"
56 #include "nel/misc/mem_displayer.h"
57 #include "nel/misc/command.h"
58 #include "nel/misc/report.h"
59 #include "nel/misc/path.h"
60 #include "nel/misc/variable.h"
61 #include "nel/misc/system_info.h"
62 #include "nel/misc/system_utils.h"
64 #define NL_NO_DEBUG_FILES 1
66 using namespace std;
68 // If you don't want to add default displayer, put 0 instead of 1. In this case, you
69 // have to manage yourself displayer (in final release for example, we have to put 0)
70 // Alternatively, you can use --without-logging when using configure to set
71 // it to 0.
72 #ifndef NEL_DEFAULT_DISPLAYER
73 #define NEL_DEFAULT_DISPLAYER 1
74 #endif // NEL_DEFAULT_DISPLAYER
76 // Put 0 if you don't want to display in file "log.log"
77 // Alternatively, you can use --without-logging when using configure to set
78 // it to 0.
79 #ifndef NEL_LOG_IN_FILE
80 #define NEL_LOG_IN_FILE 1
81 #endif // NEL_LOG_IN_FILE
83 #define DEFAULT_DISPLAYER NEL_DEFAULT_DISPLAYER
85 #define LOG_IN_FILE NEL_LOG_IN_FILE
87 // If true, debug system will trap crash even if the application is in debugger
88 static const bool TrapCrashInDebugger = false;
90 #ifdef DEBUG_NEW
91 #define new DEBUG_NEW
92 #endif
94 namespace NLMISC
98 // Globals
101 bool DisableNLDebug= false;
102 NLMISC::CVariablePtr<bool> _DisableNLDebug("nel","DisableNLDebug","Disables generation and output of nldebug logs (no code associated with the log generation is executed)",&DisableNLDebug,true);
104 static std::string LogPath;
106 //bool DebugNeedAssert = false;
107 //bool NoAssert = false;
110 // ***************************************************************************
111 CImposterLog::CImposterLog(TAccessor accessor)
112 : _Accessor(accessor)
115 CLog* CImposterLog::operator -> ()
117 if(NLMISC::INelContext::isContextInitialised())
119 return (NLMISC::INelContext::getInstance().*_Accessor)();
121 return NULL;
124 CImposterLog::operator CLog*()
126 if(NLMISC::INelContext::isContextInitialised())
128 return (NLMISC::INelContext::getInstance().*_Accessor)();
130 return NULL;
133 CLog &CImposterLog::operator ()()
135 return *(operator CLog*());
139 //CLog *ErrorLog = NULL;
140 CImposterLog ErrorLog(&INelContext::getErrorLog);
141 //CLog *WarningLog = NULL;
142 CImposterLog WarningLog(&INelContext::getWarningLog);
143 //CLog *InfoLog = NULL;
144 CImposterLog InfoLog(&INelContext::getInfoLog);
145 //CLog *DebugLog = NULL;
146 CImposterLog DebugLog(&INelContext::getDebugLog);
147 //CLog *AssertLog = NULL;
148 CImposterLog AssertLog(&INelContext::getAssertLog);
151 // ***************************************************************************
152 CMemDisplayer *DefaultMemDisplayer = NULL;
153 CMsgBoxDisplayer *DefaultMsgBoxDisplayer = NULL;
155 static CStdDisplayer *sd = NULL;
156 static CFileDisplayer *fd = NULL;
158 static TCrashCallback CrashCallback = NULL;
160 void setCrashCallback(TCrashCallback crashCallback)
162 CrashCallback = crashCallback;
165 // Yoyo: allow only the crash report to be emailed once
166 static bool CrashAlreadyReported = false;
167 bool isCrashAlreadyReported()
169 return CrashAlreadyReported;
171 void setCrashAlreadyReported(bool state)
173 CrashAlreadyReported= state;
177 void setAssert (bool assert)
179 INelContext::getInstance().setNoAssert(!assert);
182 void nlFatalError (const char *format, ...)
184 char *str;
185 NLMISC_CONVERT_VARGS (str, format, 256/*NLMISC::MaxCStringSize*/);
187 INelContext::getInstance().setDebugNeedAssert( NLMISC::DefaultMsgBoxDisplayer == NULL );
189 NLMISC::ErrorLog->displayNL (str);
191 if (INelContext::getInstance().getDebugNeedAssert())
192 NLMISC_BREAKPOINT;
194 #ifndef NL_OS_WINDOWS
196 // exit(EXIT_FAILURE);
197 abort ();
198 #endif
201 void nlError (const char *format, ...)
203 char *str;
204 NLMISC_CONVERT_VARGS (str, format, 256/*NLMISC::MaxCStringSize*/);
206 INelContext::getInstance().setDebugNeedAssert( NLMISC::DefaultMsgBoxDisplayer == NULL );
208 NLMISC::ErrorLog->displayNL (str);
210 if (INelContext::getInstance().getDebugNeedAssert())
211 NLMISC_BREAKPOINT;
213 #ifndef NL_OS_WINDOWS
214 // exit(EXIT_FAILURE);
215 abort ();
216 #endif
219 // the default behavior is to display all in standard output and to a file named "log.log";
221 static void initDebug2 (bool logInFile)
223 #if DEFAULT_DISPLAYER
225 // put the standard displayer everywhere
227 //#ifdef NL_DEBUG
228 INelContext::getInstance().getDebugLog()->addDisplayer (sd);
229 //#endif // NL_DEBUG
230 INelContext::getInstance().getInfoLog()->addDisplayer (sd);
231 INelContext::getInstance().getWarningLog()->addDisplayer (sd);
232 INelContext::getInstance().getAssertLog()->addDisplayer (sd);
233 INelContext::getInstance().getErrorLog()->addDisplayer (sd);
235 // put the memory displayer everywhere
237 // use the memory displayer and bypass all filter (even for the debug mode)
238 INelContext::getInstance().getDebugLog()->addDisplayer (DefaultMemDisplayer, true);
239 INelContext::getInstance().getInfoLog()->addDisplayer (DefaultMemDisplayer, true);
240 INelContext::getInstance().getWarningLog()->addDisplayer (DefaultMemDisplayer, true);
241 INelContext::getInstance().getAssertLog()->addDisplayer (DefaultMemDisplayer, true);
242 INelContext::getInstance().getErrorLog()->addDisplayer (DefaultMemDisplayer, true);
244 // put the file displayer only if wanted
246 #if LOG_IN_FILE
247 if (logInFile)
249 //#ifdef NL_DEBUG
250 INelContext::getInstance().getDebugLog()->addDisplayer (fd);
251 //#endif // NL_DEBUG
252 INelContext::getInstance().getInfoLog()->addDisplayer (fd);
253 INelContext::getInstance().getWarningLog()->addDisplayer (fd);
254 INelContext::getInstance().getAssertLog()->addDisplayer (fd);
255 INelContext::getInstance().getErrorLog()->addDisplayer (fd);
257 #endif // LOG_IN_FILE
259 // put the message box only in release for error
261 if (DefaultMsgBoxDisplayer)
263 INelContext::getInstance().getAssertLog()->addDisplayer (DefaultMsgBoxDisplayer);
264 INelContext::getInstance().getErrorLog()->addDisplayer (DefaultMsgBoxDisplayer);
267 #endif // DEFAULT_DISPLAYER
271 // ***************************************************************************
272 // Method called when an assert arise
274 void _assertex_stop_0(bool &ignoreNextTime, sint line, const char *file, const char *funcName, const char *exp)
276 INelContext::getInstance().setDebugNeedAssert( false );
277 NLMISC::createDebug ();
278 if (NLMISC::DefaultMsgBoxDisplayer)
279 NLMISC::DefaultMsgBoxDisplayer->IgnoreNextTime = ignoreNextTime;
280 else if(!INelContext::getInstance().getNoAssert())
281 INelContext::getInstance().setDebugNeedAssert(true);
282 NLMISC::AssertLog->setPosition (line, file, funcName);
283 if(exp) NLMISC::AssertLog->displayNL ("\"%s\" ", exp);
284 else NLMISC::AssertLog->displayNL ("STOP");
287 bool _assertex_stop_1(bool &ignoreNextTime)
289 if (NLMISC::DefaultMsgBoxDisplayer)
290 ignoreNextTime = NLMISC::DefaultMsgBoxDisplayer->IgnoreNextTime;
291 return INelContext::getInstance().getDebugNeedAssert();
294 bool _assert_stop(bool &ignoreNextTime, sint line, const char *file, const char *funcName, const char *exp)
296 _assertex_stop_0(ignoreNextTime, line, file, funcName, exp);
297 return _assertex_stop_1(ignoreNextTime);
301 #ifdef NL_OS_WINDOWS
304 // ***************************************************************************
305 static DWORD __stdcall GetModuleBase(HANDLE hProcess, DWORD dwReturnAddress)
307 IMAGEHLP_MODULE moduleInfo;
309 if (SymGetModuleInfo(hProcess, dwReturnAddress, &moduleInfo))
310 return moduleInfo.BaseOfImage;
311 else
313 MEMORY_BASIC_INFORMATION memoryBasicInfo;
315 if (::VirtualQueryEx(hProcess, (LPVOID) dwReturnAddress,
316 &memoryBasicInfo, sizeof(memoryBasicInfo)))
318 DWORD cch = 0;
319 wchar_t szFile[MAX_PATH] = { 0 };
321 cch = GetModuleFileNameW((HINSTANCE)memoryBasicInfo.AllocationBase,
322 szFile, MAX_PATH);
324 if (cch && (lstrcmpA(szFile, "DBFN")== 0))
326 if (!SymLoadModule(hProcess,
327 NULL, "MN",
328 NULL, (DWORD) memoryBasicInfo.AllocationBase, 0))
330 DWORD dwError = GetLastError();
331 // nlinfo("Error: %d", dwError);
334 else
336 if (!SymLoadModule(hProcess,
337 NULL, ((cch) ? szFile : NULL),
338 NULL, (DWORD) memoryBasicInfo.AllocationBase, 0))
340 DWORD dwError = GetLastError();
341 // nlinfo("Error: %d", dwError);
346 return (DWORD) memoryBasicInfo.AllocationBase;
348 // else
349 // nlinfo("Error is %d", GetLastError());
352 return 0;
355 LPVOID __stdcall FunctionTableAccess (HANDLE hProcess, DWORD AddrBase)
357 AddrBase = 0x40291f;
358 DWORD addr = SymGetModuleBase (hProcess, AddrBase);
359 HRESULT hr = GetLastError ();
361 IMAGEHLP_MODULE moduleInfo;
362 moduleInfo.SizeOfStruct = sizeof(IMAGEHLP_MODULE);
363 SymGetModuleInfo(hProcess, addr, &moduleInfo);
364 hr = GetLastError ();
365 SymLoadModule(hProcess, NULL, NULL, NULL, 0, 0);
366 hr = GetLastError ();
368 LPVOID temp = SymFunctionTableAccess (hProcess, AddrBase);
369 hr = GetLastError ();
370 return temp;
374 /* can't include dbghelp.h */
375 typedef struct _NEL_MINIDUMP_EXCEPTION_INFORMATION { DWORD ThreadId; PEXCEPTION_POINTERS ExceptionPointers; BOOL ClientPointers;
376 } NEL_MINIDUMP_EXCEPTION_INFORMATION, *PNEL_MINIDUMP_EXCEPTION_INFORMATION;
377 typedef enum _NEL_MINIDUMP_TYPE
379 MiniDumpNormal = 0x00000000,
380 MiniDumpWithDataSegs = 0x00000001,
381 MiniDumpWithFullMemory = 0x00000002,
382 MiniDumpWithHandleData = 0x00000004,
383 MiniDumpFilterMemory = 0x00000010,
384 MiniDumpWithUnloaded = 0x00000020,
385 MiniDumpWithIndirectlyReferencedMemory = 0x00000040,
386 MiniDumpFilterModulePaths = 0x00000080,
387 MiniDumpWithProcessThreadData = 0x00000100,
388 MiniDumpWithPrivateReadWriteMemory = 0x00000200,
389 MiniDumpWithoutOptionalData = 0x00000400,
390 MiniDumpWithFullMemoryInfo = 0x00000800,
391 MiniDumpWithThreadInfo = 0x00001000,
392 MiniDumpWithCodeSegs = 0x00002000
393 } NEL_MINIDUMP_TYPE;
395 static void DumpMiniDump(PEXCEPTION_POINTERS excpInfo)
397 HANDLE file = CreateFileA (NL_CRASH_DUMP_FILE, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
398 if (file)
400 HMODULE hm = LoadLibraryA ("dbghelp.dll");
401 if (hm)
403 BOOL (WINAPI* MiniDumpWriteDump)(
404 HANDLE hProcess,
405 DWORD ProcessId,
406 HANDLE hFile,
407 NEL_MINIDUMP_TYPE DumpType,
408 PNEL_MINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
409 PNEL_MINIDUMP_EXCEPTION_INFORMATION UserStreamParam,
410 PNEL_MINIDUMP_EXCEPTION_INFORMATION CallbackParam
411 ) = NULL;
412 *(FARPROC*)&MiniDumpWriteDump = GetProcAddress(hm, "MiniDumpWriteDump");
413 if (MiniDumpWriteDump)
415 // OutputDebugString(_T("writing minidump\r\n"));
416 NEL_MINIDUMP_EXCEPTION_INFORMATION eInfo;
417 eInfo.ThreadId = GetCurrentThreadId();
418 eInfo.ExceptionPointers = excpInfo;
419 eInfo.ClientPointers = FALSE;
421 // note: MiniDumpWithIndirectlyReferencedMemory does not work on Win98
422 MiniDumpWriteDump(
423 GetCurrentProcess(),
424 GetCurrentProcessId(),
425 file,
426 MiniDumpNormal,
427 excpInfo ? &eInfo : NULL,
428 NULL,
429 NULL);
431 else
433 nlwarning ("Can't get proc MiniDumpWriteDump in dbghelp.dll");
436 else
438 nlwarning ("Can't load dbghelp.dll");
440 CloseHandle (file);
442 else
443 nlwarning ("Can't create mini dump file");
446 class EDebug : public ETrapDebug
448 public:
450 EDebug() { _Reason = "Nothing about EDebug"; }
452 virtual ~EDebug() NL_OVERRIDE {}
454 EDebug(EXCEPTION_POINTERS * pexp) : m_pexp(pexp) { nlassert(pexp != 0); createWhat(); }
455 EDebug(const EDebug& se) : m_pexp(se.m_pexp) { createWhat(); }
457 void createWhat ()
459 string shortExc, longExc, subject;
460 string addr, ext;
461 ULONG_PTR skipNFirst = 0;
462 _Reason.clear();
464 if (m_pexp == NULL)
466 _Reason = "Unknown exception, don't have context.";
468 else
470 switch (m_pexp->ExceptionRecord->ExceptionCode)
472 case EXCEPTION_ACCESS_VIOLATION : shortExc="Access Violation"; longExc="The thread attempted to read from or write to a virtual address for which it does not have the appropriate access";
473 ext = ", thread attempts to ";
474 ext += m_pexp->ExceptionRecord->ExceptionInformation[0]?"write":"read";
475 if (m_pexp->ExceptionRecord->ExceptionInformation[1])
476 ext += toString(" at 0x%X",m_pexp->ExceptionRecord->ExceptionInformation[1]);
477 else
478 ext += " at <NULL>";
479 break;
480 case EXCEPTION_DATATYPE_MISALIGNMENT : shortExc="Datatype Misalignment"; longExc="The thread attempted to read or write data that is misaligned on hardware that does not provide alignment. For example, 16-bit values must be aligned on 2-byte boundaries, 32-bit values on 4-byte boundaries, and so on"; break;
481 case EXCEPTION_BREAKPOINT : shortExc="Breakpoint"; longExc="A breakpoint was encountered"; break;
482 case EXCEPTION_SINGLE_STEP : shortExc="Single Step"; longExc="A trace trap or other single-instruction mechanism signaled that one instruction has been executed"; break;
483 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED : shortExc="Array Bounds Exceeded"; longExc="The thread attempted to access an array element that is out of bounds, and the underlying hardware supports bounds checking"; break;
484 case EXCEPTION_FLT_DENORMAL_OPERAND : shortExc="Float Denormal Operand"; longExc="One of the operands in a floating-point operation is denormal. A denormal value is one that is too small to represent as a standard floating-point value"; break;
485 case EXCEPTION_FLT_DIVIDE_BY_ZERO : shortExc="Float Divide By Zero"; longExc="The thread attempted to divide a floating-point value by a floating-point divisor of zero"; break;
486 case EXCEPTION_FLT_INEXACT_RESULT : shortExc="Float Inexact Result"; longExc="The result of a floating-point operation cannot be represented exactly as a decimal fraction"; break;
487 case EXCEPTION_FLT_INVALID_OPERATION : shortExc="Float Invalid Operation"; longExc="This exception represents any floating-point exception not included in this list"; break;
488 case EXCEPTION_FLT_OVERFLOW : shortExc="Float Overflow"; longExc="The exponent of a floating-point operation is greater than the magnitude allowed by the corresponding type"; break;
489 case EXCEPTION_FLT_STACK_CHECK : shortExc="Float Stack Check"; longExc="The stack overflowed or underflowed as the result of a floating-point operation"; break;
490 case EXCEPTION_FLT_UNDERFLOW : shortExc="Float Underflow"; longExc="The exponent of a floating-point operation is less than the magnitude allowed by the corresponding type"; break;
491 case EXCEPTION_INT_DIVIDE_BY_ZERO : shortExc="Integer Divide By Zero"; longExc="The thread attempted to divide an integer value by an integer divisor of zero"; break;
492 case EXCEPTION_INT_OVERFLOW : shortExc="Integer Overflow"; longExc="The result of an integer operation caused a carry out of the most significant bit of the result"; break;
493 case EXCEPTION_PRIV_INSTRUCTION : shortExc="Privileged Instruction"; longExc="The thread attempted to execute an instruction whose operation is not allowed in the current machine mode"; break;
494 case EXCEPTION_IN_PAGE_ERROR : shortExc="In Page Error"; longExc="The thread tried to access a page that was not present, and the system was unable to load the page. -ie. the program or memory mapped file couldn't be paged in because it isn't accessable any more. Device drivers can return this exception if something went wrong with the read (i.e hardware problems)"; break;
495 case EXCEPTION_ILLEGAL_INSTRUCTION : shortExc="Illegal Instruction"; longExc="The thread tried to execute an invalid instruction -such as MMX opcodes on a non MMX system. Branching to an invalid location can cause this -something stack corruption often causes"; break;
496 case EXCEPTION_NONCONTINUABLE_EXCEPTION : shortExc="Noncontinuable Exception"; longExc="The thread attempted to continue execution after a noncontinuable exception occurred"; break;
497 case EXCEPTION_STACK_OVERFLOW : shortExc="Stack Overflow"; longExc="Stack overflow. Can occur during errant recursion, or when a function creates a particularly large array on the stack"; break;
498 case EXCEPTION_INVALID_DISPOSITION : shortExc="Invalid Disposition"; longExc="Whatever number the exception filter returned, it wasn't a value the OS knows about"; break;
499 case EXCEPTION_GUARD_PAGE : shortExc="Guard Page"; longExc="Memory Allocated as PAGE_GUARD by VirtualAlloc() has been accessed"; break;
500 case EXCEPTION_INVALID_HANDLE : shortExc="Invalid Handle"; longExc.clear(); break;
501 case CONTROL_C_EXIT : shortExc="Control-C"; longExc="Lets the debugger know the user hit Ctrl-C. Seemingly for console apps only"; break;
502 case STATUS_NO_MEMORY : shortExc="No Memory"; longExc="Called by HeapAlloc() if you specify HEAP_GENERATE_EXCEPTIONS and there is no memory or heap corruption";
503 ext = ", unable to allocate ";
504 ext += toString ("%d bytes", m_pexp->ExceptionRecord->ExceptionInformation [0]);
505 break;
506 case STATUS_WAIT_0 : shortExc="Wait 0"; longExc.clear(); break;
507 case STATUS_ABANDONED_WAIT_0 : shortExc="Abandoned Wait 0"; longExc.clear(); break;
508 case STATUS_USER_APC : shortExc="User APC"; longExc="A user APC was delivered to the current thread before the specified Timeout interval expired"; break;
509 case STATUS_TIMEOUT : shortExc="Timeout"; longExc.clear(); break;
510 case STATUS_PENDING : shortExc="Pending"; longExc.clear(); break;
511 case STATUS_SEGMENT_NOTIFICATION : shortExc="Segment Notification"; longExc.clear(); break;
512 case STATUS_FLOAT_MULTIPLE_FAULTS : shortExc="Float Multiple Faults"; longExc.clear(); break;
513 case STATUS_FLOAT_MULTIPLE_TRAPS : shortExc="Float Multiple Traps"; longExc.clear(); break;
514 #ifdef NL_COMP_VC6
515 case STATUS_ILLEGAL_VLM_REFERENCE : shortExc="Illegal VLM Reference"; longExc.clear(); break;
516 #endif
517 case 0xE06D7363 : shortExc="Microsoft C++ Exception"; longExc="Microsoft C++ Exception"; break; // cpp exception
518 case 0xACE0ACE : shortExc.clear(); longExc.clear();
519 if (m_pexp->ExceptionRecord->NumberParameters == 1)
520 skipNFirst = m_pexp->ExceptionRecord->ExceptionInformation [0];
521 break; // just want the stack
522 default : shortExc="Unknown Exception"; longExc="Unknown Exception "+toString("0x%X", m_pexp->ExceptionRecord->ExceptionCode); break;
525 if(m_pexp->ExceptionRecord != NULL)
527 if (m_pexp->ExceptionRecord->ExceptionAddress)
528 addr = toString(" at 0x%X", m_pexp->ExceptionRecord->ExceptionAddress);
529 else
530 addr = " at <NULL>";
533 string progname;
534 if(!shortExc.empty() || !longExc.empty())
536 wchar_t name[1024];
537 GetModuleFileNameW (NULL, name, 1023);
538 progname = CFile::getFilename(wideToUtf8(name));
539 progname += " ";
542 subject = progname + shortExc + addr;
544 if (_Reason.empty())
546 if (!shortExc.empty()) _Reason += shortExc + " exception generated" + addr + ext + ".\n";
547 if (!longExc.empty()) _Reason += longExc + ".\n";
550 // display the stack
551 addStackAndLogToReason (skipNFirst);
553 if(!shortExc.empty() || !longExc.empty())
555 // yoyo: allow only to send the crash report once. Because users usually click ignore,
556 // which create noise into list of bugs (once a player crash, it will surely continues to do it).
557 report(progname + shortExc, subject, _Reason, NL_CRASH_DUMP_FILE, true, !isCrashAlreadyReported(), ReportAbort);
558 // TODO: Does this need to be synchronous? Why does this not handle the report result?
560 // no more sent mail for crash
561 setCrashAlreadyReported(true);
566 // display the callstack
567 void addStackAndLogToReason (ULONG_PTR /* skipNFirst */ = 0)
569 #ifdef NL_OS_WINDOWS
570 // ace hack
571 /* skipNFirst = 0;
573 DWORD symOptions = SymGetOptions();
574 symOptions |= SYMOPT_LOAD_LINES;
575 symOptions &= ~SYMOPT_UNDNAME;
576 SymSetOptions (symOptions);
578 nlverify (SymInitialize(getProcessHandle(), NULL, FALSE) == TRUE);
580 STACKFRAME callStack;
581 ::ZeroMemory (&callStack, sizeof(callStack));
582 callStack.AddrPC.Mode = AddrModeFlat;
583 callStack.AddrPC.Offset = m_pexp->ContextRecord->Eip;
584 callStack.AddrStack.Mode = AddrModeFlat;
585 callStack.AddrStack.Offset = m_pexp->ContextRecord->Esp;
586 callStack.AddrFrame.Mode = AddrModeFlat;
587 callStack.AddrFrame.Offset = m_pexp->ContextRecord->Ebp;
589 _Reason += "\nCallstack:\n";
590 _Reason += "-------------------------------\n";
591 for (sint32 i = 0; ; i++)
593 SetLastError(0);
594 BOOL res = StackWalk (IMAGE_FILE_MACHINE_I386, getProcessHandle(), GetCurrentThread(), &callStack,
595 m_pexp->ContextRecord, NULL, FunctionTableAccess, GetModuleBase, NULL);
597 if (res == FALSE || callStack.AddrFrame.Offset == 0)
598 break;
600 string symInfo, srcInfo;
602 if (i >= skipNFirst)
604 srcInfo = getSourceInfo (callStack.AddrPC.Offset);
605 symInfo = getFuncInfo (callStack.AddrPC.Offset, callStack.AddrFrame.Offset);
606 _Reason += srcInfo + ": " + symInfo + "\n";
609 SymCleanup(getProcessHandle());
611 #elif !defined(NL_OS_MAC)
612 // Make place for stack frames and function names
613 const uint MaxFrame=64;
614 void *trace[MaxFrame];
615 char **messages = (char **)NULL;
616 int i, trace_size = 0;
618 trace_size = backtrace(trace, MaxFrame);
619 messages = backtrace_symbols(trace, trace_size);
620 result += "Callstack:\n";
621 _Reason += "-------------------------------\n";
622 for (i=0; i<trace_size; ++i)
623 _Reason += toString("%i : %s\n", i, messages[i]);
624 // free the messages
625 free(messages);
626 #endif
628 // _Reason += "-------------------------------\n";
629 // _Reason += "\n";
630 // if(DefaultMemDisplayer)
631 // {
632 // _Reason += "Log with no filter:\n";
633 // _Reason += "-------------------------------\n";
634 // DefaultMemDisplayer->write (_Reason);
635 // }
636 // else
637 // {
638 // _Reason += "No log\n";
639 // }
640 // _Reason += "-------------------------------\n";
642 // add specific information about the application
643 // if(CrashCallback)
644 // {
645 // _Reason += "User Crash Callback:\n";
646 // _Reason += "-------------------------------\n";
647 // static bool looping = false;
648 // if(looping)
649 // {
650 // _Reason += "******* WARNING: crashed in the user crash callback *******\n";
651 // looping = false;
652 // }
653 // else
654 // {
655 // looping = true;
656 // _Reason += CrashCallback();
657 // looping = false;
658 // }
659 // _Reason += "-------------------------------\n";
660 // }
663 string getSourceInfo (DWORD_TYPE addr)
665 string str;
667 IMAGEHLP_LINE line;
668 ::ZeroMemory (&line, sizeof (line));
669 line.SizeOfStruct = sizeof(line);
671 // ACE: removed the next code because "SymGetLineFromAddr" is not available on windows 98
672 bool ok = false;
673 DWORD displacement = 0 ;
674 DWORD resdisp = 0;
678 // "Debugging Applications" John Robbins
679 // The problem is that the symbol engine finds only those source
680 // line addresses (after the first lookup) that fall exactly on
681 // a zero displacement. I'll walk backward 100 bytes to
682 // find the line and return the proper displacement.
683 bool ok = true;
684 DWORD displacement = 0 ;
685 DWORD resdisp;
687 while (!SymGetLineFromAddr (getProcessHandle(), addr - displacement, (DWORD*)&resdisp, &line))
689 if (100 == ++displacement)
691 ok = false;
692 break;
698 // "Debugging Applications" John Robbins
699 // I found the line, and the source line information is correct, so
700 // change the displacement if I had to search backward to find the source line.
701 if (displacement)
702 resdisp = displacement;
704 if (ok)
706 str = line.FileName;
707 str += "(" + toString ((uint32)line.LineNumber) + ")";
708 str += toString(": 0x%X", addr);
710 else
712 IMAGEHLP_MODULE module;
713 ::ZeroMemory (&module, sizeof(module));
714 module.SizeOfStruct = sizeof(module);
716 if (SymGetModuleInfo (getProcessHandle(), addr, &module))
718 str = module.ModuleName;
720 else
722 str = "<NoModule>";
725 str += toString("!0x%p", (void*)addr);
730 /*DWORD disp;
731 if (SymGetLineFromAddr (getProcessHandle(), addr, &disp, &line))
733 str = line.FileName;
734 str += "(" + toString (line.LineNumber) + ")";
736 else
738 IMAGEHLP_MODULE module;
739 ::ZeroMemory (&module, sizeof(module));
740 module.SizeOfStruct = sizeof(module);
742 if (SymGetModuleInfo (getProcessHandle(), addr, &module))
744 str = module.ModuleName;
746 else
748 str = "<NoModule>";
751 str += toString("!0x%p", (void*)addr);
753 str +=" DEBUG:"+toString("0x%p", addr);
757 return str;
760 HANDLE getProcessHandle()
762 return CSystemInfo::isNT()?GetCurrentProcess():(HANDLE)(uintptr_t)GetCurrentProcessId();
765 // return true if found
766 bool findAndErase(string &str, const char *token, const char *replace = NULL)
768 string::size_type pos;
769 if ((pos = str.find(token)) != string::npos)
771 str.erase (pos,strlen(token));
772 if (replace != NULL)
773 str.insert (pos, replace);
774 return true;
776 else
777 return false;
780 // remove space and const stuffs
781 // rawType contains the type without anything (to compare with known type)
782 // displayType contains the type without std:: and stl ugly things
783 void cleanType(string &rawType, string &displayType)
785 while (findAndErase(rawType, "std::")) ;
786 while (findAndErase(displayType, "std::")) ;
788 while (findAndErase(rawType, "_STL::")) ;
789 while (findAndErase(displayType, "_STL::")) ;
791 while (findAndErase(rawType, "const")) ;
793 while (findAndErase(rawType, " ")) ;
795 while (findAndErase(rawType, "&")) ;
797 // rename ugly stl type
799 while (findAndErase(rawType, "classbasic_string<char,classchar_traits<char>,classallocator<char>>", "string")) ;
800 while (findAndErase(displayType, "class basic_string<char,class char_traits<char>,class allocator<char> >", "string")) ;
801 while (findAndErase(rawType, "classvector<char,class char_traits<char>,class allocator<char> >", "string")) ;
804 string getFuncInfo (uintptr_t funcAddr, uintptr_t stackAddr)
806 string str ("NoSymbol");
808 DWORD symSize = 10000;
809 PIMAGEHLP_SYMBOL sym = (PIMAGEHLP_SYMBOL) GlobalAlloc (GMEM_FIXED, symSize);
810 if (!sym) return str;
812 ::ZeroMemory (sym, symSize);
813 sym->SizeOfStruct = symSize;
814 sym->MaxNameLength = symSize - sizeof(IMAGEHLP_SYMBOL);
816 DWORD_TYPE disp = 0;
817 if (SymGetSymFromAddr (getProcessHandle(), funcAddr, &disp, sym) == FALSE)
819 return str;
822 CHAR undecSymbol[1024];
823 if (UnDecorateSymbolName (sym->Name, undecSymbol, 1024, UNDNAME_COMPLETE | UNDNAME_NO_THISTYPE | UNDNAME_NO_SPECIAL_SYMS | UNDNAME_NO_MEMBER_TYPE | UNDNAME_NO_MS_KEYWORDS | UNDNAME_NO_ACCESS_SPECIFIERS ) > 0)
825 str = undecSymbol;
827 else if (SymUnDName (sym, undecSymbol, 1024) == TRUE)
829 str = undecSymbol;
832 // replace param with the value of the stack for this param
834 string parse = str;
835 str.clear();
836 uint pos2 = 0;
837 sint stop = 0;
839 string type;
841 string::size_type i = parse.find ("(");
843 // copy the function name
844 str = parse.substr(0, i);
846 // nlinfo ("not parsed '%s'", parse.c_str());
848 // if there s parameter, parse them
849 if(i!=string::npos)
851 // copy the '('
852 str += parse[i];
853 for (i++; i < parse.size (); i++)
855 if (parse[i] == '<')
856 stop++;
857 if (parse[i] == '>')
858 stop--;
860 if (stop==0 && (parse[i] == ',' || parse[i] == ')'))
862 uintptr_t *addr = (uintptr_t*)(stackAddr) + 2 + pos2++;
864 string displayType = type;
865 cleanType (type, displayType);
867 char tmp[1024];
868 tmp[0]='\0';
869 if(type == "void")
871 // tmp[0]='\0';
873 else if(type == "int")
875 if (!IsBadReadPtr(addr,sizeof(int)))
876 sprintf (tmp, "%p", (void *)(*addr));
878 else if (type == "char")
880 if (!IsBadReadPtr(addr,sizeof(char)))
881 if (nlisprint(*addr))
883 sprintf (tmp, "'%c'", (char)((*addr) & 0xFF));
885 else
887 sprintf (tmp, "%p", (void *)(*addr));
890 else if (type == "char*")
892 if (!IsBadReadPtr(addr,sizeof(char*)) && *addr != 0)
894 if (!IsBadStringPtrA((char*)*addr,32))
896 uint pos = 0;
897 tmp[pos++] = '\"';
898 for (uint j = 0; j < 32; j++)
900 char c = ((char *)*addr)[j];
901 if (c == '\0')
902 break;
903 else if (c == '\n')
905 tmp[pos++] = '\\';
906 tmp[pos++] = 'n';
908 else if (c == '\r')
910 tmp[pos++] = '\\';
911 tmp[pos++] = 'r';
913 else if (c == '\t')
915 tmp[pos++] = '\\';
916 tmp[pos++] = 't';
918 else
919 tmp[pos++] = c;
921 tmp[pos++] = '\"';
922 tmp[pos++] = '\0';
926 else if (type == "string") // we assume a string is always passed by reference (i.e. addr is a string**)
928 if (!IsBadReadPtr(addr,sizeof(string*)))
930 if (*addr != 0)
932 if (!IsBadReadPtr((void*)*addr,sizeof(string)))
933 sprintf (tmp, "\"%s\"", ((string*)*addr)->c_str());
937 else
939 if (!IsBadReadPtr(addr,sizeof(uintptr_t*)))
941 if(*addr == 0)
942 sprintf (tmp, "<NULL>");
943 else
944 sprintf (tmp, "0x%p", (void *)*addr);
948 str += displayType;
949 if(tmp[0]!='\0')
951 str += "=";
952 str += tmp;
954 str += parse[i];
955 type.clear();
957 else
959 type += parse[i];
962 GlobalFree (sym);
963 if (disp != 0)
965 str += " + ";
966 str += toString ((uintptr_t)disp);
967 str += " bytes";
971 // nlinfo ("after parsing '%s'", str.c_str());
973 return str;
976 private:
977 EXCEPTION_POINTERS * m_pexp;
980 // workaround of VCPP synchronous exception and se translator
981 bool global_force_exception_flag = false;
982 #define WORKAROUND_VCPP_SYNCHRONOUS_EXCEPTION if (global_force_exception_flag) force_exception_frame();
983 void force_exception_frame(...) {std::cout.flush();}
985 static void exceptionTranslator(unsigned, EXCEPTION_POINTERS *pexp)
987 #ifndef NL_NO_DEBUG_FILES
988 CFile::createEmptyFile(getLogDirectory() + "exception_catched");
989 #endif
990 if (pexp->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT)
992 #ifndef NL_NO_DEBUG_FILES
993 CFile::createEmptyFile(getLogDirectory() + "breakpointed");
994 #endif
995 return;
997 #if FINAL_VERSION
998 // In final version, throw EDebug to display a smart dialog box with callstack & log when crashing
999 # pragma message ( "Smart crash enabled" )
1000 DumpMiniDump(pexp);
1001 throw EDebug (pexp);
1002 #else
1003 // In debug version, let the program crash and use a debugger (clicking "Cancel")
1004 // Ace: 'if' not activated because we can't debug if enabled: keeping only 0xACE0ACE for nlstop...
1005 //if (!TrapCrashInDebugger && IsDebuggerPresent ())
1007 if (pexp->ExceptionRecord->ExceptionCode == 0xACE0ACE)
1008 throw EDebug (pexp);
1009 else
1010 return;
1012 /*else
1014 if (pexp->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT)
1015 return;
1016 else
1017 throw EDebug (pexp);
1019 #endif
1022 #endif // NL_OS_WINDOWS
1024 void getCallStack(std::string &result, sint skipNFirst)
1026 #ifdef NL_OS_WINDOWS
1029 WORKAROUND_VCPP_SYNCHRONOUS_EXCEPTION // force to install a exception frame
1031 DWORD_PTR array[1];
1032 array[0] = skipNFirst;
1033 RaiseException (0xACE0ACE, 0, 1, array);
1035 catch (const EDebug &e)
1037 result += e.what();
1039 #elif !defined(NL_OS_MAC)
1040 // Make place for stack frames and function names
1041 const uint MaxFrame=64;
1042 void *trace[MaxFrame];
1043 char **messages = (char **)NULL;
1044 int i, trace_size = 0;
1046 // on mac, require at least os 10.5
1047 trace_size = backtrace(trace, MaxFrame);
1048 messages = backtrace_symbols(trace, trace_size);
1049 result += "Dumping call stack :\n";
1050 for (i=0; i<trace_size; ++i)
1051 result += toString("%i : %s\n", i, messages[i]);
1052 // free the messages
1053 free(messages);
1054 #endif
1058 void getCallStackAndLog (string &result, sint skipNFirst)
1060 getCallStack(result, skipNFirst);
1061 //#ifdef NL_OS_WINDOWS
1062 // try
1063 // {
1064 // WORKAROUND_VCPP_SYNCHRONOUS_EXCEPTION // force to install a exception frame
1066 // DWORD array[1];
1067 // array[0] = skipNFirst;
1068 // RaiseException (0xACE0ACE, 0, 1, array);
1069 // }
1070 // catch (const EDebug &e)
1071 // {
1072 // result += e.what();
1073 // }
1074 //#else
1076 // // Make place for stack frames and function names
1077 // const uint MaxFrame=64;
1078 // void *trace[MaxFrame];
1079 // char **messages = (char **)NULL;
1080 // int i, trace_size = 0;
1082 // trace_size = backtrace(trace, MaxFrame);
1083 // messages = backtrace_symbols(trace, trace_size);
1084 // result += "Dumping call stack :\n";
1085 // for (i=0; i<trace_size; ++i)
1086 // result += toString("%i : %s\n", i, messages[i]);
1087 // // free the messages
1088 // free(messages);
1089 //#endif
1091 result += "-------------------------------\n";
1092 result += "\n";
1093 if(DefaultMemDisplayer)
1095 result += "Log with no filter:\n";
1096 result += "-------------------------------\n";
1097 DefaultMemDisplayer->write (result);
1099 else
1101 result += "No log\n";
1103 result += "-------------------------------\n";
1105 // add specific information about the application
1106 if(CrashCallback)
1108 result += "User Crash Callback:\n";
1109 result += "-------------------------------\n";
1110 static bool looping = false;
1111 if(looping)
1113 result += "******* WARNING: crashed in the user crash callback *******\n";
1114 looping = false;
1116 else
1118 looping = true;
1119 result += CrashCallback();
1120 looping = false;
1122 result += "-------------------------------\n";
1126 void changeLogDirectory(const std::string &dir)
1128 if (fd == NULL)return;
1129 LogPath = CPath::standardizePath(dir);
1130 string p = LogPath + "log.log";
1131 fd->setParam(p);
1134 std::string getLogDirectory()
1136 return LogPath;
1139 // You should not call this, unless you know what you're trying to do (it kills debug/log)!
1140 // Destroys debug environment, to clear up the memleak log.
1141 // NeL context must be deleted immediately after debug destroyed,
1142 // or there will be various issues when static destructors call nldebug etc...
1143 void destroyDebug()
1145 delete sd; sd = NULL;
1146 delete DefaultMsgBoxDisplayer; DefaultMsgBoxDisplayer = NULL;
1147 delete fd; fd = NULL;
1148 delete DefaultMemDisplayer; DefaultMemDisplayer = NULL;
1149 if (INelContext::isContextInitialised())
1151 CLog *log;
1152 INelContext &context = INelContext::getInstance();
1153 log = context.getErrorLog(); context.setErrorLog(NULL); delete log; log = NULL;
1154 log = context.getWarningLog(); context.setWarningLog(NULL); delete log; log = NULL;
1155 log = context.getInfoLog(); context.setInfoLog(NULL); delete log; log = NULL;
1156 log = context.getDebugLog(); context.setDebugLog(NULL); delete log; log = NULL;
1157 log = context.getAssertLog(); context.setAssertLog(NULL); delete log; log = NULL;
1158 INelContext::getInstance().setAlreadyCreateSharedAmongThreads(false);
1162 void attachExceptionHandler()
1164 #ifndef NL_COMP_MINGW
1165 # ifdef NL_OS_WINDOWS
1166 _set_se_translator(exceptionTranslator);
1167 # endif // NL_OS_WINDOWS
1168 #endif //!NL_COMP_MINGW
1171 void createDebug (const char *logPath, bool logInFile, bool eraseLastLog)
1173 // Do some basic compiler time check on type size
1174 nlctassert(sizeof(char) == 1);
1176 // static bool alreadyCreateSharedAmongThreads = false;
1177 // if ( !alreadyCreateSharedAmongThreads )
1178 if (!INelContext::getInstance().getAlreadyCreateSharedAmongThreads())
1180 // Debug Info for mutexes
1181 #ifdef MUTEX_DEBUG
1182 initAcquireTimeMap();
1183 #endif
1185 #ifndef NL_COMP_MINGW
1186 # ifdef NL_OS_WINDOWS
1187 // if (!IsDebuggerPresent ())
1189 // Use an environment variable to share the value among the EXE and its child DLLs
1190 // (otherwise there would be one distinct bool by module, and the last
1191 // _set_se_translator would overwrite the previous ones)
1192 const char *SE_TRANSLATOR_IN_MAIN_MODULE = "NEL_SE_TRANS";
1193 char envBuf [2];
1194 if ( GetEnvironmentVariableA( SE_TRANSLATOR_IN_MAIN_MODULE, envBuf, 2 ) == 0)
1196 _set_se_translator(exceptionTranslator);
1197 SetEnvironmentVariableA( SE_TRANSLATOR_IN_MAIN_MODULE, "1" );
1200 # endif // NL_OS_WINDOWS
1201 #endif //!NL_COMP_MINGW
1203 INelContext::getInstance().setErrorLog(new CLog (CLog::LOG_ERROR));
1204 INelContext::getInstance().setWarningLog(new CLog (CLog::LOG_WARNING));
1205 INelContext::getInstance().setInfoLog(new CLog (CLog::LOG_INFO));
1206 INelContext::getInstance().setDebugLog(new CLog (CLog::LOG_DEBUG));
1207 INelContext::getInstance().setAssertLog(new CLog (CLog::LOG_ASSERT));
1209 sd = new CStdDisplayer ("DEFAULT_SD");
1211 #ifdef NL_OS_WINDOWS
1212 if (TrapCrashInDebugger || !IsDebuggerPresent ())
1213 #endif
1215 DefaultMsgBoxDisplayer = new CMsgBoxDisplayer ("DEFAULT_MBD");
1218 #if LOG_IN_FILE
1219 if (logInFile)
1221 string fn;
1222 if (logPath != NULL)
1224 LogPath = CPath::standardizePath(logPath);
1225 fn += LogPath;
1227 else
1229 // we want the log.log to be in the current directory
1230 // char tmpPath[1024];
1231 // fn += getcwd(tmpPath, 1024);
1232 // fn += "/";
1234 fn += "log.log";
1235 #if FINAL_VERSION
1236 fd = new CFileDisplayer (fn, true, "DEFAULT_FD");
1237 #else // FINAL_VERSION
1238 fd = new CFileDisplayer (fn, eraseLastLog, "DEFAULT_FD");
1239 #endif // FINAL_VERSION
1241 #endif // LOG_IN_FILE
1242 DefaultMemDisplayer = new CMemDisplayer ("DEFAULT_MD");
1244 if (NLMISC::CSystemUtils::detectWindowedApplication())
1245 INelContext::getInstance().setWindowedApplication(true);
1247 initDebug2(logInFile);
1249 INelContext::getInstance().setAlreadyCreateSharedAmongThreads(true);
1250 // alreadyCreateSharedAmongThreads = true;
1256 * Beep (Windows only, no effect elsewhere)
1258 void beep( uint freq, uint duration )
1260 #ifdef NL_OS_WINDOWS
1261 Beep( freq, duration );
1262 #endif
1267 // Instance counter
1270 NLMISC_SAFE_SINGLETON_IMPL(CInstanceCounterManager);
1272 CInstanceCounterLocalManager *CInstanceCounterLocalManager::_Instance = NULL;
1274 TInstanceCounterData::TInstanceCounterData(const char *className)
1275 : _InstanceCounter(0),
1276 _DeltaCounter(0),
1277 _ClassName(className),
1278 _Touched(false)
1280 CInstanceCounterLocalManager::getInstance().registerInstanceCounter(this);
1283 TInstanceCounterData::~TInstanceCounterData()
1285 CInstanceCounterLocalManager::getInstance().unregisterInstanceCounter(this);
1289 void CInstanceCounterManager::registerInstaceCounterLocalManager(CInstanceCounterLocalManager *localMgr)
1291 _InstanceCounterMgrs.insert(localMgr);
1294 void CInstanceCounterManager::unregisterInstaceCounterLocalManager(CInstanceCounterLocalManager *localMgr)
1296 _InstanceCounterMgrs.erase(localMgr);
1300 std::string CInstanceCounterManager::displayCounters() const
1302 map<string, TInstanceCounterData> counters;
1305 // gather counter information
1306 std::set<CInstanceCounterLocalManager*>::const_iterator first2(_InstanceCounterMgrs.begin()), last2(_InstanceCounterMgrs.end());
1307 for (; first2 != last2; ++first2)
1309 // iterate over managers
1310 const CInstanceCounterLocalManager *mgr = *first2;
1312 std::set<TInstanceCounterData*>::const_iterator first(mgr->_InstanceCounters.begin()), last(mgr->_InstanceCounters.end());
1313 for (; first != last; ++first)
1315 const TInstanceCounterData *icd = *first;
1317 if (!icd->_Touched)
1318 continue;
1320 if( counters.find(icd->_ClassName) == counters.end())
1322 // insert a new item
1323 counters.insert(make_pair(string(icd->_ClassName), TInstanceCounterData(*icd)));
1325 else
1327 // accumulate the counter with the existing counter
1328 TInstanceCounterData &icddest = counters.find(icd->_ClassName)->second;
1331 icddest._DeltaCounter += icd->_DeltaCounter;
1332 icddest._InstanceCounter += icd->_InstanceCounter;
1341 string ret = toString("Listing %u Instance counters :\n", counters.size());
1342 map<string, TInstanceCounterData>::iterator first(counters.begin()), last(counters.end());
1343 for (; first != last; ++first)
1345 TInstanceCounterData &icd = first->second;
1346 ret += toString(" Class '%-20s', \t%10d instances, \t%10d delta\n",
1347 icd._ClassName,
1348 icd._InstanceCounter,
1349 icd._InstanceCounter - icd._DeltaCounter);
1352 return ret;
1355 void CInstanceCounterManager::resetDeltaCounter()
1357 std::set<CInstanceCounterLocalManager*>::iterator first2(_InstanceCounterMgrs.begin()), last2(_InstanceCounterMgrs.end());
1358 for (; first2 != last2; ++first2)
1360 // iterate over managers
1361 CInstanceCounterLocalManager *mgr = *first2;
1363 std::set<TInstanceCounterData*>::iterator first(mgr->_InstanceCounters.begin()), last(mgr->_InstanceCounters.end());
1364 for (; first != last; ++first)
1366 TInstanceCounterData *icd = *first;
1368 icd->_DeltaCounter = icd->_InstanceCounter;
1374 uint32 CInstanceCounterManager::getInstanceCounter(const std::string &className) const
1376 uint32 result = 0;
1377 std::set<CInstanceCounterLocalManager*>::const_iterator first2(_InstanceCounterMgrs.begin()), last2(_InstanceCounterMgrs.end());
1378 for (; first2 != last2; ++first2)
1380 // iterate over managers
1381 const CInstanceCounterLocalManager *mgr = *first2;
1383 std::set<TInstanceCounterData*>::const_iterator first(mgr->_InstanceCounters.begin()), last(mgr->_InstanceCounters.end());
1384 for (; first != last; ++first)
1386 const TInstanceCounterData *icd = *first;
1388 if (icd->_ClassName == className)
1390 result += icd->_InstanceCounter;
1396 return result;
1399 sint32 CInstanceCounterManager::getInstanceCounterDelta(const std::string &className) const
1401 sint32 result = 0;
1402 std::set<CInstanceCounterLocalManager*>::const_iterator first2(_InstanceCounterMgrs.begin()), last2(_InstanceCounterMgrs.end());
1403 for (; first2 != last2; ++first2)
1405 // iterate over managers
1406 const CInstanceCounterLocalManager *mgr = *first2;
1408 std::set<TInstanceCounterData*>::const_iterator first(mgr->_InstanceCounters.begin()), last(mgr->_InstanceCounters.end());
1409 for (; first != last; ++first)
1411 const TInstanceCounterData *icd = *first;
1413 if (icd->_ClassName == className)
1415 result += icd->_InstanceCounter - icd->_DeltaCounter;
1421 return result;
1424 void CInstanceCounterLocalManager::unregisterInstanceCounter(TInstanceCounterData *counter)
1426 _InstanceCounters.erase(counter);
1428 if (_InstanceCounters.empty())
1430 // no more need for the singleton
1431 releaseInstance();
1436 /// Return the last error code generated by a system call
1437 int getLastError()
1439 #ifdef NL_OS_WINDOWS
1440 return GetLastError();
1441 #else
1442 return errno;
1443 #endif
1446 /// Return a readable text according to the error code submited
1447 std::string formatErrorMessage(int errorCode)
1449 #ifdef NL_OS_WINDOWS
1450 LPWSTR lpMsgBuf = NULL;
1451 DWORD len = FormatMessageW(
1452 FORMAT_MESSAGE_ALLOCATE_BUFFER |
1453 FORMAT_MESSAGE_FROM_SYSTEM |
1454 FORMAT_MESSAGE_IGNORE_INSERTS,
1455 NULL,
1456 errorCode,
1457 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
1458 (LPWSTR)(&lpMsgBuf),
1460 NULL
1463 // empty buffer, an error occurred
1464 if (len == 0) return toString("FormatMessage returned error %d", getLastError());
1466 // convert wchar_t* to std::string
1467 string ret = wideToUtf8(lpMsgBuf);
1469 // Free the buffer.
1470 LocalFree(lpMsgBuf);
1472 return ret;
1473 #else
1474 return strerror(errorCode);
1475 #endif
1481 // Commands
1484 NLMISC_CATEGORISED_COMMAND(nel, displayInstanceCounter, "display the instance counters", "[<filter>]")
1486 string className;
1487 if (args.size() == 1)
1488 className = args[0];
1489 if (args.size() > 1)
1490 return false;
1492 string list = CInstanceCounterManager::getInstance().displayCounters();
1494 vector<string> lines;
1495 explode(list, string("\n"), lines);
1498 for (uint i=0; i<lines.size(); ++i)
1500 if (!className.empty())
1502 if (lines[i].find(className) == string::npos)
1503 continue;
1506 log.displayNL(lines[i].c_str());
1509 return true;
1512 NLMISC_CATEGORISED_COMMAND(nel, resetInstanceCounterDelta, "reset the delta value for instance counter", "")
1514 CInstanceCounterManager::getInstance().resetDeltaCounter();
1516 return true;
1519 NLMISC_CATEGORISED_COMMAND(nel, displayMemlog, "displays the last N line of the log in memory", "[<NbLines>]")
1521 uint nbLines;
1523 if (args.empty()) nbLines = 100;
1524 else if (args.size() == 1) NLMISC::fromString(args[0], nbLines);
1525 else return false;
1527 if (DefaultMemDisplayer == NULL) return false;
1529 deque<string>::const_iterator it;
1531 const deque<string> &str = DefaultMemDisplayer->lockStrings ();
1533 if (nbLines >= str.size())
1534 it = str.begin();
1535 else
1536 it = str.end() - nbLines;
1538 DefaultMemDisplayer->write (&log);
1540 DefaultMemDisplayer->unlockStrings ();
1542 return true;
1545 NLMISC_CATEGORISED_COMMAND(nel, resetFilters, "disable all filters on Nel loggers", "[debug|info|warning|error|assert]")
1547 if(args.empty())
1549 DebugLog->resetFilters();
1550 InfoLog->resetFilters();
1551 WarningLog->resetFilters();
1552 ErrorLog->resetFilters();
1553 AssertLog->resetFilters();
1555 else if (args.size() == 1)
1557 if (args[0] == "debug") DebugLog->resetFilters();
1558 else if (args[0] == "info") InfoLog->resetFilters();
1559 else if (args[0] == "warning") WarningLog->resetFilters();
1560 else if (args[0] == "error") ErrorLog->resetFilters();
1561 else if (args[0] == "assert") AssertLog->resetFilters();
1563 else
1565 return false;
1568 return true;
1571 NLMISC_CATEGORISED_COMMAND(nel, addPositiveFilterDebug, "add a positive filter on DebugLog", "<filterstr>")
1573 if(args.size() != 1) return false;
1574 DebugLog->addPositiveFilter( args[0].c_str() );
1575 return true;
1578 NLMISC_CATEGORISED_COMMAND(nel, addNegativeFilterDebug, "add a negative filter on DebugLog", "<filterstr>")
1580 if(args.size() != 1) return false;
1581 DebugLog->addNegativeFilter( args[0].c_str() );
1582 return true;
1585 NLMISC_CATEGORISED_COMMAND(nel, removeFilterDebug, "remove a filter on DebugLog", "[<filterstr>]")
1587 if(args.empty())
1588 DebugLog->removeFilter();
1589 else if(args.size() == 1)
1590 DebugLog->removeFilter( args[0].c_str() );
1591 else return false;
1592 return true;
1595 NLMISC_CATEGORISED_COMMAND(nel, displayFilterDebug, "display filter on DebugLog", "")
1597 if(!args.empty()) return false;
1598 DebugLog->displayFilter(log);
1599 return true;
1602 NLMISC_CATEGORISED_COMMAND(nel, addPositiveFilterInfo, "add a positive filter on InfoLog", "<filterstr>")
1604 if(args.size() != 1) return false;
1605 InfoLog->addPositiveFilter( args[0].c_str() );
1606 return true;
1609 NLMISC_CATEGORISED_COMMAND(nel, addNegativeFilterInfo, "add a negative filter on InfoLog", "<filterstr>")
1611 if(args.size() != 1) return false;
1612 InfoLog->addNegativeFilter( args[0].c_str() );
1613 return true;
1616 NLMISC_CATEGORISED_COMMAND(nel, removeFilterInfo, "remove a filter on InfoLog", "[<filterstr>]")
1618 if(args.empty())
1619 InfoLog->removeFilter();
1620 else if(args.size() == 1)
1621 InfoLog->removeFilter( args[0].c_str() );
1622 else return false;
1623 return true;
1626 NLMISC_CATEGORISED_COMMAND(nel, displayFilterInfo, "display filter on InfoLog", "[d|i|w|e]")
1628 if(args.size() > 1) return false;
1629 if ( args.size() == 1 )
1631 if ( strcmp( args[0].c_str(), "d" ) == 0 )
1632 InfoLog->displayFilter(*DebugLog);
1633 else if ( strcmp( args[0].c_str(), "i" ) == 0 )
1634 InfoLog->displayFilter(*InfoLog);
1635 else if ( strcmp( args[0].c_str(), "w" ) == 0 )
1636 InfoLog->displayFilter(*WarningLog);
1637 else if ( strcmp( args[0].c_str(), "e" ) == 0 )
1638 InfoLog->displayFilter(*ErrorLog);
1639 else
1640 return false;
1642 else
1644 InfoLog->displayFilter(log);
1646 return true;
1649 NLMISC_CATEGORISED_COMMAND(nel, addPositiveFilterWarning, "add a positive filter on WarningLog", "<filterstr>")
1651 if(args.size() != 1) return false;
1652 WarningLog->addPositiveFilter( args[0].c_str() );
1653 return true;
1656 NLMISC_CATEGORISED_COMMAND(nel, addNegativeFilterWarning, "add a negative filter on WarningLog", "<filterstr>")
1658 if(args.size() != 1) return false;
1659 WarningLog->addNegativeFilter( args[0].c_str() );
1660 return true;
1663 NLMISC_CATEGORISED_COMMAND(nel, removeFilterWarning, "remove a filter on WarningLog", "[<filterstr>]")
1665 if(args.empty())
1666 WarningLog->removeFilter();
1667 else if(args.size() == 1)
1668 WarningLog->removeFilter( args[0].c_str() );
1669 else return false;
1670 return true;
1673 NLMISC_CATEGORISED_COMMAND(nel, displayFilterWarning, "display filter on WarningLog", "")
1675 if(!args.empty()) return false;
1676 WarningLog->displayFilter(log);
1677 return true;
1681 #if !FINAL_VERSION
1683 // commands to generate different "crash"
1685 NLMISC_CATEGORISED_COMMAND(nel, assert, "generate a failed nlassert()", "")
1687 if(args.size() != 0) return false;
1688 nlassertex (false, ("Assert generated by the assert command"));
1689 return true;
1692 NLMISC_CATEGORISED_COMMAND(nel, stop, "generate a nlstop()", "")
1694 if(args.size() != 0) return false;
1695 nlstopex (("Stop generated by the stop command"));
1696 return true;
1699 NLMISC_CATEGORISED_COMMAND(nel, abort, "generate a abort()", "")
1701 if(args.size() != 0) return false;
1702 abort();
1703 return true;
1706 NLMISC_CATEGORISED_COMMAND(nel, divbyzero, "generate a divide by zero", "")
1708 if(args.size() != 0) return false;
1709 float a=10,b=0;
1710 a /= b;
1711 return true;
1714 NLMISC_CATEGORISED_COMMAND(nel, writeaccess, "write a uint8 value in an invalid address", "[<adr> [<value>]]")
1716 uint8 val = 123;
1717 uint8 *adr = (uint8*)0;
1718 if(args.size() >= 1)
1720 #ifdef HAVE_X86_64
1721 uint64 addr64;
1722 NLMISC::fromString(args[0], addr64);
1723 adr = (uint8*)addr64;
1724 #else
1725 uint32 addr32;
1726 NLMISC::fromString(args[0], addr32);
1727 adr = (uint8*)addr32;
1728 #endif
1730 if(args.size() >= 2) NLMISC::fromString(args[1], val);
1731 nlassume(adr);
1732 *adr = val;
1733 return true;
1736 NLMISC_CATEGORISED_COMMAND(nel, readaccess, "read a uint8 value in an invalid address", "[<adr>]")
1738 uint8 val;
1739 uint8 *adr = (uint8*)0;
1740 if(args.size() == 1)
1742 #ifdef HAVE_X86_64
1743 uint64 addr64;
1744 NLMISC::fromString(args[0], addr64);
1745 adr = (uint8*)addr64;
1746 #else
1747 uint32 addr32;
1748 NLMISC::fromString(args[0], addr32);
1749 adr = (uint8*)addr32;
1750 #endif
1752 nlassume(adr);
1753 val = *adr;
1754 log.displayNL("value is %hu", (uint16)val);
1755 return true;
1758 #endif // FINAL_VERSION
1760 } // NLMISC