malloc-stats.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. /* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
  2. /*
  3. * Copyright © 2007 Red Hat, Inc.
  4. *
  5. * Permission to use, copy, modify, distribute, and sell this software
  6. * and its documentation for any purpose is hereby granted without
  7. * fee, provided that the above copyright notice appear in all copies
  8. * and that both that copyright notice and this permission notice
  9. * appear in supporting documentation, and that the name of
  10. * Red Hat, Inc. not be used in advertising or publicity pertaining to
  11. * distribution of the software without specific, written prior
  12. * permission. Red Hat, Inc. makes no representations about the
  13. * suitability of this software for any purpose. It is provided "as
  14. * is" without express or implied warranty.
  15. *
  16. * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
  17. * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
  18. * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
  19. * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
  20. * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
  21. * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
  22. * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  23. *
  24. * Author: Behdad Esfahbod <behdad@behdad.org>
  25. */
  26. /* A simple malloc wrapper that prints out statistics on termination */
  27. #ifndef _GNU_SOURCE
  28. #define _GNU_SOURCE
  29. #endif
  30. #include <stdlib.h>
  31. #include <stdio.h>
  32. #include <stdint.h>
  33. /* caller-logging */
  34. #include <string.h>
  35. struct alloc_stat_t {
  36. unsigned int num;
  37. unsigned long long size;
  38. };
  39. struct alloc_stats_t {
  40. struct alloc_stat_t malloc, realloc, total;
  41. };
  42. struct func_stat_t {
  43. struct func_stat_t *next;
  44. const void *addr;
  45. const char *name;
  46. struct alloc_stats_t stat;
  47. };
  48. static struct alloc_stats_t total_allocations;
  49. static struct func_stat_t *func_stats[31627];
  50. static int func_stats_num;
  51. #ifndef ARRAY_LENGTH
  52. #define ARRAY_LENGTH(__array) ((int) (sizeof (__array) / sizeof (__array[0])))
  53. #endif
  54. static void
  55. alloc_stats_add (struct alloc_stats_t *stats, int is_realloc, size_t size)
  56. {
  57. struct alloc_stat_t *stat = is_realloc ? &stats->realloc : &stats->malloc;
  58. stats->total.num++;
  59. stats->total.size += size;
  60. stat->num++;
  61. stat->size += size;
  62. }
  63. #include <execinfo.h>
  64. static void *
  65. _perm_alloc (size_t size)
  66. {
  67. static uint8_t *ptr;
  68. static size_t rem;
  69. void *ret;
  70. #define SUPERBLOCK_SIZE (1<<23)
  71. #define align(x, y) (((x) + ((y)-1)) & ~((y)-1))
  72. size = align (size, 2 * sizeof (void *));
  73. if (size > rem || rem == 0) {
  74. ptr = malloc (SUPERBLOCK_SIZE);
  75. if (ptr == NULL)
  76. exit (1);
  77. rem = SUPERBLOCK_SIZE;
  78. }
  79. #undef SUPERBLOCK_SIZE
  80. #undef align
  81. ret = ptr;
  82. rem -= size;
  83. ptr += size;
  84. return ret;
  85. }
  86. static void
  87. resolve_addrs (struct func_stat_t *func_stats, int num)
  88. {
  89. int i;
  90. void **addrs;
  91. char **strings;
  92. addrs = malloc (num * sizeof (void *));
  93. for (i = 0; i < num; i++)
  94. addrs[i] = (void *) func_stats[i].addr;
  95. strings = backtrace_symbols (addrs, num);
  96. for (i = 0; i < num; i++) {
  97. char *p;
  98. char *name;
  99. int len;
  100. p = strchr (strings[i], '\t');
  101. if (p)
  102. p++;
  103. else
  104. p = strings[i];
  105. len = strlen (p) + 1;
  106. name = _perm_alloc (len);
  107. memcpy (name, p, len);
  108. func_stats[i].name = name;
  109. }
  110. free (strings);
  111. free (addrs);
  112. }
  113. static void
  114. func_stats_add (const void *caller, int is_realloc, size_t size)
  115. {
  116. int i;
  117. struct func_stat_t *elt;
  118. alloc_stats_add (&total_allocations, is_realloc, size);
  119. i = ((uintptr_t) caller ^ 1215497) % ARRAY_LENGTH (func_stats);
  120. for (elt = func_stats[i]; elt != NULL; elt = elt->next) {
  121. if (elt->addr == caller)
  122. break;
  123. }
  124. if (elt == NULL) {
  125. func_stats_num++;
  126. elt = _perm_alloc (sizeof (struct func_stat_t));
  127. elt->next = func_stats[i];
  128. func_stats[i] = elt;
  129. elt->addr = caller;
  130. elt->name = NULL;
  131. memset (&elt->stat, 0, sizeof (struct alloc_stats_t));
  132. }
  133. alloc_stats_add (&elt->stat, is_realloc, size);
  134. }
  135. /* wrapper stuff */
  136. #include <malloc.h>
  137. static void *(*old_malloc)(size_t, const void *);
  138. static void *(*old_realloc)(void *, size_t, const void *);
  139. static void *my_malloc(size_t, const void *);
  140. static void *my_realloc(void *, size_t, const void *);
  141. static void
  142. save_hooks (void)
  143. {
  144. old_malloc = __malloc_hook;
  145. old_realloc = __realloc_hook;
  146. }
  147. static void
  148. old_hooks (void)
  149. {
  150. __malloc_hook = old_malloc;
  151. __realloc_hook = old_realloc;
  152. }
  153. static void
  154. my_hooks (void)
  155. {
  156. /* should always save the current value */
  157. save_hooks ();
  158. __malloc_hook = my_malloc;
  159. __realloc_hook = my_realloc;
  160. }
  161. static void *
  162. my_malloc(size_t size, const void *caller)
  163. {
  164. void *ret;
  165. old_hooks ();
  166. func_stats_add (caller, 0, size);
  167. ret = malloc (size);
  168. my_hooks ();
  169. return ret;
  170. }
  171. static void *
  172. my_realloc(void *ptr, size_t size, const void *caller)
  173. {
  174. void *ret;
  175. old_hooks ();
  176. func_stats_add (caller, 1, size);
  177. ret = realloc (ptr, size);
  178. my_hooks ();
  179. return ret;
  180. }
  181. static void
  182. my_init_hook(void) {
  183. my_hooks ();
  184. }
  185. void (*__volatile __malloc_initialize_hook) (void) = my_init_hook;
  186. /* reporting */
  187. #include <locale.h>
  188. static void
  189. add_alloc_stats (struct alloc_stats_t *a, struct alloc_stats_t *b)
  190. {
  191. a->total.num += b->total.num;
  192. a->total.size += b->total.size;
  193. a->malloc.num += b->malloc.num;
  194. a->malloc.size += b->malloc.size;
  195. a->realloc.num += b->realloc.num;
  196. a->realloc.size += b->realloc.size;
  197. }
  198. static void
  199. dump_alloc_stats (struct alloc_stats_t *stats, const char *name)
  200. {
  201. printf ("%8u %'11llu %8u %'11llu %8u %'11llu %s\n",
  202. stats->total.num, stats->total.size,
  203. stats->malloc.num, stats->malloc.size,
  204. stats->realloc.num, stats->realloc.size,
  205. name);
  206. }
  207. static int
  208. compare_func_stats_name (const void *pa, const void *pb)
  209. {
  210. const struct func_stat_t *a = pa, *b = pb;
  211. int i;
  212. i = strcmp (a->name, b->name);
  213. if (i)
  214. return i;
  215. return ((char *) a->addr - (char *) b->addr);
  216. }
  217. static int
  218. compare_func_stats (const void *pa, const void *pb)
  219. {
  220. const struct func_stat_t *a = pa, *b = pb;
  221. if (a->stat.total.num != b->stat.total.num)
  222. return (a->stat.total.num - b->stat.total.num);
  223. if (a->stat.total.size != b->stat.total.size)
  224. return (a->stat.total.size - b->stat.total.size);
  225. return compare_func_stats_name (pa, pb);
  226. }
  227. static int
  228. merge_similar_entries (struct func_stat_t *func_stats, int num)
  229. {
  230. int i, j;
  231. j = 0;
  232. for (i = 1; i < num; i++) {
  233. if (i != j && 0 == strcmp (func_stats[i].name, func_stats[j].name)) {
  234. add_alloc_stats (&func_stats[j].stat, &func_stats[i].stat);
  235. } else {
  236. j++;
  237. if (i != j)
  238. func_stats[j] = func_stats[i];
  239. }
  240. }
  241. j++;
  242. return j;
  243. }
  244. __attribute__ ((destructor))
  245. void
  246. malloc_stats (void)
  247. {
  248. unsigned int i, j;
  249. struct func_stat_t *sorted_func_stats;
  250. old_hooks ();
  251. if (! func_stats_num)
  252. return;
  253. sorted_func_stats = malloc (sizeof (struct func_stat_t) * (func_stats_num + 1));
  254. if (sorted_func_stats == NULL)
  255. return;
  256. j = 0;
  257. for (i = 0; i < ARRAY_LENGTH (func_stats); i++) {
  258. struct func_stat_t *elt;
  259. for (elt = func_stats[i]; elt != NULL; elt = elt->next)
  260. sorted_func_stats[j++] = *elt;
  261. }
  262. resolve_addrs (sorted_func_stats, j);
  263. /* merge entries with same name */
  264. qsort (sorted_func_stats, j,
  265. sizeof (struct func_stat_t), compare_func_stats_name);
  266. j = merge_similar_entries (sorted_func_stats, j);
  267. qsort (sorted_func_stats, j,
  268. sizeof (struct func_stat_t), compare_func_stats);
  269. /* add total */
  270. sorted_func_stats[j].next = NULL;
  271. sorted_func_stats[j].addr = (void *) -1;
  272. sorted_func_stats[j].name = "(total)";
  273. sorted_func_stats[j].stat = total_allocations;
  274. j++;
  275. setlocale (LC_ALL, "");
  276. printf (" TOTAL MALLOC REALLOC\n");
  277. printf (" num size num size num size\n");
  278. for (i = 0; i < j; i++) {
  279. dump_alloc_stats (&sorted_func_stats[i].stat,
  280. sorted_func_stats[i].name);
  281. }
  282. /* XXX free other stuff? */
  283. free (sorted_func_stats);
  284. }