Elliott Hughes | ed39800 | 2017-06-21 14:41:24 -0700 | [diff] [blame^] | 1 | |
| 2 | /*--------------------------------------------------------------------*/ |
| 3 | /*--- Support functions for xtree memory reports. m_xtmemory.c ---*/ |
| 4 | /*--------------------------------------------------------------------*/ |
| 5 | |
| 6 | /* |
| 7 | This file is part of Valgrind, a dynamic binary instrumentation |
| 8 | framework. |
| 9 | |
| 10 | Copyright (C) 2016-2017 Philippe Waroquiers |
| 11 | |
| 12 | This program is free software; you can redistribute it and/or |
| 13 | modify it under the terms of the GNU General Public License as |
| 14 | published by the Free Software Foundation; either version 2 of the |
| 15 | License, or (at your option) any later version. |
| 16 | |
| 17 | This program is distributed in the hope that it will be useful, but |
| 18 | WITHOUT ANY WARRANTY; without even the implied warranty of |
| 19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 20 | General Public License for more details. |
| 21 | |
| 22 | You should have received a copy of the GNU General Public License |
| 23 | along with this program; if not, write to the Free Software |
| 24 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
| 25 | 02111-1307, USA. |
| 26 | |
| 27 | The GNU General Public License is contained in the file COPYING. |
| 28 | */ |
| 29 | |
| 30 | #include "pub_core_libcassert.h" |
| 31 | #include "pub_core_libcbase.h" |
| 32 | #include "pub_core_libcprint.h" |
| 33 | #include "pub_core_libcproc.h" |
| 34 | #include "pub_core_mallocfree.h" |
| 35 | #include "pub_core_options.h" |
| 36 | #include "pub_core_xarray.h" |
| 37 | #include "pub_core_xtree.h" |
| 38 | #include "pub_core_xtmemory.h" /* self */ |
| 39 | |
| 40 | static void VG_(XT_Allocs_init)(void* xt_allocs) |
| 41 | { |
| 42 | VG_(memset) (xt_allocs, 0, sizeof(XT_Allocs)); |
| 43 | } |
| 44 | static void VG_(XT_Allocs_add) (void* to, const void* xt_allocs) |
| 45 | { |
| 46 | XT_Allocs* xto = to; |
| 47 | const XT_Allocs* xta = xt_allocs; |
| 48 | |
| 49 | xto->nbytes += xta->nbytes; |
| 50 | xto->nblocks += xta->nblocks; |
| 51 | } |
| 52 | static void VG_(XT_Allocs_sub) (void* from, const void* xt_allocs) |
| 53 | { |
| 54 | XT_Allocs* xfrom = from; |
| 55 | const XT_Allocs* xta = xt_allocs; |
| 56 | |
| 57 | xfrom->nbytes -= xta->nbytes; |
| 58 | xfrom->nblocks -= xta->nblocks; |
| 59 | } |
| 60 | static const HChar* VG_(XT_Allocs_img) (const void* xt_allocs) |
| 61 | { |
| 62 | static HChar buf[100]; |
| 63 | |
| 64 | const XT_Allocs* xta = xt_allocs; |
| 65 | |
| 66 | if (xta->nbytes > 0 || xta->nblocks > 0) { |
| 67 | VG_(sprintf) (buf, "%lu %lu", |
| 68 | xta->nbytes, xta->nblocks); |
| 69 | return buf; |
| 70 | } else { |
| 71 | return NULL; |
| 72 | } |
| 73 | } |
| 74 | const HChar* XT_Allocs_events = "curB : currently allocated Bytes" "," |
| 75 | "curBk : currently allocated Blocks"; |
| 76 | |
| 77 | /* Type and functions for full xtree memory profiling. */ |
| 78 | static XTree* full_xt; |
| 79 | typedef |
| 80 | struct _XT_Full { |
| 81 | // Current nr of bytes/blocks allocated by this ec |
| 82 | SizeT cur_alloc_nbytes; |
| 83 | SizeT cur_alloc_nblocks; |
| 84 | |
| 85 | // Total/cumulative nr of bytes/blocks allocated by this ec |
| 86 | ULong tot_alloc_nbytes; |
| 87 | ULong tot_alloc_nblocks; |
| 88 | |
| 89 | // Total/cumulative nr of bytes/blocks freed by this ec |
| 90 | ULong tot_freed_nbytes; |
| 91 | ULong tot_freed_nblocks; |
| 92 | } XT_Full; |
| 93 | /* Note: normally, an ec should never be used as both an alloc_ec and |
| 94 | a free_ec. This implies that we should never have a XT_Full that has |
| 95 | at the same time some alloc and some freed components > 0. |
| 96 | We however still will support this possibility, just in case very |
| 97 | strange ec are produced and/or given by the tool. */ |
| 98 | |
| 99 | static void VG_(XT_Full_init)(void* xtfull) |
| 100 | { |
| 101 | VG_(memset) (xtfull, 0, sizeof(XT_Full)); |
| 102 | } |
| 103 | static void VG_(XT_Full_add) (void* to, const void* xtfull) |
| 104 | { |
| 105 | XT_Full* xto = to; |
| 106 | const XT_Full* xtf = xtfull; |
| 107 | |
| 108 | xto->cur_alloc_nbytes += xtf->cur_alloc_nbytes; |
| 109 | xto->cur_alloc_nblocks += xtf->cur_alloc_nblocks; |
| 110 | xto->tot_alloc_nbytes += xtf->tot_alloc_nbytes; |
| 111 | xto->tot_alloc_nblocks += xtf->tot_alloc_nblocks; |
| 112 | xto->tot_freed_nbytes += xtf->tot_freed_nbytes; |
| 113 | xto->tot_freed_nblocks += xtf->tot_freed_nblocks; |
| 114 | } |
| 115 | static void VG_(XT_Full_sub) (void* from, const void* xtfull) |
| 116 | { |
| 117 | XT_Full* xfrom = from; |
| 118 | const XT_Full* xtf = xtfull; |
| 119 | |
| 120 | xfrom->cur_alloc_nbytes -= xtf->cur_alloc_nbytes; |
| 121 | xfrom->cur_alloc_nblocks -= xtf->cur_alloc_nblocks; |
| 122 | xfrom->tot_alloc_nbytes -= xtf->tot_alloc_nbytes; |
| 123 | xfrom->tot_alloc_nblocks -= xtf->tot_alloc_nblocks; |
| 124 | xfrom->tot_freed_nbytes -= xtf->tot_freed_nbytes; |
| 125 | xfrom->tot_freed_nblocks -= xtf->tot_freed_nblocks; |
| 126 | } |
| 127 | static const HChar* VG_(XT_Full_img) (const void* xtfull) |
| 128 | { |
| 129 | static HChar buf[300]; |
| 130 | |
| 131 | const XT_Full* xtf = xtfull; |
| 132 | |
| 133 | if ( xtf->cur_alloc_nbytes > 0 |
| 134 | || xtf->cur_alloc_nblocks > 0 |
| 135 | || xtf->tot_alloc_nbytes > 0 |
| 136 | || xtf->tot_alloc_nblocks > 0 |
| 137 | || xtf->tot_freed_nbytes > 0 |
| 138 | || xtf->tot_freed_nblocks > 0) { |
| 139 | VG_(sprintf) (buf, |
| 140 | "%lu %lu " |
| 141 | "%llu %llu " |
| 142 | "%llu %llu", |
| 143 | xtf->cur_alloc_nbytes, xtf->cur_alloc_nblocks, |
| 144 | xtf->tot_alloc_nbytes, xtf->tot_alloc_nblocks, |
| 145 | xtf->tot_freed_nbytes, xtf->tot_freed_nblocks); |
| 146 | return buf; |
| 147 | } else { |
| 148 | return NULL; |
| 149 | } |
| 150 | } |
| 151 | static const HChar* XT_Full_events = |
| 152 | "curB : currently allocated Bytes" "," |
| 153 | "curBk : currently allocated Blocks" "," |
| 154 | "totB : total allocated Bytes" "," |
| 155 | "totBk : total allocated Blocks" "," |
| 156 | "totFdB : total Freed Bytes" "," |
| 157 | "totFdBk : total Freed Blocks"; |
| 158 | void VG_(XTMemory_Full_init)(XT_filter_IPs_t filter_IPs_fn) |
| 159 | { |
| 160 | full_xt = VG_(XT_create) (VG_(malloc), |
| 161 | "m_xtree.full_xt", |
| 162 | VG_(free), |
| 163 | sizeof(XT_Full), |
| 164 | VG_(XT_Full_init), |
| 165 | VG_(XT_Full_add), |
| 166 | VG_(XT_Full_sub), |
| 167 | filter_IPs_fn); |
| 168 | } |
| 169 | void VG_(XTMemory_Full_alloc)(SizeT szB, |
| 170 | ExeContext* ec_alloc) |
| 171 | { |
| 172 | XT_Full xtf = {szB, 1, szB, 1, 0, 0}; |
| 173 | VG_(XT_add_to_ec)(full_xt, ec_alloc, &xtf); |
| 174 | } |
| 175 | void VG_(XTMemory_Full_free)(SizeT szB, |
| 176 | ExeContext* ec_alloc, |
| 177 | ExeContext* ec_free) |
| 178 | { |
| 179 | // substract from ec_alloc the freed memory. |
| 180 | XT_Full xtf_sub = {szB, 1, 0, 0, 0, 0}; |
| 181 | VG_(XT_sub_from_ec)(full_xt, ec_alloc, &xtf_sub); |
| 182 | |
| 183 | // add to ec_free the freed memory |
| 184 | XT_Full xtf_add = {0, 0, 0, 0, szB, 1}; |
| 185 | VG_(XT_add_to_ec)(full_xt, ec_free, &xtf_add); |
| 186 | } |
| 187 | |
| 188 | void VG_(XTMemory_Full_resize_in_place)(SizeT oldSzB, SizeT newSzB, |
| 189 | ExeContext* ec_alloc) |
| 190 | { |
| 191 | if (oldSzB > newSzB) { |
| 192 | XT_Full xtf = {oldSzB - newSzB, 0, oldSzB - newSzB, 0, 0, 0}; |
| 193 | VG_(XT_sub_from_ec)(full_xt, ec_alloc, &xtf); |
| 194 | } else { |
| 195 | XT_Full xtf = {newSzB - oldSzB, 0, newSzB - oldSzB, 0, 0, 0}; |
| 196 | VG_(XT_add_to_ec)(full_xt, ec_alloc, &xtf); |
| 197 | } |
| 198 | } |
| 199 | |
| 200 | // Indicates which event nr the report_value function must return. |
| 201 | static UInt event_report_value_id; |
| 202 | static ULong XT_Full_report_value(const void* xtfull) |
| 203 | { |
| 204 | const XT_Full* xtf = xtfull; |
| 205 | switch (event_report_value_id) { |
| 206 | case 0: return (ULong) xtf->cur_alloc_nbytes; |
| 207 | case 1: return (ULong) xtf->cur_alloc_nblocks; |
| 208 | case 2: return xtf->tot_alloc_nbytes; |
| 209 | case 3: return xtf->tot_alloc_nblocks; |
| 210 | case 4: return xtf->tot_freed_nbytes; |
| 211 | case 5: return xtf->tot_freed_nblocks; |
| 212 | default: vg_assert(0); |
| 213 | } |
| 214 | } |
| 215 | static ULong XT_Allocs_report_value(const void* xt_allocs) |
| 216 | { |
| 217 | const XT_Allocs* xta = xt_allocs; |
| 218 | switch (event_report_value_id) { |
| 219 | case 0: return (ULong) xta->nbytes; |
| 220 | case 1: return (ULong) xta->nblocks; |
| 221 | default: vg_assert(0); |
| 222 | } |
| 223 | } |
| 224 | |
| 225 | static void produce_report(XTree* xt, const HChar* filename, |
| 226 | const HChar* events, |
| 227 | const HChar* (*img_value) (const void* value), |
| 228 | ULong (*report_value)(const void* value)) |
| 229 | { |
| 230 | /* The user can control the kind of report using filename extension. */ |
| 231 | if (VG_(strstr)(filename, ".ms")) { |
| 232 | /* If needed, some harcoded value below could become parameters. */ |
| 233 | MsFile* fp; |
| 234 | Massif_Header header = (Massif_Header) { |
| 235 | .snapshot_n = 0, |
| 236 | .time = VG_(read_millisecond_timer)(), |
| 237 | .sz_B = 0ul, |
| 238 | .extra_B = 0ul, |
| 239 | .stacks_B = 0ul, |
| 240 | .detailed = True, |
| 241 | .peak = False, |
| 242 | .top_node_desc = NULL, |
| 243 | .sig_threshold = 0.00000000000001 |
| 244 | // Currently, we take a very small float value to not output |
| 245 | // the 0 values, but still output all the rest. |
| 246 | }; |
| 247 | |
| 248 | // Variables to parse events |
| 249 | HChar strtok_events[VG_(strlen)(events)+1]; |
| 250 | HChar* e; |
| 251 | HChar* ssaveptr; |
| 252 | |
| 253 | fp = VG_(XT_massif_open)(filename, |
| 254 | "xtree.produce_report", |
| 255 | NULL, |
| 256 | "ms"); |
| 257 | |
| 258 | event_report_value_id = 0; |
| 259 | VG_(strcpy)(strtok_events, events); |
| 260 | for (e = VG_(strtok_r) (strtok_events, ",", &ssaveptr); |
| 261 | e != NULL; |
| 262 | e = VG_(strtok_r) (NULL, ",", &ssaveptr)) { |
| 263 | header.top_node_desc = e; |
| 264 | VG_(XT_massif_print)(fp, xt, &header, report_value); |
| 265 | header.snapshot_n++; |
| 266 | event_report_value_id++; |
| 267 | } |
| 268 | |
| 269 | VG_(XT_massif_close)(fp); |
| 270 | } else |
| 271 | VG_(XT_callgrind_print)(xt, |
| 272 | filename, |
| 273 | events, |
| 274 | img_value); |
| 275 | } |
| 276 | |
| 277 | void VG_(XTMemory_report) |
| 278 | (const HChar* filename, Bool fini, |
| 279 | void (*next_block)(XT_Allocs* xta, ExeContext** ec_alloc), |
| 280 | XT_filter_IPs_t filter_IPs_fn) |
| 281 | { |
| 282 | HChar* expanded_filename; |
| 283 | |
| 284 | if (fini && VG_(clo_xtree_memory) == Vg_XTMemory_None) |
| 285 | return; |
| 286 | |
| 287 | expanded_filename |
| 288 | = VG_(expand_file_name)("--xtree-memory-file", |
| 289 | (filename == NULL) ? |
| 290 | (fini ? |
| 291 | VG_(clo_xtree_memory_file) |
| 292 | : "xtmemory.kcg.%p.%n") |
| 293 | : filename); |
| 294 | |
| 295 | /* fini is False => even if user kept --xtree-memory=none, we |
| 296 | produce a report when explicitely requested e.g. via a monitor |
| 297 | command. */ |
| 298 | switch (VG_(clo_xtree_memory)) { |
| 299 | case Vg_XTMemory_None: |
| 300 | case Vg_XTMemory_Allocs: { |
| 301 | XTree* xt; |
| 302 | XT_Allocs xta; |
| 303 | ExeContext* ec_alloc; |
| 304 | |
| 305 | xt = VG_(XT_create) (VG_(malloc), |
| 306 | "VG_(XTMemory_report)", |
| 307 | VG_(free), |
| 308 | sizeof(XT_Allocs), |
| 309 | VG_(XT_Allocs_init), |
| 310 | VG_(XT_Allocs_add), |
| 311 | VG_(XT_Allocs_sub), |
| 312 | filter_IPs_fn); |
| 313 | (*next_block)(&xta, &ec_alloc); |
| 314 | while ( xta.nblocks > 0 ) { |
| 315 | VG_(XT_add_to_ec) (xt, ec_alloc, &xta); |
| 316 | (*next_block)(&xta, &ec_alloc); |
| 317 | } |
| 318 | |
| 319 | produce_report(xt, expanded_filename, |
| 320 | XT_Allocs_events, VG_(XT_Allocs_img), |
| 321 | XT_Allocs_report_value); |
| 322 | |
| 323 | VG_(XT_delete)(xt); |
| 324 | break; |
| 325 | } |
| 326 | case Vg_XTMemory_Full: |
| 327 | produce_report(full_xt, expanded_filename, |
| 328 | XT_Full_events, VG_(XT_Full_img), |
| 329 | XT_Full_report_value); |
| 330 | break; |
| 331 | default: |
| 332 | vg_assert(0); |
| 333 | } |
| 334 | if (VG_(clo_verbosity) >= 1 || !fini) |
| 335 | VG_(umsg)("xtree memory report: %s\n", expanded_filename); |
| 336 | |
| 337 | VG_(free)(expanded_filename); |
| 338 | } |
| 339 | |
| 340 | /*--------------------------------------------------------------------*/ |
| 341 | /*--- end m_xtree.c ---*/ |
| 342 | /*--------------------------------------------------------------------*/ |