| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- #include <list>
- #include <map>
- #include <stdarg.h>
- #include <string>
- #include <time.h>
- #include "KString.h"
- #include "KMemory.h" // last include
- #pragma warning(disable:4996)
- ///////////////////////////////////////////////////////////////////////////////
- //
- // KANJI_DUMP_LEAKED_MEM will print out the memory block that was leaked.
- // This is helpful when leaked objects have string identifiers.
- //
- ///////////////////////////////////////////////////////////////////////////////
- //#define KANJI_DUMP_LEAKED_MEM
- ///////////////////////////////////////////////////////////////////////////////
- //
- // KANJI_TRACK_MEM_USAGE will print out all memory allocations.
- //
- ///////////////////////////////////////////////////////////////////////////////
- //#define KANJI_TRACK_MEM_USAGE
- ///////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////
- // Our memory system is thread-safe, but instead of linking massive libraries,
- // we attempt to use C++11 std::mutex.
- #ifdef USE_CPP11_MUTEX_DISABLED
- #include <mutex>
- typedef std::recursive_mutex KSysLock; // rentrant
- struct KAutoLock {
- KAutoLock(KSysLock& lock) :mLock(lock) { mLock.lock(); } // acquire
- ~KAutoLock() { mLock.unlock(); } // release
- KSysLock& mLock;
- };
- #else // Fallback to unsafe. don't spawn threads
- typedef int KSysLock;
- struct KAutoLock {
- KAutoLock(KSysLock) {} // acquire
- ~KAutoLock() {} // release
- };
- #endif
- ///////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////
- struct KANJI_ALLOC_INFO {
- size_t size;
- std::string file;
- int line;
- };
- static bool showLeaks = false;
- class KAllocMap : public std::map<void *, KANJI_ALLOC_INFO> {
- public:
- KSysLock crit;
- static bool allocMapValid;
- public:
- KAllocMap() { allocMapValid = true; }
- ~KAllocMap() {
- if (showLeaks)
- KMemoryDumpUnfreed();
- allocMapValid = false;
- }
- };
- bool KAllocMap::allocMapValid = false;
- static KAllocMap allocMap; // once this static object destructs, it dumps unfreed memory
- ///////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////
- #ifdef KANJI_TRACK_MEM_USAGE
- void KMemoryDumpUsage(); // forward declaration
- class KAllocStat
- {
- public:
- typedef std::map<int, int> allocCount; // [size] = count
- typedef std::map<std::pair<std::string, int>, allocCount> allocInfo; // [file, line] = allocCount
- allocInfo memInfo;
- static bool allocMapValid;
- public:
- KAllocStat()
- {
- allocMapValid = true;
- }
- ~KAllocStat()
- {
- if (showLeaks)
- KMemoryDumpUsage();
- allocMapValid = false;
- }
- void addTrack(const char* fname, int lnum, int asize)
- {
- allocCount& info = memInfo[std::pair<std::string, int>(fname, lnum)];
- info[asize]++;
- }
- };
- bool KAllocStat::allocMapValid = false;
- static KAllocStat allocStat;
- #endif // KANJI_TRACK_MEM_USAGE
- ///////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////
- extern void KMemoryAddTrack(void *addr, size_t asize, const char *fname, int lnum) {
- if (!KAllocMap::allocMapValid || asize == 0)
- return;
- KAutoLock aCrit(allocMap.crit);
- showLeaks = true;
- KANJI_ALLOC_INFO &info = allocMap[addr];
- info.file = fname;
- info.line = lnum;
- info.size = asize;
- #ifdef KANJI_TRACK_MEM_USAGE
- if (KAllocStat::allocMapValid)
- allocStat.addTrack(fname, lnum, asize);
- #endif
- };
- ///////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////
- void KMemoryRemoveTrack(void *addr) {
- if (!KAllocMap::allocMapValid)
- return;
- KAutoLock aCrit(allocMap.crit);
- KAllocMap::iterator anItr = allocMap.find(addr);
- if (anItr != allocMap.end())
- allocMap.erase(anItr);
- };
- ///////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////
- void KMemoryDumpUnfreed() {
- if (!KAllocMap::allocMapValid)
- return;
- KAutoLock aCrit(allocMap.crit); // prevent modification of the map while iterating
- size_t totalSize = 0;
- char buf[8192];
- FILE *f = fopen("mem_leaks.txt", "wt");
- if (!f)
- return;
- time_t aTime = time(NULL);
- sprintf(buf, "Memory Leak Report for %s\n", asctime(localtime(&aTime)));
- fprintf(f, "%s", buf);
- KOutputDebug(DEBUGLVL, "\n");
- KOutputDebug(INFOLVL, buf);
- for (KAllocMap::iterator i = allocMap.begin(); i != allocMap.end(); ++i) {
- sprintf(buf, "%s(%d) : Leak %u byte%s @0x%08X\n", i->second.file.c_str(), i->second.line, i->second.size,
- i->second.size > 1 ? "s" : "", (size_t) i->first);
- KOutputDebug(ERRORLVL, buf);
- fprintf(f, "%s", buf);
- #ifdef KANJI_DUMP_LEAKED_MEM
- unsigned char* data = (unsigned char*)i->first;
- int count = 0;
- char hex_dump[1024];
- char ascii_dump[1024];
- for (int index = 0; index < i->second.size; index++)
- {
- unsigned char _c = *data;
- if (count == 0)
- sprintf(hex_dump, "\t%02X ", _c);
- else
- sprintf(hex_dump, "%s%02X ", hex_dump, _c); // technically, this is undefined behavior
- if ((_c < 32) || (_c > 126))
- _c = '.';
- if (count == 7)
- sprintf(ascii_dump, "%s%c ", ascii_dump, _c);
- else
- sprintf(ascii_dump, "%s%c", count == 0 ? "\t" : ascii_dump, _c); // technically, this is undefined behavior
- if (++count == 16)
- {
- count = 0;
- sprintf(buf, "%s\t%s\n", hex_dump, ascii_dump);
- fprintf(f, buf);
- memset((void*)hex_dump, 0, 1024);
- memset((void*)ascii_dump, 0, 1024);
- }
- data++;
- }
- if (count != 0)
- {
- fprintf(f, hex_dump);
- for (int index = 0; index < 16 - count; index++)
- fprintf(f, "\t");
- fprintf(f, ascii_dump);
- for (int index = 0; index < 16 - count; index++)
- fprintf(f, ".");
- }
- count = 0;
- fprintf(f, "\n\n");
- memset((void*)hex_dump, 0, 1024);
- memset((void*)ascii_dump, 0, 1024);
- #endif // KANJI_DUMP_LEAKED_MEM
- totalSize += i->second.size;
- }
- ErrorLevel lvl = (totalSize > 0) ? ERRORLVL : INFOLVL;
- sprintf(buf, "-----------------------------------------------------------\n");
- fprintf(f, "%s", buf);
- KOutputDebug(lvl, buf);
- sprintf(buf, "Total Unfreed: %u bytes (%luKB)\n\n", totalSize, totalSize / 1024);
- KOutputDebug(lvl, buf);
- fprintf(f, "%s", buf);
- fclose(f);
- }
- #ifdef KANJI_TRACK_MEM_USAGE
- void KMemoryDumpUsage()
- {
- if (!KAllocStat::allocMapValid)
- return;
- char buf[8192];
- FILE* f = fopen("mem_usage.txt", "wt");
- time_t aTime = time(NULL);
- sprintf(buf, "Memory Usage Report for %s\n", asctime(localtime(&aTime)));
- if (f) fprintf(f, "%s", buf);
- KOutputDebug("\n");
- KOutputDebug(buf);
- for(KAllocStat::allocInfo::iterator i = allocStat.memInfo.begin(); i != allocStat.memInfo.end(); ++i)
- {
- int aBytesTotal = 0;
- int aCallsTotal = 0;
- for (KAllocStat::allocCount::iterator index = i->second.begin(); index != i->second.end(); ++index)
- {
- aBytesTotal += index->first;
- aCallsTotal += index->second;
- sprintf(buf, "%s(%d) : %d bytes (%d %s)\n", i->first.first.c_str(), i->first.second, index->first, index->second, index->second == 1 ? "call" : "calls");
- if (f) fprintf(f, "%s", buf);
- KOutputDebug(buf);
- }
- if (i->second.size() > 1)
- {
- sprintf(buf, " %s(%d) : %d KB total (%d calls)\n", i->first.first.c_str(), i->first.second, aBytesTotal / 1024, aCallsTotal);
- if (f) fprintf(f, "%s", buf);
- KOutputDebug(buf);
- }
- }
- if (f) fclose(f);
- }
- #endif // KANJI_TRACK_MEM_USAGE
- size_t KMemoryAllocated() {
- if (!KAllocMap::allocMapValid)
- return 0;
- KAutoLock aCrit(allocMap.crit);
- size_t size = 0;
- for (auto i = allocMap.begin(); i != allocMap.end(); ++i) {
- KANJI_ALLOC_INFO &info = i->second;
- size += info.size;
- }
- return size;
- }
|