drd_malloc_wrappers.c revision 1e29ebcf5a0d5d29434d112bda3a584b4a3f8066
1/* -*- mode: C; c-basic-offset: 3; -*- */ 2/* 3 This file is part of drd, a thread error detector. 4 5 Copyright (C) 2006-2009 Bart Van Assche <bart.vanassche@gmail.com>. 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 tl_assert(p); 113 114 if (DRD_(freelike_block)(tid, (Addr)p)) 115 VG_(cli_free)(p); 116 else 117 tl_assert(False); 118} 119 120/** 121 * Remove the information that was stored by DRD_(malloclike_block)() about 122 * a memory block. 123 */ 124Bool DRD_(freelike_block)(const ThreadId tid, const Addr p) 125{ 126 DRD_Chunk* mc; 127 128 tl_assert(p); 129 130 s_cmalloc_n_frees++; 131 132 mc = VG_(HT_remove)(s_malloc_list, (UWord)p); 133 if (mc) 134 { 135 tl_assert(p == mc->data); 136 if (mc->size > 0) 137 s_stop_using_mem_callback(mc->data, mc->size); 138 VG_(free)(mc); 139 return True; 140 } 141 return False; 142} 143 144/** Wrapper for malloc(). */ 145static void* drd_malloc(ThreadId tid, SizeT n) 146{ 147 return new_block(tid, n, VG_(clo_alignment), /*is_zeroed*/False); 148} 149 150/** Wrapper for memalign(). */ 151static void* drd_memalign(ThreadId tid, SizeT align, SizeT n) 152{ 153 return new_block(tid, n, align, /*is_zeroed*/False); 154} 155 156/** Wrapper for calloc(). */ 157static void* drd_calloc(ThreadId tid, SizeT nmemb, SizeT size1) 158{ 159 return new_block(tid, nmemb*size1, VG_(clo_alignment), 160 /*is_zeroed*/True); 161} 162 163/** Wrapper for free(). */ 164static void drd_free(ThreadId tid, void* p) 165{ 166 handle_free(tid, p); 167} 168 169/** 170 * Wrapper for realloc(). Returns a pointer to the new block of memory, or 171 * NULL if no new block could not be allocated. Notes: 172 * - realloc(NULL, size) has the same effect as malloc(size). 173 * - realloc(p, 0) has the same effect as free(p). 174 * - success is not guaranteed even if the requested size is smaller than the 175 * allocated size. 176*/ 177static void* drd_realloc(ThreadId tid, void* p_old, SizeT new_size) 178{ 179 DRD_Chunk* mc; 180 void* p_new; 181 SizeT old_size; 182 183 if (! p_old) 184 return drd_malloc(tid, new_size); 185 186 if (new_size == 0) 187 { 188 drd_free(tid, p_old); 189 return NULL; 190 } 191 192 s_cmalloc_n_mallocs++; 193 s_cmalloc_n_frees++; 194 s_cmalloc_bs_mallocd += new_size; 195 196 mc = VG_(HT_lookup)(s_malloc_list, (UWord)p_old); 197 if (mc == NULL) 198 { 199 tl_assert(0); 200 return NULL; 201 } 202 203 old_size = mc->size; 204 205 if (old_size == new_size) 206 { 207 /* size unchanged */ 208 mc->where = VG_(record_ExeContext)(tid, 0); 209 p_new = p_old; 210 } 211 else if (new_size < old_size) 212 { 213 /* new size is smaller but nonzero */ 214 s_stop_using_mem_callback(mc->data + new_size, old_size - new_size); 215 mc->size = new_size; 216 mc->where = VG_(record_ExeContext)(tid, 0); 217 p_new = p_old; 218 } 219 else 220 { 221 /* new size is bigger */ 222 p_new = VG_(cli_malloc)(VG_(clo_alignment), new_size); 223 224 if (p_new) 225 { 226 /* Copy from old to new. */ 227 VG_(memcpy)(p_new, p_old, mc->size); 228 229 /* Free old memory. */ 230 VG_(cli_free)(p_old); 231 s_stop_using_mem_callback(mc->data, mc->size); 232 VG_(HT_remove)(s_malloc_list, (UWord)p_old); 233 234 /* Update state information. */ 235 mc->data = (Addr)p_new; 236 mc->size = new_size; 237 mc->where = VG_(record_ExeContext)(tid, 0); 238 VG_(HT_add_node)(s_malloc_list, mc); 239 s_start_using_mem_callback((Addr)p_new, new_size, 0/*ec_uniq*/); 240 } 241 else 242 { 243 /* Allocation failed -- leave original block untouched. */ 244 } 245 } 246 247 return p_new; 248} 249 250/** Wrapper for __builtin_new(). */ 251static void* drd___builtin_new(ThreadId tid, SizeT n) 252{ 253 return new_block(tid, n, VG_(clo_alignment), /*is_zeroed*/False); 254} 255 256/** Wrapper for __builtin_delete(). */ 257static void drd___builtin_delete(ThreadId tid, void* p) 258{ 259 handle_free(tid, p); 260} 261 262/** Wrapper for __builtin_vec_new(). */ 263static void* drd___builtin_vec_new(ThreadId tid, SizeT n) 264{ 265 return new_block(tid, n, VG_(clo_alignment), /*is_zeroed*/False); 266} 267 268/** Wrapper for __builtin_vec_delete(). */ 269static void drd___builtin_vec_delete(ThreadId tid, void* p) 270{ 271 handle_free(tid, p); 272} 273 274/** 275 * Wrapper for malloc_usable_size() / malloc_size(). This function takes 276 * a pointer to a block allocated by `malloc' and returns the amount of space 277 * that is available in the block. This may or may not be more than the size 278 * requested from `malloc', due to alignment or minimum size constraints. 279 */ 280static SizeT drd_malloc_usable_size(ThreadId tid, void* p) 281{ 282 DRD_Chunk* mc; 283 284 mc = VG_(HT_lookup)(s_malloc_list, (UWord)p); 285 286 return mc ? mc->size : 0; 287} 288 289void DRD_(register_malloc_wrappers)(const StartUsingMem start_callback, 290 const StopUsingMem stop_callback) 291{ 292 tl_assert(s_malloc_list == 0); 293 s_malloc_list = VG_(HT_construct)("drd_malloc_list"); 294 tl_assert(s_malloc_list); 295 tl_assert(start_callback); 296 tl_assert(stop_callback); 297 298 s_start_using_mem_callback = start_callback; 299 s_stop_using_mem_callback = stop_callback; 300 301 VG_(needs_malloc_replacement)(drd_malloc, 302 drd___builtin_new, 303 drd___builtin_vec_new, 304 drd_memalign, 305 drd_calloc, 306 drd_free, 307 drd___builtin_delete, 308 drd___builtin_vec_delete, 309 drd_realloc, 310 drd_malloc_usable_size, 311 0); 312} 313 314Bool DRD_(heap_addrinfo)(Addr const a, 315 Addr* const data, 316 SizeT* const size, 317 ExeContext** const where) 318{ 319 DRD_Chunk* mc; 320 321 tl_assert(data); 322 tl_assert(size); 323 tl_assert(where); 324 325 VG_(HT_ResetIter)(s_malloc_list); 326 while ((mc = VG_(HT_Next)(s_malloc_list))) 327 { 328 if (mc->data <= a && a < mc->data + mc->size) 329 { 330 *data = mc->data; 331 *size = mc->size; 332 *where = mc->where; 333 return True; 334 } 335 } 336 return False; 337} 338 339/*------------------------------------------------------------*/ 340/*--- Statistics printing ---*/ 341/*------------------------------------------------------------*/ 342 343void DRD_(print_malloc_stats)(void) 344{ 345 DRD_Chunk* mc; 346 SizeT nblocks = 0; 347 SizeT nbytes = 0; 348 349 if (VG_(clo_verbosity) == 0) 350 return; 351 if (VG_(clo_xml)) 352 return; 353 354 /* Count memory still in use. */ 355 VG_(HT_ResetIter)(s_malloc_list); 356 while ((mc = VG_(HT_Next)(s_malloc_list))) 357 { 358 nblocks++; 359 nbytes += mc->size; 360 } 361 362 VG_(message)(Vg_DebugMsg, 363 "malloc/free: in use at exit: %lu bytes in %lu blocks.\n", 364 nbytes, nblocks); 365 VG_(message)(Vg_DebugMsg, 366 "malloc/free: %lu allocs, %lu frees, %lu bytes allocated.\n", 367 s_cmalloc_n_mallocs, 368 s_cmalloc_n_frees, s_cmalloc_bs_mallocd); 369 if (VG_(clo_verbosity) > 1) 370 VG_(message)(Vg_DebugMsg, " \n"); 371} 372 373/*--------------------------------------------------------------------*/ 374/*--- end ---*/ 375/*--------------------------------------------------------------------*/ 376