d189260df3d37d4a924e21dad4fca379d5bed531
[framework/graphics/cairo.git] / util / malloc-stats.c
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
27 /* A simple malloc wrapper that prints out statistics on termination */
28
29 #ifndef _GNU_SOURCE
30 #define _GNU_SOURCE
31 #endif
32
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <stdint.h>
36
37 /* caller-logging */
38
39 #include <string.h>
40
41 struct alloc_stat_t {
42         unsigned int num;
43         unsigned long long size;
44 };
45
46 struct alloc_stats_t {
47         struct alloc_stat_t malloc, realloc, total;
48 };
49
50 struct func_stat_t {
51         struct func_stat_t *next;
52
53         const void *addr;
54         const char *name;
55
56         struct alloc_stats_t stat;
57 };
58
59 static struct alloc_stats_t total_allocations;
60 static struct func_stat_t *func_stats[31627];
61 static int func_stats_num;
62
63 #define ARRAY_SIZE(A) (sizeof (A)/sizeof (A[0]))
64
65 static void
66 alloc_stats_add (struct alloc_stats_t *stats, int is_realloc, size_t size)
67 {
68         struct alloc_stat_t *stat = is_realloc ? &stats->realloc : &stats->malloc;
69
70         stats->total.num++;
71         stats->total.size += size;
72
73         stat->num++;
74         stat->size += size;
75 }
76
77 #include <execinfo.h>
78
79 static void *
80 _perm_alloc (size_t size)
81 {
82     static uint8_t *ptr;
83     static size_t rem;
84
85     void *ret;
86
87 #define SUPERBLOCK_SIZE (1<<23)
88 #define align(x, y) (((x) + ((y)-1)) & ~((y)-1))
89
90     size = align (size, 2 * sizeof (void *));
91     if (size > rem || rem == 0) {
92         ptr = malloc (SUPERBLOCK_SIZE);
93         if (ptr == NULL)
94             exit (1);
95         rem = SUPERBLOCK_SIZE;
96     }
97
98 #undef SUPERBLOCK_SIZE
99 #undef align
100
101     ret = ptr;
102     rem -= size;
103     ptr += size;
104
105     return ret;
106 }
107
108 static void
109 resolve_addrs (struct func_stat_t *func_stats, int num)
110 {
111         int i;
112         void **addrs;
113         char **strings;
114
115         addrs = malloc (num * sizeof (void *));
116         for (i = 0; i < num; i++)
117                 addrs[i] = (void *) func_stats[i].addr;
118
119         strings = backtrace_symbols (addrs, num);
120
121         for (i = 0; i < num; i++) {
122                 char *p;
123                 char *name;
124                 int len;
125
126                 p = strchr (strings[i], '\t');
127                 if (p)
128                         p++;
129                 else
130                         p = strings[i];
131
132                 len = strlen (p) + 1;
133                 name = _perm_alloc (len);
134                 memcpy (name, p, len);
135                 func_stats[i].name = name;
136         }
137
138         free (strings);
139         free (addrs);
140 }
141
142 static void
143 func_stats_add (const void *caller, int is_realloc, size_t size)
144 {
145         int i;
146         struct func_stat_t *elt;
147
148         alloc_stats_add (&total_allocations, is_realloc, size);
149
150         i = ((uintptr_t) caller ^ 1215497) % ARRAY_SIZE (func_stats);
151         for (elt = func_stats[i]; elt != NULL; elt = elt->next) {
152                 if (elt->addr == caller)
153                         break;
154         }
155
156         if (elt == NULL) {
157                 func_stats_num++;
158
159                 elt = _perm_alloc (sizeof (struct func_stat_t));
160                 elt->next = func_stats[i];
161                 func_stats[i] = elt;
162                 elt->addr = caller;
163                 elt->name = NULL;
164                 memset (&elt->stat, 0, sizeof (struct alloc_stats_t));
165         }
166
167         alloc_stats_add (&elt->stat, is_realloc, size);
168 }
169
170 /* wrapper stuff */
171
172 #include <malloc.h>
173
174 static void *(*old_malloc)(size_t, const void *);
175 static void *(*old_realloc)(void *, size_t, const void *);
176
177 static void *my_malloc(size_t, const void *);
178 static void *my_realloc(void *, size_t, const void *);
179
180 static void
181 save_hooks (void)
182 {
183         old_malloc  = __malloc_hook;
184         old_realloc = __realloc_hook;
185 }
186
187 static void
188 old_hooks (void)
189 {
190         __malloc_hook  = old_malloc;
191         __realloc_hook  = old_realloc;
192 }
193
194 static void
195 my_hooks (void)
196 {
197         /* should always save the current value */
198         save_hooks ();
199
200         __malloc_hook  = my_malloc;
201         __realloc_hook  = my_realloc;
202 }
203
204 static void *
205 my_malloc(size_t size, const void *caller)
206 {
207         void *ret;
208
209         old_hooks ();
210
211         func_stats_add (caller, 0, size);
212
213         ret = malloc (size);
214         my_hooks ();
215
216         return ret;
217 }
218
219 static void *
220 my_realloc(void *ptr, size_t size, const void *caller)
221 {
222         void *ret;
223
224         old_hooks ();
225
226         func_stats_add (caller, 1, size);
227
228         ret = realloc (ptr, size);
229         my_hooks ();
230
231         return ret;
232 }
233
234 static void
235 my_init_hook(void) {
236         my_hooks ();
237 }
238
239 void (*__malloc_initialize_hook) (void) = my_init_hook;
240
241
242 /* reporting */
243
244 #include <locale.h>
245
246 static void
247 add_alloc_stats (struct alloc_stats_t *a, struct alloc_stats_t *b)
248 {
249         a->total.num += b->total.num;
250         a->total.size += b->total.size;
251         a->malloc.num += b->malloc.num;
252         a->malloc.size += b->malloc.size;
253         a->realloc.num += b->realloc.num;
254         a->realloc.size += b->realloc.size;
255 }
256
257 static void
258 dump_alloc_stats (struct alloc_stats_t *stats, const char *name)
259 {
260         printf ("%8u %'11llu    %8u %'11llu     %8u %'11llu     %s\n",
261                 stats->total.num, stats->total.size,
262                 stats->malloc.num, stats->malloc.size,
263                 stats->realloc.num, stats->realloc.size,
264                 name);
265 }
266
267 static int
268 compare_func_stats_name (const void *pa, const void *pb)
269 {
270         const struct func_stat_t *a = pa, *b = pb;
271         int i;
272
273         i = strcmp (a->name, b->name);
274         if (i)
275                 return i;
276
277         return ((char *) a->addr - (char *) b->addr);
278 }
279
280 static int
281 compare_func_stats (const void *pa, const void *pb)
282 {
283         const struct func_stat_t *a = pa, *b = pb;
284
285         if (a->stat.total.num != b->stat.total.num)
286                 return (a->stat.total.num - b->stat.total.num);
287
288         if (a->stat.total.size != b->stat.total.size)
289                 return (a->stat.total.size - b->stat.total.size);
290
291         return compare_func_stats_name (pa, pb);
292 }
293
294 static int
295 merge_similar_entries (struct func_stat_t *func_stats, int num)
296 {
297         int i, j;
298
299         j = 0;
300         for (i = 1; i < num; i++) {
301                 if (i != j && 0 == strcmp (func_stats[i].name, func_stats[j].name)) {
302                         add_alloc_stats (&func_stats[j].stat, &func_stats[i].stat);
303                 } else {
304                         j++;
305                         if (i != j)
306                                 func_stats[j] = func_stats[i];
307                 }
308         }
309         j++;
310
311         return j;
312 }
313
314 __attribute__ ((destructor))
315 void
316 malloc_stats (void)
317 {
318         unsigned int i, j;
319         struct func_stat_t *sorted_func_stats;
320
321         old_hooks ();
322
323         if (! func_stats_num)
324                 return;
325
326         sorted_func_stats = malloc (sizeof (struct func_stat_t) * (func_stats_num + 1));
327         if (sorted_func_stats == NULL)
328                 return;
329
330         j = 0;
331         for (i = 0; i < ARRAY_SIZE (func_stats); i++) {
332                 struct func_stat_t *elt;
333                 for (elt = func_stats[i]; elt != NULL; elt = elt->next)
334                         sorted_func_stats[j++] = *elt;
335         }
336
337         resolve_addrs (sorted_func_stats, j);
338
339         /* merge entries with same name */
340         qsort (sorted_func_stats, j,
341                sizeof (struct func_stat_t), compare_func_stats_name);
342         j = merge_similar_entries (sorted_func_stats, j);
343
344         qsort (sorted_func_stats, j,
345                sizeof (struct func_stat_t), compare_func_stats);
346
347         /* add total */
348         sorted_func_stats[j].next = NULL;
349         sorted_func_stats[j].addr = (void *) -1;
350         sorted_func_stats[j].name = "(total)";
351         sorted_func_stats[j].stat = total_allocations;
352         j++;
353
354         setlocale (LC_ALL, "");
355
356         printf ("        TOTAL                   MALLOC                 REALLOC\n");
357         printf ("     num       size         num        size         num        size\n");
358
359         for (i = 0; i < j; i++) {
360                 dump_alloc_stats (&sorted_func_stats[i].stat,
361                                   sorted_func_stats[i].name);
362         }
363
364         /* XXX free other stuff? */
365
366         free (sorted_func_stats);
367 }