--- Makefile.am.orig 2009-10-21 17:50:07.000000000 +0100 +++ Makefile.am 2009-12-01 07:22:19.000000000 +0000 @@ -7,6 +7,7 @@ cachegrind \ callgrind \ massif \ + memprof \ lackey \ none \ helgrind \ --- configure.in.orig 2009-10-21 17:50:07.000000000 +0100 +++ configure.in 2009-12-01 07:22:43.000000000 +0000 @@ -1952,6 +1952,9 @@ massif/tests/Makefile massif/perf/Makefile massif/ms_print + memprof/Makefile + memprof/tests/Makefile + memprof/docs/Makefile lackey/Makefile lackey/tests/Makefile none/Makefile --- memprof/Makefile.am.orig 2009-12-01 07:21:53.000000000 +0000 +++ memprof/Makefile.am 2009-12-01 07:29:39.000000000 +0000 @@ -0,0 +1,78 @@ +include $(top_srcdir)/Makefile.tool.am + +#---------------------------------------------------------------------------- +# Headers, etc +#---------------------------------------------------------------------------- + +#---------------------------------------------------------------------------- +# memprof- +#---------------------------------------------------------------------------- + +noinst_PROGRAMS = memprof-@VGCONF_ARCH_PRI@-@VGCONF_OS@ +if VGCONF_HAVE_PLATFORM_SEC +noinst_PROGRAMS += memprof-@VGCONF_ARCH_SEC@-@VGCONF_OS@ +endif + +MEMPROF_SOURCES_COMMON = mp_main.c + +memprof_@VGCONF_ARCH_PRI@_@VGCONF_OS@_SOURCES = $(MEMPROF_SOURCES_COMMON) +memprof_@VGCONF_ARCH_PRI@_@VGCONF_OS@_CPPFLAGS = \ + $(AM_CPPFLAGS_@VGCONF_PLATFORM_PRI_CAPS@) +memprof_@VGCONF_ARCH_PRI@_@VGCONF_OS@_CFLAGS = \ + $(AM_CFLAGS_@VGCONF_PLATFORM_PRI_CAPS@) +memprof_@VGCONF_ARCH_PRI@_@VGCONF_OS@_DEPENDENCIES = \ + $(TOOL_DEPENDENCIES_@VGCONF_PLATFORM_PRI_CAPS@) +memprof_@VGCONF_ARCH_PRI@_@VGCONF_OS@_LDADD = \ + $(TOOL_LDADD_@VGCONF_PLATFORM_PRI_CAPS@) +memprof_@VGCONF_ARCH_PRI@_@VGCONF_OS@_LDFLAGS = \ + $(TOOL_LDFLAGS_@VGCONF_PLATFORM_PRI_CAPS@) +if VGCONF_HAVE_PLATFORM_SEC +memprof_@VGCONF_ARCH_SEC@_@VGCONF_OS@_SOURCES = $(MEMPROF_SOURCES_COMMON) +memprof_@VGCONF_ARCH_SEC@_@VGCONF_OS@_CPPFLAGS = \ + $(AM_CPPFLAGS_@VGCONF_PLATFORM_SEC_CAPS@) +memprof_@VGCONF_ARCH_SEC@_@VGCONF_OS@_CFLAGS = \ + $(AM_CFLAGS_@VGCONF_PLATFORM_SEC_CAPS@) +memprof_@VGCONF_ARCH_SEC@_@VGCONF_OS@_DEPENDENCIES = \ + $(TOOL_DEPENDENCIES_@VGCONF_PLATFORM_SEC_CAPS@) +memprof_@VGCONF_ARCH_SEC@_@VGCONF_OS@_LDADD = \ + $(TOOL_LDADD_@VGCONF_PLATFORM_SEC_CAPS@) +memprof_@VGCONF_ARCH_SEC@_@VGCONF_OS@_LDFLAGS = \ + $(TOOL_LDFLAGS_@VGCONF_PLATFORM_SEC_CAPS@) +endif + +#---------------------------------------------------------------------------- +# vgpreload_memprof-.so +#---------------------------------------------------------------------------- + +noinst_PROGRAMS += vgpreload_memprof-@VGCONF_ARCH_PRI@-@VGCONF_OS@.so +if VGCONF_HAVE_PLATFORM_SEC +noinst_PROGRAMS += vgpreload_memprof-@VGCONF_ARCH_SEC@-@VGCONF_OS@.so +endif + +if VGCONF_OS_IS_DARWIN +noinst_DSYMS = $(noinst_PROGRAMS) +endif + +vgpreload_memprof_@VGCONF_ARCH_PRI@_@VGCONF_OS@_so_SOURCES = +vgpreload_memprof_@VGCONF_ARCH_PRI@_@VGCONF_OS@_so_CPPFLAGS = \ + $(AM_CPPFLAGS_@VGCONF_PLATFORM_PRI_CAPS@) +vgpreload_memprof_@VGCONF_ARCH_PRI@_@VGCONF_OS@_so_CFLAGS = \ + $(AM_CFLAGS_@VGCONF_PLATFORM_PRI_CAPS@) $(AM_CFLAGS_PIC) +vgpreload_memprof_@VGCONF_ARCH_PRI@_@VGCONF_OS@_so_DEPENDENCIES = \ + $(LIBREPLACEMALLOC_@VGCONF_PLATFORM_PRI_CAPS@) +vgpreload_memprof_@VGCONF_ARCH_PRI@_@VGCONF_OS@_so_LDFLAGS = \ + $(PRELOAD_LDFLAGS_@VGCONF_PLATFORM_PRI_CAPS@) \ + $(LIBREPLACEMALLOC_LDFLAGS_@VGCONF_PLATFORM_PRI_CAPS@) +if VGCONF_HAVE_PLATFORM_SEC +vgpreload_memprof_@VGCONF_ARCH_SEC@_@VGCONF_OS@_so_SOURCES = +vgpreload_memprof_@VGCONF_ARCH_SEC@_@VGCONF_OS@_so_CPPFLAGS = \ + $(AM_CPPFLAGS_@VGCONF_PLATFORM_SEC_CAPS@) +vgpreload_memprof_@VGCONF_ARCH_SEC@_@VGCONF_OS@_so_CFLAGS = \ + $(AM_CFLAGS_@VGCONF_PLATFORM_SEC_CAPS@) $(AM_CFLAGS_PIC) +vgpreload_memprof_@VGCONF_ARCH_SEC@_@VGCONF_OS@_so_DEPENDENCIES = \ + $(LIBREPLACEMALLOC_@VGCONF_PLATFORM_SEC_CAPS@) +vgpreload_memprof_@VGCONF_ARCH_SEC@_@VGCONF_OS@_so_LDFLAGS = \ + $(PRELOAD_LDFLAGS_@VGCONF_PLATFORM_SEC_CAPS@) \ + $(LIBREPLACEMALLOC_LDFLAGS_@VGCONF_PLATFORM_SEC_CAPS@) +endif + --- memprof/docs/Makefile.am.orig 2009-12-01 07:21:53.000000000 +0000 +++ memprof/docs/Makefile.am 2009-12-01 07:21:53.000000000 +0000 @@ -0,0 +1 @@ +#EXTRA_DIST = mp-manual.xml --- memprof/mp-gui.py.orig 2009-12-01 07:21:53.000000000 +0000 +++ memprof/mp-gui.py 2009-12-01 07:21:53.000000000 +0000 @@ -0,0 +1,264 @@ +#!/usr/bin/env python + +import pygtk +pygtk.require('2.0') +import gtk, gobject +import sys + +def cg_cmp(a, b): + if a.n_blocks < b.n_blocks: + return 1 + elif a.n_blocks > b.n_blocks: + return -1 + elif a.n_bytes < b.n_bytes: + return 1 + elif a.n_bytes > b.n_bytes: + return -1 + return 0 +class CallGraph: + by_ip = {} + by_ffname = {} + def __init__(self, ip, fname, file, line): + self.ip = ip + self.fname = fname + self.file = file + self.ffname = file + '::' + fname + self.line = line + self.parent = None + self.real = None + self.n_blocks = 0 + self.n_bytes = 0 + self.n_reallocs = 0 + self.life = 0 + self.children = {} + + if ip: + if CallGraph.by_ip.has_key (ip): + list = CallGraph.by_ip[ip] + else: + list = [] + CallGraph.by_ip[ip] = list + list.append (self) + if CallGraph.by_ffname.has_key (self.ffname): + list = CallGraph.by_ffname[self.ffname] + else: + list = [] + CallGraph.by_ffname[self.ffname] = list + list.append (self) + + def accum_stats(self, stats): + self.n_blocks += stats[0] + self.n_bytes += stats[1] + self.n_reallocs += stats[2] + self.life += stats[3] + + def add_child(self, ip, fname, file, line): + if self.children.has_key (ip): + child = self.children[ip] + else: + child = CallGraph (ip, fname, file, line) + self.children[child.ip] = child + child.parent = self + return child + + def _as_treestore_append (self, model, parent): + iter = model.append (parent, (self,)) + for cg in sorted (self.children.values(), cg_cmp): + cg._as_treestore_append (model, iter) + def as_model (self): + model = gtk.TreeStore (object) + iter = model.append (None, (self,)) + for cg in sorted (self.children.values(), cg_cmp): + cg._as_treestore_append (model, iter) + return model + + def callers_as_model (self): + model = gtk.ListStore (object) + list = [] + seen = {} + for caller in self.children.values(): + fake_cg = CallGraph(0, caller.fname, caller.file, caller.line) + fake_cg.real = caller + for cg in CallGraph.by_ffname[caller.ffname]: + if not cg.ip: continue + if seen.has_key (cg.ip): continue + fake_cg.n_blocks += cg.n_blocks + fake_cg.n_bytes += cg.n_bytes + fake_cg.n_reallocs += cg.n_reallocs + fake_cg.life += cg.life + seen[cg.ip] = cg + if fake_cg.n_blocks: list.append (fake_cg) + + for caller in sorted(list, cg_cmp): + iter = model.append ((caller,)) + return model + + def callees_as_model (self): + if not self.parent or not self.ip: + return None + + model = gtk.ListStore (object) + list = [] + seen = {} + for cg in CallGraph.by_ffname[self.ffname]: + if cg.parent and cg.parent.ip and not seen.has_key (cg.parent.ip): + fake_cg = CallGraph(0, cg.parent.fname, cg.parent.file, cg.parent.line) + fake_cg.real = cg.parent + for callee in CallGraph.by_ffname[cg.parent.ffname]: + if not callee.ip: continue + if seen.has_key (callee.ip): continue + fake_cg.n_blocks += callee.n_blocks + fake_cg.n_bytes += callee.n_bytes + fake_cg.n_reallocs += callee.n_reallocs + fake_cg.life += callee.life + seen[callee.ip] = callee + if fake_cg.n_blocks: list.append (fake_cg) + + for callee in sorted(list, cg_cmp): + iter = model.append ((callee,)) + return model + +Root = CallGraph (0, '(total)', '', 0) + +print "Loading stack traces from '%s'." % sys.argv[1] +f = file (sys.argv[1]) +line = f.readline () +count = 0 +cg = None +stats = () +while line: + elts = line.strip().split(':') + if len (elts) > 2: + if line.startswith("Trace:"): + cg = Root + stats = (int(elts[1]), int(elts[2]), int(elts[3]), int(elts[4])) + cg.accum_stats (stats) + count += 1 + else: + ip = int(elts[0], 16) + fname = elts[1] + if not fname: + fname = "0x" + elts[0] + cg = cg.add_child (ip, fname, elts[2], int (elts[3])) + cg.accum_stats (stats) + + line = f.readline () +print "Loaded %d stack traces." % count + + +def update_calls(selection, callers, callees): + callers.get_selection().unselect_all () + callees.get_selection().unselect_all () + if selection.count_selected_rows () != 1: + callers.set_model (None) + callees.set_model (None) + else: + model, iter = selection.get_selected () + cg = model.get_value (iter, 0) + callers.set_model (cg.callers_as_model ()) + callees.set_model (cg.callees_as_model ()) + +def find_ip_in_children (model, iter, ip): + while iter: + if model.get_value (iter, 0).ip == ip: + return iter + it = find_ip_in_children (model, model.iter_children(iter), ip) + if it: + return it + iter = model.iter_next (iter) + return None +def find_ip (model, ip): + return find_ip_in_children (model, model.get_iter_first(), ip) + +def row_activated(tv, path, col, fn_tv): + model = tv.get_model () + iter = model.get_iter (path) + cg = model.get_value (iter, 0) + if cg.real: cg = cg.real + if cg.ip: + model = fn_tv.get_model () + iter = find_ip (model, cg.ip) + fn_tv.get_selection().unselect_all () + if iter: + fn_tv.get_selection().select_iter (iter) + fn_tv.scroll_to_cell (model.get_path (iter)) + +def print_location(column, cell, model, iter): + cg = model.get_value (iter, 0) + s = '' + if cg and cg.file and cg.line: + s = '%s::%d' % (cg.file, cg.line) + cell.set_property ('text', s) + +def print_prop(column, cell, model, iter, prop): + cg = model.get_value (iter, 0) + s = '' + if cg and hasattr (cg, prop): + s = str (getattr (cg, prop)) + cell.set_property ('text', s) + +def build_tv_and_columns(model = None): + tv = gtk.TreeView () + #tv.props.fixed_height_mode = True + if model: tv.set_model (model) + + cr = gtk.CellRendererText() + c = gtk.TreeViewColumn ('Function', cr) + c.set_cell_data_func (cr, print_prop, 'fname') + tv.append_column (c) + + cr = gtk.CellRendererText() + c = gtk.TreeViewColumn ('Location', cr) + c.set_cell_data_func (cr, print_location) + tv.append_column (c) + + cr = gtk.CellRendererText() + c = gtk.TreeViewColumn ('nBlocks', cr) + c.set_cell_data_func (cr, print_prop, 'n_blocks') + tv.append_column (c) + + cr = gtk.CellRendererText() + c = gtk.TreeViewColumn ('nBytes', cr) + c.set_cell_data_func (cr, print_prop, 'n_bytes') + tv.append_column (c) + + cr = gtk.CellRendererText() + c = gtk.TreeViewColumn ('nReallocs', cr) + c.set_cell_data_func (cr, print_prop, 'n_reallocs') + tv.append_column (c) + + sw = gtk.ScrolledWindow () + sw.set_policy (gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) + sw.add (tv) + return sw, tv + +window = gtk.Window () +window.set_border_width (2) +table = gtk.Table (2, 2) + +vbox = gtk.VBox (False, 2) +lbl = gtk.Label ('All Callees') +vbox.pack_start (lbl, False, 0) +sw, callees = build_tv_and_columns () +vbox.add (sw) +table.attach (vbox, 1, 2, 0, 1, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 4, 4) + +vbox = gtk.VBox (False, 2) +lbl = gtk.Label ('All Callers') +vbox.pack_start (lbl, False, 0) +sw, callers = build_tv_and_columns () +vbox.add (sw) +table.attach (vbox, 1, 2, 1, 2, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 4, 4) + +sw, tv = build_tv_and_columns (Root.as_model ()) +table.attach (sw, 0, 1, 0, 2, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 4, 4) + +tv.get_selection().connect ('changed', update_calls, callers, callees) +callers.connect ('row-activated', row_activated, tv) +callees.connect ('row-activated', row_activated, tv) + +window.add (table) +window.show_all () + +window.connect ('delete-event', gtk.main_quit) +gtk.main () --- memprof/mp_main.c.orig 2009-12-01 07:21:53.000000000 +0000 +++ memprof/mp_main.c 2009-12-01 08:45:22.000000000 +0000 @@ -0,0 +1,732 @@ +/*--------------------------------------------------------------------*/ +/*--- A space-time malloc profiler. mp_main.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of MemProf, a Valgrind tool that does tells you who + is generating those calls to malloc and creating memory fragmentation. + Note this code borrows heavily from the implementation of massif. + + Copyright (C) 2007 Chris Wilson + chris@chris-wilson.co.uk + + 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 of the + License, 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, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +/* + + I'd imagine, (not being behdad that's actually looking into this), that rather than a total, what would be more interesting would be a histogram for every unique stack, (though presumably a totalling valgrind skin could be coerced into a histogram-generating skin quite easily). + just to check that I understood you... what you suggest is that for every allocation call site you would like to see the distribution of block sizes? + That's close to what I was suggesting, yes. + + But what would be even more useful, (thoug harder to do directly as a valgrind skin), would be to see things like the distribution of total size for objects like cairo_traps_t etc. So maybe that's best done with some simple instrumentation in cairo for the things we'd like to measure. + what I get currently looks like: + TOTAL MALLOC REALLOC + num size num size num size + 27 967 27 967 0 0 __strdup + 6 408 6 408 0 0 cairo_create + 74 1,628,880 0 0 74 1,628,880 _cairo_traps_grow_by + 4 64 4 64 0 0 cairo_font_options_create + 12 1,536 12 1,536 0 0 cairo_pattern_create_rgb + +*/ + + +#include "pub_tool_basics.h" +#include "pub_tool_vki.h" +#include "pub_tool_aspacemgr.h" +#include "pub_tool_debuginfo.h" +#include "pub_tool_hashtable.h" +#include "pub_tool_libcbase.h" +#include "pub_tool_libcassert.h" +#include "pub_tool_libcfile.h" +#include "pub_tool_libcprint.h" +#include "pub_tool_libcproc.h" +#include "pub_tool_machine.h" +#include "pub_tool_mallocfree.h" +#include "pub_tool_options.h" +#include "pub_tool_replacemalloc.h" +#include "pub_tool_stacktrace.h" +#include "pub_tool_tooliface.h" +#include "pub_tool_xarray.h" +#include "pub_tool_clientstate.h" + +#include "valgrind.h" + +#define container_of(T, m, ptr) (T *)((char *)(ptr) - (char *)&((T *)(0))->m) + +/* track all allocation call sites [full backtrace] */ +typedef struct _Allocator Allocator; +struct _Allocator { + VgHashNode node; /* keyed on ExeContext */ + UInt n_blocks; + UInt n_reallocs; + ULong n_bytes; + ULong accum; + ULong life; + + UInt n_ips; + Addr ips[0]; +}; +static VgHashTable Allocators; + +/* track active memory blocks */ +typedef struct _MB MB; +struct _MB { + VgHashNode node; /* keyed on block address */ + Allocator *allocator; + UInt start; /* time of alloc */ + UInt last; /* time of last [re]alloc */ + UInt n_reallocs; + SizeT size; /* current block size */ + ULong accum; /* accumulated space-time, for reallocs */ +}; +static VgHashTable MBs; + +/* statistics for a single allocation point */ +typedef struct _AllocatorHistogram AllocatorHistogram; +struct _AllocatorHistogram { + VgHashNode node; + UInt n_blocks; + UInt n_reallocs; + ULong n_bytes; + ULong life; +}; + +static Char *default_alloc_fns[] = { + "malloc", + "operator new(unsigned)", + "operator new[](unsigned)", + "operator new(unsigned, std::nothrow_t const&)", + "operator new[](unsigned, std::nothrow_t const&)", + "__builtin_new", + "__builtin_vec_new", + "calloc", + "realloc", + "memalign" +}; +static XArray *alloc_fns; + +#define BUF_LEN 1024 +static Char buf[BUF_LEN]; +static Char buf2[BUF_LEN]; +static Char buf3[BUF_LEN]; + +static ULong n_allocs, n_bytes_alloc, n_zero_allocs, n_bytes_free, n_frees; + +static Bool mp_clo_dump_stacktraces = True; +static Bool mp_clo_show_histogram = True; + +/*------------------------------------------------------------*/ +/*--- Command line options ---*/ +/*------------------------------------------------------------*/ + +static Bool mp_process_cmd_line_option (Char* arg) +{ + Char* tmp_str; + + if VG_BOOL_CLO(arg, "--dump-stacktraces", mp_clo_dump_stacktraces) {} + else if VG_BOOL_CLO(arg, "--show-histogram", mp_clo_show_histogram) {} + + else if VG_STR_CLO(arg, "--alloc-fn", tmp_str) { + VG_(addToXA)(alloc_fns, &tmp_str); + } else + return VG_(replacement_malloc_process_cmd_line_option)(arg); + + return True; +} + +static void mp_print_usage (void) +{ + VG_(printf) ( +" --dump-stacktraces=no|yes display statistics for each call site [yes]\n" +" --show-histogram=no|yes display cumulative histogram of allocators [yes]\n" +" --alloc-fn= specify as an alloc function [empty]\n" + ); +} + +static void mp_print_debug_usage (void) +{ + VG_(printf)( +" (none)\n" + ); +} + + +/*------------------------------------------------------------*/ +/*--- malloc() et al replacement wrappers ---*/ +/*------------------------------------------------------------*/ + +static Bool is_alloc_fn (Addr ip) +{ + if (VG_(get_fnname) (ip, buf, BUF_LEN)) { + Word first, last; + Char *bp = buf; + return VG_(lookupXA) (alloc_fns, &bp, &first, &last); + } + return False; +} + + +static void * +new_block (ThreadId tid, void *p, SizeT size, SizeT align, Bool is_zeroed) +{ + MB *mb; + Allocator *A; + Addr where; + if (size < 0) return NULL; + + /* Global statistics */ + n_allocs++; + n_bytes_alloc += size; + n_zero_allocs += 0 == size; + + /* Allocate and zero as necessary */ + if (!p) { + p = VG_(cli_malloc) (align, size); + if (!p) { + return NULL; + } + if (is_zeroed) VG_(memset) (p, 0, size); + } + + /* per-block statistics */ + mb = VG_(malloc)("mp.main.mb.1", + sizeof (MB)); + mb->node.key = (Addr) p; + mb->size = size; + mb->start = mb->last = VG_(read_millisecond_timer) (); + mb->n_reallocs = 0; + mb->accum = 0; + VG_(HT_add_node)(MBs, mb); + + /* per-callsite statistics */ + where = (Addr) VG_(record_ExeContext) (tid, 0); + A = VG_(HT_lookup) (Allocators, where); + if (!A) { + Addr local_ips[128], *ips=local_ips; + UInt n_ips, max_ips = sizeof (local_ips) / sizeof (local_ips[0]); + n_ips = VG_(get_StackTrace) (tid, ips, max_ips, NULL, NULL, 0); + while (n_ips == max_ips) { + max_ips *= 2; + if (ips != local_ips) + VG_(free) (ips); + ips = VG_(malloc) ("mp.main.ips.1", max_ips * sizeof (Addr)); + n_ips = VG_(get_StackTrace) (tid, ips, max_ips, NULL, NULL, 0); + } + A = VG_(malloc) ("mp.main.a.1", + sizeof (Allocator) + n_ips * sizeof (Addr)); + A->node.key = where; + A->n_bytes = 0; + A->n_blocks = 0; + A->n_reallocs = 0; + A->accum = 0; + A->life = 0; + A->n_ips = n_ips; + VG_(memcpy) (&A->ips[0], ips, n_ips * sizeof (Addr)); + VG_(HT_add_node) (Allocators, A); + + if (ips != local_ips) + VG_(free) (ips); + } + A->n_blocks++; + A->n_bytes += size; + mb->allocator = A; + + return p; +} + +static Allocator * +accum_block (MB *mb) +{ + Allocator *A; + UInt now, life; + + now = VG_(read_millisecond_timer) (); + life = now - mb->start; + mb->accum += (now - mb->last) * mb->size; + + /* add to this stacktrace total */ + A = mb->allocator; + tl_assert (A != NULL); + A->accum += mb->accum; + A->life += life; + + return A; +} + +static void +die_block (void *p, Bool custom_free) +{ + MB *mb; + Allocator *A; + + /* Global statistics */ + n_frees++; + + mb = VG_(HT_remove) (MBs, (Addr) p); + if (NULL == mb) + return; /* bogus */ + + A = accum_block (mb); + n_bytes_free += mb->size; + + VG_(free) (mb); + + /* Actually free the heap block */ + if (!custom_free) + VG_(cli_free) (p); +} + + +static void *mp_malloc (ThreadId tid, SizeT n) +{ + return new_block (tid, NULL, n, VG_(clo_alignment), False); +} + +static void *mp_calloc (ThreadId tid, SizeT m, SizeT size) +{ + return new_block (tid, NULL, m*size, VG_(clo_alignment), True); +} + +static void *mp_memalign (ThreadId tid, SizeT align, SizeT n) +{ + return new_block (tid, NULL, n, align, False); +} + +static void mp_free (ThreadId tid, void *p) +{ + die_block (p, False); +} + +static void *mp_realloc (ThreadId tid, void *p_old, SizeT new_size) +{ + MB *mb = NULL; + void *p_new; + SizeT old_size; + + p_new = NULL; + if (new_size > 0) { + mb = VG_(HT_remove) (MBs, (Addr) p_old); + if (mb == NULL) { + return mp_malloc (tid, new_size); + } + + mb->n_reallocs++; + old_size = mb->size; + + if (new_size <= mb->size) { + p_new = p_old; + } else { /* we need to grow the block */ + p_new = VG_(cli_malloc) (VG_(clo_alignment), new_size); + if (p_new != NULL) { + VG_(memcpy) (p_new, p_old, mb->size); + VG_(cli_free) (p_old); + } + } + } + + if (p_new) { + Allocator *A = mb->allocator; + UInt now = VG_(read_millisecond_timer) (); + mb->accum += (now - mb->last) * mb->size; + mb->last = now; + + tl_assert (A != NULL); + A->n_bytes += (SSizeT) (new_size - mb->size); + + mb->node.key = (Addr) p_new; + mb->size = new_size; + + VG_(HT_add_node) (MBs, mb); + } else { + VG_(HT_add_node) (MBs, mb); /* die() checks that it's in the HT */ + die_block(p_old, False); + } + + return p_new; +} + +static SizeT mp_malloc_usable_size ( ThreadId tid, void* p ) +{ + (void)tid; + return ( VG_(malloc_usable_size)(p) ); +} + + +/*------------------------------------------------------------*/ +/*--- Client Requests ---*/ +/*------------------------------------------------------------*/ + +static Bool mp_handle_client_request (ThreadId tid, UWord* argv, UWord* ret) +{ + switch (argv[0]) { + case VG_USERREQ__MALLOCLIKE_BLOCK: { + void *res; + void *p = (void*)argv[1]; + SizeT sizeB = argv[2]; + *ret = 0; + res = new_block (tid, p, sizeB, 0, False); + tl_assert (res == p); + return True; + } + case VG_USERREQ__FREELIKE_BLOCK: { + void *p = (void*)argv[1]; + *ret = 0; + die_block (p, True); + return True; + } + default: + *ret = 0; + return False; + } +} + + +/*------------------------------------------------------------*/ +/*--- Finalisation ---*/ +/*------------------------------------------------------------*/ + +static Int mp_histogram_cmp(void *a, void *b) +{ + AllocatorHistogram *A = *(AllocatorHistogram **) a; + AllocatorHistogram *B = *(AllocatorHistogram **) b; + ULong ta, tb; + + /* rank by frequent allocs */ + if (A->n_blocksn_blocks) + return -1; + else if (A->n_blocks>B->n_blocks) + return 1; + + /* rank by short-lived allocs */ + ta = A->life/A->n_blocks; + tb = B->life/B->n_blocks; + if (tatb) + return -1; + + /* rank by large allocs */ + ta = A->n_bytes/A->n_blocks; + tb = B->n_bytes/B->n_blocks; + if (tatb) + return 1; + + /* rank by allocated */ + if (A->n_bytesn_bytes) + return -1; + else if (A->n_bytes>B->n_bytes) + return -1; + else + return 0; +} + +static void +accum_histogram (Allocator *A, VgHashTable histograms) +{ + /* add block to allocator histogram */ + AllocatorHistogram *H; + Addr ip; + UInt n; + + for (n = 0; n < A->n_ips && is_alloc_fn (A->ips[n]); n++) ; + if (n == A->n_ips) + n = A->n_ips - 1; + ip = A->ips[n]; + if (n > 0) + ip -= VG_MIN_INSTR_SZB; /* point to calling line */ + + H = VG_(HT_lookup) (histograms, ip); + if (H == NULL) { + H = VG_(malloc) ("mp.main.h.1", sizeof (AllocatorHistogram)); + H->node.key = ip; + H->n_blocks = 0; + H->n_bytes = 0; + H->n_reallocs = 0; + H->life = 0; + VG_(HT_add_node) (histograms, H); + } + H->n_blocks += A->n_blocks; + H->n_bytes += A->n_bytes; + H->n_reallocs += A->n_reallocs; + H->life += A->life; +} + +static void +print_histogram (void) +{ + VgHashTable histograms; + AllocatorHistogram **as; + Allocator *ap; + Int n_as, i; + ULong n_blocks = 0; + ULong n_bytes = 0; + ULong n_reallocs = 0; + ULong life = 0; + + histograms = VG_(HT_construct)( "memprof histogram" ); + + VG_(HT_ResetIter)( Allocators ); + while ( (ap = VG_(HT_Next)( Allocators )) != NULL) { + accum_histogram(ap, histograms); + } + + as = (AllocatorHistogram **)VG_(HT_to_array) (histograms, &n_as); + VG_(ssort)((void *)as, n_as, sizeof(VgHashNode *), mp_histogram_cmp); + + VG_(umsg) ("%d distinct allocators.\n", n_as); + if (n_as) { + VG_(umsg) ( + "nBlocks\t\tnBytes\t\tnReallocs\t\tLifespan (ms)\n"); + for (i = 0; i < n_as; i++) { + AllocatorHistogram *H = as[i]; + if (VG_(get_fnname) (H->node.key, buf, BUF_LEN)) { + if (VG_(get_filename) (H->node.key, buf2, BUF_LEN)){ + UInt line = 0; + VG_(get_linenum) (H->node.key, &line); + VG_(umsg) ( + "%u\t\t%llu\t\t%u\t\t%llu\t\t%s [%s::%u]\n", + H->n_blocks, + H->n_bytes, + H->n_reallocs, + H->life / H->n_blocks, + buf, buf2, line); + } else { + VG_(umsg) ( + "%u\t\t%llu\t\t%u\t\t%llu\t\t%s\n", + H->n_blocks, + H->n_bytes, + H->n_reallocs, + H->life / H->n_blocks, + buf); + } + } + n_blocks += H->n_blocks; + n_bytes += H->n_bytes; + n_reallocs += H->n_reallocs; + life += H->life; + } + VG_(umsg) ("%llu\t\t%llu\t\t%llu\t\t%llu\t\t(total)\n", + n_blocks, n_bytes, n_reallocs, life/n_blocks); + } + + VG_(free)(as); + VG_(HT_destruct) (histograms); +} + +static void +print_summary (void) +{ + if (VG_(clo_verbosity) > 1) { + VG_(message)(Vg_DebugMsg, " allocs: %llu", n_allocs); + VG_(message)(Vg_DebugMsg, "zero allocs: %llu", n_zero_allocs); + VG_(message)(Vg_DebugMsg, " frees: %llu", n_frees); + } +} + + +#define FWRITE_BUFSIZE 32000 +#define FWRITE_THROUGH 10000 +static Char fwrite_buf[FWRITE_BUFSIZE]; +static Int fwrite_pos; + +static __inline__ +void mp_fwrite_flush (Int fd) +{ + if (fwrite_pos > 0) + VG_(write) (fd, fwrite_buf, fwrite_pos); + fwrite_pos = 0; +} + +static void mp_fwrite (Int fd, void *bp, Int len) +{ + if (len > FWRITE_THROUGH) { + mp_fwrite_flush (fd); + VG_(write) (fd, bp, len); + return; + } + if (FWRITE_BUFSIZE - fwrite_pos <= len) mp_fwrite_flush (fd); + VG_(memcpy) (fwrite_buf + fwrite_pos, bp, len); + fwrite_pos += len; +} + +static void +dump_allocator (Allocator *A, Int fd) +{ + UInt n, len; + + len = VG_(sprintf) (buf3, "Trace:%u:%llu:%u:%llu\n", + A->n_blocks, A->n_bytes, A->n_reallocs, A->life); + mp_fwrite (fd, buf3, len); + for (n = 0; n < A->n_ips; n++) { + Addr ip = A->ips[n]; + UInt line; + if (n > 0) + ip -= VG_MIN_INSTR_SZB; + + if (!VG_(get_fnname) (ip, buf, BUF_LEN)) + buf[0] = '\0'; + + line = 0; + if (VG_(get_filename) (ip, buf2, BUF_LEN)) + VG_(get_linenum) (ip, &line); + else + buf2[0] = '\0'; + len = VG_(sprintf) (buf3, "%p:%s:%s:%d\n", (void *)ip, buf, buf2, line); + mp_fwrite (fd, buf3, len); + } + mp_fwrite(fd, "\n", 1); +} + +static Char* mp_memprof_out_file = "memprof.out.%p"; + +// XXX refactor writes to use FP macro from massif. + +static void +dump_stacktraces (void) +{ + Allocator *ap; + Int fd, len; + SysRes sres; + + Char* memprof_out_file = + VG_(expand_file_name)("--mp-out-file", mp_memprof_out_file); + + sres = VG_(open) (memprof_out_file, VKI_O_CREAT|VKI_O_TRUNC|VKI_O_WRONLY, + VKI_S_IRUSR|VKI_S_IWUSR); + if (sr_isError(sres)) { + sres = VG_(open) (buf, VKI_O_CREAT|VKI_O_WRONLY, + VKI_S_IRUSR|VKI_S_IWUSR); + if (sr_isError(sres)) { + VG_(umsg) ("error: can not open memprof output file '%s'\n", + memprof_out_file); + VG_(free)(memprof_out_file); + return; + } + } + + fd = sr_Res(sres); + VG_(free)(memprof_out_file); + + len = VG_(sprintf)(buf, "version: 1\n\n"); + mp_fwrite (fd, buf, len); + + VG_(HT_ResetIter)( Allocators ); + while ( (ap = VG_(HT_Next)( Allocators )) != NULL) { + dump_allocator(ap, fd); + } + + mp_fwrite_flush (fd); + VG_(close) (fd); +} + +/*------------------------------------------------------------*/ +/*--- Basic tool functions ---*/ +/*------------------------------------------------------------*/ + +static Int +mp_alloc_fns_cmp (void *A, void *B) +{ + Char *a = *(Char **)A; + Char *b = *(Char **)B; + return VG_(strcmp)(a, b); +} + +static void +mp_post_clo_init(void) +{ + VG_(setCmpFnXA) (alloc_fns, mp_alloc_fns_cmp); + VG_(sortXA) (alloc_fns); +} + +static IRSB * +mp_instrument (VgCallbackClosure *closure, + IRSB *sbIn, + VexGuestLayout *layout, + VexGuestExtents *vge, + IRType gWordTy, IRType hWordTy) +{ + return sbIn; +} + +static void mp_fini(Int exitcode) +{ + MB* mb; + + VG_(HT_ResetIter)( MBs ); + while ( (mb = VG_(HT_Next)( MBs )) != NULL) { + accum_block(mb); + } + + VG_(HT_destruct) (MBs); + + if (mp_clo_dump_stacktraces) + dump_stacktraces (); + if (mp_clo_show_histogram) + print_histogram (); + VG_(HT_destruct) (Allocators); + + print_summary (); +} + +static void mp_pre_clo_init (void) +{ + UInt n; + + VG_(details_name) ("MemProf"); + VG_(details_version) (NULL); + VG_(details_description) ("a space-time malloc profiler"); + VG_(details_copyright_author)( + "Copyright (C) 2007, and GNU GPL'd, by Chris Wilson."); + VG_(details_bug_reports_to) ("Chris Wilson "); + + VG_(basic_tool_funcs) (mp_post_clo_init, + mp_instrument, + mp_fini); + + VG_(needs_libc_freeres)(); + VG_(needs_command_line_options)(mp_process_cmd_line_option, + mp_print_usage, + mp_print_debug_usage); + VG_(needs_client_requests) (mp_handle_client_request); + VG_(needs_malloc_replacement) (mp_malloc, + mp_malloc, /* __builtin_new */ + mp_malloc, /* __builtin_vec_new */ + mp_memalign, + mp_calloc, + mp_free, + mp_free, /* __builtin_delete */ + mp_free, /* __builtin_vec_delete */ + mp_realloc, + mp_malloc_usable_size, + 0 ); + + + MBs = VG_(HT_construct)( "memprof heap blocks" ); + Allocators = VG_(HT_construct)( "memprof allocators" ); + + alloc_fns = VG_(newXA)(VG_(malloc), "mp.main.iaf.1", + VG_(free), sizeof (Char *)); + for (n = 0; n < sizeof (default_alloc_fns) / sizeof (default_alloc_fns[0]); n++) + VG_(addToXA) (alloc_fns, &default_alloc_fns[n]); +} + +VG_DETERMINE_INTERFACE_VERSION(mp_pre_clo_init) --- memprof/tests/Makefile.am.orig 2009-12-01 07:21:53.000000000 +0000 +++ memprof/tests/Makefile.am 2009-12-01 07:21:53.000000000 +0000 @@ -0,0 +1,4 @@ +#noinst_SCRIPTS = filter_stderr + +#EXTRA_DIST = $(noinst_SCRIPTS) \ +# true.stderr.exp true.vgtest