2 * Copyright (c) 1999-2001
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 #include <sys/types.h>
42 typedef struct Alloc_info
44 struct Alloc_info *next;
51 static ainfo ainfos = NULL;
52 static region profile_region = NULL;
54 /* perror(s) then exit */
55 void pfail(const char *s)
61 /**************************************************************************
63 * Log information about an allocation -- generic *
65 **************************************************************************/
67 static int registered_exit = 0;
69 static ainfo find_ainfo(char *file, int line)
73 for (ai = ainfos; ai; ai = ai->next)
74 if (line == ai->line && !strcmp(file, ai->file))
80 fprintf(stderr, "Registration of profile at exit failed\n");
85 profile_region = newregion();
86 ai = ralloc(profile_region, struct Alloc_info);
96 /**************************************************************************
98 * Log information about an allocation -- GCC *
100 * WARNING: This code uses __builtin_return_address, a non-portable *
101 * feature of gcc, to trace the call chain back. You'll also get ugly *
102 * output unless the addr2line (in GNU binutils) is installed. *
104 * ANOTHER WARNING: The depths hard-coded in find_cinfo are only correct *
105 * if find_cinfo is inlined. Ack! *
107 **************************************************************************/
109 #define REGION_PROFILE_DEPTH 2
111 #if defined(__GNUC__) && defined(__OPTIMIZE__) && REGION_PROFILE_DEPTH > 1
117 #if REGION_PROFILE_DEPTH > 6
118 #error "REGION_PROFILE_DEPTH must be less than 6. See find_cinfo()."
121 typedef struct Call_info
123 struct Call_info *next;
124 void **stack; /* Array holding the call chain */
129 static cinfo cinfos = NULL;
131 /* Find the current call chain and return a pointer to our status for
132 it, or allocate a new entry if there is none. */
133 static cinfo find_cinfo(void)
135 void *calls[REGION_PROFILE_DEPTH];
139 /* Compute the call chain. This is an awful hack. */
141 if (i < REGION_PROFILE_DEPTH)
142 calls[i++] = __builtin_return_address(1);
143 if (i < REGION_PROFILE_DEPTH)
144 calls[i++] = __builtin_return_address(2);
145 if (i < REGION_PROFILE_DEPTH)
146 calls[i++] = __builtin_return_address(3);
147 if (i < REGION_PROFILE_DEPTH)
148 calls[i++] = __builtin_return_address(4);
149 if (i < REGION_PROFILE_DEPTH)
150 calls[i++] = __builtin_return_address(5);
151 if (i < REGION_PROFILE_DEPTH)
152 calls[i++] = __builtin_return_address(6);
153 /* Add more if you want a higher call-depth (why would you?) */
156 for (ci = cinfos; ci; ci = ci->next)
157 if (!memcmp(calls, ci->stack, REGION_PROFILE_DEPTH*sizeof(void *)))
161 profile_region = newregion();
162 ci = ralloc(profile_region, struct Call_info);
163 ci->stack = rarrayalloc(profile_region, REGION_PROFILE_DEPTH, void *);
164 memcpy(ci->stack, calls, REGION_PROFILE_DEPTH*sizeof(void *));
174 static void add_alloc(char *file, int line, int size)
176 ainfo ai = find_ainfo(file, line);
190 /**************************************************************************
192 * Intercept and log calls to region library *
194 **************************************************************************/
196 void *profile_typed_ralloc(region r, size_t size, type_t type, char *file,
199 add_alloc(file, line, size);
200 return typed_ralloc(r, size, type);
203 void *profile_typed_rarrayalloc(region r, size_t n, size_t size, type_t type,
204 char *file, int line)
206 add_alloc(file, line, n*size);
207 return typed_rarrayalloc(r, n, size, type);
210 void *profile_typed_rarrayextend(region r, void *old, size_t n, size_t size,
211 type_t type, char *file, int line)
213 add_alloc(file, line, n*size); /* XXX: Fix */
214 return typed_rarrayextend(r, old, n, size, type);
217 char *profile_rstralloc(region r, size_t size, char *file, int line)
219 add_alloc(file, line, size);
220 return rstralloc(r, size);
223 char *profile_rstralloc0(region r, size_t size, char *file, int line)
225 add_alloc(file, line, size);
226 return rstralloc0(r, size);
229 char *profile_rstrdup(region r, const char *s, char *file, int line)
231 add_alloc(file, line, strlen(s));
232 return rstrdup(r, s);
235 char *profile_rstrextend(region r, const char *old, size_t newsize,
236 char *file, int line)
238 add_alloc(file, line, newsize); /* XXX: Fix */
239 return rstrextend(r, old, newsize);
242 char *profile_rstrextend0(region r, const char *old, size_t newsize,
243 char *file, int line)
245 add_alloc(file, line, newsize); /* XXX: Fix */
246 return rstrextend0(r, old, newsize);
249 /**************************************************************************
251 * Display results -- generic *
253 **************************************************************************/
255 static FILE *out = NULL;
257 /* Generic list -- used for generic sorting. Note that next field is
264 /* Sort a list. cmp should sort in reverse order. */
265 static list sort_list(list l, int (*cmp)(const void *, const void *))
272 /* Compute length of list */
273 for (cur = l, length = 0; cur; cur = cur->next, length++);
275 temp_region = newregion();
276 sorted = rarrayalloc(temp_region, length, list *);
277 for (cur = l, i = 0; cur; cur = cur->next)
279 qsort(sorted, length, sizeof(list *), cmp);
282 for (i = 0; i < length; i++)
288 deleteregion(temp_region);
293 typedef struct File_info
295 struct File_info *next;
302 static finfo finfos = NULL;
304 static int finfo_cmp(const void *a, const void *b)
306 finfo *afi = (finfo *) a;
307 finfo *bfi = (finfo *) b;
308 return (*afi)->size - (*bfi)->size; /* Reverse order */
311 static void print_finfos(void)
314 unsigned long size, sites, calls;
316 finfos = (finfo) sort_list((list) finfos, finfo_cmp);
317 size = sites = calls = 0;
318 fprintf(out, " Bytes | Sites | Calls | File\n");
319 fprintf(out, " ------------+-------+----------+---------------------\n");
320 for (fi = finfos; fi; fi = fi->next)
325 fprintf(out, " %12lu | %5lu | %8lu | %s\n",
326 fi->size, fi->sites, fi->calls, fi->file);
328 fprintf(out, " ------------+-------+----------+---------------------\n");
329 fprintf(out, " %12lu | %5lu | %8lu | Total\n",
334 static int ainfo_cmp(const void *a, const void *b)
336 ainfo *afi = (ainfo *) a;
337 ainfo *bfi = (ainfo *) b;
338 return (*afi)->size - (*bfi)->size; /* Reverse order */
341 static void print_ainfos(void)
345 unsigned long size, calls;
347 ainfos = (ainfo) sort_list((list) ainfos, ainfo_cmp);
349 fprintf(out, " Bytes | Calls | Site\n");
350 fprintf(out, " ------------+----------+---------------------\n");
351 for (ai = ainfos; ai; ai = ai->next)
355 fprintf(out, " %12lu | %8lu | %s:%d\n",
356 ai->size, ai->calls, ai->file, ai->line);
358 fprintf(out, " ------------+----------+---------------------\n");
359 fprintf(out, " %12lu | %8lu | Total\n",
363 static finfo find_finfo(char *file)
367 for (fi = finfos; fi; fi = fi->next)
368 if (!strcmp(file, fi->file))
371 fi = ralloc(profile_region, struct File_info);
381 static void gather_finfo(void)
385 for (ai = ainfos; ai; ai = ai->next)
387 finfo fi = find_finfo(ai->file);
388 fi->size += ai->size;
389 fi->calls += ai->calls;
394 /**************************************************************************
396 * Display results -- GCC *
398 **************************************************************************/
403 int child_in[2], child_out[2]; /* pipes to child process */
405 static void start_prettiness(void)
407 if (pipe(child_in) || pipe(child_out))
408 pfail("Unable to open pipe to child process");
409 if (!(child_pid = fork()))
415 if (dup2(child_in[0], STDIN_FILENO) == -1)
416 pfail("Unable to open pipe from parent");
419 if (dup2(child_out[1], STDOUT_FILENO) == -1)
420 pfail("Unable to open pipe to parent");
424 parent_pid = getppid();
425 snprintf(filename, 64, "/proc/%d/exe", parent_pid);
427 execlp("addr2line", "addr2line", "-s", "-e", filename, 0);
428 fprintf(stderr, "Unable to fork addr2line\n");
438 /* Turn p into a file:line string */
439 static char *prettify(void *p)
442 static char buf[BUFSIZE];
445 /*printf("To child: %p\n", p);*/
446 size = snprintf(buf, BUFSIZE, "%p\n", p);
447 write(child_in[1], buf, size);
448 size = read(child_out[0], buf, BUFSIZE - 1);
450 pfail("Unable to read from child process");
451 buf[size-1] = '\0'; /* Kill \n */
452 /*printf("Read: [%s]\n", buf);*/
456 static void end_prettiness(void)
459 kill(child_pid, SIGHUP);
462 static int cinfo_cmp(const void *a, const void *b)
464 cinfo *aci = (cinfo *) a;
465 cinfo *bci = (cinfo *) b;
466 return (*aci)->size - (*bci)->size; /* Reverse order */
469 /* Print the call chain information out to a file. */
470 static void print_cinfos(void)
473 unsigned long size, calls;
476 cinfos = (cinfo) sort_list((list) cinfos, cinfo_cmp);
479 fprintf(out, " Bytes | Calls | Call Stack\n");
480 fprintf(out, " ------------+----------+---------------------\n");
481 for (ci = cinfos; ci; ci = ci->next)
485 fprintf(out, " %12lu | %8lu | ", ci->size, ci->calls);
486 for (i = 0; i < REGION_PROFILE_DEPTH; i++)
487 fprintf(out, "%s ", prettify(ci->stack[i]));
490 fprintf(out, " ------------+----------+---------------------\n");
491 fprintf(out, " %12lu | %8lu | Total\n",
500 if (profile_region == NULL)
505 if (!(out = fopen("profile.out", "w")))
506 pfail("Unable to open profile.out");
508 fprintf(out, "---------------------------\n");
509 fprintf(out, "Region Library Memory Usage\n");
510 fprintf(out, "---------------------------\n\n");