| |
| /*--------------------------------------------------------------------*/ |
| /*--- 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 ---*/ |
| /*--------------------------------------------------------------------*/ |