diff options
author | Peter Weilbacher <mozilla@weilbacher.org> | 2007-04-23 23:18:05 +0200 |
---|---|---|
committer | Peter Weilbacher <mozilla@weilbacher.org> | 2007-04-23 23:18:05 +0200 |
commit | ac0ef0302e68ba17ec5a59c689579f1854c7386c (patch) | |
tree | 0a25bb6cb999b7bd0d19c736f693c178b8ffc50c | |
parent | d3cf2144dbd42737d5e2c45908fbca082dd54022 (diff) | |
parent | 260dcb316e14945e7600406954e4876e8827a777 (diff) |
Merge branch 'master' of git+ssh://pmw@git.freedesktop.org/git/cairo
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | ROADMAP | 1 | ||||
-rw-r--r-- | configure.in | 1 | ||||
-rw-r--r-- | perf/Makefile.am | 4 | ||||
-rw-r--r-- | perf/cairo-perf-os2.c | 95 | ||||
-rw-r--r-- | src/cairo-beos-surface.cpp | 34 | ||||
-rw-r--r-- | src/cairo-mutex-private.h | 38 | ||||
-rw-r--r-- | src/cairo-png.c | 32 | ||||
-rw-r--r-- | util/.gitignore | 17 | ||||
-rw-r--r-- | util/Makefile.am | 26 | ||||
-rw-r--r-- | util/backtrace-symbols.c | 361 | ||||
-rwxr-xr-x | util/malloc-stats.c | 301 |
12 files changed, 851 insertions, 61 deletions
diff --git a/Makefile.am b/Makefile.am index 518369b3c..4410c02ee 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ -DIST_SUBDIRS = pixman src boilerplate test perf doc +DIST_SUBDIRS = pixman src boilerplate test perf doc util SUBDIRS = pixman src doc # libpng is required for our test programs if CAIRO_HAS_PNG_FUNCTIONS @@ -46,6 +46,7 @@ cairo 1.4.x (not scheduled, may become part of 1.6) cairo 1.6 (scheduled for July 2007, targeting firefox and GNOME) ================================================================ • David Turner's cairo-ft rewrite. (behdad) + http://david.freetype.org/cairo/ • Change from 16.16 to 24.8 (or so) for fixed-point storage (cworth) diff --git a/configure.in b/configure.in index 02c5ada33..b2aa53fcf 100644 --- a/configure.in +++ b/configure.in @@ -917,6 +917,7 @@ src/Makefile test/Makefile test/pdiff/Makefile perf/Makefile +util/Makefile doc/Makefile doc/public/Makefile doc/public/version.xml diff --git a/perf/Makefile.am b/perf/Makefile.am index 86bdd26f8..1f8e6fcc9 100644 --- a/perf/Makefile.am +++ b/perf/Makefile.am @@ -40,8 +40,12 @@ cairo_perf_SOURCES = \ if CAIRO_HAS_WIN32_SURFACE cairo_perf_SOURCES += cairo-perf-win32.c else +if CAIRO_HAS_OS2_SURFACE +cairo_perf_SOURCES += cairo-perf-os2.c +else cairo_perf_SOURCES += cairo-perf-posix.c endif +endif cairo_perf_diff_files_SOURCES = \ cairo-perf-diff-files.c \ diff --git a/perf/cairo-perf-os2.c b/perf/cairo-perf-os2.c new file mode 100644 index 000000000..4cb6447a9 --- /dev/null +++ b/perf/cairo-perf-os2.c @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2007 Netlabs + * Copyright (c) 2006 Mozilla Corporation + * Copyright (c) 2006 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 + * the authors not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior + * permission. The authors make no representations about the + * suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE AUTHORS 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. + * + * Authors: Peter Weilbacher <mozilla@weilbacher.org> + * Vladimir Vukicevic <vladimir@pobox.com> (win32/linux code) + * Carl Worth <cworth@cworth.org> (win32/linux code) + */ + +#define INCL_BASE +#include <os2.h> + +#include "cairo-perf.h" + +/* timers */ +typedef struct _cairo_perf_timer +{ + /* make them double so that they can store the full QWORD precision */ + double start; + double stop; +} cairo_perf_timer_t; + +static cairo_perf_timer_t timer; + +static cairo_perf_timer_synchronize_t cairo_perf_timer_synchronize = NULL; +static void *cairo_perf_timer_synchronize_closure = NULL; +void +cairo_perf_timer_set_synchronize (cairo_perf_timer_synchronize_t synchronize, + void *closure) +{ + cairo_perf_timer_synchronize = synchronize; + cairo_perf_timer_synchronize_closure = closure; +} + +void +cairo_perf_timer_start (void) { + QWORD time; + + if (cairo_perf_timer_synchronize) + cairo_perf_timer_synchronize (cairo_perf_timer_synchronize_closure); + DosTmrQueryTime(&time); + timer.start = (time.ulHi*4294967296.0 + time.ulLo); +} + +void +cairo_perf_timer_stop (void) { + QWORD time; + + if (cairo_perf_timer_synchronize) + cairo_perf_timer_synchronize (cairo_perf_timer_synchronize_closure); + DosTmrQueryTime(&time); + timer.stop = (time.ulHi*4294967296.0 + time.ulLo); +} + +cairo_perf_ticks_t +cairo_perf_timer_elapsed (void) { + ULONG freq; + + DosTmrQueryFreq(&freq); + /* return time difference in milliseconds */ + return (timer.stop - timer.start) / freq * 1000; +} + +cairo_perf_ticks_t +cairo_perf_ticks_per_second (void) { + return 1000; /* in ms */ +} + + +/* yield */ +void +cairo_perf_yield (void) { + /* try to deactivate this thread until the scheduler calls it again */ + DosSleep (0); +} diff --git a/src/cairo-beos-surface.cpp b/src/cairo-beos-surface.cpp index e0ff29719..abf094a11 100644 --- a/src/cairo-beos-surface.cpp +++ b/src/cairo-beos-surface.cpp @@ -977,37 +977,3 @@ cairo_beos_surface_create_for_bitmap (BView* view, { return _cairo_beos_surface_create_internal(view, bmp); } - -// --------------------------------------------------------------------------- -// Cairo uses locks without explicit initialization. To support that, we -// provide a class here which manages the locks and is in global scope, so the -// compiler will instantiate it on library load and destroy it on library -// unload. - -class BeLocks { - public: - BLocker _cairo_font_face_mutex; - BLocker _cairo_scaled_font_map_mutex; -#ifdef CAIRO_HAS_FT_FONT - BLocker _cairo_ft_unscaled_font_map_mutex; -#endif -}; - -static BeLocks locks; - -void* _cairo_font_face_mutex = &locks._cairo_font_face_mutex; -void* _cairo_scaled_font_map_mutex = &locks._cairo_scaled_font_map_mutex; -#ifdef CAIRO_HAS_FT_FONT -void* _cairo_ft_unscaled_font_map_mutex = &locks._cairo_ft_unscaled_font_map_mutex; -#endif - -void _cairo_beos_lock (void* locker) { - BLocker* bLocker = reinterpret_cast<BLocker*>(locker); - bLocker->Lock(); -} - -void _cairo_beos_unlock (void* locker) { - BLocker* bLocker = reinterpret_cast<BLocker*>(locker); - bLocker->Unlock(); -} - diff --git a/src/cairo-mutex-private.h b/src/cairo-mutex-private.h index 1e0c06869..9b9adbae2 100644 --- a/src/cairo-mutex-private.h +++ b/src/cairo-mutex-private.h @@ -57,11 +57,14 @@ CAIRO_BEGIN_DECLS #if CAIRO_NO_MUTEX -typedef int cairo_mutex_t; -# define CAIRO_MUTEX_INITIALIZE() CAIRO_MUTEX_NOOP -# define CAIRO_MUTEX_LOCK(name) CAIRO_MUTEX_NOOP -# define CAIRO_MUTEX_UNLOCK(name) CAIRO_MUTEX_NOOP -# define CAIRO_MUTEX_NIL_INITIALIZER 0 +/* A poor man's mutex */ + + typedef int cairo_mutex_t; + +# define CAIRO_MUTEX_INITIALIZE() CAIRO_MUTEX_NOOP +# define CAIRO_MUTEX_LOCK(name) do { while (name) ; (name) = 1; } while (0) +# define CAIRO_MUTEX_UNLOCK(name) (name) = 0 +# define CAIRO_MUTEX_NIL_INITIALIZER 0 #elif HAVE_PTHREAD_H /*******************************************************/ @@ -70,8 +73,8 @@ typedef int cairo_mutex_t; typedef pthread_mutex_t cairo_mutex_t; # define CAIRO_MUTEX_INITIALIZE() CAIRO_MUTEX_NOOP -# define CAIRO_MUTEX_LOCK(name) pthread_mutex_lock (&name) -# define CAIRO_MUTEX_UNLOCK(name) pthread_mutex_unlock (&name) +# define CAIRO_MUTEX_LOCK(name) pthread_mutex_lock (&(name)) +# define CAIRO_MUTEX_UNLOCK(name) pthread_mutex_unlock (&(name)) # define CAIRO_MUTEX_FINI(mutex) pthread_mutex_destroy (mutex) # define CAIRO_MUTEX_NIL_INITIALIZER PTHREAD_MUTEX_INITIALIZER @@ -93,8 +96,8 @@ typedef int cairo_mutex_t; typedef CRITICAL_SECTION cairo_mutex_t; -# define CAIRO_MUTEX_LOCK(name) EnterCriticalSection (&name) -# define CAIRO_MUTEX_UNLOCK(name) LeaveCriticalSection (&name) +# define CAIRO_MUTEX_LOCK(name) EnterCriticalSection (&(name)) +# define CAIRO_MUTEX_UNLOCK(name) LeaveCriticalSection (&(name)) # define CAIRO_MUTEX_INIT(mutex) InitializeCriticalSection (mutex) # define CAIRO_MUTEX_FINI(mutex) DeleteCriticalSection (mutex) # define CAIRO_MUTEX_NIL_INITIALIZER { NULL, 0, 0, NULL, NULL, 0 } @@ -120,18 +123,13 @@ typedef int cairo_mutex_t; #elif CAIRO_HAS_BEOS_SURFACE /***********************************************/ - typedef void* cairo_mutex_t; - - cairo_private void _cairo_beos_lock(cairo_mutex_t*); - cairo_private void _cairo_beos_unlock(cairo_mutex_t*); - -/* the real initialization takes place in a global constructor */ -# define CAIRO_MUTEX_LOCK(name) _cairo_beos_lock (&name) -# define CAIRO_MUTEX_UNLOCK(name) _cairo_beos_unlock (&name) + typedef BLocker* cairo_mutex_t; -# warning "XXX: Someone who understands BeOS needs to add definitions for" \ - " cairo_mutex_t, CAIRO_MUTEX_INIT, and CAIRO_MUTEX_FINI," \ - " and CAIRO_MUTEX_NIL_INITIALIZER to cairo-mutex-private.h" +# define CAIRO_MUTEX_LOCK(name) (name)->Lock() +# define CAIRO_MUTEX_UNLOCK(name) (name)->Unlock() +# define CAIRO_MUTEX_INIT(mutex) (*(mutex)) = new BLocker() +# define CAIRO_MUTEX_FINI(mutex) delete (*(mutex)) +# define CAIRO_MUTEX_NIL_INITIALIZER NULL #else /**********************************************************************/ diff --git a/src/cairo-png.c b/src/cairo-png.c index 24b6ba61d..76e901f67 100644 --- a/src/cairo-png.c +++ b/src/cairo-png.c @@ -83,6 +83,25 @@ convert_data_to_bytes (png_structp png, png_row_infop row_info, png_bytep data) } } +/* Use a couple of simple error callbacks that do not print anything to + * stderr and rely on the user to check for errors via the cairo_status_t + * return. + */ +static void +png_simple_error_callback (png_structp png_save_ptr, + png_const_charp error_msg) +{ + _cairo_error (CAIRO_STATUS_NO_MEMORY); + longjmp (png_save_ptr->jmpbuf, CAIRO_STATUS_NO_MEMORY); +} + +static void +png_simple_warning_callback (png_structp png_save_ptr, + png_const_charp error_msg) +{ +} + + static cairo_status_t write_png (cairo_surface_t *surface, png_rw_ptr write_func, @@ -118,7 +137,9 @@ write_png (cairo_surface_t *surface, for (i = 0; i < image->height; i++) rows[i] = (png_byte *) image->data + i * image->stride; - png = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + png = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, + png_simple_error_callback, + png_simple_warning_callback); if (png == NULL) { status = CAIRO_STATUS_NO_MEMORY; goto BAIL2; @@ -130,10 +151,9 @@ write_png (cairo_surface_t *surface, goto BAIL3; } - if (setjmp (png_jmpbuf (png))) { - status = CAIRO_STATUS_NO_MEMORY; + status = setjmp (png_jmpbuf (png)); + if (status) goto BAIL3; - } png_set_write_fn (png, closure, write_func, NULL); @@ -350,8 +370,8 @@ read_png (png_rw_ptr read_func, /* XXX: Perhaps we'll want some other error handlers? */ png = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, - NULL, - NULL); + png_simple_error_callback, + png_simple_warning_callback); if (png == NULL) goto BAIL; diff --git a/util/.gitignore b/util/.gitignore new file mode 100644 index 000000000..aa0e19b55 --- /dev/null +++ b/util/.gitignore @@ -0,0 +1,17 @@ +.deps +.libs +Makefile +Makefile.in +*.so +*.la +*.lo +*.loT +*.o +*.obj +*.pdb +*.dll +*.exp +*.lib +*~ +.*.sw? +TAGS diff --git a/util/Makefile.am b/util/Makefile.am new file mode 100644 index 000000000..1c0cff993 --- /dev/null +++ b/util/Makefile.am @@ -0,0 +1,26 @@ + +util: 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 + diff --git a/util/backtrace-symbols.c b/util/backtrace-symbols.c new file mode 100644 index 000000000..e6ac2563d --- /dev/null +++ b/util/backtrace-symbols.c @@ -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; +} diff --git a/util/malloc-stats.c b/util/malloc-stats.c new file mode 100755 index 000000000..77c2f33f5 --- /dev/null +++ b/util/malloc-stats.c @@ -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); + } + } +} + |