blob: b1bc3df000c69d42378bbd407deb2c297e91f20f [file] [log] [blame]
/*--------------------------------------------------------------------*/
/*--- Support functions for xtree memory reports. m_xtmemory.c ---*/
/*--------------------------------------------------------------------*/
/*
This file is part of Valgrind, a dynamic binary instrumentation
framework.
Copyright (C) 2016-2017 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.%p.%n")
: 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 ---*/
/*--------------------------------------------------------------------*/