|
From: <sv...@va...> - 2016-11-11 14:07:13
|
Author: philippe
Date: Fri Nov 11 14:07:03 2016
New Revision: 16123
Log:
Addition of the pub_tool_xtree.h and pub_tool_xtmemory.h modules, and of the --xtree-memory* options
This commit is the bulk of the new code.
There is however no functional impact yet : the new modules are not used by anybody.
Added:
trunk/coregrind/m_xtmemory.c
trunk/coregrind/m_xtree.c
trunk/coregrind/pub_core_xtmemory.h
trunk/coregrind/pub_core_xtree.h
trunk/include/pub_tool_xtmemory.h
trunk/include/pub_tool_xtree.h
Modified:
trunk/coregrind/Makefile.am
trunk/coregrind/m_options.c
trunk/coregrind/pub_core_options.h
trunk/include/Makefile.am
trunk/include/pub_tool_options.h
Modified: trunk/coregrind/Makefile.am
==============================================================================
--- trunk/coregrind/Makefile.am (original)
+++ trunk/coregrind/Makefile.am Fri Nov 11 14:07:03 2016
@@ -234,6 +234,8 @@
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 @@
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 \
Modified: trunk/coregrind/m_options.c
==============================================================================
--- trunk/coregrind/m_options.c (original)
+++ trunk/coregrind/m_options.c Fri Nov 11 14:07:03 2016
@@ -112,6 +112,10 @@
// 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
Added: trunk/coregrind/m_xtmemory.c
==============================================================================
--- trunk/coregrind/m_xtmemory.c (added)
+++ trunk/coregrind/m_xtmemory.c Fri Nov 11 14:07:03 2016
@@ -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 ---*/
+/*--------------------------------------------------------------------*/
Added: trunk/coregrind/m_xtree.c
==============================================================================
--- trunk/coregrind/m_xtree.c (added)
+++ trunk/coregrind/m_xtree.c Fri Nov 11 14:07:03 2016
@@ -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
+ nj...@va...
+
+ 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 ---*/
+/*--------------------------------------------------------------------*/
Modified: trunk/coregrind/pub_core_options.h
==============================================================================
--- trunk/coregrind/pub_core_options.h (original)
+++ trunk/coregrind/pub_core_options.h Fri Nov 11 14:07:03 2016
@@ -207,7 +207,6 @@
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);
Added: trunk/coregrind/pub_core_xtmemory.h
==============================================================================
--- trunk/coregrind/pub_core_xtmemory.h (added)
+++ trunk/coregrind/pub_core_xtmemory.h Fri Nov 11 14:07:03 2016
@@ -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 ---*/
+/*-----------------------------------------------------------------------*/
Added: trunk/coregrind/pub_core_xtree.h
==============================================================================
--- trunk/coregrind/pub_core_xtree.h (added)
+++ trunk/coregrind/pub_core_xtree.h Fri Nov 11 14:07:03 2016
@@ -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 ---*/
+/*--------------------------------------------------------------------*/
Modified: trunk/include/Makefile.am
==============================================================================
--- trunk/include/Makefile.am (original)
+++ trunk/include/Makefile.am Fri Nov 11 14:07:03 2016
@@ -43,6 +43,8 @@
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 \
Modified: trunk/include/pub_tool_options.h
==============================================================================
--- trunk/include/pub_tool_options.h (original)
+++ trunk/include/pub_tool_options.h Fri Nov 11 14:07:03 2016
@@ -223,6 +223,26 @@
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);
Added: trunk/include/pub_tool_xtmemory.h
==============================================================================
--- trunk/include/pub_tool_xtmemory.h (added)
+++ trunk/include/pub_tool_xtmemory.h Fri Nov 11 14:07:03 2016
@@ -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 ---*/
+/*-----------------------------------------------------------------------*/
Added: trunk/include/pub_tool_xtree.h
==============================================================================
--- trunk/include/pub_tool_xtree.h (added)
+++ trunk/include/pub_tool_xtree.h Fri Nov 11 14:07:03 2016
@@ -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 organ...
[truncated message content] |