# Home    # nevrax.com   
Nevrax
Nevrax.org
#News
#Mailing-list
#Documentation
#CVS
#Bugs
#License
Docs
 
Documentation  
Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages   Search  

debug.cpp

Go to the documentation of this file.
00001 
00007 /* Copyright, 2000 Nevrax Ltd.
00008  *
00009  * This file is part of NEVRAX NEL.
00010  * NEVRAX NEL is free software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2, or (at your option)
00013  * any later version.
00014 
00015  * NEVRAX NEL is distributed in the hope that it will be useful, but
00016  * WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00018  * General Public License for more details.
00019 
00020  * You should have received a copy of the GNU General Public License
00021  * along with NEVRAX NEL; see the file COPYING. If not, write to the
00022  * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
00023  * MA 02111-1307, USA.
00024  */
00025 
00026 #ifdef HAVE_NELCONFIG_H
00027 #  include "nelconfig.h"
00028 #endif // HAVE_NELCONFIG_H
00029 
00030 #include "stdmisc.h"
00031 #include "nel/misc/log.h"
00032 #include "nel/misc/displayer.h"
00033 #include "nel/misc/mem_displayer.h"
00034 #include "nel/misc/command.h"
00035 #include "nel/misc/report.h"
00036 #include "nel/misc/path.h"
00037 
00038 #ifdef NL_OS_WINDOWS
00039 #       define _WIN32_WINDOWS   0x0410
00040 #       define WINVER                   0x0400
00041 #       include <windows.h>
00042 #       include <direct.h>
00043 #       include <imagehlp.h>
00044 #       pragma comment(lib, "imagehlp.lib")
00045 #       define getcwd(_a, _b) (_getcwd(_a,_b))
00046 #elif defined NL_OS_UNIX
00047 #       include <unistd.h>
00048 #       include <stdio.h>
00049 #       include <stdlib.h>
00050 #       define IsDebuggerPresent() false
00051 #endif
00052 
00053 #include <stdarg.h>
00054 #include <iostream>
00055 
00056 using namespace std;
00057  
00058 // If you don't want to add default displayer, put 0 instead of 1. In this case, you
00059 // have to manage yourself displayer (in final release for example, we have to put 0)
00060 // Alternatively, you can use --without-logging when using configure to set
00061 // it to 0.
00062 #ifndef NEL_DEFAULT_DISPLAYER
00063 #define NEL_DEFAULT_DISPLAYER 1
00064 #endif // NEL_DEFAULT_DISPLAYER
00065 
00066 // Put 0 if you don't want to display in file "log.log"
00067 // Alternatively, you can use --without-logging when using configure to set
00068 // it to 0.
00069 #ifndef NEL_LOG_IN_FILE
00070 #define NEL_LOG_IN_FILE 1
00071 #endif // NEL_LOG_IN_FILE
00072 
00073 #define DEFAULT_DISPLAYER NEL_DEFAULT_DISPLAYER
00074 
00075 #define LOG_IN_FILE NEL_LOG_IN_FILE
00076 
00077 // If true, debug system will trap crashs even if the appli is in debugger
00078 static const bool TrapCrashInDebugger = false;
00079 
00080 namespace NLMISC 
00081 {
00082 
00083 // Need an assert in the macro
00084 bool DebugNeedAssert = false;
00085 
00086 CLog *ErrorLog = NULL;
00087 CLog *WarningLog = NULL;
00088 CLog *InfoLog = NULL;
00089 CLog *DebugLog = NULL;
00090 CLog *AssertLog = NULL;
00091 
00092 CMemDisplayer *DefaultMemDisplayer = NULL;
00093 CMsgBoxDisplayer *DefaultMsgBoxDisplayer = NULL;
00094 
00095 static CStdDisplayer *sd = NULL;
00096 static CFileDisplayer *fd = NULL;
00097 
00098 void nlFatalError (const char *format, ...)
00099 {
00100         char *str;
00101         NLMISC_CONVERT_VARGS (str, format, 256/*NLMISC::MaxCStringSize*/);
00102 
00103         NLMISC::DebugNeedAssert = NLMISC::DefaultMsgBoxDisplayer==0;
00104 
00105         NLMISC::ErrorLog->displayNL (str);
00106 
00107         if (NLMISC::DebugNeedAssert)
00108                 NLMISC_BREAKPOINT;
00109 
00110 #ifndef NL_OS_WINDOWS
00111         exit(EXIT_FAILURE);
00112 #endif
00113 
00114 }
00115 
00116 void nlError (const char *format, ...)
00117 {
00118         char *str;
00119         NLMISC_CONVERT_VARGS (str, format, 256/*NLMISC::MaxCStringSize*/);
00120 
00121         NLMISC::DebugNeedAssert = NLMISC::DefaultMsgBoxDisplayer==0;
00122 
00123         NLMISC::ErrorLog->displayNL (str);
00124 
00125         if (NLMISC::DebugNeedAssert)
00126                 NLMISC_BREAKPOINT;
00127 
00128 #ifndef NL_OS_WINDOWS
00129         exit(EXIT_FAILURE);
00130 #endif
00131 }
00132 
00133 // the default behavior is to display all in standard output and to a file named "log.log";
00134 
00135 void initDebug2 (bool logInFile)
00136 {
00137         static bool alreadyInit = false;
00138 
00139         if (!alreadyInit)
00140         {
00141 #if DEFAULT_DISPLAYER
00142 
00143                 // put the standard displayer everywhere
00144 
00145 #ifdef NL_DEBUG
00146                 DebugLog->addDisplayer (sd);
00147 #endif // NL_DEBUG
00148                 InfoLog->addDisplayer (sd);
00149                 WarningLog->addDisplayer (sd);
00150                 AssertLog->addDisplayer (sd);
00151                 ErrorLog->addDisplayer (sd);
00152 
00153                 // put the memory displayer everywhere
00154 
00155                 // use the memory displayer and bypass all filter (even for the debug mode)
00156                 DebugLog->addDisplayer (DefaultMemDisplayer, true);
00157                 InfoLog->addDisplayer (DefaultMemDisplayer, true);
00158                 WarningLog->addDisplayer (DefaultMemDisplayer, true);
00159                 AssertLog->addDisplayer (DefaultMemDisplayer, true);
00160                 ErrorLog->addDisplayer (DefaultMemDisplayer, true);
00161 
00162                 // put the file displayer only if wanted
00163 
00164 #if LOG_IN_FILE
00165                 if (logInFile)
00166                 {
00167 #ifdef NL_DEBUG
00168                         DebugLog->addDisplayer (fd);
00169 #endif // NL_DEBUG
00170                         InfoLog->addDisplayer (fd);
00171                         WarningLog->addDisplayer (fd);
00172                         AssertLog->addDisplayer (fd);
00173                         ErrorLog->addDisplayer (fd);
00174                 }
00175 #endif // LOG_IN_FILE
00176 
00177                 // put the message box only in release for error
00178 
00179                 if (TrapCrashInDebugger || !IsDebuggerPresent ())
00180                 {
00181                         AssertLog->addDisplayer (DefaultMsgBoxDisplayer);
00182                         ErrorLog->addDisplayer (DefaultMsgBoxDisplayer);
00183                 }
00184 
00185 #endif // DEFAULT_DISPLAYER
00186                 alreadyInit = true;
00187         }
00188         else
00189         {
00190                 nlwarning ("NLMISC::initDebug2() already called");
00191         }
00192 }
00193 
00194 
00195 #ifdef NL_OS_WINDOWS
00196 
00197 //
00198 //
00199 //
00200 
00201 static DWORD __stdcall GetModuleBase(HANDLE hProcess, DWORD dwReturnAddress)
00202 {
00203         IMAGEHLP_MODULE moduleInfo;
00204 
00205         if (SymGetModuleInfo(hProcess, dwReturnAddress, &moduleInfo))
00206                 return moduleInfo.BaseOfImage;
00207         else
00208         {
00209                 MEMORY_BASIC_INFORMATION memoryBasicInfo;
00210 
00211                 if (::VirtualQueryEx(hProcess, (LPVOID) dwReturnAddress,
00212                         &memoryBasicInfo, sizeof(memoryBasicInfo)))
00213                 {
00214                         DWORD cch = 0;
00215                         char szFile[MAX_PATH] = { 0 };
00216 
00217                  cch = GetModuleFileNameA((HINSTANCE)memoryBasicInfo.AllocationBase,
00218                                                                  szFile, MAX_PATH);
00219 
00220                 if (cch && (lstrcmp(szFile, "DBFN")== 0))
00221                 {
00222                          if (!SymLoadModule(hProcess,
00223                                    NULL, "MN",
00224                                    NULL, (DWORD) memoryBasicInfo.AllocationBase, 0))
00225                                 {
00226                                         DWORD dwError = GetLastError();
00227 //                                      nlinfo("Error: %d", dwError);
00228                                 }
00229                 }
00230                 else
00231                 {
00232                  if (!SymLoadModule(hProcess,
00233                            NULL, ((cch) ? szFile : NULL),
00234                            NULL, (DWORD) memoryBasicInfo.AllocationBase, 0))
00235                         {
00236                                 DWORD dwError = GetLastError();
00237 //                              nlinfo("Error: %d", dwError);
00238                          }
00239 
00240                 }
00241 
00242                  return (DWORD) memoryBasicInfo.AllocationBase;
00243           }
00244 //              else
00245 //                      nlinfo("Error is %d", GetLastError());
00246         }
00247 
00248         return 0;
00249 }
00250 
00251 LPVOID __stdcall FunctionTableAccess (HANDLE hProcess, DWORD AddrBase)
00252 {
00253         AddrBase = 0x40291f;
00254         DWORD addr = SymGetModuleBase (hProcess, AddrBase);
00255         HRESULT hr = GetLastError ();
00256         
00257         IMAGEHLP_MODULE moduleInfo;
00258         moduleInfo.SizeOfStruct = sizeof(IMAGEHLP_MODULE);
00259         SymGetModuleInfo(hProcess, addr, &moduleInfo);
00260         hr = GetLastError ();
00261         SymLoadModule(hProcess, NULL, NULL, NULL, 0, 0);
00262         hr = GetLastError ();
00263 
00264         LPVOID temp = SymFunctionTableAccess (hProcess, AddrBase);
00265         hr = GetLastError ();
00266         return temp;
00267 }
00268 
00269 class EDebug : public ETrapDebug
00270 {
00271 public: 
00272         
00273         EDebug() { _Reason = "Nothing about EDebug"; }
00274 
00275         ~EDebug () { }
00276 
00277         EDebug(EXCEPTION_POINTERS * pexp) : m_pexp(pexp) { nlassert(pexp != 0); createWhat(); }
00278         EDebug(const EDebug& se) : m_pexp(se.m_pexp) { createWhat(); }
00279 
00280         void createWhat ()
00281         {
00282                 string shortExc, longExc, subject;
00283                 string addr, ext;
00284                 sint skipNFirst = 0;
00285                 _Reason = "";
00286 
00287                 if (m_pexp == NULL)
00288                 {
00289                         _Reason = "Unknown exception, don't have context.";
00290                 }
00291                 else
00292                 {
00293                         switch (m_pexp->ExceptionRecord->ExceptionCode)
00294                         {
00295                         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";
00296                                 ext = ", thread attempts to ";
00297                                 ext += m_pexp->ExceptionRecord->ExceptionInformation[0]?"write":"read";
00298                                 if (m_pexp->ExceptionRecord->ExceptionInformation[1])
00299                                         ext += toString(" at 0x%X",m_pexp->ExceptionRecord->ExceptionInformation[1]);
00300                                 else
00301                                         ext += " at <NULL>";
00302                                 break;
00303                         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;
00304                         case EXCEPTION_BREAKPOINT                : shortExc="Breakpoint"; longExc="A breakpoint was encountered"; break;
00305                         case EXCEPTION_SINGLE_STEP               : shortExc="Single Step"; longExc="A trace trap or other single-instruction mechanism signaled that one instruction has been executed"; break;
00306                         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;
00307                         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;
00308                         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;
00309                         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;
00310                         case EXCEPTION_FLT_INVALID_OPERATION     : shortExc="Float Invalid Operation"; longExc="This exception represents any floating-point exception not included in this list"; break;
00311                         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;
00312                         case EXCEPTION_FLT_STACK_CHECK           : shortExc="Float Stack Check"; longExc="The stack overflowed or underflowed as the result of a floating-point operation"; break;
00313                         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;
00314                         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;
00315                         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;
00316                         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;
00317                         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;
00318                         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;
00319                         case EXCEPTION_NONCONTINUABLE_EXCEPTION  : shortExc="Noncontinuable Exception"; longExc="The thread attempted to continue execution after a noncontinuable exception occurred"; break;
00320                         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;
00321                         case EXCEPTION_INVALID_DISPOSITION       : shortExc="Invalid Disposition"; longExc="Whatever number the exception filter returned, it wasn't a value the OS knows about"; break;
00322                         case EXCEPTION_GUARD_PAGE                : shortExc="Guard Page"; longExc="Memory Allocated as PAGE_GUARD by VirtualAlloc() has been accessed"; break;
00323                         case EXCEPTION_INVALID_HANDLE            : shortExc="Invalid Handle"; longExc=""; break;
00324                         case CONTROL_C_EXIT                      : shortExc="Control-C"; longExc="Lets the debugger know the user hit Ctrl-C. Seemingly for console apps only"; break;
00325                         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";
00326                                 ext = ", unable to allocate ";
00327                                 ext += toString ("%d bytes", m_pexp->ExceptionRecord->ExceptionInformation [0]);
00328                                 break;
00329                         case STATUS_WAIT_0                       : shortExc="Wait 0"; longExc=""; break;
00330                         case STATUS_ABANDONED_WAIT_0             : shortExc="Abandoned Wait 0"; longExc=""; break;
00331                         case STATUS_USER_APC                     : shortExc="User APC"; longExc="A user APC was delivered to the current thread before the specified Timeout interval expired"; break;
00332                         case STATUS_TIMEOUT                      : shortExc="Timeout"; longExc=""; break;
00333                         case STATUS_PENDING                      : shortExc="Pending"; longExc=""; break;
00334                         case STATUS_SEGMENT_NOTIFICATION         : shortExc="Segment Notification"; longExc=""; break;
00335                         case STATUS_FLOAT_MULTIPLE_FAULTS        : shortExc="Float Multiple Faults"; longExc=""; break;
00336                         case STATUS_FLOAT_MULTIPLE_TRAPS         : shortExc="Float Multiple Traps"; longExc=""; break;
00337                         case STATUS_ILLEGAL_VLM_REFERENCE        : shortExc="Illegal VLM Reference"; longExc=""; break;
00338                         case 0xE06D7363                          : shortExc="Microsoft C++ Exception"; longExc="Microsoft C++ Exception"; break;        // cpp exception
00339                         case 0xACE0ACE                           : shortExc=""; longExc="";
00340                                 if (m_pexp->ExceptionRecord->NumberParameters == 1)
00341                                         skipNFirst = m_pexp->ExceptionRecord->ExceptionInformation [0];
00342                                 break;  // just want the stack
00343                         default                                  : shortExc="Unknown Exception"; longExc="Unknown Exception "+toString("0x%X", m_pexp->ExceptionRecord->ExceptionCode); break;
00344                         };
00345 
00346                         if(m_pexp->ExceptionRecord != NULL)
00347                         {
00348                                 if (m_pexp->ExceptionRecord->ExceptionAddress)
00349                                         addr = toString(" at 0x%X", m_pexp->ExceptionRecord->ExceptionAddress);
00350                                 else
00351                                         addr = " at <NULL>";
00352                         }
00353 
00354                         string progname;
00355                         if(!shortExc.empty() || !longExc.empty())
00356                         {
00357                                 char name[1024];
00358                                 GetModuleFileName (NULL, name, 1023);
00359                                 progname = CFile::getFilename(name);
00360                                 progname += " ";
00361                         }
00362 
00363                         subject = progname + shortExc + addr;
00364 
00365                         if (_Reason.empty())
00366                         {
00367                                 if (!shortExc.empty()) _Reason += shortExc + " exception generated" + addr + ext + ".\n";
00368                                 if (!longExc.empty()) _Reason += longExc + ".\n";
00369                         }
00370 
00371                         // display the stack
00372                         addStackAndLogToReason (skipNFirst);
00373 
00374                         if(!shortExc.empty() || !longExc.empty())
00375                         {
00376                                 bool i = false;
00377                                 report (progname+shortExc, "", subject, _Reason, true, 1, true, 1, true, i);
00378                         }
00379                 }
00380         }
00381 
00382         // display the callstack
00383         void addStackAndLogToReason (sint skipNFirst = 0)
00384         {
00385                 // ace hack
00386                 skipNFirst = 0;
00387                 
00388                 DWORD symOptions = SymGetOptions();
00389                 symOptions |= SYMOPT_LOAD_LINES;
00390                 symOptions &= ~SYMOPT_UNDNAME;
00391                 SymSetOptions (symOptions);
00392                 
00393                 nlverify (SymInitialize(getProcessHandle(), NULL, FALSE) == TRUE);
00394 
00395                 STACKFRAME callStack;
00396                 ::ZeroMemory (&callStack, sizeof(callStack));
00397                 callStack.AddrPC.Mode      = AddrModeFlat;
00398                 callStack.AddrPC.Offset    = m_pexp->ContextRecord->Eip;
00399                 callStack.AddrStack.Mode   = AddrModeFlat;
00400                 callStack.AddrStack.Offset = m_pexp->ContextRecord->Esp;
00401                 callStack.AddrFrame.Mode   = AddrModeFlat;
00402                 callStack.AddrFrame.Offset = m_pexp->ContextRecord->Ebp;
00403 
00404                 _Reason += "\nCallstack:\n";
00405                 _Reason += "-------------------------------\n";
00406                 for (sint32 i = 0; ; i++)
00407                 {
00408                         SetLastError(0);
00409                         BOOL res = StackWalk (IMAGE_FILE_MACHINE_I386, getProcessHandle(), GetCurrentThread(), &callStack,
00410                                 m_pexp->ContextRecord, NULL, FunctionTableAccess, GetModuleBase, NULL);
00411 
00412                         if (res == FALSE || callStack.AddrFrame.Offset == 0)
00413                                 break;
00414                 
00415                         string symInfo, srcInfo;
00416 
00417                         if (i >= skipNFirst)
00418                         {
00419                                 srcInfo = getSourceInfo (callStack.AddrPC.Offset);
00420                                 symInfo = getFuncInfo (callStack.AddrPC.Offset, callStack.AddrFrame.Offset);
00421                                 _Reason += srcInfo + ": " + symInfo + "\n";
00422                         }
00423                 }
00424                 _Reason += "-------------------------------\n";
00425                 _Reason += "\n";
00426                 if(DefaultMemDisplayer)
00427                 {
00428                         _Reason += "Log with no filter:\n";
00429                         _Reason += "-------------------------------\n";
00430                         DefaultMemDisplayer->write (_Reason);
00431                 }
00432                 else
00433                 {
00434                         _Reason += "No log\n";
00435                 }
00436                 _Reason += "-------------------------------\n";
00437 
00438                 nlverify (SymCleanup(getProcessHandle()) == TRUE);
00439         }
00440 
00441         string getSourceInfo (DWORD addr)
00442         {
00443                 string str;
00444 
00445                 IMAGEHLP_LINE  line;
00446                 ::ZeroMemory (&line, sizeof (line));
00447                 line.SizeOfStruct = sizeof(line);
00448 
00449                 // "Debugging Applications" John Robbins
00450                 // The problem is that the symbol engine finds only those source
00451                 // line addresses (after the first lookup) that fall exactly on
00452                 // a zero displacement. I'll walk backward 100 bytes to
00453                 // find the line and return the proper displacement.
00454                 bool ok = true;
00455                 DWORD displacement = 0 ;
00456                 DWORD resdisp;
00457                 while (!SymGetLineFromAddr (getProcessHandle(), addr - displacement, (DWORD*)&resdisp, &line))
00458                 {        
00459                         if (100 == ++displacement)
00460                         {
00461                                 ok = false;
00462                                 break;
00463                         }
00464                 }
00465 
00466                 // "Debugging Applications" John Robbins
00467                 // I found the line, and the source line information is correct, so
00468                 // change the displacement if I had to search backward to find the source line.
00469                 if (displacement)    
00470                         resdisp = displacement;    
00471 
00472                 if (ok)
00473                 {
00474                         str = line.FileName;
00475                         str += "(" + toString (line.LineNumber) + ")";
00476                         str += toString(": 0x%X", addr);
00477                 }
00478                 else
00479                 {
00480                         HRESULT hr = GetLastError();
00481                         IMAGEHLP_MODULE module;
00482                         ::ZeroMemory (&module, sizeof(module));
00483                         module.SizeOfStruct = sizeof(module);
00484 
00485                         if (SymGetModuleInfo (getProcessHandle(), addr, &module))
00486                         {
00487                                 str = module.ModuleName;
00488                         }
00489                         else
00490                         {
00491                                 HRESULT hr = GetLastError ();
00492                                 str = "<NoModule>";
00493                         }
00494                         str += toString("!0x%X", addr);
00495                 }
00496 
00497                 /*
00498                 DWORD disp;
00499                 if (SymGetLineFromAddr (getProcessHandle(), addr, &disp, &line))
00500                 {
00501                         str = line.FileName;
00502                         str += "(" + toString (line.LineNumber) + ")";
00503                 }
00504                 else
00505                 {
00506                         HRESULT hr = GetLastError();
00507                         IMAGEHLP_MODULE module;
00508                         ::ZeroMemory (&module, sizeof(module));
00509                         module.SizeOfStruct = sizeof(module);
00510 
00511                         if (SymGetModuleInfo (getProcessHandle(), addr, &module))
00512                         {
00513                                 str = module.ModuleName;
00514                         }
00515                         else
00516                         {
00517                                 HRESULT hr = GetLastError ();
00518                                 str = "<NoModule>";
00519                         }
00520                         char tmp[32];
00521                         sprintf (tmp, "!0x%X", addr);
00522                         str += tmp;
00523                 }
00524                 str +=" DEBUG:"+toString("0x%08X", addr);*/
00525                 
00526                 return str;
00527         }
00528 
00529         HANDLE getProcessHandle()
00530         {
00531                 return CSystemInfo::isNT()?GetCurrentProcess():(HANDLE)GetCurrentProcessId();
00532         }
00533 
00534         // return true if found
00535         bool findAndErase(string &str, const char *token, const char *replace = NULL)
00536         {
00537                 int pos;
00538                 if ((pos = str.find(token)) != string::npos)
00539                 {
00540                         str.erase (pos,strlen(token));
00541                         if (replace != NULL)
00542                                 str.insert (pos, replace);
00543                         return true;
00544                 }
00545                 else
00546                         return false;
00547         }
00548 
00549         // remove space and const stuffs
00550         // rawType contains the type without anything (to compare with known type)
00551         // displayType contains the type without std:: and stl ugly things
00552         void cleanType(string &rawType, string &displayType)
00553         {
00554                 while (findAndErase(rawType, "std::")) ;
00555                 while (findAndErase(displayType, "std::")) ;
00556 
00557                 while (findAndErase(rawType, "const")) ;
00558 
00559                 while (findAndErase(rawType, " ")) ;
00560 
00561                 while (findAndErase(rawType, "&")) ;
00562 
00563                 // rename ugly stl type
00564 
00565                 while (findAndErase(rawType, "classbasic_string<char,classchar_traits<char>,classallocator<char>>", "string")) ;
00566                 while (findAndErase(displayType, "class basic_string<char,class char_traits<char>,class allocator<char> >", "string")) ;
00567                 while (findAndErase(rawType, "classvector<char,class char_traits<char>,class allocator<char> >", "string")) ;
00568         }
00569 
00570         string getFuncInfo (DWORD funcAddr, DWORD stackAddr)
00571         {
00572                 string str ("NoSymbol");
00573 
00574                 DWORD symSize = 10000;
00575                 PIMAGEHLP_SYMBOL  sym = (PIMAGEHLP_SYMBOL) GlobalAlloc (GMEM_FIXED, symSize);
00576                 ::ZeroMemory (sym, symSize);
00577                 sym->SizeOfStruct = symSize;
00578                 sym->MaxNameLength = symSize - sizeof(IMAGEHLP_SYMBOL);
00579 
00580                 DWORD disp = 0;
00581                 if (SymGetSymFromAddr (getProcessHandle(), funcAddr, &disp, sym) == FALSE)
00582                 {
00583                         return str;
00584                 }
00585 
00586                 CHAR undecSymbol[1024];
00587                 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)
00588                 {
00589                         str = undecSymbol;
00590                 }
00591                 else if (SymUnDName (sym, undecSymbol, 1024) == TRUE)
00592                 {
00593                         str = undecSymbol;
00594                 }
00595 
00596                 // replace param with the value of the stack for this param
00597 
00598                 string parse = str;
00599                 str = "";
00600                 uint pos = 0;
00601                 sint stop = 0;
00602 
00603                 string type;
00604 
00605                 uint i = parse.find ("(");
00606 
00607                 // copy the function name
00608                 str = parse.substr(0, i);
00609 
00610 //              nlinfo ("not parsed '%s'", parse.c_str());
00611 
00613 
00614 /*              // if there s parameter, parse them
00615                 if(i!=string::npos)
00616                 {
00617                         // copy the '('
00618                         str += parse[i];
00619                         for (i++; i < parse.size (); i++)
00620                         {
00621                                 if (parse[i] == '<')
00622                                          stop++;
00623                                 if (parse[i] == '>')
00624                                          stop--;
00625 
00626                                 if (stop==0 && (parse[i] == ',' || parse[i] == ')'))
00627                                 {
00628                                         ULONG *addr = (ULONG*)(stackAddr) + 2 + pos++;
00629 
00630                                         string displayType = type;
00631                                         cleanType (type, displayType);
00632 
00633                                         char tmp[1024];
00634                                         if(type == "void")
00635                                         {
00636                                                 tmp[0]='\0';
00637                                         }
00638                                         else if(type == "int")
00639                                         {
00640                                                 sprintf (tmp, "%d", *addr);
00641                                         }
00642                                         else if (type == "char")
00643                                         {
00644                                                 if (isprint(*addr))
00645                                                 {
00646                                                         sprintf (tmp, "'%c'", *addr);
00647                                                 }
00648                                                 else
00649                                                 {
00650                                                         sprintf (tmp, "%d", *addr);
00651                                                 }
00652                                         }
00653                                         else if (type == "char*" && *addr != NULL)
00654                                         {
00655                                                 uint pos = 0;
00656                                                 tmp[pos++] = '\"';
00657                                                 for (uint i = 0; i < strlen((char*)*addr); i++)
00658                                                 {
00659                                                         if (pos == 1020)
00660                                                                 break;
00661                                                         char c = ((char *)*addr)[i];
00662                                                         if (c == '\n')
00663                                                         {
00664                                                                 tmp[pos++] = '\\';
00665                                                                 tmp[pos++] = 'n';
00666                                                         }
00667                                                         else if (c == '\r')
00668                                                         {
00669                                                                 tmp[pos++] = '\\';
00670                                                                 tmp[pos++] = 'r';
00671                                                         }
00672                                                         else if (c == '\t')
00673                                                         {
00674                                                                 tmp[pos++] = '\\';
00675                                                                 tmp[pos++] = 't';
00676                                                         }
00677                                                         else
00678                                                                 tmp[pos++] = c;
00679                                                 }
00680                                                 tmp[pos++] = '\"';
00681                                                 tmp[pos++] = '\0';
00682                                         }
00683                                         else if (type == "string" && *addr != NULL)
00684                                         {
00685                                                 sprintf (tmp, "\"%s\"", ((string*)addr)->c_str());
00686                                         }
00687                                         else
00688                                         {
00689                                                 if(*addr == NULL)
00690                                                         sprintf (tmp, "<NULL>");
00691                                                 else
00692                                                         sprintf (tmp, "0x%X", *addr);
00693                                         }
00694 
00695                                         str += displayType;
00696                                         if(tmp[0]!='\0')
00697                                         {
00698                                                 str += "=";
00699                                                 str += tmp;
00700                                         }
00701                                         str += parse[i];
00702                                         type = "";
00703                                 }
00704                                 else
00705                                 {
00706                                         type += parse[i];
00707                                 }
00708                         }
00709                         GlobalFree (sym);
00710                         if (disp != 0)
00711                         {
00712                                 str += " + ";
00713                                 str += toString (disp);
00714                                 str += " bytes";
00715                         }
00716                 }
00717 */
00718 //              nlinfo ("after parsing '%s'", str.c_str());
00719 
00720                 return str;
00721         }
00722 
00723 private:
00724         EXCEPTION_POINTERS * m_pexp;
00725 };
00726 
00727 // workaround of VCPP synchronous exception and se translator
00728 bool global_force_exception_flag = false;
00729 #define WORKAROUND_VCPP_SYNCHRONOUS_EXCEPTION  if (global_force_exception_flag) force_exception_frame();
00730 void force_exception_frame(...) {std::cout.flush();}
00731 
00732 static void exceptionTranslator(unsigned, EXCEPTION_POINTERS *pexp)
00733 {
00734         // ace desactive because we can't debug if activated
00735         //if (!TrapCrashInDebugger && IsDebuggerPresent ())
00736         {
00737                 if (pexp->ExceptionRecord->ExceptionCode == 0xACE0ACE)
00738                         throw EDebug (pexp);
00739                 else
00740                         return;
00741         }
00742         /*else
00743         {
00744                 if (pexp->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT)
00745                         return;
00746                 else
00747                         throw EDebug (pexp);
00748         }*/
00749 }
00750 
00751 #endif // NL_OS_WINDOWS
00752 
00753 void getCallStackAndLog (string &result, sint skipNFirst)
00754 {
00755 #ifdef NL_OS_WINDOWS
00756         try
00757         {
00758                 WORKAROUND_VCPP_SYNCHRONOUS_EXCEPTION // force to install a exception frame             
00759                         
00760                 DWORD array[1];
00761                 array[0] = skipNFirst;
00762                 RaiseException (0xACE0ACE, 0, 1, array);
00763         }
00764         catch (EDebug &e)
00765         {
00766                 result += e.what();
00767         }
00768 #else
00769         result += "No callstack available";
00770 #endif
00771 }
00772 
00773 
00774 void createDebug (const char *logPath, bool logInFile)
00775 {
00776         NL_ALLOC_CONTEXT (_Debug)
00777         
00778         static bool alreadyCreate = false;
00779         if (!alreadyCreate)
00780         {
00781                 // Debug Info for mutexes
00782 #ifdef MUTEX_DEBUG
00783                 initAcquireTimeMap();
00784 #endif
00785 
00786 #ifdef NL_OS_WINDOWS
00787                 //if (!IsDebuggerPresent ())
00788                 {
00789                         _set_se_translator(exceptionTranslator);
00790                 }
00791 #endif // NL_OS_WINDOWS
00792 
00793 
00794                 ErrorLog = new CLog (CLog::LOG_ERROR);
00795                 WarningLog = new CLog (CLog::LOG_WARNING);
00796                 InfoLog = new CLog (CLog::LOG_INFO);
00797                 DebugLog = new CLog (CLog::LOG_DEBUG);
00798                 AssertLog = new CLog (CLog::LOG_ASSERT);
00799 
00800                 sd = new CStdDisplayer ("DEFAULT_SD");
00801                 if (TrapCrashInDebugger || !IsDebuggerPresent ())
00802                 {
00803                         DefaultMsgBoxDisplayer = new CMsgBoxDisplayer ("DEFAULT_MBD");
00804                 }
00805 #if LOG_IN_FILE
00806                 if (logInFile)
00807                 {
00808                         string fn;
00809                         if (logPath != NULL)
00810                         {
00811                                 fn += logPath;
00812                         }
00813                         else
00814                         {
00815                                 // log.log must always be accessed in creation current path directory
00816                                 char    tmpPath[1024];
00817                                 fn += getcwd(tmpPath, 1024);
00818                                 fn += "/";
00819                         }
00820                         fn += "log.log";
00821                         fd = new CFileDisplayer (fn, false, "DEFAULT_FD");
00822                 }
00823 #endif // LOG_IN_FILE
00824                 DefaultMemDisplayer = new CMemDisplayer ("DEFAULT_MD");
00825                 
00826                 initDebug2(logInFile);
00827 
00828                 alreadyCreate = true;
00829         }
00830 }
00831 
00832 
00833 
00834 
00835 //
00836 // Commands
00837 //
00838 
00839 NLMISC_COMMAND (displayMemlog, "displays the last N line of the log in memory", "[<NbLines>]")
00840 {
00841         uint nbLines;
00842 
00843         if (args.size() == 0) nbLines = 100;
00844         else if (args.size() == 1) nbLines = atoi(args[0].c_str());
00845         else return false;
00846 
00847         if (DefaultMemDisplayer == NULL) return false;
00848 
00849         deque<string>::const_iterator it;
00850         
00851         const deque<string> &str = DefaultMemDisplayer->lockStrings ();
00852 
00853         if (nbLines >= str.size())
00854                 it = str.begin();
00855         else
00856                 it = str.end() - nbLines;
00857 
00858         DefaultMemDisplayer->write (&log);
00859 
00860         DefaultMemDisplayer->unlockStrings ();
00861 
00862         return true;
00863 }
00864 
00865 
00866 NLMISC_COMMAND(resetFilters, "disable all filters on Nel loggers", "[debug|info|warning|error|assert]")
00867 {
00868         if(args.size() == 0)
00869         {
00870                 DebugLog->resetFilters();
00871                 InfoLog->resetFilters();
00872                 WarningLog->resetFilters();
00873                 ErrorLog->resetFilters();
00874                 AssertLog->resetFilters();
00875         }
00876         else if (args.size() == 1)
00877         {
00878                 if (args[0] == "debug") DebugLog->resetFilters();
00879                 else if (args[0] == "info") InfoLog->resetFilters();
00880                 else if (args[0] == "warning") WarningLog->resetFilters();
00881                 else if (args[0] == "error") ErrorLog->resetFilters();
00882                 else if (args[0] == "assert") AssertLog->resetFilters();
00883         }
00884         else
00885         {
00886                 return false;
00887         }
00888 
00889         return true;
00890 }
00891 
00892 NLMISC_COMMAND(addPositiveFilterDebug, "add a positive filter on DebugLog", "<filterstr>")
00893 {
00894         if(args.size() != 1) return false;
00895         DebugLog->addPositiveFilter( args[0].c_str() );
00896         return true;
00897 }
00898 
00899 NLMISC_COMMAND(addNegativeFilterDebug, "add a negative filter on DebugLog", "<filterstr>")
00900 {
00901         if(args.size() != 1) return false;
00902         DebugLog->addNegativeFilter( args[0].c_str() );
00903         return true;
00904 }
00905 
00906 NLMISC_COMMAND(removeFilterDebug, "remove a filter on DebugLog", "[<filterstr>]")
00907 {
00908         if(args.size() == 0)
00909                 DebugLog->removeFilter();
00910         else if(args.size() == 1)
00911                 DebugLog->removeFilter( args[0].c_str() );
00912         else return false;
00913         return true;
00914 }
00915 
00916 NLMISC_COMMAND(displayFilterDebug, "display filter on DebugLog", "")
00917 {
00918         if(args.size() != 0) return false;
00919         DebugLog->displayFilter(log);
00920         return true;
00921 }
00922 
00923 NLMISC_COMMAND(addPositiveFilterInfo, "add a positive filter on InfoLog", "<filterstr>")
00924 {
00925         if(args.size() != 1) return false;
00926         InfoLog->addPositiveFilter( args[0].c_str() );
00927         return true;
00928 }
00929 
00930 NLMISC_COMMAND(addNegativeFilterInfo, "add a negative filter on InfoLog", "<filterstr>")
00931 {
00932         if(args.size() != 1) return false;
00933         InfoLog->addNegativeFilter( args[0].c_str() );
00934         return true;
00935 }
00936 
00937 NLMISC_COMMAND(removeFilterInfo, "remove a filter on InfoLog", "[<filterstr>]")
00938 {
00939         if(args.size() == 0)
00940                 InfoLog->removeFilter();
00941         else if(args.size() == 1)
00942                 InfoLog->removeFilter( args[0].c_str() );
00943         else return false;
00944         return true;
00945 }
00946 
00947 NLMISC_COMMAND(displayFilterInfo, "display filter on InfoLog", "[d|i|w|e]")
00948 {
00949         if(args.size() > 1) return false;
00950         if ( args.size() == 1 )
00951         {
00952                 if ( strcmp( args[0].c_str(), "d" ) == 0 )
00953                         InfoLog->displayFilter(*DebugLog);
00954                 else if ( strcmp( args[0].c_str(), "i" ) == 0 )
00955                         InfoLog->displayFilter(*InfoLog);
00956                 else if ( strcmp( args[0].c_str(), "w" ) == 0 )
00957                         InfoLog->displayFilter(*WarningLog);
00958                 else if ( strcmp( args[0].c_str(), "e" ) == 0 )
00959                         InfoLog->displayFilter(*ErrorLog);
00960                 else
00961                         return false;
00962         }
00963         else
00964         {
00965                 InfoLog->displayFilter(log);
00966         }
00967         return true;
00968 }
00969 
00970 NLMISC_COMMAND(addPositiveFilterWarning, "add a positive filter on WarningLog", "<filterstr>")
00971 {
00972         if(args.size() != 1) return false;
00973         WarningLog->addPositiveFilter( args[0].c_str() );
00974         return true;
00975 }
00976 
00977 NLMISC_COMMAND(addNegativeFilterWarning, "add a negative filter on WarningLog", "<filterstr>")
00978 {
00979         if(args.size() != 1) return false;
00980         WarningLog->addNegativeFilter( args[0].c_str() );
00981         return true;
00982 }
00983 
00984 NLMISC_COMMAND(removeFilterWarning, "remove a filter on WarningLog", "[<filterstr>]")
00985 {
00986         if(args.size() == 0)
00987                 WarningLog->removeFilter();
00988         else if(args.size() == 1)
00989                 WarningLog->removeFilter( args[0].c_str() );
00990         else return false;
00991         return true;
00992 }
00993 
00994 NLMISC_COMMAND(displayFilterWarning, "display filter on WarningLog", "")
00995 {
00996         if(args.size() != 0) return false;
00997         WarningLog->displayFilter(log);
00998         return true;
00999 }
01000 
01001 NLMISC_COMMAND(assert, "generate a failed assert", "")
01002 {
01003         if(args.size() != 0) return false;
01004         nlassertex (false, ("Assert generated by the assert command"));
01005         return true;
01006 }
01007 
01008 NLMISC_COMMAND(stop, "generate a stop", "")
01009 {
01010         if(args.size() != 0) return false;
01011         nlstopex (("Stop generated by the stop command"));
01012         return true;
01013 }
01014 
01015 } // NLMISC