memtrace.c 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. #include "precompile.h"
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <stdarg.h>
  6. #include <ctype.h>
  7. #include <assert.h>
  8. #include "fileutil.h"
  9. #include "toolkit.h"
  10. #include "memutil.h"
  11. #include <winpr/wlog.h>
  12. #include "dbgutil.h"
  13. #include "memtrace.h"
  14. #undef malloc
  15. #undef calloc
  16. #undef realloc
  17. #undef free
  18. #undef strdup
  19. #define TAG TOOLKIT_TAG(".memtrace")
  20. /** This is a special maker which is set right before the beginning of a
  21. * memory chunk and right after the end of a memory chunk. When memory is
  22. * freed, the markers are checked to see if they were overwritten by the
  23. * client */
  24. #ifndef MEMTRACE_MARK
  25. #define MEMTRACE_MARK ((char)0x55)
  26. #endif
  27. /** Used to write over freed memory, and write over malloced memory. Must be
  28. * different than MEMTRACE_MARK */
  29. #ifndef MEMTRACE_JUNK
  30. #define MEMTRACE_JUNK ((char)0xAA)
  31. #endif
  32. /** How many bytes should be written before and after every allocation to be
  33. * checked for possible overflows when memory is freed? Must be >= 0.
  34. * Should be a multiple of sizeof(wchar_t) to prevent bugs with glibc */
  35. #ifndef MEMTRACE_GUARDSIZE
  36. #define MEMTRACE_GUARDSIZE 4
  37. #endif
  38. #define MEMTRACE_HEADER_SIZE (sizeof(mem_trace_t) + MEMTRACE_GUARDSIZE)
  39. /** The number of functions to keep track of at a time. The larger the stack
  40. * size, the more effecient the function stack printing can be */
  41. #ifndef MEMTRACE_FUNCSTACK_SIZE
  42. #define MEMTRACE_FUNCSTACK_SIZE 1024
  43. #endif
  44. /** Should we add code to keep track of function calls? */
  45. #ifndef MEMTRACE_TRACK_FUNCTIONS
  46. #define MEMTRACE_TRACK_FUNCTIONS 1
  47. #endif
  48. /** This structure is added before the memory returned to the client, and
  49. * can be used to retain information. This is no longer necessary in a full
  50. * memtrace report, but we leave it so that we can still do simple reports
  51. * efficiently */
  52. typedef struct {
  53. /** The total number of bytes in this allocation */
  54. size_t bytes;
  55. } mem_trace_t;
  56. static const char* s_program_name = NULL;
  57. /** The total number of allocated bytes */
  58. static intmax_t s_mem_allocated_bytes = 0;
  59. static toolkit_mutex_t global_allocated_lock;
  60. static toolkit_once_t once = TOOLKIT_ONCE_INIT;
  61. /** The depth of the function calls. Does not depend on how many functions
  62. * we have printed out. */
  63. static intmax_t MemTrace_funcDepth = 0;
  64. /** Stack of function pointers */
  65. static void* MemTrace_funcStack[MEMTRACE_FUNCSTACK_SIZE];
  66. static void* MemTrace_callStack[MEMTRACE_FUNCSTACK_SIZE];
  67. /** Points to where the next function should be added in MemTrace_funcStack*/
  68. static int s_mem_stack_index = 0;
  69. TOOLKIT_API const char* tk_get_program_name()
  70. {
  71. return s_program_name;
  72. }
  73. TOOLKIT_API void tk_set_program_name(const char* name)
  74. {
  75. if (s_program_name != NULL) {
  76. char* just_progname = strrchr(name, SPLIT_SLASH);
  77. if (just_progname != NULL) s_program_name = toolkit_strdup(just_progname + 1);
  78. s_program_name = toolkit_strdup(name);
  79. }
  80. }
  81. //////////////////////////////////////////////////////////////////////////
  82. static void init_allocated_mutex_once(void)
  83. {
  84. toolkit_mutex_init(&global_allocated_lock);
  85. }
  86. static void increment_allocated_bytes(size_t bytes)
  87. {
  88. toolkit_once(&once, init_allocated_mutex_once);
  89. toolkit_mutex_lock(&global_allocated_lock);
  90. s_mem_allocated_bytes += bytes;
  91. toolkit_mutex_unlock(&global_allocated_lock);
  92. }
  93. static void decrement_allocated_bytes(size_t bytes)
  94. {
  95. toolkit_once(&once, init_allocated_mutex_once);
  96. toolkit_mutex_lock(&global_allocated_lock);
  97. s_mem_allocated_bytes -= bytes;
  98. toolkit_mutex_unlock(&global_allocated_lock);
  99. }
  100. static void* mark_allocated_buffer(char* buf, size_t bytes)
  101. {
  102. char* ret_buf = NULL;
  103. increment_allocated_bytes(bytes);
  104. ((mem_trace_t*)buf)->bytes = bytes;
  105. ret_buf = (char*)(buf + MEMTRACE_HEADER_SIZE);
  106. do {
  107. /* Fill beginning markers */
  108. memset(buf + sizeof(mem_trace_t), MEMTRACE_MARK, MEMTRACE_GUARDSIZE);
  109. /* Fill end markers */
  110. memset(buf + MEMTRACE_HEADER_SIZE + bytes, MEMTRACE_MARK, MEMTRACE_GUARDSIZE);
  111. } while (0);
  112. return ret_buf;
  113. }
  114. void* toolkit_memtrace_zalloc(size_t bytes, const char* file, const char* function, int line)
  115. {
  116. void* buf = toolkit_memtrace_malloc(bytes, file, function, line);
  117. if (buf) {
  118. memset(buf, 0, bytes);
  119. }
  120. return buf;
  121. }
  122. void* toolkit_memtrace_malloc(size_t bytes, const char* file, const char* function, int line)
  123. {
  124. char* pcvoid = NULL;
  125. void* ret = NULL;
  126. bytes = bytes == 0 ? 1 : bytes;
  127. pcvoid = (char*)malloc(bytes + sizeof(mem_trace_t) + 2 * MEMTRACE_GUARDSIZE);
  128. if (pcvoid == NULL) {
  129. WLog_ERR(TAG, "malloc failed in <%s>[%d]{%s}", function, line, file);
  130. return NULL;
  131. }
  132. ret = mark_allocated_buffer(pcvoid, bytes);
  133. /* Overwrite memory to return with junk */
  134. memset(ret, MEMTRACE_JUNK, bytes);
  135. WLog_DBG(TAG, "malloc: %p+%u in <%s>[%d]{%s}", ret, (unsigned int)bytes, function, line, file);
  136. return ret;
  137. }
  138. void* toolkit_memtrace_calloc(size_t elem, size_t ele_size, const char* file, const char* function, int line)
  139. {
  140. char* pcvoid = NULL;
  141. void* ret = NULL;
  142. size_t bytes = 0;
  143. bytes = elem * ele_size;
  144. pcvoid = (char*)calloc(1, bytes + sizeof(mem_trace_t) + 2 * MEMTRACE_GUARDSIZE);
  145. if (pcvoid == NULL) {
  146. WLog_ERR(TAG, "calloc failed in <%s>[%d]{%s}", function, line, file);
  147. return NULL;
  148. }
  149. ret = mark_allocated_buffer(pcvoid, bytes);
  150. WLog_DBG(TAG, "calloc: %p+%u in <%s>[%d]{%s}", ret, (unsigned int)bytes, function, line, file);
  151. return ret;
  152. }
  153. void* toolkit_memtrace_czalloc(size_t elem, size_t ele_size, const char* file, const char* function, int line)
  154. {
  155. void* buf = toolkit_memtrace_calloc(elem, ele_size, file, function, line);
  156. if (buf) {
  157. memset(buf, 0, elem * ele_size);
  158. }
  159. return buf;
  160. }
  161. static int check_and_junk_buffer(char* pcvoid, size_t bytes)
  162. {
  163. int ret = 0;
  164. int i;
  165. do {
  166. int err = 0;
  167. char* mark = pcvoid + sizeof(mem_trace_t);
  168. for (i = 0; i < MEMTRACE_GUARDSIZE; ++i) {
  169. if (*mark != MEMTRACE_MARK) {
  170. err = 1;
  171. break;
  172. }
  173. *mark = MEMTRACE_JUNK;
  174. mark++;
  175. }
  176. if (err) {
  177. ret = 1;
  178. break;
  179. }
  180. err = 0;
  181. mark += bytes;
  182. for (i = 0; i < MEMTRACE_GUARDSIZE; ++i) {
  183. if (*mark != MEMTRACE_MARK) {
  184. err = 1;
  185. break;
  186. }
  187. *mark = MEMTRACE_JUNK;
  188. mark++;
  189. }
  190. if (err) {
  191. ret = 2;
  192. break;
  193. }
  194. } while (0);
  195. return ret;
  196. }
  197. void toolkit_memtrace_free(void* space, const char* file, const char* function, int line)
  198. {
  199. char* pcvoid = NULL;
  200. size_t bytes = 0;
  201. int i = 0;
  202. int err = 0;
  203. if (space == NULL)
  204. return;
  205. pcvoid = (char*)space - MEMTRACE_HEADER_SIZE;
  206. bytes = ((mem_trace_t*)pcvoid)->bytes;
  207. decrement_allocated_bytes(bytes);
  208. err = check_and_junk_buffer(pcvoid, bytes);
  209. if (err == 1) {
  210. WLog_ERR(TAG, "A memory underflow was detected, address: %p in <%s>[%d]{%s}", space, function, line, file);
  211. abort();
  212. }
  213. else if (err == 2) {
  214. WLog_ERR(TAG, "A memory overflow was detected. address: %p in <%s>[%d]{%s}", space, function, line, file);
  215. abort();
  216. }
  217. /* Overwrite memory to return with junk */
  218. memset(space, MEMTRACE_JUNK, bytes);
  219. WLog_DBG(TAG, "free: %p in <%s>[%d]{%s}", space, function, line, file);
  220. /* Actually free the memory */
  221. free(pcvoid);
  222. WLog_WARN(TAG, "%u bytes still not free for now!", s_mem_allocated_bytes);
  223. return;
  224. }
  225. void* toolkit_memtrace_realloc(void* space, size_t bytes, const char* file, const char* function, int line)
  226. {
  227. void* ret = NULL;
  228. char* pcvoid = NULL;
  229. size_t old_bytes = 0;
  230. int i;
  231. int err;
  232. if (bytes == 0) {
  233. toolkit_memtrace_free(space, file, function, line);
  234. return NULL;
  235. }
  236. if (space == NULL) {
  237. return toolkit_memtrace_malloc(bytes, file, function, line);
  238. }
  239. pcvoid = (char*)space - MEMTRACE_HEADER_SIZE;
  240. old_bytes = ((mem_trace_t*)pcvoid)->bytes;
  241. err = check_and_junk_buffer(pcvoid, old_bytes);
  242. if (err == 1) {
  243. WLog_ERR(TAG, "A memory underflow was detected, address: %p in <%s>[%d]{%s}", space, function, line, file);
  244. abort();
  245. }
  246. else if (err == 2) {
  247. WLog_ERR(TAG, "A memory overflow was detected. address: %p in <%s>[%d]{%s}", space, function, line, file);
  248. abort();
  249. }
  250. pcvoid = (char*)realloc(pcvoid, bytes + sizeof(mem_trace_t) + 2 * MEMTRACE_GUARDSIZE);
  251. if (pcvoid == NULL) {
  252. WLog_ERR(TAG, "realloc() ran out of memory. %u in <%s>[%d]{%s}", (unsigned int)bytes, function, line, file);
  253. return NULL;
  254. }
  255. ret = mark_allocated_buffer(pcvoid, bytes);
  256. decrement_allocated_bytes(old_bytes);
  257. /* If we allocated more memory, then we should fill in junk */
  258. if (old_bytes < bytes) {
  259. const size_t count = bytes - old_bytes;
  260. char* mark = pcvoid + MEMTRACE_HEADER_SIZE + old_bytes;
  261. memset(mark, MEMTRACE_JUNK, count);
  262. }
  263. WLog_DBG(TAG, "realloc: %p+%u from address: %p+%u in <%s>[%d]{%s}",
  264. ret, (unsigned int)bytes, space, (unsigned int)old_bytes, function, line, file);
  265. WLog_DBG(TAG, "free: %p in <%s>[%d]{%s}", space, function, line, file);
  266. return ret;
  267. }
  268. char* toolkit_memtrace_strdup(const char* string, const char* file, const char* function, int line)
  269. {
  270. char* string_copy = NULL;
  271. /* Don't actually handle NULL string, since regular strdup() does not */
  272. TOOLKIT_ASSERT(string != NULL);
  273. //if (strlen(string) == 0) {
  274. // abort(); /**debug: memory leak.*/
  275. //}
  276. string_copy = toolkit_memtrace_malloc(strlen(string) + 1, file, function, line);
  277. if (string_copy != NULL) {
  278. strcpy(string_copy, string);
  279. }
  280. return string_copy;
  281. }