memtrace.c 9.3 KB

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