289 lines
9.5 KiB
C++
289 lines
9.5 KiB
C++
/***************************************************************************
|
|
* (c) Jürgen Riegel (juergen.riegel@web.de) 2006 *
|
|
* *
|
|
* This file is part of the FreeCAD CAx development system. *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU Library General Public License (LGPL) *
|
|
* as published by the Free Software Foundation; either version 2 of *
|
|
* the License, or (at your option) any later version. *
|
|
* for detail see the LICENCE text file. *
|
|
* *
|
|
* FreeCAD is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU Library General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU Library General Public *
|
|
* License along with FreeCAD; if not, write to the Free Software *
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
|
* USA *
|
|
* *
|
|
* Juergen Riegel 2006 *
|
|
***************************************************************************/
|
|
|
|
/** \defgroup MemDebug Memory debugging
|
|
* \ingroup BASE
|
|
* \section Overview
|
|
* In C++ applications there are a lot of ways to handle memory allocation and deallocation.
|
|
* As many ways to do it wrong or simply forget to free memory. One way to overcome
|
|
* this problem is e.g. usage of handle classes (like OpenCASCADE it does) or use a lot of factories.
|
|
* But all of them has drawbacks or performance penalties. One good way to get memory
|
|
* problems hunted down is the MSCRT Heap debugging facility. This set of functions
|
|
* opens the possibility to track and locate all kind of memory problems, e.g.
|
|
* memory leaks.
|
|
*
|
|
* \section Implementation
|
|
* The FreeCAD memory debugging is located in the Base::MemDebug class.
|
|
*/
|
|
|
|
#include "PreCompiled.h"
|
|
|
|
#ifndef _PreComp_
|
|
# ifdef _MSC_VER
|
|
# include <cstdio>
|
|
# include <time.h>
|
|
# include <windows.h>
|
|
# include <crtdbg.h>
|
|
# endif
|
|
#endif
|
|
|
|
|
|
/// Here the FreeCAD includes sorted by Base,App,Gui......
|
|
#include "MemDebug.h"
|
|
|
|
using namespace Base;
|
|
|
|
#if defined(_MSC_VER)
|
|
|
|
|
|
/** Memory debugging class
|
|
* This class is an interface to the Windows CRT debugging
|
|
* facility. If the define MemDebugOn in the src/FCConfig.h is
|
|
* set the class get intatiated
|
|
* globally and tracks all memory allocations on the heap. The
|
|
* result get written in the MemLog.txt in the active directory.
|
|
* \par
|
|
* NOTE: you must not instaciate this class!
|
|
*
|
|
*
|
|
* \author Juergen Riegel
|
|
*/
|
|
class MemDebug
|
|
{
|
|
public:
|
|
/// Construction
|
|
MemDebug();
|
|
/// Destruction
|
|
virtual ~MemDebug();
|
|
|
|
protected:
|
|
static FILE *logFile;
|
|
|
|
/** @name static callbacks for the Crt */
|
|
//@{
|
|
static void __cdecl sDumpClientHook(void * pUserData, size_t nBytes);
|
|
static int __cdecl sAllocHook(int nAllocType, void* pvData, size_t nSize,int nBlockUse,long lRequest,const unsigned char * szFileName,int nLine);
|
|
static int sReportHook(int nRptType,char *szMsg,int *retVal);
|
|
//@}
|
|
};
|
|
|
|
// the one and only MemDebug instance.
|
|
#ifdef MemDebugOn
|
|
MemDebug cSingelton;
|
|
#endif
|
|
|
|
|
|
#define SET_CRT_DEBUG_FIELD(a) _CrtSetDbgFlag((a) | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG))
|
|
#define CLEAR_CRT_DEBUG_FIELD(a) _CrtSetDbgFlag(~(a) & _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG))
|
|
|
|
FILE *MemDebug::logFile = NULL;
|
|
|
|
//**************************************************************************
|
|
// Construction/Destruction
|
|
|
|
|
|
MemDebug::MemDebug()
|
|
{
|
|
//_CrtMemState checkPt1;
|
|
char timeStr[15], dateStr[15]; // Used to set up log file
|
|
|
|
|
|
// Send all reports to STDOUT, since this example is a console app
|
|
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE );
|
|
_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR );
|
|
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE );
|
|
_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR );
|
|
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE );
|
|
_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR );
|
|
|
|
// Set the debug heap to report memory leaks when the process terminates,
|
|
// and to keep freed blocks in the linked list.
|
|
SET_CRT_DEBUG_FIELD( _CRTDBG_LEAK_CHECK_DF | _CRTDBG_DELAY_FREE_MEM_DF );
|
|
|
|
// Open a log file for the hook functions to use
|
|
if ( logFile != NULL )
|
|
throw "Base::MemDebug::MemDebug():38: Dont call the constructor by your self!";
|
|
#if (_MSC_VER >= 1400)
|
|
fopen_s( &logFile, "MemLog.txt", "w" );
|
|
if ( logFile == NULL )
|
|
throw "Base::MemDebug::MemDebug():41: File IO Error. Canot open log file...";
|
|
_strtime_s( timeStr, 15 );
|
|
_strdate_s( dateStr, 15 );
|
|
#elif (_MSC_VER >= 1200)
|
|
logFile = fopen( "MemLog.txt", "w" );
|
|
if ( logFile == NULL )
|
|
throw "Base::MemDebug::MemDebug():41: File IO Error. Canot open log file...";
|
|
_strtime( timeStr );
|
|
_strdate( dateStr );
|
|
#endif
|
|
fprintf( logFile,
|
|
"Memory Allocation Log File for FreeCAD, run at %s on %s.\n",
|
|
timeStr, dateStr );
|
|
fputs( "-------------------------------------------------------------------\n", logFile );
|
|
|
|
// Install the hook functions
|
|
_CrtSetDumpClient( sDumpClientHook );
|
|
_CrtSetAllocHook( sAllocHook );
|
|
_CrtSetReportHook( sReportHook );
|
|
|
|
}
|
|
|
|
MemDebug::~MemDebug()
|
|
{
|
|
_CrtMemDumpAllObjectsSince( NULL );
|
|
//_CrtCheckMemory( );
|
|
|
|
// This fflush needs to be removed...
|
|
fflush( logFile );
|
|
fclose( logFile );
|
|
}
|
|
|
|
|
|
//**************************************************************************
|
|
// separator for other implemetation aspects
|
|
|
|
|
|
/* REPORT HOOK FUNCTION
|
|
--------------------
|
|
Again, report hook functions can serve a very wide variety of purposes.
|
|
This one logs error and assertion failure debug reports in the
|
|
log file, along with 'Damage' reports about overwritten memory.
|
|
|
|
By setting the retVal parameter to zero, we are instructing _CrtDbgReport
|
|
to return zero, which causes execution to continue. If we want the function
|
|
to start the debugger, we should have _CrtDbgReport return one.
|
|
*/
|
|
int MemDebug::sReportHook(int nRptType,char *szMsg,int *retVal)
|
|
{
|
|
char *RptTypes[] = { "Warning", "Error", "Assert" };
|
|
|
|
if ( ( nRptType > 0 ) || ( strstr( szMsg, "HEAP CORRUPTION DETECTED" ) ) )
|
|
fprintf( logFile, "%s: %s", RptTypes[nRptType], szMsg );
|
|
|
|
retVal = 0;
|
|
|
|
return( 7 ); // Allow the report to be made as usual (True = 7, False = 0)
|
|
}
|
|
|
|
/* ALLOCATION HOOK FUNCTION
|
|
-------------------------
|
|
An allocation hook function can have many, many different
|
|
uses. This one simply logs each allocation operation in a file.
|
|
*/
|
|
int __cdecl MemDebug::sAllocHook(
|
|
int nAllocType,
|
|
void * pvData,
|
|
size_t nSize,
|
|
int nBlockUse,
|
|
long lRequest,
|
|
const unsigned char * szFileName,
|
|
int nLine
|
|
)
|
|
{
|
|
char *operation[] = { " :", "Alloc :", "Realloc:", "Free :" };
|
|
char *blockType[] = { "Free", "Normal", "CRT", "Ignore", "Client" };
|
|
|
|
if ( nBlockUse == _CRT_BLOCK ) // Ignore internal C runtime library allocations
|
|
return( 7 ); // (True = 7, False = 0)
|
|
|
|
_ASSERT( ( nAllocType > 0 ) && ( nAllocType < 4 ) );
|
|
_ASSERT( ( nBlockUse >= 0 ) && ( nBlockUse < 5 ) );
|
|
|
|
if( nBlockUse !=4 )
|
|
return(7);
|
|
|
|
fprintf( logFile,
|
|
"%s (#%7d) %12ld byte (%s) in %s line %d",
|
|
operation[nAllocType],lRequest, nSize, blockType[nBlockUse],szFileName, nLine);
|
|
if ( pvData != NULL )
|
|
fprintf( logFile, " at %p\n", pvData );
|
|
else
|
|
fprintf( logFile, "\n" );
|
|
|
|
return( 7 ); // Allow the memory operation to proceed (True = 7, False = 0)
|
|
}
|
|
|
|
|
|
/* CLIENT DUMP HOOK FUNCTION
|
|
-------------------------
|
|
A hook function for dumping a Client block usually reports some
|
|
or all of the contents of the block in question. The function
|
|
below also checks the data in several ways, and reports corruption
|
|
or inconsistency as an assertion failure.
|
|
*/
|
|
void __cdecl MemDebug::sDumpClientHook(
|
|
void * pUserData,
|
|
size_t nBytes
|
|
)
|
|
{
|
|
long requestNumber=0;
|
|
_CrtIsMemoryBlock(pUserData,(unsigned int)nBytes,&requestNumber,NULL,NULL);
|
|
fprintf( logFile, "Leak : (#%7d) %12ld bytes (%p) \n", requestNumber, nBytes, pUserData );
|
|
|
|
}
|
|
|
|
// -----------------------------------------------------
|
|
|
|
MemCheck::MemCheck()
|
|
{
|
|
// Store a memory checkpoint in the s1 memory-state structure
|
|
_CrtMemCheckpoint( &s1 );
|
|
}
|
|
|
|
MemCheck::~MemCheck()
|
|
{
|
|
// Store a 2nd memory checkpoint in s2
|
|
_CrtMemCheckpoint( &s2 );
|
|
if ( _CrtMemDifference( &s3, &s1, &s2 ) )
|
|
_CrtMemDumpStatistics( &s3 );
|
|
}
|
|
|
|
void MemCheck::setNextCheckpoint()
|
|
{
|
|
// Store a 2nd memory checkpoint in s2
|
|
_CrtMemCheckpoint( &s2 );
|
|
if ( _CrtMemDifference( &s3, &s1, &s2 ) )
|
|
_CrtMemDumpStatistics( &s3 );
|
|
|
|
// Store a memory checkpoint in the s1 memory-state structure
|
|
_CrtMemCheckpoint( &s1 );
|
|
}
|
|
|
|
bool MemCheck::checkMemory()
|
|
{
|
|
return _CrtCheckMemory() ? true : false;
|
|
}
|
|
|
|
bool MemCheck::dumpLeaks()
|
|
{
|
|
return _CrtDumpMemoryLeaks() ? true : false;
|
|
}
|
|
|
|
bool MemCheck::isValidHeapPointer(const void* userData)
|
|
{
|
|
return _CrtIsValidHeapPointer(userData) ? true : false;
|
|
}
|
|
|
|
#endif
|