--- /dev/null
+
+/*--------------------------------------------------------------------*/
+/*--- 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 ---*/
+/*--------------------------------------------------------------------*/
--- /dev/null
+
+/*--------------------------------------------------------------------*/
+/*--- 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 ---*/
+/*--------------------------------------------------------------------*/
--- /dev/null
+
+/*--------------------------------------------------------------------*/
+/*--- 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 ---*/
+/*--------------------------------------------------------------------*/