1/* -*- mode: C; c-basic-offset: 3; -*- */ 2/* 3 This file is part of drd, a thread error detector. 4 5 Copyright (C) 2006-2010 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 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 if (mc->size > 0) 232 s_stop_using_mem_callback(mc->data, mc->size); 233 VG_(HT_remove)(s_malloc_list, (UWord)p_old); 234 235 /* Update state information. */ 236 mc->data = (Addr)p_new; 237 mc->size = new_size; 238 mc->where = VG_(record_ExeContext)(tid, 0); 239 VG_(HT_add_node)(s_malloc_list, mc); 240 s_start_using_mem_callback((Addr)p_new, new_size, 0/*ec_uniq*/); 241 } 242 else 243 { 244 /* Allocation failed -- leave original block untouched. */ 245 } 246 } 247 248 return p_new; 249} 250 251/** Wrapper for __builtin_new(). */ 252static void* drd___builtin_new(ThreadId tid, SizeT n) 253{ 254 return new_block(tid, n, VG_(clo_alignment), /*is_zeroed*/False); 255} 256 257/** Wrapper for __builtin_delete(). */ 258static void drd___builtin_delete(ThreadId tid, void* p) 259{ 260 handle_free(tid, p); 261} 262 263/** Wrapper for __builtin_vec_new(). */ 264static void* drd___builtin_vec_new(ThreadId tid, SizeT n) 265{ 266 return new_block(tid, n, VG_(clo_alignment), /*is_zeroed*/False); 267} 268 269/** Wrapper for __builtin_vec_delete(). */ 270static void drd___builtin_vec_delete(ThreadId tid, void* p) 271{ 272 handle_free(tid, p); 273} 274 275/** 276 * Wrapper for malloc_usable_size() / malloc_size(). This function takes 277 * a pointer to a block allocated by `malloc' and returns the amount of space 278 * that is available in the block. This may or may not be more than the size 279 * requested from `malloc', due to alignment or minimum size constraints. 280 */ 281static SizeT drd_malloc_usable_size(ThreadId tid, void* p) 282{ 283 DRD_Chunk* mc; 284 285 mc = VG_(HT_lookup)(s_malloc_list, (UWord)p); 286 287 return mc ? mc->size : 0; 288} 289 290void DRD_(register_malloc_wrappers)(const StartUsingMem start_callback, 291 const StopUsingMem stop_callback) 292{ 293 tl_assert(s_malloc_list == 0); 294 s_malloc_list = VG_(HT_construct)("drd_malloc_list"); 295 tl_assert(s_malloc_list); 296 tl_assert(start_callback); 297 tl_assert(stop_callback); 298 299 s_start_using_mem_callback = start_callback; 300 s_stop_using_mem_callback = stop_callback; 301 302 VG_(needs_malloc_replacement)(drd_malloc, 303 drd___builtin_new, 304 drd___builtin_vec_new, 305 drd_memalign, 306 drd_calloc, 307 drd_free, 308 drd___builtin_delete, 309 drd___builtin_vec_delete, 310 drd_realloc, 311 drd_malloc_usable_size, 312 0); 313} 314 315Bool DRD_(heap_addrinfo)(Addr const a, 316 Addr* const data, 317 SizeT* const size, 318 ExeContext** const where) 319{ 320 DRD_Chunk* mc; 321 322 tl_assert(data); 323 tl_assert(size); 324 tl_assert(where); 325 326 VG_(HT_ResetIter)(s_malloc_list); 327 while ((mc = VG_(HT_Next)(s_malloc_list))) 328 { 329 if (mc->data <= a && a < mc->data + mc->size) 330 { 331 *data = mc->data; 332 *size = mc->size; 333 *where = mc->where; 334 return True; 335 } 336 } 337 return False; 338} 339 340/*------------------------------------------------------------*/ 341/*--- Statistics printing ---*/ 342/*------------------------------------------------------------*/ 343 344void DRD_(print_malloc_stats)(void) 345{ 346 DRD_Chunk* mc; 347 SizeT nblocks = 0; 348 SizeT nbytes = 0; 349 350 if (VG_(clo_verbosity) == 0) 351 return; 352 if (VG_(clo_xml)) 353 return; 354 355 /* Count memory still in use. */ 356 VG_(HT_ResetIter)(s_malloc_list); 357 while ((mc = VG_(HT_Next)(s_malloc_list))) 358 { 359 nblocks++; 360 nbytes += mc->size; 361 } 362 363 VG_(message)(Vg_DebugMsg, 364 "malloc/free: in use at exit: %lu bytes in %lu blocks.\n", 365 nbytes, nblocks); 366 VG_(message)(Vg_DebugMsg, 367 "malloc/free: %lu allocs, %lu frees, %lu bytes allocated.\n", 368 s_cmalloc_n_mallocs, 369 s_cmalloc_n_frees, s_cmalloc_bs_mallocd); 370 if (VG_(clo_verbosity) > 1) 371 VG_(message)(Vg_DebugMsg, " \n"); 372} 373 374/*--------------------------------------------------------------------*/ 375/*--- end ---*/ 376/*--------------------------------------------------------------------*/ 377