KMemory.cpp 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. #include <list>
  2. #include <map>
  3. #include <stdarg.h>
  4. #include <string>
  5. #include <time.h>
  6. #include "KString.h"
  7. #include "KMemory.h" // last include
  8. #pragma warning(disable:4996)
  9. ///////////////////////////////////////////////////////////////////////////////
  10. //
  11. // KANJI_DUMP_LEAKED_MEM will print out the memory block that was leaked.
  12. // This is helpful when leaked objects have string identifiers.
  13. //
  14. ///////////////////////////////////////////////////////////////////////////////
  15. //#define KANJI_DUMP_LEAKED_MEM
  16. ///////////////////////////////////////////////////////////////////////////////
  17. //
  18. // KANJI_TRACK_MEM_USAGE will print out all memory allocations.
  19. //
  20. ///////////////////////////////////////////////////////////////////////////////
  21. //#define KANJI_TRACK_MEM_USAGE
  22. ///////////////////////////////////////////////////////////////////////////////
  23. ///////////////////////////////////////////////////////////////////////////////
  24. // Our memory system is thread-safe, but instead of linking massive libraries,
  25. // we attempt to use C++11 std::mutex.
  26. #ifdef USE_CPP11_MUTEX_DISABLED
  27. #include <mutex>
  28. typedef std::recursive_mutex KSysLock; // rentrant
  29. struct KAutoLock {
  30. KAutoLock(KSysLock& lock) :mLock(lock) { mLock.lock(); } // acquire
  31. ~KAutoLock() { mLock.unlock(); } // release
  32. KSysLock& mLock;
  33. };
  34. #else // Fallback to unsafe. don't spawn threads
  35. typedef int KSysLock;
  36. struct KAutoLock {
  37. KAutoLock(KSysLock) {} // acquire
  38. ~KAutoLock() {} // release
  39. };
  40. #endif
  41. ///////////////////////////////////////////////////////////////////////////////
  42. ///////////////////////////////////////////////////////////////////////////////
  43. struct KANJI_ALLOC_INFO {
  44. size_t size;
  45. std::string file;
  46. int line;
  47. };
  48. static bool showLeaks = false;
  49. class KAllocMap : public std::map<void *, KANJI_ALLOC_INFO> {
  50. public:
  51. KSysLock crit;
  52. static bool allocMapValid;
  53. public:
  54. KAllocMap() { allocMapValid = true; }
  55. ~KAllocMap() {
  56. if (showLeaks)
  57. KMemoryDumpUnfreed();
  58. allocMapValid = false;
  59. }
  60. };
  61. bool KAllocMap::allocMapValid = false;
  62. static KAllocMap allocMap; // once this static object destructs, it dumps unfreed memory
  63. ///////////////////////////////////////////////////////////////////////////////
  64. ///////////////////////////////////////////////////////////////////////////////
  65. #ifdef KANJI_TRACK_MEM_USAGE
  66. void KMemoryDumpUsage(); // forward declaration
  67. class KAllocStat
  68. {
  69. public:
  70. typedef std::map<int, int> allocCount; // [size] = count
  71. typedef std::map<std::pair<std::string, int>, allocCount> allocInfo; // [file, line] = allocCount
  72. allocInfo memInfo;
  73. static bool allocMapValid;
  74. public:
  75. KAllocStat()
  76. {
  77. allocMapValid = true;
  78. }
  79. ~KAllocStat()
  80. {
  81. if (showLeaks)
  82. KMemoryDumpUsage();
  83. allocMapValid = false;
  84. }
  85. void addTrack(const char* fname, int lnum, int asize)
  86. {
  87. allocCount& info = memInfo[std::pair<std::string, int>(fname, lnum)];
  88. info[asize]++;
  89. }
  90. };
  91. bool KAllocStat::allocMapValid = false;
  92. static KAllocStat allocStat;
  93. #endif // KANJI_TRACK_MEM_USAGE
  94. ///////////////////////////////////////////////////////////////////////////////
  95. ///////////////////////////////////////////////////////////////////////////////
  96. extern void KMemoryAddTrack(void *addr, size_t asize, const char *fname, int lnum) {
  97. if (!KAllocMap::allocMapValid || asize == 0)
  98. return;
  99. KAutoLock aCrit(allocMap.crit);
  100. showLeaks = true;
  101. KANJI_ALLOC_INFO &info = allocMap[addr];
  102. info.file = fname;
  103. info.line = lnum;
  104. info.size = asize;
  105. #ifdef KANJI_TRACK_MEM_USAGE
  106. if (KAllocStat::allocMapValid)
  107. allocStat.addTrack(fname, lnum, asize);
  108. #endif
  109. };
  110. ///////////////////////////////////////////////////////////////////////////////
  111. ///////////////////////////////////////////////////////////////////////////////
  112. void KMemoryRemoveTrack(void *addr) {
  113. if (!KAllocMap::allocMapValid)
  114. return;
  115. KAutoLock aCrit(allocMap.crit);
  116. KAllocMap::iterator anItr = allocMap.find(addr);
  117. if (anItr != allocMap.end())
  118. allocMap.erase(anItr);
  119. };
  120. ///////////////////////////////////////////////////////////////////////////////
  121. ///////////////////////////////////////////////////////////////////////////////
  122. void KMemoryDumpUnfreed() {
  123. if (!KAllocMap::allocMapValid)
  124. return;
  125. KAutoLock aCrit(allocMap.crit); // prevent modification of the map while iterating
  126. size_t totalSize = 0;
  127. char buf[8192];
  128. FILE *f = fopen("mem_leaks.txt", "wt");
  129. if (!f)
  130. return;
  131. time_t aTime = time(NULL);
  132. sprintf(buf, "Memory Leak Report for %s\n", asctime(localtime(&aTime)));
  133. fprintf(f, "%s", buf);
  134. KOutputDebug(DEBUGLVL, "\n");
  135. KOutputDebug(INFOLVL, buf);
  136. for (KAllocMap::iterator i = allocMap.begin(); i != allocMap.end(); ++i) {
  137. sprintf(buf, "%s(%d) : Leak %u byte%s @0x%08X\n", i->second.file.c_str(), i->second.line, i->second.size,
  138. i->second.size > 1 ? "s" : "", (size_t) i->first);
  139. KOutputDebug(ERRORLVL, buf);
  140. fprintf(f, "%s", buf);
  141. #ifdef KANJI_DUMP_LEAKED_MEM
  142. unsigned char* data = (unsigned char*)i->first;
  143. int count = 0;
  144. char hex_dump[1024];
  145. char ascii_dump[1024];
  146. for (int index = 0; index < i->second.size; index++)
  147. {
  148. unsigned char _c = *data;
  149. if (count == 0)
  150. sprintf(hex_dump, "\t%02X ", _c);
  151. else
  152. sprintf(hex_dump, "%s%02X ", hex_dump, _c); // technically, this is undefined behavior
  153. if ((_c < 32) || (_c > 126))
  154. _c = '.';
  155. if (count == 7)
  156. sprintf(ascii_dump, "%s%c ", ascii_dump, _c);
  157. else
  158. sprintf(ascii_dump, "%s%c", count == 0 ? "\t" : ascii_dump, _c); // technically, this is undefined behavior
  159. if (++count == 16)
  160. {
  161. count = 0;
  162. sprintf(buf, "%s\t%s\n", hex_dump, ascii_dump);
  163. fprintf(f, buf);
  164. memset((void*)hex_dump, 0, 1024);
  165. memset((void*)ascii_dump, 0, 1024);
  166. }
  167. data++;
  168. }
  169. if (count != 0)
  170. {
  171. fprintf(f, hex_dump);
  172. for (int index = 0; index < 16 - count; index++)
  173. fprintf(f, "\t");
  174. fprintf(f, ascii_dump);
  175. for (int index = 0; index < 16 - count; index++)
  176. fprintf(f, ".");
  177. }
  178. count = 0;
  179. fprintf(f, "\n\n");
  180. memset((void*)hex_dump, 0, 1024);
  181. memset((void*)ascii_dump, 0, 1024);
  182. #endif // KANJI_DUMP_LEAKED_MEM
  183. totalSize += i->second.size;
  184. }
  185. ErrorLevel lvl = (totalSize > 0) ? ERRORLVL : INFOLVL;
  186. sprintf(buf, "-----------------------------------------------------------\n");
  187. fprintf(f, "%s", buf);
  188. KOutputDebug(lvl, buf);
  189. sprintf(buf, "Total Unfreed: %u bytes (%luKB)\n\n", totalSize, totalSize / 1024);
  190. KOutputDebug(lvl, buf);
  191. fprintf(f, "%s", buf);
  192. fclose(f);
  193. }
  194. #ifdef KANJI_TRACK_MEM_USAGE
  195. void KMemoryDumpUsage()
  196. {
  197. if (!KAllocStat::allocMapValid)
  198. return;
  199. char buf[8192];
  200. FILE* f = fopen("mem_usage.txt", "wt");
  201. time_t aTime = time(NULL);
  202. sprintf(buf, "Memory Usage Report for %s\n", asctime(localtime(&aTime)));
  203. if (f) fprintf(f, "%s", buf);
  204. KOutputDebug("\n");
  205. KOutputDebug(buf);
  206. for(KAllocStat::allocInfo::iterator i = allocStat.memInfo.begin(); i != allocStat.memInfo.end(); ++i)
  207. {
  208. int aBytesTotal = 0;
  209. int aCallsTotal = 0;
  210. for (KAllocStat::allocCount::iterator index = i->second.begin(); index != i->second.end(); ++index)
  211. {
  212. aBytesTotal += index->first;
  213. aCallsTotal += index->second;
  214. 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");
  215. if (f) fprintf(f, "%s", buf);
  216. KOutputDebug(buf);
  217. }
  218. if (i->second.size() > 1)
  219. {
  220. sprintf(buf, " %s(%d) : %d KB total (%d calls)\n", i->first.first.c_str(), i->first.second, aBytesTotal / 1024, aCallsTotal);
  221. if (f) fprintf(f, "%s", buf);
  222. KOutputDebug(buf);
  223. }
  224. }
  225. if (f) fclose(f);
  226. }
  227. #endif // KANJI_TRACK_MEM_USAGE
  228. size_t KMemoryAllocated() {
  229. if (!KAllocMap::allocMapValid)
  230. return 0;
  231. KAutoLock aCrit(allocMap.crit);
  232. size_t size = 0;
  233. for (auto i = allocMap.begin(); i != allocMap.end(); ++i) {
  234. KANJI_ALLOC_INFO &info = i->second;
  235. size += info.size;
  236. }
  237. return size;
  238. }