gcc/libbanshee/libcompat/profile.c
2004-05-13 02:41:07 -04:00

522 lines
14 KiB
C

/*
* Copyright (c) 1999-2001
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
#undef REGION_PROFILE
#include "regions.h"
#include "profile.h"
typedef struct Alloc_info
{
struct Alloc_info *next;
char *file;
int line;
unsigned long size;
unsigned long calls;
} *ainfo;
static ainfo ainfos = NULL;
static region profile_region = NULL;
/* perror(s) then exit */
void pfail(const char *s)
{
perror(s);
exit(EXIT_FAILURE);
}
/**************************************************************************
* *
* Log information about an allocation -- generic *
* *
**************************************************************************/
static int registered_exit = 0;
static ainfo find_ainfo(char *file, int line)
{
ainfo ai;
for (ai = ainfos; ai; ai = ai->next)
if (line == ai->line && !strcmp(file, ai->file))
return ai;
if (!registered_exit)
{
if (atexit(profile))
fprintf(stderr, "Registration of profile at exit failed\n");
registered_exit = 1;
}
if (!profile_region)
profile_region = newregion();
ai = ralloc(profile_region, struct Alloc_info);
ai->file = file;
ai->line = line;
ai->size = 0;
ai->calls = 0;
ai->next = ainfos;
ainfos = ai;
return ai;
}
/**************************************************************************
* *
* Log information about an allocation -- GCC *
* *
* WARNING: This code uses __builtin_return_address, a non-portable *
* feature of gcc, to trace the call chain back. You'll also get ugly *
* output unless the addr2line (in GNU binutils) is installed. *
* *
* ANOTHER WARNING: The depths hard-coded in find_cinfo are only correct *
* if find_cinfo is inlined. Ack! *
* *
**************************************************************************/
#define REGION_PROFILE_DEPTH 2
#undef TRACE_STACK
#if defined(__GNUC__) && defined(__OPTIMIZE__) && REGION_PROFILE_DEPTH > 1
#define TRACE_STACK
#endif
#ifdef TRACE_STACK
#if REGION_PROFILE_DEPTH > 6
#error "REGION_PROFILE_DEPTH must be less than 6. See find_cinfo()."
#endif
typedef struct Call_info
{
struct Call_info *next;
void **stack; /* Array holding the call chain */
unsigned long size;
unsigned long calls;
} *cinfo;
static cinfo cinfos = NULL;
/* Find the current call chain and return a pointer to our status for
it, or allocate a new entry if there is none. */
static cinfo find_cinfo(void)
{
void *calls[REGION_PROFILE_DEPTH];
int i;
cinfo ci;
/* Compute the call chain. This is an awful hack. */
i = 0;
if (i < REGION_PROFILE_DEPTH)
calls[i++] = __builtin_return_address(1);
if (i < REGION_PROFILE_DEPTH)
calls[i++] = __builtin_return_address(2);
if (i < REGION_PROFILE_DEPTH)
calls[i++] = __builtin_return_address(3);
if (i < REGION_PROFILE_DEPTH)
calls[i++] = __builtin_return_address(4);
if (i < REGION_PROFILE_DEPTH)
calls[i++] = __builtin_return_address(5);
if (i < REGION_PROFILE_DEPTH)
calls[i++] = __builtin_return_address(6);
/* Add more if you want a higher call-depth (why would you?) */
/* Find it */
for (ci = cinfos; ci; ci = ci->next)
if (!memcmp(calls, ci->stack, REGION_PROFILE_DEPTH*sizeof(void *)))
return ci;
if (!profile_region)
profile_region = newregion();
ci = ralloc(profile_region, struct Call_info);
ci->stack = rarrayalloc(profile_region, REGION_PROFILE_DEPTH, void *);
memcpy(ci->stack, calls, REGION_PROFILE_DEPTH*sizeof(void *));
ci->size = 0;
ci->calls = 0;
ci->next = cinfos;
cinfos = ci;
return ci;
}
#endif
static void add_alloc(char *file, int line, int size)
{
ainfo ai = find_ainfo(file, line);
ai->calls++;
ai->size += size;
#ifdef TRACE_STACK
{
cinfo ci;
ci = find_cinfo();
ci->calls++;
ci->size += size;
}
#endif
}
/**************************************************************************
* *
* Intercept and log calls to region library *
* *
**************************************************************************/
void *profile_typed_ralloc(region r, size_t size, type_t type, char *file,
int line)
{
add_alloc(file, line, size);
return typed_ralloc(r, size, type);
}
void *profile_typed_rarrayalloc(region r, size_t n, size_t size, type_t type,
char *file, int line)
{
add_alloc(file, line, n*size);
return typed_rarrayalloc(r, n, size, type);
}
void *profile_typed_rarrayextend(region r, void *old, size_t n, size_t size,
type_t type, char *file, int line)
{
add_alloc(file, line, n*size); /* XXX: Fix */
return typed_rarrayextend(r, old, n, size, type);
}
char *profile_rstralloc(region r, size_t size, char *file, int line)
{
add_alloc(file, line, size);
return rstralloc(r, size);
}
char *profile_rstralloc0(region r, size_t size, char *file, int line)
{
add_alloc(file, line, size);
return rstralloc0(r, size);
}
char *profile_rstrdup(region r, const char *s, char *file, int line)
{
add_alloc(file, line, strlen(s));
return rstrdup(r, s);
}
char *profile_rstrextend(region r, const char *old, size_t newsize,
char *file, int line)
{
add_alloc(file, line, newsize); /* XXX: Fix */
return rstrextend(r, old, newsize);
}
char *profile_rstrextend0(region r, const char *old, size_t newsize,
char *file, int line)
{
add_alloc(file, line, newsize); /* XXX: Fix */
return rstrextend0(r, old, newsize);
}
/**************************************************************************
* *
* Display results -- generic *
* *
**************************************************************************/
static FILE *out = NULL;
/* Generic list -- used for generic sorting. Note that next field is
at the top. */
typedef struct List
{
struct List *next;
} *list;
/* Sort a list. cmp should sort in reverse order. */
static list sort_list(list l, int (*cmp)(const void *, const void *))
{
list cur, result;
list *sorted;
int i, length;
region temp_region;
/* Compute length of list */
for (cur = l, length = 0; cur; cur = cur->next, length++);
temp_region = newregion();
sorted = rarrayalloc(temp_region, length, list *);
for (cur = l, i = 0; cur; cur = cur->next)
sorted[i++] = cur;
qsort(sorted, length, sizeof(list *), cmp);
result = NULL;
for (i = 0; i < length; i++)
{
cur = result;
result = sorted[i];
result->next = cur;
}
deleteregion(temp_region);
return result;
}
typedef struct File_info
{
struct File_info *next;
char *file;
unsigned long size;
unsigned long calls;
unsigned long sites;
} *finfo;
static finfo finfos = NULL;
static int finfo_cmp(const void *a, const void *b)
{
finfo *afi = (finfo *) a;
finfo *bfi = (finfo *) b;
return (*afi)->size - (*bfi)->size; /* Reverse order */
}
static void print_finfos(void)
{
finfo fi;
unsigned long size, sites, calls;
finfos = (finfo) sort_list((list) finfos, finfo_cmp);
size = sites = calls = 0;
fprintf(out, " Bytes | Sites | Calls | File\n");
fprintf(out, " ------------+-------+----------+---------------------\n");
for (fi = finfos; fi; fi = fi->next)
{
size += fi->size;
sites += fi->sites;
calls += fi->calls;
fprintf(out, " %12lu | %5lu | %8lu | %s\n",
fi->size, fi->sites, fi->calls, fi->file);
}
fprintf(out, " ------------+-------+----------+---------------------\n");
fprintf(out, " %12lu | %5lu | %8lu | Total\n",
size, sites, calls);
}
static int ainfo_cmp(const void *a, const void *b)
{
ainfo *afi = (ainfo *) a;
ainfo *bfi = (ainfo *) b;
return (*afi)->size - (*bfi)->size; /* Reverse order */
}
static void print_ainfos(void)
{
ainfo ai;
unsigned long size, calls;
ainfos = (ainfo) sort_list((list) ainfos, ainfo_cmp);
size = calls = 0;
fprintf(out, " Bytes | Calls | Site\n");
fprintf(out, " ------------+----------+---------------------\n");
for (ai = ainfos; ai; ai = ai->next)
{
size += ai->size;
calls += ai->calls;
fprintf(out, " %12lu | %8lu | %s:%d\n",
ai->size, ai->calls, ai->file, ai->line);
}
fprintf(out, " ------------+----------+---------------------\n");
fprintf(out, " %12lu | %8lu | Total\n",
size, calls);
}
static finfo find_finfo(char *file)
{
finfo fi;
for (fi = finfos; fi; fi = fi->next)
if (!strcmp(file, fi->file))
return fi;
fi = ralloc(profile_region, struct File_info);
fi->file = file;
fi->size = 0;
fi->calls = 0;
fi->sites = 0;
fi->next = finfos;
finfos = fi;
return fi;
}
static void gather_finfo(void)
{
ainfo ai;
for (ai = ainfos; ai; ai = ai->next)
{
finfo fi = find_finfo(ai->file);
fi->size += ai->size;
fi->calls += ai->calls;
fi->sites++;
}
}
/**************************************************************************
* *
* Display results -- GCC *
* *
**************************************************************************/
#ifdef TRACE_STACK
pid_t child_pid = 0;
int child_in[2], child_out[2]; /* pipes to child process */
static void start_prettiness(void)
{
if (pipe(child_in) || pipe(child_out))
pfail("Unable to open pipe to child process");
if (!(child_pid = fork()))
{
/* Child process */
pid_t parent_pid;
char filename[64];
if (dup2(child_in[0], STDIN_FILENO) == -1)
pfail("Unable to open pipe from parent");
close(child_in[0]);
close(child_in[1]);
if (dup2(child_out[1], STDOUT_FILENO) == -1)
pfail("Unable to open pipe to parent");
close(child_out[0]);
close(child_out[1]);
parent_pid = getppid();
snprintf(filename, 64, "/proc/%d/exe", parent_pid);
filename[63] = '\0';
execlp("addr2line", "addr2line", "-s", "-e", filename, 0);
fprintf(stderr, "Unable to fork addr2line\n");
exit(EXIT_FAILURE);
}
else
{
close(child_in[0]);
close(child_out[1]);
}
}
/* Turn p into a file:line string */
static char *prettify(void *p)
{
#define BUFSIZE 1024
static char buf[BUFSIZE];
int size;
/*printf("To child: %p\n", p);*/
size = snprintf(buf, BUFSIZE, "%p\n", p);
write(child_in[1], buf, size);
size = read(child_out[0], buf, BUFSIZE - 1);
if (!size)
pfail("Unable to read from child process");
buf[size-1] = '\0'; /* Kill \n */
/*printf("Read: [%s]\n", buf);*/
return buf;
}
static void end_prettiness(void)
{
if (child_pid)
kill(child_pid, SIGHUP);
}
static int cinfo_cmp(const void *a, const void *b)
{
cinfo *aci = (cinfo *) a;
cinfo *bci = (cinfo *) b;
return (*aci)->size - (*bci)->size; /* Reverse order */
}
/* Print the call chain information out to a file. */
static void print_cinfos(void)
{
cinfo ci;
unsigned long size, calls;
int i;
cinfos = (cinfo) sort_list((list) cinfos, cinfo_cmp);
size = calls = 0;
start_prettiness();
fprintf(out, " Bytes | Calls | Call Stack\n");
fprintf(out, " ------------+----------+---------------------\n");
for (ci = cinfos; ci; ci = ci->next)
{
size += ci->size;
calls += ci->calls;
fprintf(out, " %12lu | %8lu | ", ci->size, ci->calls);
for (i = 0; i < REGION_PROFILE_DEPTH; i++)
fprintf(out, "%s ", prettify(ci->stack[i]));
fprintf(out, "\n");
}
fprintf(out, " ------------+----------+---------------------\n");
fprintf(out, " %12lu | %8lu | Total\n",
size, calls);
end_prettiness();
}
#endif
void profile(void)
{
if (profile_region == NULL)
return;
gather_finfo();
if (!(out = fopen("profile.out", "w")))
pfail("Unable to open profile.out");
fprintf(out, "---------------------------\n");
fprintf(out, "Region Library Memory Usage\n");
fprintf(out, "---------------------------\n\n");
print_finfos();
fprintf(out, "\n");
print_ainfos();
#ifdef TRACE_STACK
fprintf(out, "\n");
print_cinfos();
#endif
fclose(out);
}