mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-05-22 13:48:08 +02:00
[util] Import my malloc wrapper that prints simple statistics
To build, do: make malloc-stats.so inside util/, and to use, run: LD_PRELOAD=malloc-stats.so some-program For binaries managed by libtool, eg, cairo-perf, do: ../libtool --mode=execute /bin/true ./cairo-perf LD_PRELOAD="../util/malloc-stats.so" .libs/lt-cairo-perf The code also includes Jeff Muizelaar's libbacktracesymbols that is a much better implementation of backtrace_symbols() than what is provided by glibc. That can be built by: make backtrace-symbols.so
This commit is contained in:
parent
515491334c
commit
3b46e105bd
4 changed files with 701 additions and 0 deletions
17
util/.gitignore
vendored
Normal file
17
util/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
.deps
|
||||
.libs
|
||||
Makefile
|
||||
Makefile.in
|
||||
*.so
|
||||
*.la
|
||||
*.lo
|
||||
*.loT
|
||||
*.o
|
||||
*.obj
|
||||
*.pdb
|
||||
*.dll
|
||||
*.exp
|
||||
*.lib
|
||||
*~
|
||||
.*.sw?
|
||||
TAGS
|
||||
|
|
@ -1,4 +1,26 @@
|
|||
|
||||
tools: malloc-stats.so
|
||||
|
||||
%.so: %.la
|
||||
$(RM) $@
|
||||
$(LN_S) .libs/$*.so $@
|
||||
|
||||
CLEANFILES = *.so *.la
|
||||
|
||||
SOLDFLAGS = -module -avoid-version -export-dynamic -rpath /dev/null
|
||||
|
||||
EXTRA_LTLIBRARIES = malloc-stats.la backtrace-symbols.la
|
||||
|
||||
|
||||
backtrace_symbols_la_LDFLAGS = $(SOLDFLAGS)
|
||||
backtrace_symbols_la_LIBADD = -lbfd -liberty
|
||||
backtrace_symbols_la_SOURCES = backtrace-symbols.c
|
||||
|
||||
malloc_stats_la_LDFLAGS = $(SOLDFLAGS)
|
||||
malloc_stats_la_LIBADD = $(backtrace_symbols_la_LIBADD)
|
||||
malloc_stats_la_SOURCES = $(backtrace_symbols_la_SOURCES) malloc-stats.c
|
||||
|
||||
EXTRA_DIST = \
|
||||
xr2cairo \
|
||||
cairo-api-update
|
||||
|
||||
|
|
|
|||
361
util/backtrace-symbols.c
Normal file
361
util/backtrace-symbols.c
Normal file
|
|
@ -0,0 +1,361 @@
|
|||
/*
|
||||
A hacky replacement for backtrace_symbols in glibc
|
||||
|
||||
backtrace_symbols in glibc looks up symbols using dladdr which is limited in
|
||||
the symbols that it sees. libbacktracesymbols opens the executable and shared
|
||||
libraries using libbfd and will look up backtrace information using the symbol
|
||||
table and the dwarf line information.
|
||||
|
||||
It may make more sense for this program to use libelf instead of libbfd.
|
||||
However, I have not investigated that yet.
|
||||
|
||||
Derived from addr2line.c from GNU Binutils by Jeff Muizelaar
|
||||
|
||||
Copyright 2007 Jeff Muizelaar
|
||||
*/
|
||||
|
||||
/* addr2line.c -- convert addresses to line number and function name
|
||||
Copyright 1997, 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
|
||||
Contributed by Ulrich Lauther <Ulrich.Lauther@mchp.siemens.de>
|
||||
|
||||
This file was part of GNU Binutils.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||||
|
||||
#define fatal(a, b) exit(1)
|
||||
#define bfd_fatal(a) exit(1)
|
||||
#define bfd_nonfatal(a) exit(1)
|
||||
#define list_matching_formats(a) exit(1)
|
||||
|
||||
/* 2 characters for each byte, plus 1 each for 0, x, and NULL */
|
||||
#define PTRSTR_LEN (sizeof(void *) * 2 + 3)
|
||||
#define true 1
|
||||
#define false 0
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <execinfo.h>
|
||||
#include <bfd.h>
|
||||
#include <libiberty.h>
|
||||
#include <dlfcn.h>
|
||||
#include <link.h>
|
||||
#if 0
|
||||
|
||||
void (*dbfd_init)(void);
|
||||
bfd_vma (*dbfd_scan_vma)(const char *string, const char **end, int base);
|
||||
bfd* (*dbfd_openr)(const char *filename, const char *target);
|
||||
bfd_boolean (*dbfd_check_format)(bfd *abfd, bfd_format format);
|
||||
bfd_boolean (*dbfd_check_format_matches)(bfd *abfd, bfd_format format, char ***matching);
|
||||
bfd_boolean (*dbfd_close)(bfd *abfd);
|
||||
bfd_boolean (*dbfd_map_over_sections)(bfd *abfd, void (*func)(bfd *abfd, asection *sect, void *obj),
|
||||
void *obj);
|
||||
#define bfd_init dbfd_init
|
||||
|
||||
static void load_funcs(void)
|
||||
{
|
||||
void * handle = dlopen("libbfd.so", RTLD_NOW);
|
||||
dbfd_init = dlsym(handle, "bfd_init");
|
||||
dbfd_scan_vma = dlsym(handle, "bfd_scan_vma");
|
||||
dbfd_openr = dlsym(handle, "bfd_openr");
|
||||
dbfd_check_format = dlsym(handle, "bfd_check_format");
|
||||
dbfd_check_format_matches = dlsym(handle, "bfd_check_format_matches");
|
||||
dbfd_close = dlsym(handle, "bfd_close");
|
||||
dbfd_map_over_sections = dlsym(handle, "bfd_map_over_sections");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static asymbol **syms; /* Symbol table. */
|
||||
|
||||
/* 150 isn't special; it's just an arbitrary non-ASCII char value. */
|
||||
#define OPTION_DEMANGLER (150)
|
||||
|
||||
static void slurp_symtab(bfd * abfd);
|
||||
static void find_address_in_section(bfd *abfd, asection *section, void *data);
|
||||
|
||||
/* Read in the symbol table. */
|
||||
|
||||
static void slurp_symtab(bfd * abfd)
|
||||
{
|
||||
long symcount;
|
||||
unsigned int size;
|
||||
|
||||
if ((bfd_get_file_flags(abfd) & HAS_SYMS) == 0)
|
||||
return;
|
||||
|
||||
symcount = bfd_read_minisymbols(abfd, false, (PTR) & syms, &size);
|
||||
if (symcount == 0)
|
||||
symcount = bfd_read_minisymbols(abfd, true /* dynamic */ ,
|
||||
(PTR) & syms, &size);
|
||||
|
||||
if (symcount < 0)
|
||||
bfd_fatal(bfd_get_filename(abfd));
|
||||
}
|
||||
|
||||
/* These global variables are used to pass information between
|
||||
translate_addresses and find_address_in_section. */
|
||||
|
||||
static bfd_vma pc;
|
||||
static const char *filename;
|
||||
static const char *functionname;
|
||||
static unsigned int line;
|
||||
static int found;
|
||||
|
||||
/* Look for an address in a section. This is called via
|
||||
bfd_map_over_sections. */
|
||||
|
||||
static void find_address_in_section(bfd *abfd, asection *section, void *data __attribute__ ((__unused__)) )
|
||||
{
|
||||
bfd_vma vma;
|
||||
bfd_size_type size;
|
||||
|
||||
if (found)
|
||||
return;
|
||||
|
||||
if ((bfd_get_section_flags(abfd, section) & SEC_ALLOC) == 0)
|
||||
return;
|
||||
|
||||
vma = bfd_get_section_vma(abfd, section);
|
||||
if (pc < vma)
|
||||
return;
|
||||
|
||||
size = bfd_section_size(abfd, section);
|
||||
if (pc >= vma + size)
|
||||
return;
|
||||
|
||||
found = bfd_find_nearest_line(abfd, section, syms, pc - vma,
|
||||
&filename, &functionname, &line);
|
||||
}
|
||||
|
||||
/* Read hexadecimal addresses from stdin, translate into
|
||||
file_name:line_number and optionally function name. */
|
||||
#if 0
|
||||
static void translate_addresses(bfd * abfd, char (*addr)[PTRSTR_LEN], int naddr)
|
||||
{
|
||||
while (naddr) {
|
||||
pc = bfd_scan_vma(addr[naddr-1], NULL, 16);
|
||||
|
||||
found = false;
|
||||
bfd_map_over_sections(abfd, find_address_in_section,
|
||||
(PTR) NULL);
|
||||
|
||||
if (!found) {
|
||||
printf("[%s] \?\?() \?\?:0\n",addr[naddr-1]);
|
||||
} else {
|
||||
const char *name;
|
||||
|
||||
name = functionname;
|
||||
if (name == NULL || *name == '\0')
|
||||
name = "??";
|
||||
if (filename != NULL) {
|
||||
char *h;
|
||||
|
||||
h = strrchr(filename, '/');
|
||||
if (h != NULL)
|
||||
filename = h + 1;
|
||||
}
|
||||
|
||||
printf("\t%s:%u\t", filename ? filename : "??",
|
||||
line);
|
||||
|
||||
printf("%s()\n", name);
|
||||
|
||||
}
|
||||
|
||||
/* fflush() is essential for using this command as a server
|
||||
child process that reads addresses from a pipe and responds
|
||||
with line number information, processing one address at a
|
||||
time. */
|
||||
fflush(stdout);
|
||||
naddr--;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static char** translate_addresses_buf(bfd * abfd, bfd_vma *addr, int naddr)
|
||||
{
|
||||
int naddr_orig = naddr;
|
||||
char b;
|
||||
int total = 0;
|
||||
enum { Count, Print } state;
|
||||
char *buf = &b;
|
||||
int len = 0;
|
||||
char **ret_buf = NULL;
|
||||
/* iterate over the formating twice.
|
||||
* the first time we count how much space we need
|
||||
* the second time we do the actual printing */
|
||||
for (state=Count; state<=Print; state++) {
|
||||
if (state == Print) {
|
||||
ret_buf = malloc(total + sizeof(char*)*naddr);
|
||||
buf = (char*)(ret_buf + naddr);
|
||||
len = total;
|
||||
}
|
||||
while (naddr) {
|
||||
if (state == Print)
|
||||
ret_buf[naddr-1] = buf;
|
||||
pc = addr[naddr-1];
|
||||
|
||||
found = false;
|
||||
bfd_map_over_sections(abfd, find_address_in_section,
|
||||
(PTR) NULL);
|
||||
|
||||
if (!found) {
|
||||
total += snprintf(buf, len, "[0x%llx] \?\?() \?\?:0",(long long unsigned int) addr[naddr-1]) + 1;
|
||||
} else {
|
||||
const char *name;
|
||||
|
||||
name = functionname;
|
||||
if (name == NULL || *name == '\0')
|
||||
name = "??";
|
||||
if (filename != NULL) {
|
||||
char *h;
|
||||
|
||||
h = strrchr(filename, '/');
|
||||
if (h != NULL)
|
||||
filename = h + 1;
|
||||
}
|
||||
total += snprintf(buf, len, "%s:%u\t%s()", filename ? filename : "??",
|
||||
line, name) + 1;
|
||||
|
||||
}
|
||||
if (state == Print) {
|
||||
/* set buf just past the end of string */
|
||||
buf = buf + total + 1;
|
||||
}
|
||||
naddr--;
|
||||
}
|
||||
naddr = naddr_orig;
|
||||
}
|
||||
return ret_buf;
|
||||
}
|
||||
/* Process a file. */
|
||||
|
||||
static char **process_file(const char *file_name, bfd_vma *addr, int naddr)
|
||||
{
|
||||
bfd *abfd;
|
||||
char **matching;
|
||||
char **ret_buf;
|
||||
|
||||
abfd = bfd_openr(file_name, NULL);
|
||||
|
||||
if (abfd == NULL)
|
||||
bfd_fatal(file_name);
|
||||
|
||||
if (bfd_check_format(abfd, bfd_archive))
|
||||
fatal("%s: can not get addresses from archive", file_name);
|
||||
|
||||
if (!bfd_check_format_matches(abfd, bfd_object, &matching)) {
|
||||
bfd_nonfatal(bfd_get_filename(abfd));
|
||||
if (bfd_get_error() ==
|
||||
bfd_error_file_ambiguously_recognized) {
|
||||
list_matching_formats(matching);
|
||||
free(matching);
|
||||
}
|
||||
xexit(1);
|
||||
}
|
||||
|
||||
slurp_symtab(abfd);
|
||||
|
||||
ret_buf = translate_addresses_buf(abfd, addr, naddr);
|
||||
|
||||
if (syms != NULL) {
|
||||
free(syms);
|
||||
syms = NULL;
|
||||
}
|
||||
|
||||
bfd_close(abfd);
|
||||
return ret_buf;
|
||||
}
|
||||
|
||||
#define MAX_DEPTH 16
|
||||
|
||||
struct file_match {
|
||||
const char *file;
|
||||
void *address;
|
||||
void *base;
|
||||
void *hdr;
|
||||
};
|
||||
|
||||
static int find_matching_file(struct dl_phdr_info *info,
|
||||
size_t size, void *data)
|
||||
{
|
||||
struct file_match *match = data;
|
||||
/* This code is modeled from Gfind_proc_info-lsb.c:callback() from libunwind */
|
||||
long n;
|
||||
const ElfW(Phdr) *phdr;
|
||||
ElfW(Addr) load_base = info->dlpi_addr;
|
||||
phdr = info->dlpi_phdr;
|
||||
for (n = info->dlpi_phnum; --n >= 0; phdr++) {
|
||||
if (phdr->p_type == PT_LOAD) {
|
||||
ElfW(Addr) vaddr = phdr->p_vaddr + load_base;
|
||||
if (match->address >= vaddr && match->address < vaddr + phdr->p_memsz) {
|
||||
/* we found a match */
|
||||
match->file = info->dlpi_name;
|
||||
match->base = info->dlpi_addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char **backtrace_symbols(void *const *buffer, int size)
|
||||
{
|
||||
int stack_depth = size - 1;
|
||||
int x,y;
|
||||
/* discard calling function */
|
||||
int total = 0;
|
||||
|
||||
char ***locations;
|
||||
char **final;
|
||||
char *f_strings;
|
||||
|
||||
locations = malloc(sizeof(char**) * (stack_depth+1));
|
||||
|
||||
bfd_init();
|
||||
for(x=stack_depth, y=0; x>=0; x--, y++){
|
||||
struct file_match match = { .address = buffer[x] };
|
||||
char **ret_buf;
|
||||
bfd_vma addr;
|
||||
dl_iterate_phdr(find_matching_file, &match);
|
||||
addr = buffer[x] - match.base;
|
||||
if (match.file && strlen(match.file))
|
||||
ret_buf = process_file(match.file, &addr, 1);
|
||||
else
|
||||
ret_buf = process_file("/proc/self/exe", &addr, 1);
|
||||
locations[x] = ret_buf;
|
||||
total += strlen(ret_buf[0]) + 1;
|
||||
}
|
||||
|
||||
/* allocate the array of char* we are going to return and extra space for
|
||||
* all of the strings */
|
||||
final = malloc(total + (stack_depth + 1) * sizeof(char*));
|
||||
/* get a pointer to the extra space */
|
||||
f_strings = (char*)(final + stack_depth + 1);
|
||||
|
||||
/* fill in all of strings and pointers */
|
||||
for(x=stack_depth; x>=0; x--){
|
||||
strcpy(f_strings, locations[x][0]);
|
||||
free(locations[x]);
|
||||
final[x] = f_strings;
|
||||
f_strings += strlen(f_strings) + 1;
|
||||
}
|
||||
|
||||
free(locations);
|
||||
|
||||
return final;
|
||||
}
|
||||
301
util/malloc-stats.c
Executable file
301
util/malloc-stats.c
Executable file
|
|
@ -0,0 +1,301 @@
|
|||
/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
|
||||
/*
|
||||
* Copyright © 2007 Red Hat, Inc.
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software
|
||||
* and its documentation for any purpose is hereby granted without
|
||||
* fee, provided that the above copyright notice appear in all copies
|
||||
* and that both that copyright notice and this permission notice
|
||||
* appear in supporting documentation, and that the name of
|
||||
* Red Hat, Inc. not be used in advertising or publicity pertaining to
|
||||
* distribution of the software without specific, written prior
|
||||
* permission. Red Hat, Inc. makes no representations about the
|
||||
* suitability of this software for any purpose. It is provided "as
|
||||
* is" without express or implied warranty.
|
||||
*
|
||||
* RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
|
||||
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
|
||||
* INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
|
||||
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* Author: Behdad Esfahbod <behdad@behdad.org>
|
||||
*/
|
||||
|
||||
/* A simple malloc wrapper that prints out statistics on termination */
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* caller-logging */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
struct alloc_stat_t {
|
||||
int num;
|
||||
long size;
|
||||
};
|
||||
|
||||
struct alloc_stats_t {
|
||||
struct alloc_stat_t malloc, realloc, total;
|
||||
};
|
||||
|
||||
struct func_stat_t {
|
||||
const void *addr;
|
||||
const char *name;
|
||||
|
||||
struct alloc_stats_t stat;
|
||||
};
|
||||
|
||||
static struct func_stat_t *func_stats = NULL;
|
||||
static int func_stats_num = 0;
|
||||
static int func_stats_size = 0;
|
||||
|
||||
static void
|
||||
alloc_stats_add (struct alloc_stats_t *stats, int is_realloc, size_t size)
|
||||
{
|
||||
struct alloc_stat_t *stat = is_realloc ? &stats->realloc : &stats->malloc;
|
||||
|
||||
stats->total.num++;
|
||||
stats->total.size += size;
|
||||
|
||||
stat->num++;
|
||||
stat->size += size;
|
||||
}
|
||||
|
||||
#include <execinfo.h>
|
||||
|
||||
static const char *
|
||||
resolve_addr (const void *addr) {
|
||||
|
||||
char **strings;
|
||||
char *p;
|
||||
const char *name = NULL;
|
||||
|
||||
if (addr == NULL)
|
||||
return "(other)";
|
||||
if (addr == (void *) -1)
|
||||
return "(total)";
|
||||
|
||||
strings = backtrace_symbols ((void**)&addr, 1);
|
||||
name = strdup (strings[0]);
|
||||
|
||||
p = strchr (name, '\t');
|
||||
if (p)
|
||||
name = p + 1;
|
||||
|
||||
free (strings);
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
static void
|
||||
func_stats_add (const void *caller, int is_realloc, size_t size)
|
||||
{
|
||||
int i;
|
||||
const char *name;
|
||||
|
||||
if (caller != (void *) -1 && caller != NULL)
|
||||
func_stats_add ((void *) -1, is_realloc, size);
|
||||
|
||||
for (i = 0; i < func_stats_num; i++) {
|
||||
if (func_stats[i].addr == caller) {
|
||||
alloc_stats_add (&func_stats[i].stat, is_realloc, size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == func_stats_size) {
|
||||
func_stats_size = func_stats_size ? func_stats_size * 2 : 16;
|
||||
func_stats = realloc (func_stats, func_stats_size * sizeof (func_stats[0]));
|
||||
}
|
||||
|
||||
name = resolve_addr (caller);
|
||||
|
||||
if (name) {
|
||||
func_stats_num++;
|
||||
func_stats[i].addr = caller;
|
||||
func_stats[i].name = name;
|
||||
memset (&func_stats[i].stat, 0, sizeof (func_stats[i].stat));
|
||||
alloc_stats_add (&func_stats[i].stat, is_realloc, size);
|
||||
return;
|
||||
}
|
||||
|
||||
func_stats_add (NULL, is_realloc, size);
|
||||
}
|
||||
|
||||
/* wrapper stuff */
|
||||
|
||||
#include <malloc.h>
|
||||
|
||||
static void *(*old_malloc)(size_t, const void *);
|
||||
static void *(*old_realloc)(void *, size_t, const void *);
|
||||
|
||||
static void *my_malloc(size_t, const void *);
|
||||
static void *my_realloc(void *, size_t, const void *);
|
||||
|
||||
static void
|
||||
save_hooks (void)
|
||||
{
|
||||
old_malloc = __malloc_hook;
|
||||
old_realloc = __realloc_hook;
|
||||
}
|
||||
|
||||
static void
|
||||
old_hooks (void)
|
||||
{
|
||||
__malloc_hook = old_malloc;
|
||||
__realloc_hook = old_realloc;
|
||||
}
|
||||
|
||||
static void
|
||||
my_hooks (void)
|
||||
{
|
||||
/* should always save the current value */
|
||||
save_hooks ();
|
||||
|
||||
__malloc_hook = my_malloc;
|
||||
__realloc_hook = my_realloc;
|
||||
}
|
||||
|
||||
static void *
|
||||
my_malloc(size_t size, const void *caller)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
old_hooks ();
|
||||
|
||||
func_stats_add (caller, 0, size);
|
||||
|
||||
ret = malloc (size);
|
||||
my_hooks ();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *
|
||||
my_realloc(void *ptr, size_t size, const void *caller)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
old_hooks ();
|
||||
|
||||
func_stats_add (caller, 1, size);
|
||||
|
||||
ret = realloc (ptr, size);
|
||||
my_hooks ();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
my_init_hook(void) {
|
||||
my_hooks ();
|
||||
}
|
||||
|
||||
void (*__malloc_initialize_hook) (void) = my_init_hook;
|
||||
|
||||
|
||||
/* reporting */
|
||||
|
||||
#include <locale.h>
|
||||
|
||||
static void
|
||||
add_alloc_stats (struct alloc_stats_t *a, struct alloc_stats_t *b)
|
||||
{
|
||||
a->total.num += b->total.num;
|
||||
a->total.size += b->total.size;
|
||||
a->malloc.num += b->malloc.num;
|
||||
a->malloc.size += b->malloc.size;
|
||||
a->realloc.num += b->realloc.num;
|
||||
a->realloc.size += b->realloc.size;
|
||||
}
|
||||
|
||||
static void
|
||||
dump_alloc_stats (struct alloc_stats_t *stats, const char *name)
|
||||
{
|
||||
printf ("%8d %'11ld %8d %'11ld %8d %'11ld %s\n",
|
||||
stats->total.num, stats->total.size,
|
||||
stats->malloc.num, stats->malloc.size,
|
||||
stats->realloc.num, stats->realloc.size,
|
||||
name);
|
||||
}
|
||||
|
||||
static int
|
||||
compare_func_stats_name (const void *pa, const void *pb)
|
||||
{
|
||||
const struct func_stat_t *a = pa, *b = pb;
|
||||
int i;
|
||||
|
||||
i = strcmp (a->name, b->name);
|
||||
if (i)
|
||||
return i;
|
||||
|
||||
return ((char *) a->addr - (char *) b->addr);
|
||||
}
|
||||
|
||||
static int
|
||||
compare_func_stats (const void *pa, const void *pb)
|
||||
{
|
||||
const struct func_stat_t *a = pa, *b = pb;
|
||||
|
||||
if (a->stat.total.num != b->stat.total.num)
|
||||
return (a->stat.total.num - b->stat.total.num);
|
||||
|
||||
if (a->stat.total.size != b->stat.total.size)
|
||||
return (a->stat.total.size - b->stat.total.size);
|
||||
|
||||
return compare_func_stats_name (pa, pb);
|
||||
}
|
||||
|
||||
static void
|
||||
merge_similar_entries (void)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
j = 0;
|
||||
for (i = 1; i < func_stats_num; i++) {
|
||||
if (i != j && 0 == strcmp (func_stats[i].name, func_stats[j].name)) {
|
||||
add_alloc_stats (&func_stats[j].stat, &func_stats[i].stat);
|
||||
} else {
|
||||
j++;
|
||||
if (i != j)
|
||||
func_stats[j] = func_stats[i];
|
||||
}
|
||||
}
|
||||
j++;
|
||||
if (j < func_stats_num)
|
||||
func_stats_num = j;
|
||||
}
|
||||
|
||||
__attribute__ ((destructor))
|
||||
static void
|
||||
finish (void)
|
||||
{
|
||||
int i;
|
||||
|
||||
old_hooks ();
|
||||
|
||||
/* merge entries with same name */
|
||||
qsort (func_stats, func_stats_num, sizeof (func_stats[0]), compare_func_stats_name);
|
||||
merge_similar_entries ();
|
||||
qsort (func_stats, func_stats_num, sizeof (func_stats[0]), compare_func_stats);
|
||||
|
||||
if (func_stats_num) {
|
||||
setlocale (LC_ALL, "");
|
||||
|
||||
printf (" TOTAL MALLOC REALLOC\n");
|
||||
printf (" num size num size num size\n");
|
||||
|
||||
for (i = 0; i < func_stats_num; i++) {
|
||||
dump_alloc_stats (&func_stats[i].stat, func_stats[i].name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Add table
Reference in a new issue