memory.c 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. #include "ik/memory.h"
  2. #include "ik/bst_vector.h"
  3. #include "ik/backtrace.h"
  4. #include <stdlib.h>
  5. #include <stdio.h>
  6. #include <string.h>
  7. #include <assert.h>
  8. #define BACKTRACE_OMIT_COUNT 2
  9. #ifdef IK_MEMORY_DEBUGGING
  10. static uintptr_t g_allocations = 0;
  11. static uintptr_t d_deg_allocations = 0;
  12. static uintptr_t g_ignore_bstv_malloc = 0;
  13. static bstv_t report;
  14. typedef struct report_info_t
  15. {
  16. uintptr_t location;
  17. uintptr_t size;
  18. # ifdef IK_MEMORY_BACKTRACE
  19. int backtrace_size;
  20. char** backtrace;
  21. # endif
  22. } report_info_t;
  23. /* ------------------------------------------------------------------------- */
  24. void
  25. ik_memory_init(void)
  26. {
  27. g_allocations = 0;
  28. d_deg_allocations = 0;
  29. /*
  30. * Init bst vector of report objects and force it to allocate by adding
  31. * and removing one item. This fixes a bug where the number of memory leaks
  32. * would be wrong in the case of MALLOC() never being called.
  33. */
  34. g_ignore_bstv_malloc = 1;
  35. bstv_construct(&report);
  36. bstv_insert(&report, 0, NULL); bstv_erase(&report, 0);
  37. g_ignore_bstv_malloc = 0;
  38. }
  39. /* ------------------------------------------------------------------------- */
  40. void*
  41. malloc_wrapper(intptr_t size)
  42. {
  43. void* p = NULL;
  44. report_info_t* info = NULL;
  45. /* breaking from this will clean up and return NULL */
  46. for (;;)
  47. {
  48. /* allocate */
  49. p = malloc(size);
  50. if (p)
  51. ++g_allocations;
  52. else
  53. break;
  54. /*
  55. * Record allocation info. Call to bstv may allocate memory,
  56. * so set flag to ignore the call to malloc() when inserting.
  57. */
  58. if (!g_ignore_bstv_malloc)
  59. {
  60. g_ignore_bstv_malloc = 1;
  61. info = (report_info_t*)malloc(sizeof(report_info_t));
  62. if (!info)
  63. {
  64. fprintf(stderr, "[memory] ERROR: malloc() for report_info_t failed"
  65. " -- not enough memory.\n");
  66. g_ignore_bstv_malloc = 0;
  67. break;
  68. }
  69. /* record the location and size of the allocation */
  70. info->location = (uintptr_t)p;
  71. info->size = size;
  72. /* if (enabled, generate a backtrace so we know where memory leaks
  73. * occurred */
  74. # ifdef IK_MEMORY_BACKTRACE
  75. if (!(info->backtrace = get_backtrace(&info->backtrace_size)))
  76. fprintf(stderr, "[memory] WARNING: Failed to generate backtrace\n");
  77. # endif
  78. /* insert into bstv */
  79. if (bstv_insert(&report, (uintptr_t)p, info) == 1)
  80. {
  81. fprintf(stderr,
  82. "[memory] WARNING: Hash collision occurred when inserting\n"
  83. "into memory report bstv. On 64-bit systems the pointers are\n"
  84. "rounded down to 32-bit unsigned integers, so even though\n"
  85. "it's rare, collisions can happen.\n\n"
  86. "The matching call to FREE() will generate a warning saying\n"
  87. "something is being freed that was never allocated. This is to\n"
  88. "be expected and can be ignored.\n");
  89. # ifdef IK_MEMORY_BACKTRACE
  90. {
  91. char** bt;
  92. int bt_size, i;
  93. if ((bt = get_backtrace(&bt_size)))
  94. {
  95. printf(" backtrace to where malloc() was called:\n");
  96. for (i = 0; i < bt_size; ++i)
  97. printf(" %s\n", bt[i]);
  98. printf(" -----------------------------------------\n");
  99. free(bt);
  100. }
  101. else
  102. fprintf(stderr, "[memory] WARNING: Failed to generate backtrace\n");
  103. }
  104. # endif
  105. }
  106. g_ignore_bstv_malloc = 0;
  107. }
  108. /* success */
  109. return p;
  110. }
  111. /* failure */
  112. if (p)
  113. {
  114. free(p);
  115. --g_allocations;
  116. }
  117. if (info)
  118. {
  119. # ifdef IK_MEMORY_BACKTRACE
  120. if (info->backtrace)
  121. free(info->backtrace);
  122. # endif
  123. free(info);
  124. }
  125. return NULL;
  126. }
  127. /* ------------------------------------------------------------------------- */
  128. void
  129. free_wrapper(void* ptr)
  130. {
  131. /* find matching allocation and remove from bstv */
  132. if (!g_ignore_bstv_malloc)
  133. {
  134. report_info_t* info = (report_info_t*)bstv_erase(&report, (uintptr_t)ptr);
  135. if (info)
  136. {
  137. # ifdef IK_MEMORY_BACKTRACE
  138. if (info->backtrace)
  139. free(info->backtrace);
  140. else
  141. fprintf(stderr, "[memory] WARNING: free(): Allocation didn't "
  142. "have a backtrace (it was NULL)\n");
  143. # endif
  144. free(info);
  145. }
  146. else
  147. {
  148. # ifdef IK_MEMORY_BACKTRACE
  149. char** bt;
  150. int bt_size, i;
  151. fprintf(stderr, " -----------------------------------------\n");
  152. # endif
  153. fprintf(stderr, " WARNING: Freeing something that was never allocated\n");
  154. # ifdef IK_MEMORY_BACKTRACE
  155. if ((bt = get_backtrace(&bt_size)))
  156. {
  157. fprintf(stderr, " backtrace to where free() was called:\n");
  158. for (i = 0; i < bt_size; ++i)
  159. fprintf(stderr, " %s\n", bt[i]);
  160. fprintf(stderr, " -----------------------------------------\n");
  161. free(bt);
  162. }
  163. else
  164. fprintf(stderr, "[memory] WARNING: Failed to generate backtrace\n");
  165. # endif
  166. }
  167. }
  168. if (ptr)
  169. {
  170. ++d_deg_allocations;
  171. free(ptr);
  172. }
  173. else
  174. fprintf(stderr, "Warning: free(NULL)\n");
  175. }
  176. /* ------------------------------------------------------------------------- */
  177. uintptr_t
  178. ik_memory_deinit(void)
  179. {
  180. uintptr_t leaks;
  181. --g_allocations; /* this is the single allocation still held by the report vector */
  182. printf("=========================================\n");
  183. printf("Inverse Kinematics Memory Report\n");
  184. printf("=========================================\n");
  185. /* report details on any g_allocations that were not de-allocated */
  186. if (report.vector.count != 0)
  187. {
  188. BSTV_FOR_EACH(&report, report_info_t, key, info)
  189. printf(" un-freed memory at %p, size %p\n", (void*)info->location, (void*)info->size);
  190. mutated_string_and_hex_dump((void*)info->location, info->size);
  191. # ifdef IK_MEMORY_BACKTRACE
  192. printf(" Backtrace to where malloc() was called:\n");
  193. {
  194. intptr_t i;
  195. for (i = BACKTRACE_OMIT_COUNT; i < info->backtrace_size; ++i)
  196. printf(" %s\n", info->backtrace[i]);
  197. }
  198. free(info->backtrace); /* this was allocated when malloc() was called */
  199. printf(" -----------------------------------------\n");
  200. # endif
  201. free(info);
  202. BSTV_END_EACH
  203. printf("=========================================\n");
  204. }
  205. /* overall report */
  206. leaks = (g_allocations > d_deg_allocations ? g_allocations - d_deg_allocations : d_deg_allocations - g_allocations);
  207. printf("allocations: %lu\n", g_allocations);
  208. printf("deallocations: %lu\n", d_deg_allocations);
  209. printf("memory leaks: %lu\n", leaks);
  210. printf("=========================================\n");
  211. ++g_allocations; /* this is the single allocation still held by the report vector */
  212. g_ignore_bstv_malloc = 1;
  213. bstv_clear_free(&report);
  214. return leaks;
  215. }
  216. #else /* IK_MEMORY_DEBUGGING */
  217. void ik_memory_init(void) {}
  218. uintptr_t ik_memory_deinit(void) { return 0; }
  219. #endif /* IK_MEMORY_DEBUGGING */
  220. /* ------------------------------------------------------------------------- */
  221. void
  222. mutated_string_and_hex_dump(void* data, intptr_t length_in_bytes)
  223. {
  224. char* dump;
  225. intptr_t i;
  226. /* allocate and copy data into new buffer */
  227. if (!(dump = malloc(length_in_bytes + 1)))
  228. {
  229. fprintf(stderr, "[memory] WARNING: Failed to malloc() space for dump\n");
  230. return;
  231. }
  232. memcpy(dump, data, length_in_bytes);
  233. dump[length_in_bytes] = '\0';
  234. /* mutate null terminators into dots */
  235. for (i = 0; i != length_in_bytes; ++i)
  236. if (dump[i] == '\0')
  237. dump[i] = '.';
  238. /* dump */
  239. printf(" mutated string dump: %s\n", dump);
  240. printf(" hex dump: ");
  241. for (i = 0; i != length_in_bytes; ++i)
  242. printf(" %02x", (unsigned char)dump[i]);
  243. printf("\n");
  244. free(dump);
  245. }