1/* -*- mode: C; c-basic-offset: 3; indent-tabs-mode: nil; -*- */ 2/* 3 This file is part of drd, a thread error detector. 4 5 Copyright (C) 2006-2011 Bart Van Assche <bvanassche@acm.org>. 6 7 This program is free software; you can redistribute it and/or 8 modify it under the terms of the GNU General Public License as 9 published by the Free Software Foundation; either version 2 of the 10 License, or (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, but 13 WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program; if not, write to the Free Software 19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 20 02111-1307, USA. 21 22 The GNU General Public License is contained in the file COPYING. 23*/ 24 25 26#include "drd_malloc_wrappers.h" 27#include "drd_thread.h" 28#include "pub_tool_basics.h" 29#include "pub_tool_execontext.h" 30#include "pub_tool_hashtable.h" 31#include "pub_tool_libcassert.h" 32#include "pub_tool_libcbase.h" 33#include "pub_tool_libcprint.h" 34#include "pub_tool_mallocfree.h" 35#include "pub_tool_options.h" 36#include "pub_tool_replacemalloc.h" 37#include "pub_tool_threadstate.h" 38#include "pub_tool_tooliface.h" 39 40 41/* Local type definitions. */ 42 43/** 44 * Node with per-allocation information that will be stored in a hash map. 45 * As specified in <pub_tool_hashtable.h>, the first member must be a pointer 46 * and the second member must be an UWord. 47 */ 48typedef struct _DRD_Chunk { 49 struct _DRD_Chunk* next; 50 UWord data; // pointer to actual block 51 SizeT size; // size requested 52 ExeContext* where; // where it was allocated 53} DRD_Chunk; 54 55 56/* Local variables. */ 57 58static StartUsingMem s_start_using_mem_callback; 59static StopUsingMem s_stop_using_mem_callback; 60/* Statistics. */ 61static SizeT s_cmalloc_n_mallocs = 0; 62static SizeT s_cmalloc_n_frees = 0; 63static SizeT s_cmalloc_bs_mallocd = 0; 64/* Record malloc'd blocks. */ 65static VgHashTable s_malloc_list = NULL; 66 67 68/* Function definitions. */ 69 70/** Allocate client memory memory and update the hash map. */ 71static void* new_block(ThreadId tid, SizeT size, SizeT align, Bool is_zeroed) 72{ 73 void* p; 74 75 p = VG_(cli_malloc)(align, size); 76 if (!p) 77 return NULL; 78 if (is_zeroed) 79 VG_(memset)(p, 0, size); 80 81 DRD_(malloclike_block)(tid, (Addr)p, size); 82 83 return p; 84} 85 86/** 87 * Store information about a memory block that has been allocated by 88 * malloc() or a malloc() replacement in the hash map. 89 */ 90void DRD_(malloclike_block)(const ThreadId tid, const Addr p, const SizeT size) 91{ 92 DRD_Chunk* mc; 93 94 tl_assert(p); 95 96 if (size > 0) 97 s_start_using_mem_callback(p, size, 0/*ec_uniq*/); 98 99 s_cmalloc_n_mallocs++; 100 // Only update this stat if allocation succeeded. 101 s_cmalloc_bs_mallocd += size; 102 103 mc = VG_(malloc)("drd.malloc_wrappers.cDC.1", sizeof(DRD_Chunk)); 104 mc->data = p; 105 mc->size = size; 106 mc->where = VG_(record_ExeContext)(tid, 0); 107 VG_(HT_add_node)(s_malloc_list, mc); 108} 109 110static void handle_free(ThreadId tid, void* p) 111{ 112 Bool success; 113 114 tl_assert(p); 115 success = DRD_(freelike_block)(tid, (Addr)p, True); 116 tl_assert(success); 117} 118 119/** 120 * Remove the information that was stored by DRD_(malloclike_block)() about 121 * a memory block. 122 */ 123Bool DRD_(freelike_block)(const ThreadId tid, const Addr p, const Bool dealloc) 124{ 125 DRD_Chunk* mc; 126 127 tl_assert(p); 128 129 s_cmalloc_n_frees++; 130 131 mc = VG_(HT_lookup)(s_malloc_list, (UWord)p); 132 if (mc) 133 { 134 tl_assert(p == mc->data); 135 if (dealloc) 136 VG_(cli_free)((void*)p); 137 if (mc->size > 0) 138 s_stop_using_mem_callback(mc->data, mc->size); 139 VG_(HT_remove)(s_malloc_list, (UWord)p); 140 VG_(free)(mc); 141 return True; 142 } 143 return False; 144} 145 146/** Wrapper for malloc(). */ 147static void* drd_malloc(ThreadId tid, SizeT n) 148{ 149 return new_block(tid, n, VG_(clo_alignment), /*is_zeroed*/False); 150} 151 152/** Wrapper for memalign(). */ 153static void* drd_memalign(ThreadId tid, SizeT align, SizeT n) 154{ 155 return new_block(tid, n, align, /*is_zeroed*/False); 156} 157 158/** Wrapper for calloc(). */ 159static void* drd_calloc(ThreadId tid, SizeT nmemb, SizeT size1) 160{ 161 return new_block(tid, nmemb*size1, VG_(clo_alignment), 162 /*is_zeroed*/True); 163} 164 165/** Wrapper for free(). */ 166static void drd_free(ThreadId tid, void* p) 167{ 168 handle_free(tid, p); 169} 170 171/** 172 * Wrapper for realloc(). Returns a pointer to the new block of memory, or 173 * NULL if no new block could not be allocated. Notes: 174 * - realloc(NULL, size) has the same effect as malloc(size). 175 * - realloc(p, 0) has the same effect as free(p). 176 * - success is not guaranteed even if the requested size is smaller than the 177 * allocated size. 178*/ 179static void* drd_realloc(ThreadId tid, void* p_old, SizeT new_size) 180{ 181 DRD_Chunk* mc; 182 void* p_new; 183 SizeT old_size; 184 185 if (! p_old) 186 return drd_malloc(tid, new_size); 187 188 if (new_size == 0) 189 { 190 drd_free(tid, p_old); 191 return NULL; 192 } 193 194 s_cmalloc_n_mallocs++; 195 s_cmalloc_n_frees++; 196 s_cmalloc_bs_mallocd += new_size; 197 198 mc = VG_(HT_lookup)(s_malloc_list, (UWord)p_old); 199 if (mc == NULL) 200 { 201 tl_assert(0); 202 return NULL; 203 } 204 205 old_size = mc->size; 206 207 if (old_size == new_size) 208 { 209 /* size unchanged */ 210 mc->where = VG_(record_ExeContext)(tid, 0); 211 p_new = p_old; 212 } 213 else if (new_size < old_size) 214 { 215 /* new size is smaller but nonzero */ 216 s_stop_using_mem_callback(mc->data + new_size, old_size - new_size); 217 mc->size = new_size; 218 mc->where = VG_(record_ExeContext)(tid, 0); 219 p_new = p_old; 220 } 221 else 222 { 223 /* new size is bigger */ 224 p_new = VG_(cli_malloc)(VG_(clo_alignment), new_size); 225 226 if (p_new) 227 { 228 /* Copy from old to new. */ 229 VG_(memcpy)(p_new, p_old, mc->size); 230 231 /* Free old memory. */ 232 VG_(cli_free)(p_old); 233 if (mc->size > 0) 234 s_stop_using_mem_callback(mc->data, mc->size); 235 VG_(HT_remove)(s_malloc_list, (UWord)p_old); 236 237 /* Update state information. */ 238 mc->data = (Addr)p_new; 239 mc->size = new_size; 240 mc->where = VG_(record_ExeContext)(tid, 0); 241 VG_(HT_add_node)(s_malloc_list, mc); 242 s_start_using_mem_callback((Addr)p_new, new_size, 0/*ec_uniq*/); 243 } 244 else 245 { 246 /* Allocation failed -- leave original block untouched. */ 247 } 248 } 249 250 return p_new; 251} 252 253/** Wrapper for __builtin_new(). */ 254static void* drd___builtin_new(ThreadId tid, SizeT n) 255{ 256 return new_block(tid, n, VG_(clo_alignment), /*is_zeroed*/False); 257} 258 259/** Wrapper for __builtin_delete(). */ 260static void drd___builtin_delete(ThreadId tid, void* p) 261{ 262 handle_free(tid, p); 263} 264 265/** Wrapper for __builtin_vec_new(). */ 266static void* drd___builtin_vec_new(ThreadId tid, SizeT n) 267{ 268 return new_block(tid, n, VG_(clo_alignment), /*is_zeroed*/False); 269} 270 271/** Wrapper for __builtin_vec_delete(). */ 272static void drd___builtin_vec_delete(ThreadId tid, void* p) 273{ 274 handle_free(tid, p); 275} 276 277/** 278 * Wrapper for malloc_usable_size() / malloc_size(). This function takes 279 * a pointer to a block allocated by `malloc' and returns the amount of space 280 * that is available in the block. This may or may not be more than the size 281 * requested from `malloc', due to alignment or minimum size constraints. 282 */ 283static SizeT drd_malloc_usable_size(ThreadId tid, void* p) 284{ 285 DRD_Chunk* mc; 286 287 mc = VG_(HT_lookup)(s_malloc_list, (UWord)p); 288 289 return mc ? mc->size : 0; 290} 291 292void DRD_(register_malloc_wrappers)(const StartUsingMem start_callback, 293 const StopUsingMem stop_callback) 294{ 295 tl_assert(s_malloc_list == 0); 296 s_malloc_list = VG_(HT_construct)("drd_malloc_list"); 297 tl_assert(s_malloc_list); 298 tl_assert(start_callback); 299 tl_assert(stop_callback); 300 301 s_start_using_mem_callback = start_callback; 302 s_stop_using_mem_callback = stop_callback; 303 304 VG_(needs_malloc_replacement)(drd_malloc, 305 drd___builtin_new, 306 drd___builtin_vec_new, 307 drd_memalign, 308 drd_calloc, 309 drd_free, 310 drd___builtin_delete, 311 drd___builtin_vec_delete, 312 drd_realloc, 313 drd_malloc_usable_size, 314 0); 315} 316 317Bool DRD_(heap_addrinfo)(Addr const a, 318 Addr* const data, 319 SizeT* const size, 320 ExeContext** const where) 321{ 322 DRD_Chunk* mc; 323 324 tl_assert(data); 325 tl_assert(size); 326 tl_assert(where); 327 328 VG_(HT_ResetIter)(s_malloc_list); 329 while ((mc = VG_(HT_Next)(s_malloc_list))) 330 { 331 if (mc->data <= a && a < mc->data + mc->size) 332 { 333 *data = mc->data; 334 *size = mc->size; 335 *where = mc->where; 336 return True; 337 } 338 } 339 return False; 340} 341 342/*------------------------------------------------------------*/ 343/*--- Statistics printing ---*/ 344/*------------------------------------------------------------*/ 345 346void DRD_(print_malloc_stats)(void) 347{ 348 DRD_Chunk* mc; 349 SizeT nblocks = 0; 350 SizeT nbytes = 0; 351 352 if (VG_(clo_verbosity) == 0) 353 return; 354 if (VG_(clo_xml)) 355 return; 356 357 /* Count memory still in use. */ 358 VG_(HT_ResetIter)(s_malloc_list); 359 while ((mc = VG_(HT_Next)(s_malloc_list))) 360 { 361 nblocks++; 362 nbytes += mc->size; 363 } 364 365 VG_(message)(Vg_DebugMsg, 366 "malloc/free: in use at exit: %lu bytes in %lu blocks.\n", 367 nbytes, nblocks); 368 VG_(message)(Vg_DebugMsg, 369 "malloc/free: %lu allocs, %lu frees, %lu bytes allocated.\n", 370 s_cmalloc_n_mallocs, 371 s_cmalloc_n_frees, s_cmalloc_bs_mallocd); 372 if (VG_(clo_verbosity) > 1) 373 VG_(message)(Vg_DebugMsg, " \n"); 374} 375 376/*--------------------------------------------------------------------*/ 377/*--- end ---*/ 378/*--------------------------------------------------------------------*/ 379