OSDN Git Service

2004-07-15 Roman Zippel <zippel@linux-m68k.org>
[pf3gnuchains/gcc-fork.git] / libbanshee / libcompat / profile.c
1 /*
2  * Copyright (c) 1999-2001
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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.
16  *
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
27  * SUCH DAMAGE.
28  *
29  */
30
31 #include <assert.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <sys/types.h>
37 #include <signal.h>
38 #undef REGION_PROFILE
39 #include "regions.h"
40 #include "profile.h"
41
42 typedef struct Alloc_info
43 {
44   struct Alloc_info *next;
45   char *file;
46   int line;
47   unsigned long size;
48   unsigned long calls;
49 } *ainfo;
50
51 static ainfo ainfos = NULL;
52 static region profile_region = NULL;
53
54 /* perror(s) then exit */
55 void pfail(const char *s)
56 {
57   perror(s);
58   exit(EXIT_FAILURE);
59 }
60
61 /**************************************************************************
62  *                                                                        *
63  * Log information about an allocation -- generic                         *
64  *                                                                        *
65  **************************************************************************/
66
67 static int registered_exit = 0;
68
69 static ainfo find_ainfo(char *file, int line)
70 {
71   ainfo ai;
72
73   for (ai = ainfos; ai; ai = ai->next)
74     if (line == ai->line && !strcmp(file, ai->file))
75       return ai;
76
77   if (!registered_exit)
78     {
79       if (atexit(profile))
80         fprintf(stderr, "Registration of profile at exit failed\n");
81       registered_exit = 1;
82     }
83
84   if (!profile_region)
85     profile_region = newregion();
86   ai = ralloc(profile_region, struct Alloc_info);
87   ai->file = file;
88   ai->line = line;
89   ai->size = 0;
90   ai->calls = 0;
91   ai->next = ainfos;
92   ainfos = ai;
93   return ai;
94 }
95
96 /**************************************************************************
97  *                                                                        *
98  * Log information about an allocation -- GCC                             *
99  *                                                                        *
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.            *
103  *                                                                        *
104  * ANOTHER WARNING:  The depths hard-coded in find_cinfo are only correct *
105  * if find_cinfo is inlined.  Ack!                                        *
106  *                                                                        *
107  **************************************************************************/
108
109 #define REGION_PROFILE_DEPTH 2
110 #undef TRACE_STACK
111 #if defined(__GNUC__) && defined(__OPTIMIZE__) && REGION_PROFILE_DEPTH > 1
112 #define TRACE_STACK
113 #endif
114
115 #ifdef TRACE_STACK
116
117 #if REGION_PROFILE_DEPTH > 6
118 #error "REGION_PROFILE_DEPTH must be less than 6.  See find_cinfo()."
119 #endif
120
121 typedef struct Call_info
122 {
123   struct Call_info *next;
124   void **stack;         /* Array holding the call chain */
125   unsigned long size;
126   unsigned long calls;
127 } *cinfo;
128
129 static cinfo cinfos = NULL;
130
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)
134 {
135   void *calls[REGION_PROFILE_DEPTH];
136   int i;
137   cinfo ci;
138
139   /* Compute the call chain.  This is an awful hack. */
140   i = 0;
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?) */
154
155   /* Find it */
156   for (ci = cinfos; ci; ci = ci->next)
157     if (!memcmp(calls, ci->stack, REGION_PROFILE_DEPTH*sizeof(void *)))
158       return ci;
159
160   if (!profile_region)
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 *));
165   ci->size = 0;
166   ci->calls = 0;
167   ci->next = cinfos;
168   cinfos = ci;
169   return ci;
170   
171 }
172 #endif
173
174 static void add_alloc(char *file, int line, int size)
175 {
176   ainfo ai = find_ainfo(file, line);
177   ai->calls++;
178   ai->size += size;
179 #ifdef TRACE_STACK
180   {
181     cinfo ci;
182
183     ci = find_cinfo();
184     ci->calls++;
185     ci->size += size;
186   }
187 #endif
188 }
189
190 /**************************************************************************
191  *                                                                        *
192  * Intercept and log calls to region library                              *
193  *                                                                        *
194  **************************************************************************/
195
196 void *profile_typed_ralloc(region r, size_t size, type_t type, char *file,
197                            int line)
198 {
199   add_alloc(file, line, size);
200   return typed_ralloc(r, size, type);
201 }
202
203 void *profile_typed_rarrayalloc(region r, size_t n, size_t size, type_t type,
204                                 char *file, int line)
205 {
206   add_alloc(file, line, n*size);
207   return typed_rarrayalloc(r, n, size, type);
208 }
209
210 void *profile_typed_rarrayextend(region r, void *old, size_t n, size_t size,
211                                  type_t type, char *file, int line)
212 {
213   add_alloc(file, line, n*size); /* XXX: Fix */
214   return typed_rarrayextend(r, old, n, size, type);
215 }
216
217 char *profile_rstralloc(region r, size_t size, char *file, int line)
218 {
219   add_alloc(file, line, size);
220   return rstralloc(r, size);
221 }
222
223 char *profile_rstralloc0(region r, size_t size, char *file, int line)
224 {
225   add_alloc(file, line, size);
226   return rstralloc0(r, size);
227 }
228
229 char *profile_rstrdup(region r, const char *s, char *file, int line)
230 {
231   add_alloc(file, line, strlen(s));
232   return rstrdup(r, s);
233 }
234
235 char *profile_rstrextend(region r, const char *old, size_t newsize,
236                          char *file, int line)
237 {
238   add_alloc(file, line, newsize); /* XXX: Fix */
239   return rstrextend(r, old, newsize);
240 }
241
242 char *profile_rstrextend0(region r, const char *old, size_t newsize,
243                           char *file, int line)
244 {
245   add_alloc(file, line, newsize); /* XXX: Fix */
246   return rstrextend0(r, old, newsize);
247 }
248
249 /**************************************************************************
250  *                                                                        *
251  * Display results -- generic                                             *
252  *                                                                        *
253  **************************************************************************/
254
255 static FILE *out = NULL;
256
257 /* Generic list -- used for generic sorting.  Note that next field is
258    at the top. */
259 typedef struct List
260 {
261   struct List *next;
262 } *list;
263
264 /* Sort a list.  cmp should sort in reverse order. */
265 static list sort_list(list l, int (*cmp)(const void *, const void *))
266 {
267   list cur, result;
268   list *sorted;
269   int i, length;
270   region temp_region;
271
272   /* Compute length of list */
273   for (cur = l, length = 0; cur; cur = cur->next, length++);
274
275   temp_region = newregion();
276   sorted = rarrayalloc(temp_region, length, list *);
277   for (cur = l, i = 0; cur; cur = cur->next)
278     sorted[i++] = cur;
279   qsort(sorted, length, sizeof(list *), cmp);
280
281   result = NULL;
282   for (i = 0; i < length; i++)
283     {
284       cur = result;
285       result = sorted[i];
286       result->next = cur;
287     }
288   deleteregion(temp_region);
289   return result;
290 }
291
292
293 typedef struct File_info
294 {
295   struct File_info *next;
296   char *file;
297   unsigned long size;
298   unsigned long calls;
299   unsigned long sites;
300 } *finfo;
301
302 static finfo finfos = NULL;
303
304 static int finfo_cmp(const void *a, const void *b)
305 {
306   finfo *afi = (finfo *) a;
307   finfo *bfi = (finfo *) b;
308   return (*afi)->size - (*bfi)->size;  /* Reverse order */
309 }
310
311 static void print_finfos(void)
312 {
313   finfo fi;
314   unsigned long size, sites, calls;
315
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)
321     {
322       size += fi->size;
323       sites += fi->sites;
324       calls += fi->calls;
325       fprintf(out, " %12lu | %5lu | %8lu | %s\n",
326               fi->size, fi->sites, fi->calls, fi->file);
327     }
328   fprintf(out, "  ------------+-------+----------+---------------------\n");
329     fprintf(out, " %12lu | %5lu | %8lu | Total\n",
330             size, sites, calls);
331
332 }
333
334 static int ainfo_cmp(const void *a, const void *b)
335 {
336   ainfo *afi = (ainfo *) a;
337   ainfo *bfi = (ainfo *) b;
338   return (*afi)->size - (*bfi)->size;  /* Reverse order */
339 }
340
341 static void print_ainfos(void)
342 {
343   ainfo ai;
344
345   unsigned long size, calls;
346
347   ainfos = (ainfo) sort_list((list) ainfos, ainfo_cmp);
348   size = calls = 0;
349   fprintf(out, "        Bytes |    Calls | Site\n");
350   fprintf(out, "  ------------+----------+---------------------\n");
351   for (ai = ainfos; ai; ai = ai->next)
352     {
353       size += ai->size;
354       calls += ai->calls;
355       fprintf(out, " %12lu | %8lu | %s:%d\n",
356               ai->size, ai->calls, ai->file, ai->line);
357     }
358   fprintf(out, "  ------------+----------+---------------------\n");
359     fprintf(out, " %12lu | %8lu | Total\n",
360             size, calls);
361 }
362
363 static finfo find_finfo(char *file)
364 {
365   finfo fi;
366
367   for (fi = finfos; fi; fi = fi->next)
368     if (!strcmp(file, fi->file))
369       return fi;
370
371   fi = ralloc(profile_region, struct File_info);
372   fi->file = file;
373   fi->size = 0;
374   fi->calls = 0;
375   fi->sites = 0;
376   fi->next = finfos;
377   finfos = fi;
378   return fi;
379 }
380
381 static void gather_finfo(void)
382 {
383   ainfo ai;
384
385   for (ai = ainfos; ai; ai = ai->next)
386     {
387       finfo fi = find_finfo(ai->file);
388       fi->size += ai->size;
389       fi->calls += ai->calls;
390       fi->sites++;
391     }
392 }
393
394 /**************************************************************************
395  *                                                                        *
396  * Display results -- GCC                                                 *
397  *                                                                        *
398  **************************************************************************/
399
400 #ifdef TRACE_STACK
401
402 pid_t child_pid = 0;
403 int child_in[2], child_out[2]; /* pipes to child process */
404
405 static void start_prettiness(void)
406 {
407   if (pipe(child_in) || pipe(child_out))
408     pfail("Unable to open pipe to child process");
409   if (!(child_pid = fork()))
410     {
411       /* Child process */
412       pid_t parent_pid;
413       char filename[64];
414
415       if (dup2(child_in[0], STDIN_FILENO) == -1)
416         pfail("Unable to open pipe from parent");
417       close(child_in[0]);
418       close(child_in[1]);
419       if (dup2(child_out[1], STDOUT_FILENO) == -1)
420         pfail("Unable to open pipe to parent");
421       close(child_out[0]);
422       close(child_out[1]);
423
424       parent_pid = getppid();
425       snprintf(filename, 64, "/proc/%d/exe", parent_pid);
426       filename[63] = '\0';
427       execlp("addr2line", "addr2line", "-s", "-e", filename, 0);
428       fprintf(stderr, "Unable to fork addr2line\n");
429       exit(EXIT_FAILURE);
430     }
431   else
432     {
433       close(child_in[0]);
434       close(child_out[1]);
435     }
436 }
437
438 /* Turn p into a file:line string */
439 static char *prettify(void *p)
440 {
441 #define BUFSIZE 1024
442   static char buf[BUFSIZE];
443   int size;
444
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);
449   if (!size)
450     pfail("Unable to read from child process");
451   buf[size-1] = '\0'; /* Kill \n */
452   /*printf("Read: [%s]\n", buf);*/
453   return buf;
454 }
455
456 static void end_prettiness(void)
457 {
458   if (child_pid)
459     kill(child_pid, SIGHUP);
460 }
461
462 static int cinfo_cmp(const void *a, const void *b)
463 {
464   cinfo *aci = (cinfo *) a;
465   cinfo *bci = (cinfo *) b;
466   return (*aci)->size - (*bci)->size;  /* Reverse order */
467 }
468
469 /* Print the call chain information out to a file. */
470 static void print_cinfos(void)
471 {
472   cinfo ci;
473   unsigned long size, calls;
474   int i;
475
476   cinfos = (cinfo) sort_list((list) cinfos, cinfo_cmp);
477   size = calls = 0;
478   start_prettiness();
479   fprintf(out, "        Bytes |    Calls | Call Stack\n");
480   fprintf(out, "  ------------+----------+---------------------\n");
481   for (ci = cinfos; ci; ci = ci->next)
482     {
483       size += ci->size;
484       calls += ci->calls;
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]));
488       fprintf(out, "\n");
489     }
490   fprintf(out, "  ------------+----------+---------------------\n");
491     fprintf(out, " %12lu | %8lu | Total\n",
492             size, calls);
493     end_prettiness();
494 }
495 #endif
496
497
498 void profile(void)
499 {
500   if (profile_region == NULL)
501     return;
502
503   gather_finfo();
504
505   if (!(out = fopen("profile.out", "w")))
506     pfail("Unable to open profile.out");
507
508   fprintf(out, "---------------------------\n");
509   fprintf(out, "Region Library Memory Usage\n");
510   fprintf(out, "---------------------------\n\n");
511
512   print_finfos();
513   fprintf(out, "\n");
514   print_ainfos();
515 #ifdef TRACE_STACK
516   fprintf(out, "\n");
517   print_cinfos();
518 #endif
519
520   fclose(out);
521 }