VC环境下检查内存泄漏memory leak的方法

通过改写delete new方法,我们可以记录内存分配的地址,数量。因此,也就可以知道哪此内存在程序结束后没有释放。

将如下代码改存放为trace.cpp ,将trace.cpp放入要检测的工程。运行Debug调试程序,在Debug的输出里会有提示信息。

/**********************************************************************
Trace alloc
-----------
Purpose:

  Implement a allocation check routine that reports the whole
  callstack for each leaked allocation.

  Based _disibledevent="mailto:[email protected]">[email protected].

Author:

  Erik Rydgren, [email protected].
  
Usage:
  1/ Define DETECT_LEAKS in the project settings under
     C++/preprocessor.

     If you want checking of overwrites then define DETECT_OVERWRITES
     in the project settings. Change the frequency of the checks by
     altering the NML_CHECK_EVERY define in tracealloc.cpp.
  
  2/ Compile.
  
     If you get multiple defined symbols (overloaded new and delete)
     add linker switch /FORCE:MULTIPLE _disibledevent="file://\\system32">file://System32/" );
 }

   // Add user defined path
 if ( lpszIniPath != NULL )
  if ( lpszIniPath[0] != '\0' )
  {
     strcat( lpszSymbolPath, ";" );
   strcat( lpszSymbolPath, lpszIniPath );
  }
}

// Uninitialize the loaded symbol files
BOOL UninitSymInfo()
{
 return SymCleanup( GetCurrentProcess() );
}

// Initializes the symbol files
BOOL InitSymInfo( PCSTR lpszInitialSymbolPath )
{
 CHAR     lpszSymbolPath[BUFFERSIZE];
   DWORD    symOptions = SymGetOptions();

 symOptions |= SYMOPT_LOAD_LINES; 
 symOptions &= ~SYMOPT_UNDNAME;
 SymSetOptions( symOptions );

   // Get the search path for the symbol files
 InitSymbolPath( lpszSymbolPath, lpszInitialSymbolPath );

 return SymInitialize( GetCurrentProcess(), lpszSymbolPath, TRUE);
}

// Get the module name from a given address
BOOL GetModuleNameFromAddress( UINT address, LPTSTR lpszModule )
{
 BOOL              ret = FALSE;
 IMAGEHLP_MODULE   moduleInfo;

 ::ZeroMemory( &moduleInfo, sizeof(moduleInfo) );
 moduleInfo.SizeOfStruct = sizeof(moduleInfo);

 if ( SymGetModuleInfo( GetCurrentProcess(), (DWORD)address, &moduleInfo ) )
 {
    // Got it!
  PCSTR2LPTSTR( moduleInfo.ModuleName, lpszModule );
  ret = TRUE;
 }
 else
    // Not found :(
  _tcscpy( lpszModule, _T("?") );
 
 return ret;
}

// Get function prototype and parameter info from ip address and stack address
BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, LPTSTR lpszSymbol )
{
 BOOL              ret = FALSE;
 DWORD             dwDisp = 0;
 DWORD             dwSymSize = 10000;
   TCHAR             lpszUnDSymbol[BUFFERSIZE]=_T("?");
 CHAR              lpszNonUnicodeUnDSymbol[BUFFERSIZE]="?";
 LPTSTR            lpszParamSep = NULL;
 LPCTSTR           lpszParsed = lpszUnDSymbol;
 PIMAGEHLP_SYMBOL  pSym = (PIMAGEHLP_SYMBOL)GlobalAlloc( GMEM_FIXED, dwSymSize );

 ::ZeroMemory( pSym, dwSymSize );
 pSym->SizeOfStruct = dwSymSize;
 pSym->MaxNameLength = dwSymSize - sizeof(IMAGEHLP_SYMBOL);

   // Set the default to unknown
 _tcscpy( lpszSymbol, _T("?") );

 // Get symbol info for IP
 if ( SymGetSymFromAddr( GetCurrentProcess(), (ULONG)fnAddress, &dwDisp, pSym ) )
 {
    // Make the symbol readable for humans
  UnDecorateSymbolName( pSym->Name, lpszNonUnicodeUnDSymbol, BUFFERSIZE, 
   UNDNAME_COMPLETE | 
   UNDNAME_NO_THISTYPE |
   UNDNAME_NO_SPECIAL_SYMS |
   UNDNAME_NO_MEMBER_TYPE |
   UNDNAME_NO_MS_KEYWORDS |
   UNDNAME_NO_ACCESS_SPECIFIERS );

      // Symbol information is ANSI string
  PCSTR2LPTSTR( lpszNonUnicodeUnDSymbol, lpszUnDSymbol );

      // I am just smarter than the symbol file :)
  if ( _tcscmp(lpszUnDSymbol, _T("_WinMain@16")) == 0 )
   _tcscpy(lpszUnDSymbol, _T("WinMain(HINSTANCE,HINSTANCE,LPCTSTR,int)"));
  else
  if ( _tcscmp(lpszUnDSymbol, _T("_main")) == 0 )
   _tcscpy(lpszUnDSymbol, _T("main(int,TCHAR * *)"));
  else
  if ( _tcscmp(lpszUnDSymbol, _T("_mainCRTStartup")) == 0 )
   _tcscpy(lpszUnDSymbol, _T("mainCRTStartup()"));
  else
  if ( _tcscmp(lpszUnDSymbol, _T("_wmain")) == 0 )
   _tcscpy(lpszUnDSymbol, _T("wmain(int,TCHAR * *,TCHAR * *)"));
  else
  if ( _tcscmp(lpszUnDSymbol, _T("_wmainCRTStartup")) == 0 )
   _tcscpy(lpszUnDSymbol, _T("wmainCRTStartup()"));

  lpszSymbol[0] = _T('\0');

      // Let's go through the stack, and modify the function prototype, and insert the actual
      // parameter values from the stack
  if ( _tcsstr( lpszUnDSymbol, _T("(void)") ) == NULL && _tcsstr( lpszUnDSymbol, _T("()") ) == NULL)
  {
   ULONG index = 0;
   for( ; ; index++ )
   {
    lpszParamSep = _tcschr( lpszParsed, _T(',') );
    if ( lpszParamSep == NULL )
     break;

    *lpszParamSep = _T('\0');

    _tcscat( lpszSymbol, lpszParsed );
    _stprintf( lpszSymbol + _tcslen(lpszSymbol), _T("=0x%08X,"), *((ULONG*)(stackAddress) + 2 + index) );

    lpszParsed = lpszParamSep + 1;
   }

   lpszParamSep = _tcschr( lpszParsed, _T(')') );
   if ( lpszParamSep != NULL )
   {
    *lpszParamSep = _T('\0');

    _tcscat( lpszSymbol, lpszParsed );
    _stprintf( lpszSymbol + _tcslen(lpszSymbol), _T("=0x%08X)"), *((ULONG*)(stackAddress) + 2 + index) );

    lpszParsed = lpszParamSep + 1;
   }
  }

  _tcscat( lpszSymbol, lpszParsed );
   
  ret = TRUE;
 }

 GlobalFree( pSym );

 return ret;
}

// Get source file name and line number from IP address
// The output format is: "sourcefile(linenumber)" or
//                       "modulename!address" or
//                       "address"
BOOL GetSourceInfoFromAddress( UINT address, LPTSTR lpszSourceInfo )
{
 BOOL           ret = FALSE;
 IMAGEHLP_LINE  lineInfo;
 DWORD          dwDisp;
 TCHAR          lpszFileName[BUFFERSIZE] = _T("");
 TCHAR          lpModuleInfo[BUFFERSIZE] = _T("");

 _tcscpy( lpszSourceInfo, _T("?(?)") );

 ::ZeroMemory( &lineInfo, sizeof( lineInfo ) );
 lineInfo.SizeOfStruct = sizeof( lineInfo );

 if ( SymGetLineFromAddr( GetCurrentProcess(), address, &dwDisp, &lineInfo ) )
 {
    // Got it. Let's use "sourcefile(linenumber)" format
  PCSTR2LPTSTR( lineInfo.FileName, lpszFileName );
  _stprintf( lpszSourceInfo, _T("%s(%d)"), lpszFileName, lineInfo.LineNumber );
  ret = TRUE;
 }
 else
 {
      // There is no source file information. :(
      // Let's use the "modulename!address" format
    GetModuleNameFromAddress( address, lpModuleInfo );

  if ( lpModuleInfo[0] == _T('?') || lpModuleInfo[0] == _T('\0'))
     // There is no modulename information. :((
         // Let's use the "address" format
   _stprintf( lpszSourceInfo, _T("0x%08X"), lpModuleInfo, address );
  else
   _stprintf( lpszSourceInfo, _T("%s!0x%08X"), lpModuleInfo, address );

  ret = FALSE;
 }
 
 return ret;
}

void GetStackTrace(HANDLE hThread, ULONG ranOffsets[][2], ULONG nMaxStack )
{
 STACKFRAME     callStack;
 BOOL           bResult;
 CONTEXT        context;
 TCHAR          symInfo[BUFFERSIZE] = _T("?");
 TCHAR          srcInfo[BUFFERSIZE] = _T("?");
 HANDLE         hProcess = GetCurrentProcess();

   // If it's not this thread, let's suspend it, and resume it at the end
 if ( hThread != GetCurrentThread() )
  if ( SuspendThread( hThread ) == -1 )
  {
     // whaaat ?!
     OutputDebugStringFormat( _T("Call stack info(thread=0x%X) failed.\n") );
   return;
  }

 ::ZeroMemory( &context, sizeof(context) );
 context.ContextFlags = CONTEXT_FULL;

 if ( !GetThreadContext( hThread, &context ) )
 {
      OutputDebugStringFormat( _T("Call stack info(thread=0x%X) failed.\n") );
    return;
 }
 
 ::ZeroMemory( &callStack, sizeof(callStack) );
 callStack.AddrPC.Offset    = context.Eip;
 callStack.AddrStack.Offset = context.Esp;
 callStack.AddrFrame.Offset = context.Ebp;
 callStack.AddrPC.Mode      = AddrModeFlat;
 callStack.AddrStack.Mode   = AddrModeFlat;
 callStack.AddrFrame.Mode   = AddrModeFlat;

 for( ULONG index = 0; ; index++ ) 
 {
  bResult = StackWalk(
   IMAGE_FILE_MACHINE_I386,
   hProcess,
   hThread,
       &callStack,
   NULL, 
   NULL,
   SymFunctionTableAccess,
   SymGetModuleBase,
   NULL);

    // Ignore the first two levels (it's _disibledevent="http://blog.csdn.net/kastolo/archive/2006/07/06/885307.aspx">http://blog.csdn.net/kastolo/archive/2006/07/06/885307.aspx

你可能感兴趣的:(C/C++)