]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Addition of the pub_tool_xtree.h and pub_tool_xtmemory.h modules, and of the --xtree...
authorPhilippe Waroquiers <philippe.waroquiers@skynet.be>
Fri, 11 Nov 2016 14:07:03 +0000 (14:07 +0000)
committerPhilippe Waroquiers <philippe.waroquiers@skynet.be>
Fri, 11 Nov 2016 14:07:03 +0000 (14:07 +0000)
This commit is the bulk of the new code.
There is however no functional impact yet : the new modules are not used by anybody.

git-svn-id: svn://svn.valgrind.org/valgrind/trunk@16123

coregrind/Makefile.am
coregrind/m_options.c
coregrind/m_xtmemory.c [new file with mode: 0644]
coregrind/m_xtree.c [new file with mode: 0644]
coregrind/pub_core_options.h
coregrind/pub_core_xtmemory.h [new file with mode: 0644]
coregrind/pub_core_xtree.h [new file with mode: 0644]
include/Makefile.am
include/pub_tool_options.h
include/pub_tool_xtmemory.h [new file with mode: 0644]
include/pub_tool_xtree.h [new file with mode: 0644]

index d798015d74d815521993bf0eb0593895b619e5e0..41c5ef49319b0b986de5ecf7d2ef407e732d0656 100644 (file)
@@ -234,6 +234,8 @@ noinst_HEADERS = \
        pub_core_vkiscnums_asm.h\
        pub_core_wordfm.h       \
        pub_core_xarray.h       \
+       pub_core_xtree.h        \
+       pub_core_xtmemory.h     \
        m_aspacemgr/priv_aspacemgr.h \
        m_debuginfo/priv_misc.h \
        m_debuginfo/priv_storage.h      \
@@ -332,6 +334,8 @@ COREGRIND_SOURCES_COMMON = \
        m_vkiscnums.c \
        m_wordfm.c \
        m_xarray.c \
+       m_xtree.c \
+       m_xtmemory.c \
        m_aspacehl.c \
        m_aspacemgr/aspacemgr-common.c \
        m_aspacemgr/aspacemgr-linux.c \
index d5e07da8509936274ea782d040d21f8e742c38ff..591e40e7d9ae897d81da08961e47a7314b0bd02f 100644 (file)
@@ -112,6 +112,10 @@ Int    VG_(clo_core_redzone_size) = CORE_REDZONE_DEFAULT_SZB;
 // A value != -1 overrides the tool-specific value
 // VG_(needs_malloc_replacement).tool_client_redzone_szB
 Int    VG_(clo_redzone_size)   = -1;
+VgXTMemory VG_(clo_xtree_memory) =  Vg_XTMemory_None;
+const HChar* VG_(clo_xtree_memory_file) = "xtmemory.kcg.%p";
+Bool VG_(clo_xtree_compress_strings) = True;
+
 Int    VG_(clo_dump_error)     = 0;
 Int    VG_(clo_backtrace_size) = 12;
 Int    VG_(clo_merge_recursive_frames) = 0; // default value: no merge
diff --git a/coregrind/m_xtmemory.c b/coregrind/m_xtmemory.c
new file mode 100644 (file)
index 0000000..5e92cad
--- /dev/null
@@ -0,0 +1,341 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Support functions for xtree memory reports. m_xtmemory.c     ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) 2016-2016 Philippe Waroquiers
+
+   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.
+*/
+
+#include "pub_core_libcassert.h"
+#include "pub_core_libcbase.h"
+#include "pub_core_libcprint.h"
+#include "pub_core_libcproc.h"
+#include "pub_core_mallocfree.h"
+#include "pub_core_options.h"
+#include "pub_core_xarray.h"
+#include "pub_core_xtree.h"
+#include "pub_core_xtmemory.h"    /* self */
+
+static void VG_(XT_Allocs_init)(void* xt_allocs)
+{
+   VG_(memset) (xt_allocs, 0, sizeof(XT_Allocs));
+}
+static void VG_(XT_Allocs_add) (void* to, const void* xt_allocs)
+{
+   XT_Allocs* xto = to;
+   const XT_Allocs* xta = xt_allocs;
+
+   xto->nbytes  += xta->nbytes;
+   xto->nblocks += xta->nblocks;
+}
+static void VG_(XT_Allocs_sub) (void* from, const void* xt_allocs)
+{
+   XT_Allocs* xfrom = from;
+   const XT_Allocs* xta = xt_allocs;
+
+   xfrom->nbytes  -= xta->nbytes;
+   xfrom->nblocks -= xta->nblocks;
+}
+static const HChar* VG_(XT_Allocs_img) (const void* xt_allocs)
+{
+   static HChar buf[100];
+
+   const XT_Allocs* xta = xt_allocs;
+   
+   if (xta->nbytes > 0 || xta->nblocks > 0) {
+      VG_(sprintf) (buf, "%lu %lu",
+                    xta->nbytes, xta->nblocks);
+      return buf;
+   } else {
+      return NULL;
+   }
+}
+const HChar* XT_Allocs_events = "curB : currently allocated Bytes"   ","
+                                "curBk : currently allocated Blocks";
+
+/* Type and functions for full xtree memory profiling. */
+static XTree* full_xt;
+typedef
+   struct _XT_Full {
+      // Current nr of bytes/blocks allocated by this ec
+      SizeT cur_alloc_nbytes;
+      SizeT cur_alloc_nblocks;
+
+      // Total/cumulative nr of bytes/blocks allocated by this ec
+      ULong tot_alloc_nbytes;
+      ULong tot_alloc_nblocks;
+
+      // Total/cumulative nr of bytes/blocks freed by this ec
+      ULong tot_freed_nbytes;
+      ULong tot_freed_nblocks;
+   } XT_Full;
+/* Note: normally, an ec should never be used as both an alloc_ec and
+   a free_ec. This implies that we should never have a XT_Full that has
+   at the same time some alloc and some freed components > 0.
+   We however still will support this possibility, just in case very
+   strange ec are produced and/or given by the tool. */
+
+static void VG_(XT_Full_init)(void* xtfull)
+{
+   VG_(memset) (xtfull, 0, sizeof(XT_Full));
+}
+static void VG_(XT_Full_add) (void* to, const void* xtfull)
+{
+   XT_Full* xto = to;
+   const XT_Full* xtf = xtfull;
+
+   xto->cur_alloc_nbytes  += xtf->cur_alloc_nbytes;
+   xto->cur_alloc_nblocks += xtf->cur_alloc_nblocks;
+   xto->tot_alloc_nbytes  += xtf->tot_alloc_nbytes;
+   xto->tot_alloc_nblocks += xtf->tot_alloc_nblocks;
+   xto->tot_freed_nbytes  += xtf->tot_freed_nbytes;
+   xto->tot_freed_nblocks += xtf->tot_freed_nblocks;
+}
+static void VG_(XT_Full_sub) (void* from, const void* xtfull)
+{
+   XT_Full* xfrom = from;
+   const XT_Full* xtf = xtfull;
+
+   xfrom->cur_alloc_nbytes  -= xtf->cur_alloc_nbytes;
+   xfrom->cur_alloc_nblocks -= xtf->cur_alloc_nblocks;
+   xfrom->tot_alloc_nbytes  -= xtf->tot_alloc_nbytes;
+   xfrom->tot_alloc_nblocks -= xtf->tot_alloc_nblocks;
+   xfrom->tot_freed_nbytes  -= xtf->tot_freed_nbytes;
+   xfrom->tot_freed_nblocks -= xtf->tot_freed_nblocks;
+}
+static const HChar* VG_(XT_Full_img) (const void* xtfull)
+{
+   static HChar buf[300];
+
+   const XT_Full* xtf = xtfull;
+   
+   if (   xtf->cur_alloc_nbytes  > 0
+       || xtf->cur_alloc_nblocks > 0
+       || xtf->tot_alloc_nbytes  > 0
+       || xtf->tot_alloc_nblocks > 0
+       || xtf->tot_freed_nbytes  > 0
+       || xtf->tot_freed_nblocks > 0) {
+      VG_(sprintf) (buf, 
+                    "%lu %lu "
+                    "%llu %llu "
+                    "%llu %llu",
+                    xtf->cur_alloc_nbytes, xtf->cur_alloc_nblocks,
+                    xtf->tot_alloc_nbytes, xtf->tot_alloc_nblocks,
+                    xtf->tot_freed_nbytes, xtf->tot_freed_nblocks);
+      return buf;
+   } else {
+      return NULL;
+   }
+}
+static const HChar* XT_Full_events = 
+   "curB : currently allocated Bytes"   ","
+   "curBk : currently allocated Blocks" ","
+   "totB : total allocated Bytes"       ","
+   "totBk : total allocated Blocks"     ","
+   "totFdB : total Freed Bytes"         ","
+   "totFdBk : total Freed Blocks";
+void VG_(XTMemory_Full_init)(XT_filter_IPs_t filter_IPs_fn)
+{
+   full_xt = VG_(XT_create) (VG_(malloc),
+                             "m_xtree.full_xt",
+                             VG_(free),
+                             sizeof(XT_Full),
+                             VG_(XT_Full_init),
+                             VG_(XT_Full_add),
+                             VG_(XT_Full_sub),
+                             filter_IPs_fn);
+}
+void VG_(XTMemory_Full_alloc)(SizeT szB,
+                              ExeContext* ec_alloc)
+{
+   XT_Full xtf = {szB, 1, szB, 1, 0, 0};
+   VG_(XT_add_to_ec)(full_xt, ec_alloc, &xtf);
+}
+void VG_(XTMemory_Full_free)(SizeT szB,
+                             ExeContext* ec_alloc,
+                             ExeContext* ec_free)
+{
+   // substract from ec_alloc the freed memory.
+   XT_Full xtf_sub = {szB, 1, 0, 0, 0, 0};
+   VG_(XT_sub_from_ec)(full_xt, ec_alloc, &xtf_sub);
+
+   // add to ec_free the freed memory
+   XT_Full xtf_add = {0, 0, 0, 0, szB, 1};
+   VG_(XT_add_to_ec)(full_xt, ec_free, &xtf_add);
+}
+
+void VG_(XTMemory_Full_resize_in_place)(SizeT oldSzB, SizeT newSzB,
+                                        ExeContext* ec_alloc)
+{
+   if (oldSzB > newSzB) {
+      XT_Full xtf = {oldSzB - newSzB, 0, oldSzB - newSzB, 0, 0, 0};
+      VG_(XT_sub_from_ec)(full_xt, ec_alloc, &xtf);
+   } else {
+      XT_Full xtf = {newSzB - oldSzB, 0, newSzB - oldSzB, 0, 0, 0};
+      VG_(XT_add_to_ec)(full_xt, ec_alloc, &xtf);
+   }
+}
+
+// Indicates which event nr the report_value function must return.
+static UInt event_report_value_id;
+static ULong XT_Full_report_value(const void* xtfull)
+{
+   const XT_Full* xtf = xtfull;   
+   switch (event_report_value_id) {
+      case 0: return (ULong) xtf->cur_alloc_nbytes;
+      case 1: return (ULong) xtf->cur_alloc_nblocks;
+      case 2: return xtf->tot_alloc_nbytes;
+      case 3: return xtf->tot_alloc_nblocks;
+      case 4: return xtf->tot_freed_nbytes;
+      case 5: return xtf->tot_freed_nblocks;
+      default: vg_assert(0);
+   }
+}
+static ULong XT_Allocs_report_value(const void* xt_allocs)
+{
+   const XT_Allocs* xta = xt_allocs;   
+   switch (event_report_value_id) {
+      case 0: return (ULong) xta->nbytes;
+      case 1: return (ULong) xta->nblocks;
+      default: vg_assert(0);
+   }
+}
+
+static void produce_report(XTree* xt, const HChar* filename,
+                           const HChar* events,
+                           const HChar* (*img_value) (const void* value),
+                           ULong (*report_value)(const void* value))
+{
+   /* The user can control the kind of report using filename extension. */
+   if (VG_(strstr)(filename, ".ms")) {
+      /* If needed, some harcoded value below could become parameters. */
+      MsFile* fp;
+      Massif_Header header = (Massif_Header) {
+         .snapshot_n    = 0,
+         .time          = VG_(read_millisecond_timer)(),
+         .sz_B          = 0ul,
+         .extra_B       = 0ul,
+         .stacks_B      = 0ul,
+         .detailed      = True,
+         .peak          = False,
+         .top_node_desc = NULL,
+         .sig_threshold = 0.00000000000001
+         // Currently, we take a very small float value to not output
+         // the 0 values, but still output all the rest.
+      };
+
+      // Variables to parse events
+      HChar strtok_events[VG_(strlen)(events)+1];
+      HChar* e;
+      HChar* ssaveptr;
+
+      fp = VG_(XT_massif_open)(filename,
+                               "xtree.produce_report",
+                               NULL,
+                               "ms");
+
+      event_report_value_id = 0;
+      VG_(strcpy)(strtok_events, events);
+      for (e = VG_(strtok_r) (strtok_events, ",", &ssaveptr); 
+           e != NULL; 
+           e = VG_(strtok_r) (NULL, ",", &ssaveptr)) {
+         header.top_node_desc = e;
+         VG_(XT_massif_print)(fp, xt, &header, report_value);
+         header.snapshot_n++;
+         event_report_value_id++;
+      }
+
+      VG_(XT_massif_close)(fp);
+   } else
+      VG_(XT_callgrind_print)(xt,
+                             filename,
+                             events,
+                             img_value);
+}
+
+void VG_(XTMemory_report) 
+     (const HChar* filename, Bool fini,
+      void (*next_block)(XT_Allocs* xta, ExeContext** ec_alloc),
+      XT_filter_IPs_t filter_IPs_fn)
+{
+   HChar* expanded_filename;
+
+   if (fini && VG_(clo_xtree_memory) == Vg_XTMemory_None)
+      return;
+
+   expanded_filename 
+      = VG_(expand_file_name)("--xtree-memory-file",
+                              (filename == NULL) ?
+                              (fini ? 
+                               VG_(clo_xtree_memory_file) : "xtmemory.kcg")
+                              : filename);
+
+   /* fini is False => even if user kept --xtree-memory=none, we
+      produce a report when explicitely requested e.g. via a monitor
+      command. */
+   switch (VG_(clo_xtree_memory)) {
+      case Vg_XTMemory_None:
+      case Vg_XTMemory_Allocs: {
+         XTree* xt;
+         XT_Allocs  xta;
+         ExeContext* ec_alloc;
+
+         xt = VG_(XT_create) (VG_(malloc),
+                              "VG_(XTMemory_report)",
+                              VG_(free),
+                              sizeof(XT_Allocs),
+                              VG_(XT_Allocs_init),
+                              VG_(XT_Allocs_add),
+                              VG_(XT_Allocs_sub),
+                              filter_IPs_fn);
+         (*next_block)(&xta, &ec_alloc);
+         while ( xta.nblocks > 0 ) {
+            VG_(XT_add_to_ec) (xt, ec_alloc, &xta);
+            (*next_block)(&xta, &ec_alloc);
+         }
+
+         produce_report(xt, expanded_filename,
+                        XT_Allocs_events, VG_(XT_Allocs_img),
+                        XT_Allocs_report_value);
+
+         VG_(XT_delete)(xt);
+         break;
+      }
+      case Vg_XTMemory_Full:
+         produce_report(full_xt, expanded_filename,
+                        XT_Full_events, VG_(XT_Full_img),
+                        XT_Full_report_value);
+         break;
+      default: 
+         vg_assert(0);
+   }
+   if (VG_(clo_verbosity) >= 1 || !fini)
+      VG_(umsg)("xtree memory report: %s\n", expanded_filename);
+
+   VG_(free)(expanded_filename);
+}
+
+/*--------------------------------------------------------------------*/
+/*--- end                                                m_xtree.c ---*/
+/*--------------------------------------------------------------------*/
diff --git a/coregrind/m_xtree.c b/coregrind/m_xtree.c
new file mode 100644 (file)
index 0000000..5da5986
--- /dev/null
@@ -0,0 +1,1013 @@
+
+/*--------------------------------------------------------------------*/
+/*--- An xtree, tree of stacktraces with data            m_xtree.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) 2016-2016 Philippe Waroquiers
+
+   This code generalises the XTree idea that was implemented in
+   the massif tool in Valgrind versions <= 3.12, which is
+      Copyright (C) 2005-2015 Nicholas Nethercote
+       njn@valgrind.org
+
+   The XTree implementation in this file is however implemented completely
+   differently. Some code has been re-used for the production of the
+   massif file header (e.g. FP_cmd function).
+
+   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.
+*/
+
+#include "pub_core_basics.h"
+#include "pub_core_debuglog.h"
+#include "pub_core_clientstate.h"
+#include "pub_core_stacktrace.h"
+#include "pub_core_execontext.h"
+#include "pub_core_libcbase.h"
+#include "pub_core_libcassert.h"
+#include "pub_core_libcfile.h"
+#include "pub_core_libcprint.h"
+#include "pub_core_libcproc.h"
+#include "pub_core_hashtable.h"
+#include "pub_core_mallocfree.h"
+#include "pub_core_options.h"
+#include "pub_core_debuginfo.h"
+#include "pub_core_deduppoolalloc.h"
+#include "pub_core_xtree.h"    /* self */
+
+#define DMSG(level, ...) (level <= VG_(debugLog_getLevel)() ?         \
+                          VG_(dmsg)(__VA_ARGS__)                      \
+                          : 0)
+
+/* Defines the relevant part of an ec. This is shared between an xt
+   and its snapshots (see XT_shared XArray of xec). */
+typedef
+   struct _xec {
+     ExeContext* ec;
+     UShort top;        // The first ips of ec to take into account.
+     UShort n_ips_sel;  // The nr of ips from top to take into account.
+   }
+   xec;
+
+/* XT_shared maintains the information shared between an XT and all
+   its snapshots. */
+typedef
+   struct _XT_shared {
+      UWord nrRef; /* nr of XTrees referencing this shared memory. */
+
+      void* (*alloc_fn)( const HChar*, SizeT ); /* alloc fn (nofail) */
+      const HChar* cc;                    /* cost centre for alloc */
+      void  (*free_fn)( void* );         /* free fn */
+
+      /* The data associated to each ec is stored in 2 arrays:
+           an xec array, shared between an xt and all its snapshots.
+           a  data array, private to each XTree.
+         For an ec with an ECU ecu, d4ecu2xecu[ecu/4] gives the offset in
+         xec and data arrays where the ec information is located (this
+         indirection is used to avoid huge xec and data arrays, in
+         case an XTree contains data only for a small number of ec.
+         The offset in the xec and data array is used as xtree ec unique
+         id i.e. an xecu. */
+
+      UInt  d4ecu2xecu_sz; /* size of d4ecu2xecu (in nr of elements). */
+      UInt* d4ecu2xecu;
+
+      /* ec information common to an xt and its snapshots. */
+      XArray* xec; /* XArray of xec, indexed by xecu (== d4ecu2xecu[ecu/4]). */
+   
+      /* XArray of xecu, sorted by StackTrace ips[top..top+n_ips_sel-1].
+         See ips_order_cmp. */
+      XArray* ips_order_xecu;
+   } XT_shared;
+
+/* NO_OFFSET indicates in d4ecu2xecu  there is no data (yet) for this ec
+   (with the index ecu/4). */
+#define NO_OFFSET 0xffffffff
+
+static XT_shared* new_XT_shared (void*  (*alloc_fn)(const HChar*, SizeT),
+                                 const  HChar* cc,
+                                 void   (*free_fn)(void*))
+{
+   XT_shared* shared;
+
+   vg_assert(alloc_fn);
+   vg_assert(cc);
+   vg_assert(free_fn);
+   shared = alloc_fn(cc, sizeof(*shared));
+   shared->nrRef = 0;
+   shared->alloc_fn = alloc_fn;
+   shared->cc = cc;
+   shared->free_fn = free_fn;
+
+   shared->d4ecu2xecu_sz = 0;
+   shared->d4ecu2xecu = NULL;
+   shared->xec = VG_(newXA)(alloc_fn, cc, free_fn, sizeof(xec));
+   shared->ips_order_xecu = NULL; // Allocated when needed.
+
+   return shared;
+}
+
+static void delete_XT_shared (XT_shared* shared)
+{
+   vg_assert(shared->nrRef == 0);
+   shared->free_fn(shared->d4ecu2xecu);
+   VG_(deleteXA)(shared->xec);
+   if (shared->ips_order_xecu != NULL)
+      VG_(deleteXA)(shared->ips_order_xecu);
+   shared->free_fn(shared);
+}
+
+/* Compare 2 entries in ips_order_xecu by StackTrace elements. Note
+   that a not existing ips is considered smaller than any other
+   address. */
+static XArray* xec_data_for_sort; // Needed to translate an xecu into an xec
+static Int ips_order_cmp(const void* vleft, const void* vright)
+{
+   const Xecu left_xecu  = *(const Xecu*)vleft;
+   const Xecu right_xecu = *(const Xecu*)vright;
+   const xec* left  = VG_(indexXA)(xec_data_for_sort, left_xecu);
+   const xec* right = VG_(indexXA)(xec_data_for_sort, right_xecu);
+   const StackTrace left_ips  = VG_(get_ExeContext_StackTrace)(left->ec)
+      + left->top;
+   const StackTrace right_ips = VG_(get_ExeContext_StackTrace)(right->ec)
+      + right->top;
+   UInt i;
+
+   const UInt c_n_ips_sel = left->n_ips_sel < right->n_ips_sel 
+      ? left->n_ips_sel : right->n_ips_sel;
+
+   // First see if we have a difference on the common nr of ips selected
+   for (i = 0; i < c_n_ips_sel; i++) {
+      if (left_ips[i] == right_ips[i]) continue;
+      if (left_ips[i] < right_ips[i]) return -1;
+      return  1;
+   }
+   // Common part is equal => compare lengths.
+   if (left->n_ips_sel < right->n_ips_sel) return -1;
+   if (left->n_ips_sel > right->n_ips_sel) return  1;
+   return 0;
+}
+
+// If needed, build or refresh shared->ips_order_xecu
+static void ensure_ips_order_xecu_valid(XT_shared* shared)
+{
+   UInt i;
+   UInt n_xecu;
+
+   if (shared->ips_order_xecu == NULL) {
+      shared->ips_order_xecu = VG_(newXA)(shared->alloc_fn, shared->cc, 
+                                          shared->free_fn, sizeof(Xecu));
+      VG_(hintSizeXA)(shared->ips_order_xecu, VG_(sizeXA)(shared->xec));
+      VG_(setCmpFnXA)(shared->ips_order_xecu, ips_order_cmp);
+   }
+
+   if (VG_(sizeXA)(shared->xec) == VG_(sizeXA)(shared->ips_order_xecu))
+      return;
+
+   n_xecu = VG_(sizeXA)(shared->xec);
+   for (i = VG_(sizeXA)(shared->ips_order_xecu); i < n_xecu; i++)
+      VG_(addToXA)(shared->ips_order_xecu, &i);
+   xec_data_for_sort = shared->xec;
+   VG_(sortXA)(shared->ips_order_xecu);
+}
+
+static void addRef_XT_shared (XT_shared* shared)
+{
+   shared->nrRef++;
+}
+
+static UWord release_XT_shared(XT_shared* shared)
+{
+   UWord nrRef;
+
+   vg_assert(shared->nrRef > 0);
+   nrRef = --shared->nrRef;
+   if (nrRef == 0)
+      delete_XT_shared(shared);
+   return nrRef;
+}
+
+   
+struct _XTree {
+   void* (*alloc_fn)( const HChar*, SizeT ); /* alloc fn (nofail) */
+   const HChar* cc;                    /* cost centre for alloc */
+   void  (*free_fn)( void* );         /* free fn */
+   Word  dataSzB;   /* data size in bytes */
+   XT_init_data_t init_data_fn;
+   XT_add_data_t add_data_fn;
+   XT_sub_data_t sub_data_fn;
+   XT_filter_IPs_t filter_IPs_fn;
+
+   XT_shared* shared;
+
+   HChar* tmp_data; /* temporary buffer, to insert new elements. */
+   XArray* data; /* of elements of size dataSzB */
+};
+
+
+XTree* VG_(XT_create) ( void*(*alloc_fn)(const HChar*, SizeT), 
+                        const HChar* cc,
+                        void(*free_fn) (void*),
+                        Word dataSzB,
+                        XT_init_data_t init_data_fn,
+                        XT_add_data_t add_data_fn,
+                        XT_sub_data_t sub_data_fn,
+                        XT_filter_IPs_t filter_IPs_fn)
+{
+   XTree* xt;
+
+   /* check user-supplied info .. */
+   vg_assert(alloc_fn);
+   vg_assert(free_fn);
+   vg_assert(dataSzB >= 0);
+   vg_assert(init_data_fn);
+   vg_assert(add_data_fn);
+   vg_assert(sub_data_fn);
+
+   xt = alloc_fn(cc, sizeof(struct _XTree) );
+   xt->alloc_fn  = alloc_fn;
+   xt->cc        = cc;
+   xt->free_fn   = free_fn;
+   xt->dataSzB   = dataSzB;
+   xt->init_data_fn = init_data_fn;
+   xt->add_data_fn = add_data_fn;
+   xt->sub_data_fn = sub_data_fn;
+   xt->filter_IPs_fn = filter_IPs_fn;
+
+   xt->shared = new_XT_shared(alloc_fn, cc, free_fn);
+   addRef_XT_shared(xt->shared);
+   xt->tmp_data = alloc_fn(cc, xt->dataSzB);
+   xt->data =  VG_(newXA)(alloc_fn, cc, free_fn, dataSzB);
+
+   return xt;
+}
+
+XTree* VG_(XT_snapshot)(XTree* xt)
+{
+   XTree* nxt;
+
+   vg_assert(xt);
+
+   nxt = xt->alloc_fn(xt->cc, sizeof(struct _XTree) );
+
+   *nxt = *xt;
+   addRef_XT_shared (nxt->shared);
+   nxt->tmp_data = nxt->alloc_fn(nxt->cc, nxt->dataSzB);
+   nxt->data = VG_(cloneXA)(nxt->cc, xt->data);
+
+   return nxt;
+}
+
+void VG_(XT_delete) ( XTree* xt )
+{
+   vg_assert(xt);
+
+   release_XT_shared(xt->shared);
+   xt->free_fn(xt->tmp_data);
+   VG_(deleteXA)(xt->data);
+   xt->free_fn(xt);
+}
+
+static Xecu find_or_insert (XTree* xt, ExeContext* ec)
+{
+
+   const UInt d4ecu = VG_(get_ECU_from_ExeContext)(ec) / 4;
+   XT_shared* shared = xt->shared;
+
+   /* First grow the d4ecu2xecu array if needed. */
+   if (d4ecu >= shared->d4ecu2xecu_sz) {
+      UInt old_sz = shared->d4ecu2xecu_sz;
+      UInt new_sz = (3 * d4ecu) / 2;
+
+      if (new_sz < 1000)
+         new_sz = 1000;
+      shared->d4ecu2xecu = VG_(realloc)(xt->cc, shared->d4ecu2xecu,
+                                        new_sz * sizeof(UInt));
+      shared->d4ecu2xecu_sz = new_sz;
+      for (UInt i = old_sz; i < new_sz; i++)
+         shared->d4ecu2xecu[i] = NO_OFFSET;
+   }
+
+   if (shared->d4ecu2xecu[d4ecu] == NO_OFFSET) {
+      xec xe;
+     
+      xe.ec = ec;
+      if (xt->filter_IPs_fn == NULL) {
+         xe.top = 0;
+         xe.n_ips_sel = (UShort)VG_(get_ExeContext_n_ips)(xe.ec);
+      } else {
+         UInt top;
+         UInt n_ips_sel = VG_(get_ExeContext_n_ips)(xe.ec);
+         xt->filter_IPs_fn(VG_(get_ExeContext_StackTrace)(xe.ec), n_ips_sel,
+                           &top, &n_ips_sel);
+         xe.top = (UShort)top;
+         xe.n_ips_sel = (UShort)n_ips_sel;
+      }
+      xt->init_data_fn(xt->tmp_data);
+      VG_(addToXA)(shared->xec, &xe);
+      shared->d4ecu2xecu[d4ecu] = (UInt)VG_(addToXA)(xt->data, xt->tmp_data);
+   } 
+
+   return shared->d4ecu2xecu[d4ecu];
+}
+
+Xecu VG_(XT_add_to_ec) (XTree* xt, ExeContext* ec, const void* value)
+{
+   Xecu xecu = find_or_insert(xt, ec);
+   void* data = VG_(indexXA)(xt->data, xecu);
+
+   xt->add_data_fn(data, value);
+   return xecu;
+}
+
+Xecu VG_(XT_sub_from_ec) (XTree* xt, ExeContext* ec, const void* value)
+{
+   Xecu xecu = find_or_insert(xt, ec);
+   void* data = VG_(indexXA)(xt->data, xecu);
+
+   xt->sub_data_fn(data, value);
+   return xecu;
+}
+
+void VG_(XT_add_to_xecu) (XTree* xt, Xecu xecu, const void* value)
+{
+   void* data = VG_(indexXA)(xt->data, xecu);
+   xt->add_data_fn(data, value);
+}
+
+void VG_(XT_sub_from_xecu) (XTree* xt, Xecu xecu, const void* value)
+{
+   void* data = VG_(indexXA)(xt->data, xecu);
+   xt->sub_data_fn(data, value);
+}
+
+UInt VG_(XT_n_ips_sel) (XTree* xt, Xecu xecu)
+{
+   xec* xe = (xec*)VG_(indexXA)(xt->shared->xec, xecu);
+   return (UInt)xe->n_ips_sel;
+}
+
+ExeContext* VG_(XT_get_ec_from_xecu) (XTree* xt, Xecu xecu)
+{
+   xec* xe = (xec*)VG_(indexXA)(xt->shared->xec, xecu);
+   return xe->ec;
+}
+
+static VgFile* xt_open (const HChar* outfilename)
+{
+   VgFile* fp;
+
+   fp = VG_(fopen)(outfilename, VKI_O_CREAT|VKI_O_WRONLY|VKI_O_TRUNC,
+                   VKI_S_IRUSR|VKI_S_IWUSR);
+   if (fp == NULL) {
+      VG_(message)(Vg_UserMsg,
+                   "Error: can not open xtree output file `%s'\n",
+                   outfilename);
+   }
+   return fp;
+}
+
+#define FP(format, args...) ({ VG_(fprintf)(fp, format, ##args); })
+
+// Print "cmd:" line.
+static void FP_cmd(VgFile* fp)
+{
+   UInt i;
+
+   FP("cmd: ");
+   FP("%s", VG_(args_the_exename));
+   for (i = 0; i < VG_(sizeXA)(VG_(args_for_client)); i++) {
+      HChar* arg = * (HChar**) VG_(indexXA)(VG_(args_for_client), i);
+      FP(" %s", arg);
+   }
+   FP("\n");
+}
+
+/* ----------- Callgrind output ------------------------------------------- */
+
+/* Output a callgrind file element in compressed or not compressed format,
+   according to VG_(clo_xtree_compress_strings). */
+static void FP_pos_str(VgFile* fp, const HChar* name, UInt pos,
+                       const HChar* value, Bool value_new)
+{
+   if (!VG_(clo_xtree_compress_strings))
+      FP("%s=%s\n", name, value);
+   else if (value_new)
+      FP("%s=(%d) %s\n", name, pos, value);
+   else
+      FP("%s=(%d)\n", name, pos);
+}
+
+void VG_(XT_callgrind_print)
+     (XTree* xt,
+      const HChar* outfilename,
+      const HChar* events,
+      const HChar* (*img_value)(const void* value))
+{
+   UInt n_xecu;
+   XT_shared* shared = xt->shared;
+   VgFile* fp = xt_open(outfilename);
+   DedupPoolAlloc* fnname_ddpa;
+   DedupPoolAlloc* filename_ddpa;
+
+   if (fp == NULL)
+      return;
+
+   fnname_ddpa = VG_(newDedupPA)(16000, 1, xt->alloc_fn,
+                                 "XT_callgrind_print.fn", xt->free_fn);
+   filename_ddpa = VG_(newDedupPA)(16000, 1, xt->alloc_fn,
+                                   "XT_callgrind_print.fl", xt->free_fn);
+
+   FP("version: 1\n");
+   FP("creator: xtree-1\n");
+   FP("pid: %d\n", VG_(getpid)());
+   FP_cmd(fp);
+
+   /* Currently, we only need/support line positions. */
+   FP("\npositions:%s\n", " line");
+
+   /* Produce one "event:" line for each event, and the "events:" line. */
+   {
+      HChar strtok_events[VG_(strlen)(events)+1];
+      HChar* e;
+      HChar* ssaveptr;
+      HChar* p;
+
+      VG_(strcpy)(strtok_events, events);
+      for (e = VG_(strtok_r) (strtok_events, ",", &ssaveptr); 
+           e != NULL; 
+           e = VG_(strtok_r) (NULL, ",", &ssaveptr))
+         FP("event: %s\n", e);
+      FP("events:");
+      VG_(strcpy)(strtok_events, events);
+      for (e = VG_(strtok_r) (strtok_events, ",", &ssaveptr); 
+           e != NULL; 
+           e = VG_(strtok_r) (NULL, ",", &ssaveptr)) {
+         p = e;
+         while (*p) {
+            if (*p == ':')
+               *p = 0;
+            p++;
+         }
+         FP(" %s", e);
+      }
+      FP("\n");
+   }
+   xt->init_data_fn(xt->tmp_data); // to compute totals
+
+   n_xecu = VG_(sizeXA)(xt->data);
+   vg_assert (n_xecu <= VG_(sizeXA)(shared->xec));
+   for (Xecu xecu = 0; xecu < n_xecu; xecu++) {
+      xec* xe = (xec*)VG_(indexXA)(shared->xec, xecu);
+      if (xe->n_ips_sel == 0)
+         continue;
+
+      const HChar* img = img_value(VG_(indexXA)(xt->data, xecu));
+      
+#define CALLED_FLF(n)                                                   \
+      if ((n) < 0                                                       \
+          || !VG_(get_filename_linenum)(ips[(n)],                       \
+                                        &called_filename,               \
+                                        NULL,                           \
+                                        &called_linenum)) {             \
+         called_filename = "file ???";                                  \
+         called_linenum = 0;                                            \
+      }                                                                 \
+      if ((n) < 0                                                       \
+          || !VG_(get_fnname)(ips[(n)], &called_fnname)) {              \
+         called_fnname = "???";                                         \
+      }                                                                 \
+      called_filename_nr = VG_(allocStrDedupPA)(filename_ddpa,          \
+                                                called_filename,        \
+                                                &called_filename_new);  \
+      called_fnname_nr = VG_(allocStrDedupPA)(fnname_ddpa,              \
+                                              called_fnname,            \
+                                              &called_fnname_new);
+
+      /* Instead of unknown fnname ???, CALLED_FLF could use instead:
+         VG_(sprintf)(unknown_fn, "%p", (void*)ips[(n)]);
+         but that creates a lot of (useless) nodes at least for
+         valgrind self-hosting. */
+      
+      if (img) {
+         const HChar* called_filename;
+         UInt called_filename_nr;
+         Bool called_filename_new;
+         const HChar* called_fnname;
+         UInt called_fnname_nr;
+         Bool called_fnname_new;
+         UInt called_linenum;
+         UInt prev_linenum;
+
+         const Addr* ips = VG_(get_ExeContext_StackTrace)(xe->ec) + xe->top;
+         Int ips_idx = xe->n_ips_sel - 1;
+
+         if (0) {
+            VG_(printf)("entry img %s\n", img);
+            VG_(pp_ExeContext)(xe->ec);
+            VG_(printf)("\n");
+         }
+         xt->add_data_fn(xt->tmp_data, VG_(indexXA)(xt->data, xecu));
+         CALLED_FLF(ips_idx);
+         for (;
+              ips_idx >= 0;
+              ips_idx--) {
+            FP_pos_str(fp, "fl", called_filename_nr,
+                       called_filename, called_filename_new);
+            FP_pos_str(fp, "fn", called_fnname_nr,
+                       called_fnname, called_fnname_new);
+            if (ips_idx == 0)
+               FP("%d %s\n", called_linenum, img);
+            else
+               FP("%d\n", called_linenum); //no self cost.
+            prev_linenum = called_linenum;
+            CALLED_FLF (ips_idx-1);
+            if (ips_idx >= 1) {
+               FP_pos_str(fp, "cfi", called_filename_nr,
+                          called_filename, called_filename_new);
+               FP_pos_str(fp, "cfn", called_fnname_nr,
+                          called_fnname, called_fnname_new);
+               called_filename_new = False;
+               called_fnname_new = False;
+               /* Giving a call count of 0 allows kcachegrind to hide the calls
+                  column. A call count of 1 means kcachegrind would show in the
+                  calls column the nr of stacktrace containing this arc, which
+                  is very confusing. So, the less bad is to give a 0 call
+                  count. */
+               FP("calls=0 %d\n", called_linenum);
+               FP("%d %s\n", prev_linenum, img);
+            }
+         }
+         FP("\n");
+      }
+   }
+   /* callgrind format is not really fully supporting (yet?) execution trees:
+      in an execution tree, self and inclusive costs are identical, and
+      cannot be added together.
+      If no totals: line is given, callgrind_annotate calculates the addition
+      of all costs, and so gives a wrong totals.
+      Giving a totals: line solves this, but the user must give the option
+      --inclusive=yes (kind of hack) to have all the functions given
+      in the output file. */
+   FP("totals: %s\n", img_value(xt->tmp_data));
+   VG_(fclose)(fp);
+   VG_(deleteDedupPA)(fnname_ddpa);
+   VG_(deleteDedupPA)(filename_ddpa);
+}
+
+
+/* ----------- Massif output ---------------------------------------------- */
+
+/* For Massif output, some functions from the execontext are not output, a.o.
+   the allocation functions at the top of the stack and the functions below
+   main. So, the StackTrace of the execontexts in the xtree must be filtered.
+   The functions below main.
+   Ms_Ec defines the subset of the stacktrace relevant for the report. */
+typedef
+   struct {
+      StackTrace ips; // ips and n_ips provides the subset of the xtree ec
+      UInt n_ips;     // to use for a massif report.
+
+      SizeT report_value; // The value to report for this stack trace.
+   } Ms_Ec;
+
+/* Ms_Group defines (at a certain depth) a group of ec context that
+   have the same IPs at the given depth, and have the same 'parent'.
+   total is the sum of the values of all group elements.
+   A Ms_Group can also represent a set of ec contexts that do not
+   have the same IP, but that have each a total which is below the
+   significant size. Such a group has a NULL ms_ec, a zero group_io.
+   n_ec is the nr of insignificant ec that have been collected inside this
+   insignificant group, and total is the sum of all non significant ec
+   at the given depth. */
+typedef
+   struct {
+      Ms_Ec* ms_ec; // The group consists in ms_ec[0 .. n_ec-1]
+      Addr group_ip;
+      UInt n_ec;
+      SizeT total;
+   } Ms_Group;
+
+/* Compare 2 groups by total, to have bigger total first. */
+static Int ms_group_revcmp_total(const void* vleft, const void* vright)
+{
+   const Ms_Group* left = (const Ms_Group*)vleft;
+   const Ms_Group* right = (const Ms_Group*)vright;
+
+   // First reverse compare total
+   if (left->total > right->total) return -1;
+   if (left->total < right->total) return  1;
+
+   /* Equal size => compare IPs.
+      This (somewhat?) helps to have deterministic test results.
+      If this would change between platforms, then we should compare
+      function names/filename/linenr */
+   if (left->group_ip < right->group_ip) return -1;
+   if (left->group_ip > right->group_ip) return  1;
+   return 0;
+}
+
+/* Scan the addresses in ms_ec at the given depth.
+   On return, 
+      *groups points to an array of Ms_Group sorted by total.
+      *n_groups is the nr of groups
+   The caller is responsible to free the allocated group array. */
+static void ms_make_groups (UInt depth, Ms_Ec* ms_ec, UInt n_ec, SizeT sig_sz,
+                            UInt* n_groups, Ms_Group** groups)
+{
+   UInt i, g;
+   Addr cur_group_ip = 0;
+
+   *n_groups = 0;
+
+   /* Handle special case somewhat more efficiently */
+   if (n_ec == 0) {
+      *groups = NULL;
+      return;
+   }
+
+   /* Compute how many groups we have. */
+   for (i = 0; i < n_ec; i++) {
+      if (ms_ec[i].n_ips > depth
+          && (*n_groups == 0 || cur_group_ip != ms_ec[i].ips[depth])) {
+         (*n_groups)++;
+         cur_group_ip = ms_ec[i].ips[depth];
+      }
+   }
+
+   /* make the group array. */
+   *groups = VG_(malloc)("ms_make_groups", *n_groups * sizeof(Ms_Group));
+   i = 0;
+   for (g = 0; g < *n_groups; g++) {
+      while (ms_ec[i].n_ips <= depth)
+         i++;
+      cur_group_ip = ms_ec[i].ips[depth];
+      (*groups)[g].group_ip = cur_group_ip;
+      (*groups)[g].ms_ec = &ms_ec[i];
+      (*groups)[g].n_ec = 1;
+      (*groups)[g].total = ms_ec[i].report_value;
+      i++;
+      while (i < n_ec 
+             && ms_ec[i].n_ips > depth
+             && cur_group_ip == ms_ec[i].ips[depth]) {
+         (*groups)[g].total += ms_ec[i].report_value;
+         i++;
+         (*groups)[g].n_ec++;
+      }
+   }
+
+   /* Search for insignificant groups, collect them all together
+      in the first insignificant group, and compact the group array. */
+   {
+      UInt insig1; // Position of first insignificant group.
+      UInt n_insig = 0; // Nr of insignificant groups found.
+      
+      for (g = 0; g < *n_groups; g++) {
+         if ((*groups)[g].total < sig_sz) {
+            if (n_insig == 0) {
+               // First insig group => transform it into the special group
+               (*groups)[g].ms_ec = NULL;
+               (*groups)[g].group_ip = 0;
+               (*groups)[g].n_ec = 0;
+               // start the sum of insig total as total
+               insig1 = g;
+            } else {
+               // Add this insig group total into insig1 first group
+               (*groups)[insig1].total += (*groups)[g].total;
+            }
+            n_insig++;
+         } else {
+            if (n_insig > 1)
+               (*groups)[g - n_insig + 1] = (*groups)[g];
+         }
+      }
+      if (n_insig > 0) {
+         (*groups)[insig1].n_ec = n_insig;
+         *n_groups -= n_insig - 1;
+      }
+      DMSG(1, "depth %u n_groups %u n_insig %u\n", depth, *n_groups, n_insig);
+   }
+
+   /* Sort on total size, bigger size first. */
+   VG_(ssort) (*groups, *n_groups, sizeof(Ms_Group), ms_group_revcmp_total);
+}
+
+static void ms_output_group (VgFile* fp, UInt depth, Ms_Group* group,
+                             SizeT sig_sz, double sig_pct_threshold)
+{
+   UInt i;
+   Ms_Group* groups;
+   UInt n_groups;
+
+   // If this is an insignificant group, handle it specially
+   if (group->ms_ec == NULL) {
+      const HChar* s = ( 1 ==  group->n_ec? "," : "s, all" );
+      vg_assert (group->group_ip == 0);
+      FP("%*sn0: %lu in %d place%s below massif's threshold (%.2f%%)\n",
+         depth+1, "", group->total, group->n_ec, s, sig_pct_threshold);
+      return;
+   }
+
+   // Normal group => output the group and its subgroups.
+   ms_make_groups (depth+1, group->ms_ec, group->n_ec, sig_sz,
+                   &n_groups, &groups);
+
+   FP("%*s" "n%u: %ld %s\n", 
+      depth + 1, "",
+      n_groups, 
+      group->total,
+      VG_(describe_IP)(group->ms_ec->ips[depth] - 1, NULL));
+   /* XTREE??? Massif original code removes 1 to get the IP description. I am
+      wondering if this is not something that predates revision r8818,
+      which introduced a -1 in the stack unwind (see m_stacktrace.c)
+      Kept for the moment to allow exact comparison with massif output, but
+      probably we should remove this, as we very probably end up 2 bytes before
+      the RA Return Address. */
+
+   /* Output sub groups of this group. */
+   for (i = 0; i < n_groups; i++)
+      ms_output_group (fp, depth+1, &groups[i], sig_sz, sig_pct_threshold);
+
+   VG_(free)(groups);
+}
+
+/* Allocate and build an array of Ms_Ec sorted by addresses in the
+   Ms_Ec StackTrace. */
+static void prepare_ms_ec (XTree* xt,
+                           ULong (*report_value)(const void* value),
+                           ULong* top_total, Ms_Ec** vms_ec, UInt* vn_ec)
+{
+   XT_shared* shared = xt->shared;
+   const UInt n_xecu = VG_(sizeXA)(shared->xec);
+   const UInt n_data_xecu = VG_(sizeXA)(xt->data);
+   Ms_Ec* ms_ec = VG_(malloc)("XT_massif_print.ms_ec", n_xecu * sizeof(Ms_Ec));
+   UInt n_xecu_sel = 0; // Nr of xecu that are selected for output.
+
+   vg_assert (n_data_xecu <= n_xecu);
+
+   // Ensure we have in shared->ips_order_xecu our xecu sorted by StackTrace.
+   ensure_ips_order_xecu_valid(shared);
+
+   *top_total = 0;
+   DMSG(1, "iteration %u\n", n_xecu);
+   for (UInt i = 0; i < n_xecu; i++) {
+      Xecu xecu = *(Xecu*)VG_(indexXA)(shared->ips_order_xecu, i);
+      xec* xe = (xec*)VG_(indexXA)(shared->xec, xecu);
+
+      if (xecu >= n_data_xecu)
+         continue; // No data for this xecu in xt->data.
+      ms_ec[n_xecu_sel].n_ips = xe->n_ips_sel;
+      if (ms_ec[n_xecu_sel].n_ips == 0)
+         continue;
+            
+      ms_ec[n_xecu_sel].ips = VG_(get_ExeContext_StackTrace)(xe->ec) + xe->top;
+      ms_ec[n_xecu_sel].report_value
+         = (*report_value)(VG_(indexXA)(xt->data, xecu));
+      *top_total += ms_ec[n_xecu_sel].report_value;
+
+      n_xecu_sel++;
+   }
+   vg_assert(n_xecu_sel <= n_xecu);
+      
+   *vms_ec = ms_ec;
+   *vn_ec = n_xecu_sel;
+}
+
+MsFile* VG_(XT_massif_open)
+     (const HChar* outfilename,
+      const HChar* desc,
+      const XArray* desc_args,
+      const HChar* time_unit)
+{
+   UInt i;
+   VgFile* fp = xt_open(outfilename);
+   
+   if (fp == NULL)
+      return NULL; // xt_open reported the error.
+   
+   /* ------ file header ------------------------------- */
+   FP("desc:");
+   if (desc)
+      FP(" %s", desc);
+   i = 0;
+   if (desc_args) {
+      for (i = 0; i < VG_(sizeXA)(desc_args); i++) {
+         HChar* arg = *(HChar**)VG_(indexXA)(desc_args, i);
+         FP(" %s", arg);
+      }
+   }
+   if (0 == i && desc == NULL) FP(" (none)");
+   FP("\n");
+
+   FP_cmd(fp);
+
+   FP("time_unit: %s\n", time_unit);
+
+   return fp;
+}
+
+void VG_(XT_massif_close)(MsFile* fp)
+{
+   if (fp == NULL)
+      return; // Error should have been reported by  VG_(XT_massif_open)
+
+   VG_(fclose)(fp);
+}
+
+void VG_(XT_massif_print) 
+     (MsFile* fp,
+      XTree* xt,
+      const Massif_Header* header,
+      ULong (*report_value)(const void* value))
+{
+   UInt i;
+
+   if (fp == NULL)
+      return; // Normally  VG_(XT_massif_open) already reported an error.
+
+   /* Compute/prepare Snapshot totals/data/... */
+   ULong top_total;
+
+   /* Following variables only used for detailed snapshot. */
+   UInt n_ec = 0;
+   Ms_Ec* ms_ec = NULL;
+   const HChar* kind = 
+      header->detailed ? (header->peak ? "peak" : "detailed") : "empty";
+
+   DMSG(1, "XT_massif_print %s\n", kind);
+   if (header->detailed) {
+      /* Prepare the Ms_Ec sorted array of stacktraces and the groups
+         at level 0. */
+      prepare_ms_ec(xt, report_value, &top_total, &ms_ec, &n_ec);
+      DMSG(1, "XT_print_massif ms_ec n_ec %u\n", n_ec);
+   } else if (xt == NULL) {
+      /* Non detailed, no xt => use the sz provided in the header. */
+      top_total = header->sz_B;
+   } else {
+      /* For non detailed snapshot, compute total directly from the xec. */
+      const XT_shared* shared = xt->shared;
+      const UInt n_xecu = VG_(sizeXA)(xt->data);
+      top_total = 0;
+      
+      for (UInt xecu = 0; xecu < n_xecu; xecu++) {
+         xec* xe = (xec*)VG_(indexXA)(shared->xec, xecu);
+         if (xe->n_ips_sel == 0)
+            continue;
+         top_total += (*report_value)(VG_(indexXA)(xt->data, xecu));
+      }
+   }
+
+   /* ------ snapshot header --------------------------- */
+   FP("#-----------\n");
+   FP("snapshot=%d\n", header->snapshot_n);
+   FP("#-----------\n");
+   FP("time=%lld\n", header->time);
+   
+   FP("mem_heap_B=%llu\n", top_total); // without extra_B and without stacks_B
+   FP("mem_heap_extra_B=%llu\n", header->extra_B);
+   FP("mem_stacks_B=%llu\n", header->stacks_B);
+   FP("heap_tree=%s\n", kind);
+
+   /* ------ detailed snapshot data ----------------------------- */
+   if (header->detailed) {
+      UInt n_groups;
+      Ms_Group* groups;
+
+      ULong sig_sz;
+      // Work out how big a child must be to be significant.  If the current
+      // top_total is zero, then we set it to 1, which means everything will be
+      // judged insignificant -- this is sensible, as there's no point showing
+      // any detail for this case.  Unless they used threshold=0, in which
+      // case we show them everything because that's what they asked for.
+      //
+      // Nb: We do this once now, rather than once per child, because if we do
+      // that the cost of all the divisions adds up to something significant.
+      if (0 == top_total && 0 != header->sig_threshold)
+         sig_sz = 1;
+      else
+         sig_sz = ((top_total + header->extra_B + header->stacks_B) 
+                   * header->sig_threshold) / 100;
+
+      /* Produce the groups at depth 0 */
+      DMSG(1, "XT_massif_print producing depth 0 groups\n");
+      ms_make_groups (0, ms_ec, n_ec, sig_sz, &n_groups, &groups);
+
+      /* Output the top node. */
+      FP("n%u: %llu %s\n", n_groups, top_total, header->top_node_desc);
+
+      /* Output depth 0 groups. */
+      DMSG(1, "XT_massif_print outputing %u depth 0 groups\n", n_groups);
+      for (i = 0; i < n_groups; i++)
+         ms_output_group (fp, 0, &groups[i], sig_sz, header->sig_threshold);
+
+      VG_(free)(groups);
+      VG_(free)(ms_ec);
+   }
+}
+
+Int VG_(XT_offset_main_or_below_main)(Addr* ips, Int n_ips)
+{
+   /* Search for main or below main function.
+      To limit the nr of ips to examine, we maintain the deepest
+      offset where main was found, and we first search main
+      from there.
+      If no main is found, we will then do a search for main or
+      below main function till the top. */
+   static Int deepest_main = 0;
+   Vg_FnNameKind kind = Vg_FnNameNormal;
+   Int mbm = n_ips - 1; // Position of deepest main or below main.
+   Vg_FnNameKind mbmkind = Vg_FnNameNormal;
+   Int i;
+
+   for (i = n_ips - 1 - deepest_main;
+        i < n_ips;
+        i++) {
+      mbmkind = VG_(get_fnname_kind_from_IP)(ips[i]);
+      if (mbmkind != Vg_FnNameNormal) {
+         mbm = i;
+         break;
+      }
+   }
+
+   /* Search for main or below main function till top. */
+   for (i = mbm - 1;
+        i >= 0 && mbmkind != Vg_FnNameMain;
+        i--) {
+      kind = VG_(get_fnname_kind_from_IP)(ips[i]);
+      if (kind != Vg_FnNameNormal) {
+         mbm = i;
+         mbmkind = kind;
+      }
+   }
+   if (Vg_FnNameMain == mbmkind || Vg_FnNameBelowMain == mbmkind) {
+      if (mbmkind == Vg_FnNameMain && (n_ips - 1 - mbm) > deepest_main)
+         deepest_main = n_ips - 1 - mbm;
+      return mbm;
+   } else
+      return n_ips-1;
+}
+
+void VG_(XT_filter_1top_and_maybe_below_main)
+     (Addr* ips, Int n_ips,
+      UInt* top, UInt* n_ips_sel)
+{
+   Int mbm;
+
+   *n_ips_sel = n_ips;
+   if (n_ips == 0) {
+      *top = 0;
+      return;
+   }
+
+   /* Filter top function. */
+   *top = 1;
+
+   if (VG_(clo_show_below_main))
+      mbm = n_ips - 1;
+   else
+      mbm = VG_(XT_offset_main_or_below_main)(ips, n_ips);
+
+   *n_ips_sel = mbm - *top + 1;
+}
+
+void VG_(XT_filter_maybe_below_main)
+     (Addr* ips, Int n_ips,
+      UInt* top, UInt* n_ips_sel)
+{
+   Int mbm;
+
+   *n_ips_sel = n_ips;
+   *top = 0;
+   if (n_ips == 0)
+      return;
+
+   if (VG_(clo_show_below_main))
+      mbm = n_ips - 1;
+   else
+      mbm = VG_(XT_offset_main_or_below_main)(ips, n_ips);
+
+   *n_ips_sel = mbm - *top + 1;
+}
+
+/*--------------------------------------------------------------------*/
+/*--- end                                                m_xtree.c ---*/
+/*--------------------------------------------------------------------*/
index 7a887fc0d9733a9c5828308904a9801441bb78e7..f249cfad21911492661443387a3c4099cb586741 100644 (file)
@@ -207,7 +207,6 @@ extern Bool  VG_(clo_profile_heap);
 extern Int VG_(clo_core_redzone_size);
 // VG_(clo_redzone_size) has default value -1, indicating to keep
 // the tool provided value.
-extern Int VG_(clo_redzone_size);
 /* DEBUG: display gory details for the k'th most popular error.
    default: Infinity. */
 extern Int   VG_(clo_dump_error);
diff --git a/coregrind/pub_core_xtmemory.h b/coregrind/pub_core_xtmemory.h
new file mode 100644 (file)
index 0000000..03f2e06
--- /dev/null
@@ -0,0 +1,42 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Support functions for xtree memory reports. pub_tool_xtmemory.h ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) 2016-2016 Philippe Waroquiers
+
+   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.
+*/
+
+#ifndef __PUB_CORE_XTMEMORY_H
+#define __PUB_CORE_XTMEMORY_H
+
+// No core-only exports; everything in this module is visible to both
+// the core and tools.
+
+#include "pub_tool_xtmemory.h"
+
+#endif   // __PUB_CORE_XTMEMORY_H
+
+/*-----------------------------------------------------------------------*/
+/*--- end                                         pub_core_xtmemory.h ---*/
+/*-----------------------------------------------------------------------*/
diff --git a/coregrind/pub_core_xtree.h b/coregrind/pub_core_xtree.h
new file mode 100644 (file)
index 0000000..3e8bcd9
--- /dev/null
@@ -0,0 +1,42 @@
+
+/*--------------------------------------------------------------------*/
+/*--- An xtree, tree of stacktraces with data     pub_core_xtree.h ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) 2015-2015 Philippe Waroquiers
+
+   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.
+*/
+
+#ifndef __PUB_CORE_XTREE_H
+#define __PUB_CORE_XTREE_H
+
+// No core-only exports; everything in this module is visible to both
+// the core and tools.
+
+#include "pub_tool_xtree.h"
+
+#endif   // __PUB_CORE_XTREE_H
+
+/*--------------------------------------------------------------------*/
+/*--- end                                         pub_core_xtree.h ---*/
+/*--------------------------------------------------------------------*/
index ccfcd2778feb0517f1e5b956d056afc969161e03..11c7ca86db510299b606cbb1e1224ccafc2b6d31 100644 (file)
@@ -43,6 +43,8 @@ nobase_pkginclude_HEADERS = \
        pub_tool_vkiscnums_asm.h        \
        pub_tool_wordfm.h               \
        pub_tool_xarray.h               \
+       pub_tool_xtree.h                \
+       pub_tool_xtmemory.h             \
        valgrind.h                      \
        vki/vki-linux.h                 \
        vki/vki-darwin.h                \
index 94ed5442fd28ff12448cbefa55c1a786ba02818b..8574fc3366b5ca9b8cae553fba5d295015599269 100644 (file)
@@ -223,6 +223,26 @@ extern const HChar* VG_(clo_xml_user_comment);
 extern VexControl VG_(clo_vex_control);
 extern VexRegisterUpdates VG_(clo_px_file_backed);
 
+extern Int VG_(clo_redzone_size);
+
+typedef 
+   enum { 
+      Vg_XTMemory_None,   // Do not do any xtree memory profiling.
+      Vg_XTMemory_Allocs, // Currently allocated size xtree memory profiling
+      Vg_XTMemory_Full,   // Full profiling : Current allocated size, total
+      // allocated size, nr of blocks, total freed size, ...
+   } 
+   VgXTMemory;
+// Tools that replace malloc can optionally implement memory profiling
+// following the value of VG_(clo_xtree_profile_memory) to produce a report
+// at the end of execution.
+extern VgXTMemory VG_(clo_xtree_memory);
+/* Holds the filename to use for xtree memory profiling output, before expansion
+   of %p and %q templates. */
+extern const HChar* VG_(clo_xtree_memory_file);
+/* Compress strings in xtree dumps. */
+extern Bool VG_(clo_xtree_compress_strings);
+
 /* Number of parents of a backtrace.  Default: 12  */
 extern Int   VG_(clo_backtrace_size);
 
diff --git a/include/pub_tool_xtmemory.h b/include/pub_tool_xtmemory.h
new file mode 100644 (file)
index 0000000..b1282d5
--- /dev/null
@@ -0,0 +1,86 @@
+
+/*-----------------------------------------------------------------------*/
+/*--- Support functions for xtree memory reports. pub_tool_xtmemory.h ---*/
+/*-----------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) 2016-2016 Philippe Waroquiers
+
+   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.
+*/
+
+#ifndef __PUB_TOOL_XTMEMORY_H
+#define __PUB_TOOL_XTMEMORY_H
+
+/* Type to profile allocated size and nr of blocks, typically used for
+   --xtree-memory=allocs. */
+typedef
+   struct _XT_Allocs {
+      SizeT nbytes;
+      SizeT nblocks;
+   } XT_Allocs;
+
+/* Support functions to produce a full xtree memory profiling. */
+/* tool must call VG_(XTMemory_Full_init) to ini full xtree memory profiling. */
+extern void VG_(XTMemory_Full_init) (XT_filter_IPs_t filter_IPs_Fn);
+/* Then each time a certain nr of blocks are allocated or freed, the below
+   functions must be called. The arguments are:
+      szB: nr of bytes for the allocated/freed block(s)
+      ec_alloc : ExeContext of the allocation (original allocation for
+                 free and resize_in_place).
+      ec_free  : ExeContext of the free.
+   The tool is responsible to properly provide the ExeContext for
+   the allocation and free. For VG_(XTMemory_Full_free), ec_alloc
+   must be the one that was used for the allocation of the just released
+   block. */
+extern void VG_(XTMemory_Full_alloc)(SizeT szB,
+                                     ExeContext* ec_alloc);
+extern void VG_(XTMemory_Full_free)(SizeT szB,
+                                    ExeContext* ec_alloc,
+                                    ExeContext* ec_free);
+extern void VG_(XTMemory_Full_resize_in_place)(SizeT oldSzB, SizeT newSzB,
+                                               ExeContext* ec_alloc);
+
+/* Handle the production of a xtree memory report, either during run (fini False
+   e.g. via a gdb monitor command), or at the end of execution (fini True).
+
+   VG_(XTMemory_report) behaviour depends on the value of the command line
+   options --xtree-memory=none|allocs|full and --xtree-memory-file=<filename> :
+     If --xtree-memory=full, the report will be produced from the data
+       provided via the calls to void VG_(XTMemory_Full_*).
+     Otherwise, for --xtree-memory=allocs or for --xtree-memory=none (if fini
+       is False), next_block is used to get the data for the report:
+   next_block is called repetitively to get information about all allocated
+   blocks, till xta->nblocks is 0.
+   If filename is NULL, --xtree-memory-file is used to produce the name.
+   filter_IPs_fn : used for --xtree-memory=allocs/none filtering (see
+   VG_(XT_create) and XT_filter_IPs_t typdef for more information). */
+extern void VG_(XTMemory_report)
+     (const HChar* filename, Bool fini,
+      void (*next_block)(XT_Allocs* xta, ExeContext** ec_alloc),
+      XT_filter_IPs_t filter_IPs_fn);
+
+#endif   // __PUB_TOOL_XTMEMORY_H
+
+
+/*-----------------------------------------------------------------------*/
+/*--- end                                         pub_tool_xtmemory.h ---*/
+/*-----------------------------------------------------------------------*/
diff --git a/include/pub_tool_xtree.h b/include/pub_tool_xtree.h
new file mode 100644 (file)
index 0000000..a976a38
--- /dev/null
@@ -0,0 +1,248 @@
+
+/*--------------------------------------------------------------------*/
+/*--- An xtree, tree of stacktraces with data     pub_tool_xtree.h ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) 2015-2016 Philippe Waroquiers
+
+   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.
+*/
+
+#ifndef __PUB_TOOL_XTREE_H
+#define __PUB_TOOL_XTREE_H
+
+#include "pub_tool_basics.h"
+#include "pub_tool_execontext.h"
+
+//--------------------------------------------------------------------
+// PURPOSE: an XTree is conceptually a set of stacktraces organised
+// as a tree structure. 
+// A stacktrace (an Addr* ips, i.e. an array of IPs : Instruction Pointers)
+// can be added to the tree once transformed into an execontext (ec).
+// Some data (typically one or more integer values) can be attached to
+// leafs of the tree.
+// Non-leaf nodes data is build by combining (typically adding together)
+// the data of their children nodes.
+// An XTree can be output in various formats.
+//
+//--------------------------------------------------------------------
+
+
+/* It's an abstract type. */
+typedef struct _XTree  XTree;
+
+/* 3 functions types used by an xtree to manipulate the data attached to leafs
+   of an XTree.
+   XT_init_data_t function is used to initialise (typically to 0) the data
+   of a new node.
+   XT_add_data_t function is used to add 'value' to the data 'to'.
+   XT_sub_data_t function is used to substract 'value' from the data 'from'.
+
+   Note that the add/sub functions can do whatever operations to
+   combine/integrate value with/into to or from. In other words, add/sub
+   functions are in fact equivalent to 'Reduce' functions. Add/sub is used
+   as it is assumed that this module will be mostly used to follow
+   resource consumption, which can be more clearly modelled with add/sub.
+   For such resource consumption usage, typically, a call to add means that
+   some additional resource has been allocated or consumed or ... by the
+   given ExeContext. Similarly, a call to sub means that some resource
+   has been released/freed/... by the given execontext.
+
+   Note however that there is no constraints in what add (or sub) can do. For
+   example, the add function could maintain Min/Max values, or an histogram of
+   values, or ... */
+typedef void (*XT_init_data_t) (void* value);
+typedef void (*XT_add_data_t) (void* to,   const void* value);
+typedef void (*XT_sub_data_t) (void* from, const void* value);
+
+/* If not NULL, the XT_filter_IPs_t function is called when a new ec is inserted
+   in the XTree. 
+   It indicates to the XTree to filter a range of IPs at the top and/or at
+   the bottom of the ec Stacktrace : *top is the offset of the first IP to take
+   into account. *n_ips_sel is the nr of IPs selected starting from *top.
+
+   If XT_filter_IPs_t gives *n_ips_sel equal to 0, then the inserted ec will
+   be fully ignored when outputting the xtree: 
+     the ec value(s) will not be counted in the XTree total,
+     the ec will not be printed/shown.
+   Note however that the filtering only influences the output of an XTree :
+     the ec is still inserted in the XTree, and the XT_*_data_t functions are
+     called in any case for such filtered ec. */
+typedef void (*XT_filter_IPs_t) (Addr* ips, Int n_ips,
+                                 UInt* top, UInt* n_ips_sel);
+
+/* Create new XTree, using given allocation and free function.
+   This function never returns NULL.
+   cc is the allocation cost centre.
+   alloc_fn must not return NULL (that is, if it returns it must have
+   succeeded.).
+   See respective typedef for *_fn arguments. */
+extern XTree* VG_(XT_create) ( void*(*alloc_fn)(const HChar*, SizeT), 
+                               const HChar* cc,
+                               void(*free_fn) (void*),
+                               Word dataSzB,
+                               XT_init_data_t init_data_fn,
+                               XT_add_data_t add_data_fn,
+                               XT_sub_data_t sub_data_fn,
+                               XT_filter_IPs_t filter_IPs_fn);
+
+
+/* General useful filtering functions. */
+
+/* Filter functions below main, unless VG_(clo_show_below_main) is True. */
+extern void VG_(XT_filter_maybe_below_main)
+     (Addr* ips, Int n_ips,
+      UInt* top, UInt* n_ips_sel);
+/* Same as VG_(XT_filter_maybe_below_main) but also filters one top function
+   (typically to ignore the top level malloc/new/... fn). */
+extern void VG_(XT_filter_1top_and_maybe_below_main)
+     (Addr* ips, Int n_ips,
+      UInt* top, UInt* n_ips_sel);
+
+/* Search in ips[0..n_ips-1] the first function which is main or below main
+   and return its offset.
+   If no main or below main is found, return n_ips-1 */
+extern Int VG_(XT_offset_main_or_below_main)(Addr* ips, Int n_ips);
+
+
+/* Take a (frozen) snapshot of xt.
+   Note that the resulting XTree is 'read-only' : calls to 
+   VG_(XT_add_to_*)/VG_(XT_sub_from_*) will assert.
+
+   Note: to spare memory, some data is shared between an xt and all its
+   snapshots. This memory is released when the last XTree using this memory
+   is deleted. */
+extern XTree* VG_(XT_snapshot)(XTree* xt);
+
+/*  Non frozen dup currently not needed : 
+    extern XTree* VG_(XT_dup)(XTree* xt); */
+
+/* Free all memory associated with an XTRee. */
+extern void VG_(XT_delete)(XTree* xt);
+
+/* an Xecu identifies an exe context+its associated data in an XTree. */
+typedef UInt Xecu;
+
+/* If not yet in xt, inserts the provided ec and initialises its
+   data by calling init_data_fn.
+   If already present (or after insertion), updates the data by calling
+   add_data_fn. */
+extern Xecu VG_(XT_add_to_ec)(XTree* xt, ExeContext* ec, const void* value);
+
+/* If not yet in xt, inserts the provided ec and initialises its
+   data by calling init_data_fn.
+   If already present (or after insertion), updates the data by calling
+   sub_data_fn to substract value from the data associated to ec. */
+extern Xecu VG_(XT_sub_from_ec)(XTree* xt, ExeContext* ec, const void* value);
+
+/* Same as (but more efficient than) VG_(XT_add_to_ec) and VG_(XT_sub_from_ec)
+   for an ec already inserted in xt. */
+extern void VG_(XT_add_to_xecu)(XTree* xt, Xecu xecu, const void* value);
+extern void VG_(XT_sub_from_xecu)(XTree* xt, Xecu xecu, const void* value);
+
+/* Return the nr of IPs selected for xecu. 0 means fully filtered. */
+extern UInt VG_(XT_n_ips_sel)(XTree* xt, Xecu xecu);
+
+/* Return the ExeContext associated to the Xecu. */
+extern ExeContext* VG_(XT_get_ec_from_xecu) (XTree* xt, Xecu xecu);
+
+/* -------------------- CALLGRIND/KCACHEGRIND OUTPUT FORMAT --------------*/
+/* Prints xt in outfilename in callgrind/kcachegrind format.
+   events is a comma separated list of events, used by 
+   kcachegrind/callgrind_annotate/... to name the value various components.
+   An event can optionally have a longer description, separated from the
+   event name by " : ", e.g.
+   "curB : currently allocated Bytes,curBk : Currently allocated Blocks"
+   img_value returns an image of the value. The image must be a space
+   separated set of integers, matching the corresponding event in events.
+   Note that the returned pointer can be static data. 
+   img_value can return NULL if value (and its associated ExeContext) should
+   not be printed.
+*/
+extern void VG_(XT_callgrind_print) 
+     (XTree* xt,
+      const HChar* outfilename,
+      const HChar* events,
+      const HChar* (*img_value) (const void* value));
+
+
+/* -------------------- MASSIF OUTPUT FORMAT --------------*/
+// Time is measured either in i or ms or bytes, depending on the --time-unit
+// option.  It's a Long because it can exceed 32-bits reasonably easily, and
+// because we need to allow negative values to represent unset times.
+typedef Long Time;
+
+typedef void MsFile;
+
+/* Create a new file or truncate existing file for printing xtrees in
+   massif format. time_unit is a string describing the unit used
+   in Massif_Header time.
+   Produces a user error msg and returns NULL if file cannot be opened.
+   Caller must VG_(XT_massif_close) the returned file. */
+extern MsFile* VG_(XT_massif_open)(const HChar* outfilename,
+                                   const HChar* desc, // can be NULL
+                                   const XArray* desc_args, // can be NULL
+                                   const HChar* time_unit);
+
+extern void VG_(XT_massif_close)(MsFile* fp);
+
+typedef 
+   struct {
+      int snapshot_n; // starting at 0.
+      Time time;
+
+      ULong sz_B;     // sum of values, only used when printing a NULL xt.
+      ULong extra_B;
+      ULong stacks_B;
+
+      Bool detailed;
+      Bool peak;
+
+      /*   top_node_desc: description for the top node.
+           Typically for memory usage, give xt_heap_alloc_functions_desc. */
+      const HChar* top_node_desc;
+
+      /* children with less than sig_threshold * total xt sz will be aggregated
+         and printed as one single child. */
+      double sig_threshold;
+
+   } Massif_Header;
+
+/* Prints xt in outfilename in massif format.
+   If a NULL xt is provided, then only the header information is used
+   to produce the (necessarily not detailed) snapshot.
+   report_value must return the value to be used for the report production.
+   It will typically be the nr of bytes allocated stored with the execontext
+   but it could be anything measured with a ULong (e.g. the nr of blocks
+   allocated, or a number of calls, ...).
+*/
+extern void VG_(XT_massif_print)
+     (MsFile* fp,
+      XTree* xt,
+      const Massif_Header* header,
+      ULong (*report_value)(const void* value));
+
+#endif   // __PUB_TOOL_XTREE_H
+
+/*--------------------------------------------------------------------*/
+/*--- end                                         pub_tool_xtree.h ---*/
+/*--------------------------------------------------------------------*/