diff options
Diffstat (limited to 'APEX_1.4/shared/external/src/MemTracker.cpp')
| -rw-r--r-- | APEX_1.4/shared/external/src/MemTracker.cpp | 1174 |
1 files changed, 1174 insertions, 0 deletions
diff --git a/APEX_1.4/shared/external/src/MemTracker.cpp b/APEX_1.4/shared/external/src/MemTracker.cpp new file mode 100644 index 00000000..d3883b86 --- /dev/null +++ b/APEX_1.4/shared/external/src/MemTracker.cpp @@ -0,0 +1,1174 @@ +/* + * Copyright (c) 2008-2015, NVIDIA CORPORATION. All rights reserved. + * + * NVIDIA CORPORATION and its licensors retain all intellectual property + * and proprietary rights in and to this software, related documentation + * and any modifications thereto. Any use, reproduction, disclosure or + * distribution of this software and related documentation without an express + * license agreement from NVIDIA CORPORATION is strictly prohibited. + */ + + +#include "MemTracker.h" + +#if PX_WINDOWS_FAMILY // only compile this source code for windows! + +#define USE_HASH_MAP 1 + +// VC10 hash maps are abominably slow with iterator debug level == 0 +// Note: All STL containers are used internally, so their should not be any resultant conflict +#if (PX_VC == 10) && USE_HASH_MAP +#define OVERRIDE_ITERATOR_DEBUG_LEVEL 1 +#else +#define OVERRIDE_ITERATOR_DEBUG_LEVEL 0 +#endif + +#if OVERRIDE_ITERATOR_DEBUG_LEVEL +#undef _ITERATOR_DEBUG_LEVEL +#define _ITERATOR_DEBUG_LEVEL 0 +#define _ALLOW_ITERATOR_DEBUG_LEVEL_MISMATCH 1 +#endif + +#include "htmltable.h" +#include "PxSimpleTypes.h" +#include "PxAssert.h" +#include <stdio.h> +#include <string> +#include <string.h> +#include <assert.h> +#include <time.h> +#include <sys/types.h> +#include <sys/timeb.h> +#include <map> +#include <vector> +#include <windows.h> + +#define _SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS +#include <hash_map> + +#pragma warning(disable:4100 4996 4267 4239) + +namespace MEM_TRACKER +{ + +class ContextType +{ +public: + ContextType(void) + { + mContext = NULL; + mType = NULL; + mAllocCount = 0; + mAllocSize = 0; + } + ContextType(const char *context,const char *type,size_t size) + { + mContext = context; + mType = type; + char scratch[512]; + sprintf_s(scratch,512,"PlatformAnalyzer/ContextType/%s/%s/Count(AllocCount)", mContext, mType ); + mAllocCountSpec = scratch; + sprintf_s(scratch,512,"PlatformAnalyzer/ContextType/%s/%s/Size(AllocSize)", mContext, mType ); + mAllocSizeSpec = scratch; + mAllocCount = 1; + mAllocSize = size; + } + + const char *mContext; + const char *mType; + uint32_t mAllocCount; + size_t mAllocSize; + std::string mAllocCountSpec; + std::string mAllocSizeSpec; +}; + +class ByContextIndex +{ +public: + ByContextIndex(const char *context,const char *type) + { + mContext = context; + mType = type; + } + + const char *mContext; + const char *mType; +}; + +class ByContextHasher +{ +public: + static const size_t bucket_size = 4; + static const size_t min_buckets = 8; + + size_t operator()(const ByContextIndex &index) const + { + size_t hash1 = (size_t)index.mContext; + size_t hash2 = (size_t)index.mType; + size_t hash = hash1 ^ hash2; + return hash; + } + + bool operator()(const ByContextIndex &s1,const ByContextIndex &s2) const + { + if ( s1.mContext < s2.mContext ) return true; + if ( s1.mContext > s2.mContext ) return false; + return s1.mType < s2.mType; + } +}; + +#if USE_HASH_MAP +typedef stdext::hash_map< ByContextIndex, ContextType, ByContextHasher > ByContextTypeHash; +#else +typedef std::map< ByContextIndex, ContextType, ByContextHasher > ByContextTypeHash; +#endif + +static void getDateTime(char *date_time) +{ + time_t ltime; + struct tm *today; + _tzset(); + time( <ime ); + today = localtime( <ime ); + strftime( date_time, 128,"%A-%B-%d-%Y-%I-%M-%S-%p", today ); +} + +size_t getPow2(size_t s,size_t &p) +{ + size_t ret = 0; + while ( s > p ) + { + p = p<<1; + ret++; + } + return ret; +} + +class MemTrack +{ +public: + MemoryType mType; + size_t mSize; + size_t mThreadId; + const char *mContext; + const char *mClassName; + const char *mFileName; + uint32_t mLineNo; + size_t mAllocCount; +}; + +class ByType +{ +public: + ByType(size_t size) + { + mSize = size; + mCount = 1; + } + ByType(void) { }; + size_t mSize; + size_t mCount; +}; + +class BySource +{ +public: + BySource(void) { }; + + BySource(const MemTrack &t) + { + mClassName = t.mClassName; + mFileName = t.mFileName; + mLineNo = t.mLineNo; + mSize = t.mSize; + mLineNo = t.mLineNo; + mCount = 0; + } + + const char *mClassName; + const char *mFileName; + size_t mLineNo; + size_t mSize; + size_t mCount; +}; + +#if USE_HASH_MAP +typedef stdext::hash_map< size_t, ByType > ByTypeHash; +#else +typedef std::map< size_t, ByType > ByTypeHash; +#endif + +class BySourceIndex +{ +public: + BySourceIndex(const char *fileName,uint32_t lineno) + { + mFileName = fileName; + mLineNo = lineno; + } + + const char *mFileName; + uint32_t mLineNo; +}; + +class BySourceHasher +{ +public: + static const size_t bucket_size = 4; + static const size_t min_buckets = 8; + + size_t operator()(const BySourceIndex &index) const + { + size_t hash = (size_t)index.mFileName; + hash = hash^index.mLineNo; + return hash; + } + + bool operator()(const BySourceIndex &s1,const BySourceIndex &s2) const + { + if ( s1.mFileName < s2.mFileName ) return true; + if ( s1.mFileName > s2.mFileName ) return false; + return s1.mLineNo < s2.mLineNo; + } +}; + +#if USE_HASH_MAP +typedef stdext::hash_map< BySourceIndex, BySource, BySourceHasher > BySourceHash; +typedef stdext::hash_map< size_t , MemTrack > MemTrackHash; +#else +typedef std::map< BySourceIndex, BySource, BySourceHasher > BySourceHash; +typedef std::map< size_t , MemTrack > MemTrackHash; +#endif + +typedef std::vector< MemTrack > MemTrackVector; + +class ReportContext +{ +public: + ReportContext(const char *context) + { + mContext = context; + } + void add(const MemTrack &t) + { + assert(t.mAllocCount > 0 ); + mMemTracks.push_back(t); + } + + const char * getContext(void) const { return mContext; }; + + // first we generate a report based on type. + void generateReport(nvidia::HtmlDocument *document,nvidia::HtmlTable *contextTable) + { + unsigned int totalMemory = 0; + unsigned int totalAllocations = 0; + unsigned int typeCount = 0; + unsigned int sourceCount = 0; + + { + + + + ByTypeHash bt; + MemTrackVector::iterator i; + for (i=mMemTracks.begin(); i!=mMemTracks.end(); i++) + { + const MemTrack &t = (*i); + size_t hash = (size_t)t.mClassName; + ByTypeHash::iterator found = bt.find(hash); + if ( found == bt.end() ) + { + ByType b(t.mSize); + bt[hash] = b; + } + else + { + ByType &b = (ByType &)(*found).second; + b.mSize+=t.mSize; + b.mCount++; + } + } + + { + typeCount = bt.size(); + + char scratch[512]; + char date_time[512]; + getDateTime(date_time); + sprintf(scratch,"Memory Usage by Class Name or Memory Type for Context: %s : %s", mContext, date_time ); + nvidia::HtmlTable *table = document->createHtmlTable(scratch); + // 1 2 3 + table->addHeader("Memory/Type,Memory/Size,Allocation/Count"); + table->addSort("Sorted By Memory Size",2,false,3,false); + for (ByTypeHash::iterator iter = bt.begin(); iter !=bt.end(); ++iter) + { + ByType b = (*iter).second; + table->addColumn( (const char *)(*iter).first ); + table->addColumn( (uint32_t)b.mSize ); + table->addColumn( (uint32_t)b.mCount ); + table->nextRow(); + } + table->computeTotals(); + } + } + + { + + BySourceHash bt; + MemTrackVector::iterator i; + for (i=mMemTracks.begin(); i!=mMemTracks.end(); i++) + { + const MemTrack &t = (*i); + BySource b(t); + BySourceIndex index(b.mFileName,b.mLineNo); + BySourceHash::iterator found = bt.find(index); + if ( found == bt.end() ) + { + b.mCount = 1; + bt[index] = b; + } + else + { + BySource &bs = (BySource &)(*found).second; + bs.mSize+=t.mSize; + bs.mCount++; + } + } + + + { + sourceCount = bt.size(); + + char scratch[512]; + char date_time[512]; + getDateTime(date_time); + sprintf(scratch,"Memory Usage by Source File and Line Number for Context: %s : %s", mContext, date_time ); + nvidia::HtmlTable *table = document->createHtmlTable(scratch); + // 1 2 3 4 5 + table->addHeader("Source/File,Line/Number,Memory/Type,Memory/Size,Allocation/Count"); + table->addSort("Sorted By Memory Size",4,false,5,false); + table->excludeTotals(2); + for (BySourceHash::iterator i=bt.begin(); i!=bt.end(); ++i) + { + BySource b = (*i).second; + table->addColumn( b.mFileName ); + table->addColumn( (uint32_t)b.mLineNo ); + table->addColumn( b.mClassName ); + table->addColumn( (uint32_t)b.mSize ); + assert( b.mCount > 0 ); + table->addColumn( (uint32_t)b.mCount ); + table->nextRow(); + totalMemory+=b.mSize; + totalAllocations+=b.mCount; + } + table->computeTotals(); + } + } + + + // Power of two, sizes 1-256 + { + char scratch[512]; + char date_time[512]; + getDateTime(date_time); + sprintf(scratch,"Power of Two Memory 1 to 256 bytes for Context: %s : %s", mContext, date_time ); + nvidia::HtmlTable *table = document->createHtmlTable(scratch); + // 1 2 3 4 + table->addHeader("Mem/Size,Total/Alloc,Alloc/Count,Fragment/Amount"); + table->addSort("Sorted By Memory Size",2,false,3,false); + table->excludeTotals(1); + + // 0 1 2 3 4 5 + // 8, 16, 32, 64, 128, 256 + struct Power2 + { + Power2(void) + { + memSize = 0; + memTotal = 0; + memCount = 0; + fragmentTotal = 0; + } + unsigned int memSize; + unsigned int memTotal; + unsigned int memCount; + unsigned int fragmentTotal; + }; + + Power2 p[6]; + + MemTrackVector::iterator i; + for (i=mMemTracks.begin(); i!=mMemTracks.end(); i++) + { + const MemTrack &t = (*i); + if ( t.mSize <= 256 ) + { + size_t p2=8; + size_t index = getPow2(t.mSize,p2); + p[index].memSize = p2; + p[index].memTotal+=t.mSize; + p[index].memCount++; + p[index].fragmentTotal+=(p2-t.mSize); + } + } + for (size_t i=0; i<6; i++) + { + Power2 &t = p[i]; + if ( t.memCount > 0 ) + { + table->addColumn(t.memSize); + table->addColumn(t.memTotal); + table->addColumn(t.memCount); + table->addColumn(t.fragmentTotal); + table->nextRow(); + } + } + table->computeTotals(); + } + + // power of two allocations > 256 bytes + { + char scratch[512]; + char date_time[512]; + getDateTime(date_time); + sprintf(scratch,"Power of Two Memory Greater than 256 bytes for Context: %s : %s", mContext, date_time ); + nvidia::HtmlTable *table = document->createHtmlTable(scratch); + // 1 2 3 4 + table->addHeader("Mem/Size,Total/Alloc,Alloc/Count,Fragment/Amount"); + table->addSort("Sorted By Memory Size",2,false,3,false); + table->excludeTotals(1); + + struct Power2 + { + Power2(void) + { + memSize = 0; + memTotal = 0; + memCount = 0; + fragmentTotal = 0; + } + unsigned int memSize; + unsigned int memTotal; + unsigned int memCount; + unsigned int fragmentTotal; + }; + + Power2 p[32]; + + MemTrackVector::iterator i; + for (i=mMemTracks.begin(); i!=mMemTracks.end(); i++) + { + const MemTrack &t = (*i); + if ( t.mSize > 256 ) + { + size_t p2=512; + size_t index = getPow2(t.mSize,p2); + p[index].memSize = p2; + p[index].memTotal+=t.mSize; + p[index].memCount++; + p[index].fragmentTotal+=(p2-t.mSize); + } + } + for (size_t i=0; i<32; i++) + { + Power2 &t = p[i]; + if ( t.memCount > 0 ) + { + table->addColumn(t.memSize); + table->addColumn(t.memTotal); + table->addColumn(t.memCount); + table->addColumn(t.fragmentTotal); + table->nextRow(); + } + } + table->computeTotals(); + } + + + // context summary.. + if ( contextTable ) + { + contextTable->addColumn( mContext ); + contextTable->addColumn( totalMemory ); + contextTable->addColumn( totalAllocations ); + contextTable->addColumn( typeCount ); + contextTable->addColumn(sourceCount ); + contextTable->nextRow(); + } + + } + +private: + const char *mContext; + MemTrackVector mMemTracks; +}; + +#if USE_HASH_MAP +typedef stdext::hash_map< size_t , ReportContext * > ReportContextHash; +#else +typedef std::map< size_t , ReportContext * > ReportContextHash; +#endif + +class MyMemTracker : public MemTracker +{ +public: + + MyMemTracker(void) + { + mSingleThreaded = false; + mFrameNo = 1; + mAllocCount = 0; + mAllocSize = 0; + mAllocFrameCount = 0; + mFreeFrameCount = 0; + mAllocFrameSize = 0; + mFreeFrameSize = 0; + mDocument = 0; + mFrameSummary = 0; + mDetailed = 0; + // nvidia::HtmlTableInterface *h = nvidia::getHtmlTableInterface(); + // if ( h ) + // { + //mDocument = h->createHtmlDocument("MemTracker"); + // } + + } + + virtual ~MyMemTracker(void) + { + if ( mDocument ) + { + nvidia::HtmlTableInterface *h = nvidia::getHtmlTableInterface(); + h->releaseHtmlDocument(mDocument); + } + } + + virtual void trackAlloc(size_t threadId,void *mem,size_t size,MemoryType type,const char *context,const char *className,const char *fileName,uint32_t lineno) + { + if ( mem ) + { + addContextType(context,className,size); + + mAllocCount++; + mAllocSize+=size; + + mAllocFrameCount++; + mAllocFrameSize+=size; + + size_t hash = (size_t) mem; + MemTrack t; + t.mType = type; + t.mSize = size; + t.mThreadId = threadId; + t.mContext = context; + t.mClassName = className; + t.mFileName = fileName; + t.mLineNo = lineno; + t.mAllocCount = 1; + MemTrackHash::iterator found = mMemory.find(hash); + if ( found != mMemory.end() ) + { + PX_ALWAYS_ASSERT(); + const MemTrack &previous = (*found).second; + printf("Prev: %s\r\n", previous.mClassName ); + PX_UNUSED(previous); + } + // track which allocation number this one was. + { + BySource b(t); + b.mCount = 1; + BySourceIndex index(b.mFileName,b.mLineNo); + BySourceHash::iterator found = mSourceHash.find(index); + if ( found == mSourceHash.end() ) + { + mSourceHash[index] = b; + } + else + { + BySource bs = (*found).second; + bs.mCount++; + t.mAllocCount = bs.mCount; + mSourceHash[index] = bs; + } + } + if ( mDetailed ) + { + mDetailed->addColumnHex( (size_t)mem ); + switch ( type ) + { + case MT_NEW: + mDetailed->addColumn("NEW"); + break; + case MT_NEW_ARRAY: + mDetailed->addColumn("NEW_ARRAY"); + break; + case MT_MALLOC: + mDetailed->addColumn("MALLOC"); + break; + case MT_GLOBAL_NEW: + mDetailed->addColumn("GLOBAL_NEW"); + break; + case MT_GLOBAL_NEW_ARRAY: + mDetailed->addColumn("GLOBAL_NEW_ARRAY"); + break; + default: + PX_ALWAYS_ASSERT(); + mDetailed->addColumn("ERROR"); + break; + } + mDetailed->addColumn((uint32_t)size); + mDetailed->addColumn((uint32_t)t.mAllocCount); + mDetailed->addColumnHex(threadId); + mDetailed->addColumn(context); + mDetailed->addColumn(className); + mDetailed->addColumn(fileName); + mDetailed->addColumn((uint32_t)lineno); + mDetailed->nextRow(); + } + + mMemory[hash] = t; + + PX_ASSERT( mAllocCount == mMemory.size() ); + } + } + + virtual void trackRealloc(size_t threadId, + void *oldMem, + void *newMem, + size_t newSize, + const char *context, + const char *className, + const char *fileName, + uint32_t lineno) + { + TrackInfo info; + bool found = trackInfo(oldMem,info); + PX_ASSERT(found); + if ( found ) + { + PX_ASSERT( info.mType == MT_MALLOC ); + trackFree(threadId,oldMem,MT_FREE,context,fileName,lineno); + trackAlloc(threadId,newMem,newSize,info.mType,context,className,fileName,lineno); + } + } + + virtual const char * typeString(MemoryType type) + { + const char *ret = "unknown"; + switch ( type ) + { + case MT_NEW: + ret = "new operator"; + break; + case MT_NEW_ARRAY: + ret = "new[] array operator"; + break; + case MT_MALLOC: + ret = "malloc"; + break; + case MT_FREE: + ret = "free"; + break; + case MT_DELETE: + ret = "delete operator"; + break; + case MT_DELETE_ARRAY: + ret = "delete[] array operator"; + break; + case MT_GLOBAL_NEW: + ret = "global new"; + break; + case MT_GLOBAL_NEW_ARRAY: + ret = "global new[] array"; + break; + case MT_GLOBAL_DELETE: + ret = "global delete"; + break; + case MT_GLOBAL_DELETE_ARRAY: + ret = "global delete array"; + break; + } + return ret; + } + + + virtual const char * trackValidateFree(size_t threadId,void *mem,MemoryType type,const char *context,const char *fileName,uint32_t lineno) + { + const char *ret = NULL; + if ( mem ) + { + char scratch[1024]; + scratch[0] = 0; + + size_t hash = (size_t) mem; + MemTrackHash::iterator found = mMemory.find(hash); + if ( found == mMemory.end() ) + { + sprintf_s(scratch,1024,"Error! Tried to free memory never tracked. Source: %s : Line: %d\r\n", fileName, lineno); + PX_ALWAYS_ASSERT(); + } + else + { + MemTrack &t = (MemTrack &)(*found).second; + + switch ( type ) + { + case MT_DELETE: + if ( t.mType != MT_NEW ) + { + sprintf_s(scratch,1024,"Error: Allocated with %s but deallocated with the delete operator\r\n", typeString(t.mType)); + } + break; + case MT_DELETE_ARRAY: + if ( t.mType != MT_NEW_ARRAY ) + { + sprintf_s(scratch,1024,"Error: Allocated with %s but deallocated with the delete array operator.\r\n", typeString(t.mType)); + } + break; + case MT_FREE: + if ( t.mType != MT_MALLOC ) + { + sprintf_s(scratch,1024,"Error: Allocated with %s but deallocated with malloc.\r\n", typeString(t.mType)); + } + break; + case MT_GLOBAL_DELETE: + if ( t.mType != MT_GLOBAL_NEW ) + { + sprintf_s(scratch,1024,"Error: Allocated with %s but deallocated with global delete.\r\n", typeString(t.mType)); + } + break; + case MT_GLOBAL_DELETE_ARRAY: + if ( t.mType != MT_GLOBAL_NEW_ARRAY ) + { + sprintf_s(scratch,1024,"Error: Allocated with %s but deallocated with global delete array.\r\n", typeString(t.mType)); + } + break; + default: + sprintf_s(scratch,1024,"Invalid memory type encountered. Data corrupted!?\r\n"); + break; + } + if ( t.mThreadId != threadId && mSingleThreaded ) + { + sprintf_s(scratch,1024,"Memory de-allocated from a different thread than it was allocated from!\r\n"); + } + if ( !context ) + { + sprintf_s(scratch,1024,"Null context!\r\n"); + } + if ( !t.mContext ) + { + sprintf_s(scratch,1024,"Original memory block allocated with a null context, or the data is corrupted!\r\n"); + } + if ( context != t.mContext ) + { + sprintf_s(scratch,1024,"Memory is de-allocated from a different context. Allocated from (%s) deallocated from (%s)\r\n", context, t.mContext ); + } + } + if ( scratch[0] ) + { + mErrorMessage = scratch; + ret = mErrorMessage.c_str(); + } + } + return ret; + } + + void addContextType(const char *context,const char *type,size_t size) + { + ByContextIndex index(context,type); + ByContextTypeHash::iterator found = mContextType.find(index); + if ( found != mContextType.end() ) + { + ContextType &c = (ContextType &)(*found).second; + PX_ASSERT( c.mContext == context ); + PX_ASSERT( c.mType == type ); + c.mAllocCount++; + c.mAllocSize+=size; + } + else + { + ContextType c(context,type,size); + mContextType[index] = c; + } +#if 0 + int foundCount=0; + { + for (ByContextTypeHash::iterator i=mContextType.begin(); i!=mContextType.end(); ++i) + { + const ByContextIndex &index = (*i).first; + if ( index.mContext == context && index.mType == type ) + { + foundCount++; + } + } + } + PX_ASSERT( foundCount == 1 ); +#endif + } + + void removeContextType(const char *context,const char *type,size_t size) + { + ByContextIndex index(context,type); + ByContextTypeHash::iterator found = mContextType.find(index); + if ( found != mContextType.end() ) + { + ContextType &c = (ContextType &)(*found).second; + c.mAllocCount--; + c.mAllocSize-=size; + } + else + { + PX_ALWAYS_ASSERT(); + } + + + } + + + virtual void trackFree(size_t threadId,void *mem,MemoryType type,const char *context,const char *fileName,uint32_t lineno) + { + if ( mem ) + { + size_t hash = (size_t) mem; + MemTrackHash::iterator found = mMemory.find(hash); + if ( found == mMemory.end() ) + { + PX_ALWAYS_ASSERT(); + } + else + { + const MemTrack &t = (*found).second; + removeContextType(t.mContext,t.mClassName,t.mSize); + if ( mDetailed ) + { + mDetailed->addColumnHex( (size_t) mem ); + switch ( type ) + { + case MT_FREE: + mDetailed->addColumn("FREE"); + break; + case MT_DELETE: + mDetailed->addColumn("DELETE"); + break; + case MT_DELETE_ARRAY: + mDetailed->addColumn("DELETE_ARRAY"); + break; + case MT_GLOBAL_DELETE: + mDetailed->addColumn("GLOBAL_DELETE"); + break; + case MT_GLOBAL_DELETE_ARRAY: + mDetailed->addColumn("GLOBAL_DELETE_ARRAY"); + break; + default: + printf("Invalid memory allocation type! %s : %d\r\n", fileName, lineno ); + mDetailed->addColumn("INVALID ALLOC TYPE"); + break; + } + mDetailed->addColumn((uint32_t)t.mSize); + mDetailed->addColumn((uint32_t)t.mAllocCount); + mDetailed->addColumnHex(threadId); + mDetailed->addColumn(context); + mDetailed->addColumn(t.mClassName); + mDetailed->addColumn(fileName); + mDetailed->addColumn((uint32_t)lineno); + mDetailed->nextRow(); + } + + mAllocCount--; + mAllocSize-=t.mSize; + + mFreeFrameCount++; + mFreeFrameSize+=t.mSize; + + mMemory.erase(hash); + + PX_ASSERT( mAllocCount == mMemory.size() ); + } + } + } + + void trackFrame(void) + { + mFrameNo++; + if ( mAllocFrameCount || mFreeFrameCount ) + { + if ( mFrameSummary ) + { + mFrameSummary->addColumn((uint32_t)mFrameNo); + mFrameSummary->addColumn((uint32_t)mAllocCount); + mFrameSummary->addColumn((uint32_t)mAllocSize); + mFrameSummary->addColumn((uint32_t)mAllocFrameCount); + mFrameSummary->addColumn((uint32_t)mAllocFrameSize); + mFrameSummary->addColumn((uint32_t)mFreeFrameCount); + mFrameSummary->addColumn((uint32_t)mFreeFrameSize); + mFrameSummary->addColumn((uint32_t)(mAllocFrameCount-mFreeFrameCount)); + mFrameSummary->addColumn((uint32_t)(mAllocFrameSize-mFreeFrameSize)); + mFrameSummary->nextRow(); + } + + mAllocFrameCount = 0; + mAllocFrameSize = 0; + mFreeFrameCount = 0; + mFreeFrameSize = 0; + + if ( mDetailed ) + { + char scratch[512]; + sprintf(scratch,"New Frame %d", (int)mFrameNo ); + mDetailed->addColumn(scratch); + mDetailed->addColumn((uint32_t)mAllocSize); + mDetailed->nextRow(); + } + + + } + } + + virtual void releaseReportMemory(void *mem) + { + ::free(mem); + } + + void *generateReport(MemoryReportFormat format,const char *fname,uint32_t &saveLen,bool reportAllLeaks) + { + void *ret = NULL; + saveLen = 0; + + // + nvidia::HtmlTable *contextTable = NULL; + { + char scratch[512]; + char date_time[512]; + getDateTime(date_time); + sprintf(scratch,"Summary Report for All Contexts : %s", date_time); + contextTable = mDocument->createHtmlTable(scratch); + // 1 2 3 + contextTable->addHeader("Memory/Context,Total/Memory,Alloc/Count,Type/Count,Source/Count"); + contextTable->addSort("Sorted By Total Memory",2,true,3,true); + + } + ReportContextHash rchash; + { + ReportContext *current = 0; + + for (MemTrackHash::iterator i=mMemory.begin(); i!=mMemory.end(); ++i) + { + MemTrack t = (*i).second; + if ( (current == 0) || current->getContext() != t.mContext ) + { + size_t hash = (size_t)t.mContext; + ReportContextHash::iterator found = rchash.find(hash); + if ( found == rchash.end() ) + { + current = new ReportContext(t.mContext); + rchash[hash] = current; + } + else + { + current = (*found).second; + } + } + current->add(t); + } + // + if ( reportAllLeaks ) + { + char scratch[512]; + char date_time[512]; + getDateTime(date_time); + sprintf(scratch,"Memory Leaks in order of allocation : %s", date_time); + + nvidia::HtmlTable *table = mDocument->createHtmlTable(scratch); + // 1 2 3 4 5 6 7 8 + table->addHeader("Memory/Address,Alloc/Count,Alloc/Size,ThreadId,Context,Class/Type,Source/File,Lineno"); + table->addSort("Sorted By Source File and Line Number",7,true,8,true); + + table->excludeTotals(2); + table->excludeTotals(8); + + for (MemTrackHash::iterator i=mMemory.begin(); i!=mMemory.end(); ++i) + { + MemTrack t = (*i).second; + table->addColumnHex( (size_t)(*i).first ); // memory address + table->addColumn((uint32_t) t.mAllocCount ); // allocation count. + table->addColumn((uint32_t) t.mSize ); // size of allocation. + table->addColumnHex( t.mThreadId ); // thread id + table->addColumn( t.mContext ); // context + table->addColumn( t.mClassName ); // + table->addColumn( t.mFileName ); + table->addColumn((uint32_t) t.mLineNo ); + table->nextRow(); + } + table->computeTotals(); + } + } + // + { + + for (ReportContextHash::iterator i=rchash.begin(); i!=rchash.end(); ++i) + { + ReportContext *rc = (*i).second; + rc->generateReport(mDocument,contextTable); + delete rc; + } + } + // + if ( mFrameSummary ) + { + mFrameSummary->excludeTotals(1); + mFrameSummary->excludeTotals(2); + mFrameSummary->excludeTotals(3); + mFrameSummary->excludeTotals(8); + mFrameSummary->excludeTotals(9); + mFrameSummary->computeTotals(); + } + + contextTable->computeTotals(); + + nvidia::HtmlSaveType saveType = nvidia::HST_SIMPLE_HTML; + switch ( format ) + { + case MRF_SIMPLE_HTML: // just a very simple HTML document containing the tables. + saveType = nvidia::HST_SIMPLE_HTML; + break; + case MRF_CSV: // Saves the Tables out as comma seperated value text + saveType = nvidia::HST_CSV; + break; + case MRF_TEXT: // Saves the tables out in human readable text format. + saveType = nvidia::HST_TEXT; + break; + case MRF_TEXT_EXTENDED: // Saves the tables out in human readable text format, but uses the MS-DOS style extended ASCII character set for the borders. + saveType = nvidia::HST_TEXT_EXTENDED; + break; + } + + size_t len; + const char *data = mDocument->saveDocument(len,saveType); + if ( data ) + { + ret = ::malloc(len); + memcpy(ret,data,len); + saveLen = len; + mDocument->releaseDocumentMemory(data); + } + return ret; + } + + + virtual void usage(void) + { + printf("On Frame Number: %d has performed %d memory allocations for a total %d bytes of memory.\r\n", (int)mFrameNo, (int)mAllocCount, (int)mAllocSize ); + } + + + virtual size_t detectLeaks(size_t &acount) + { + acount = mAllocCount; + return mAllocSize; + } + + + virtual void setLogLevel(bool logEveryAllocation,bool logEveryFrame,bool verifySingleThreaded) + { + mSingleThreaded = verifySingleThreaded; + mLogEveryAllocation = logEveryAllocation; + mLogEveryFrame = logEveryFrame; + if ( mDocument ) + { + if ( mLogEveryFrame && mFrameSummary == 0 ) + { + char date_time[512]; + getDateTime(date_time); + char scratch[1024]; + sprintf(scratch,"Per Frame Memory Usage Summary : %s ", date_time); + + mFrameSummary = mDocument->createHtmlTable(scratch); + // 1 2 3 4 5 6 7 8 9 + mFrameSummary->addHeader("Frame/Number,Total/Alloc Count,Total/Alloc Mem,Frame/Alloc Count,Frame/Alloc Mem,Frame/Free Count,Frame/Free Mem,Frame/Delta Count,Frame/Deleta Mem"); + } + if ( mLogEveryAllocation && mDetailed == 0 ) + { + char date_time[512]; + getDateTime(date_time); + char scratch[2048]; + sprintf(scratch,"Detailed Memory Usage Report : %s ",date_time); + mDetailed = mDocument->createHtmlTable(scratch); + // 1 2 3 4 5 6 7 8 + mDetailed->addHeader("Memory,Event,Size,Alloc Count,ThreadId,Context,Class or Type,Source File,Line Number"); + mDetailed->setOrder(1000); // make sure it displays this last! + } + } + } + + + virtual bool trackInfo(const void *mem,TrackInfo &info) + { + bool ret = false; + + if ( mem ) + { + size_t hash = (size_t) mem; + MemTrackHash::iterator found = mMemory.find(hash); + if ( found == mMemory.end() ) + { + printf("Error! Tried to get information for memory never tracked.\r\n"); + } + else + { + MemTrack &t = (MemTrack &)(*found).second; + info.mMemory = mem; + info.mType = t.mType; + info.mSize = t.mSize; + info.mContext = t.mContext; + info.mClassName = t.mClassName; + info.mFileName = t.mFileName; + info.mLineNo = t.mLineNo; + info.mAllocCount = t.mAllocCount; + ret = true; + } + } + return ret; + } + + +private: + + bool mLogEveryAllocation; + bool mLogEveryFrame; + + size_t mFrameNo; + MemTrackHash mMemory; + + size_t mAllocFrameCount; + size_t mFreeFrameCount; + + size_t mAllocFrameSize; + size_t mFreeFrameSize; + + size_t mAllocCount; + size_t mAllocSize; + bool mSingleThreaded; + + nvidia::HtmlTable *mFrameSummary; + nvidia::HtmlTable *mDetailed; + nvidia::HtmlDocument *mDocument; + BySourceHash mSourceHash; + ByContextTypeHash mContextType; + + std::string mErrorMessage; +}; + +MemTracker *createMemTracker(void) +{ + MyMemTracker *m = new MyMemTracker; + return static_cast< MemTracker *>(m); +} + +void releaseMemTracker(MemTracker *mt) +{ + MyMemTracker *m = static_cast< MyMemTracker *>(mt); + delete m; +} +}; + +#endif
\ No newline at end of file |