drd_malloc_wrappers.c revision bedfd237fbdc80d0c917cfcb85a94b5561c92633
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 43typedef struct _DRD_Chunk { 44 struct _DRD_Chunk* next; 45 Addr data; // ptr to actual block 46 SizeT size : (sizeof(UWord)*8)-2; //size requested; 30 or 62 bits 47 ExeContext* where; // where it was allocated 48} DRD_Chunk; 49 50 51/* Local variables. */ 52 53static StartUsingMem DRD_(s_start_using_mem_callback); 54static StopUsingMem DRD_(s_stop_using_mem_callback); 55/* Stats ... */ 56static SizeT DRD_(s_cmalloc_n_mallocs) = 0; 57static SizeT DRD_(s_cmalloc_n_frees) = 0; 58static SizeT DRD_(s_cmalloc_bs_mallocd) = 0; 59/* Record malloc'd blocks. */ 60static VgHashTable DRD_(s_malloc_list) = NULL; 61 62 63/*------------------------------------------------------------*/ 64/*--- Tracking malloc'd and free'd blocks ---*/ 65/*------------------------------------------------------------*/ 66 67/** Allocate its shadow chunk, put it on the appropriate list. */ 68static DRD_Chunk* DRD_(create_chunk)(ThreadId tid, Addr p, SizeT size) 69{ 70 DRD_Chunk* mc = VG_(malloc)("drd.malloc_wrappers.cDC.1", 71 sizeof(DRD_Chunk)); 72 mc->data = p; 73 mc->size = size; 74 mc->where = VG_(record_ExeContext)(tid, 0); 75 76 return mc; 77} 78 79/*------------------------------------------------------------*/ 80/*--- client_malloc(), etc ---*/ 81/*------------------------------------------------------------*/ 82 83/* Allocate memory and note change in memory available */ 84static 85__inline__ 86void* DRD_(new_block)(ThreadId tid, 87 SizeT size, SizeT align, 88 Bool is_zeroed) 89{ 90 Addr p; 91 92 DRD_(s_cmalloc_n_mallocs) ++; 93 94 // Allocate and zero 95 p = (Addr)VG_(cli_malloc)(align, size); 96 if (!p) { 97 return NULL; 98 } 99 if (is_zeroed) VG_(memset)((void*)p, 0, size); 100 DRD_(s_start_using_mem_callback)(p, p + size, 0/*ec_uniq*/); 101 102 // Only update this stat if allocation succeeded. 103 DRD_(s_cmalloc_bs_mallocd) += size; 104 105 VG_(HT_add_node)(DRD_(s_malloc_list), DRD_(create_chunk)(tid, p, size)); 106 107 return (void*)p; 108} 109 110static void* DRD_(malloc)(ThreadId tid, SizeT n) 111{ 112 return DRD_(new_block)(tid, n, VG_(clo_alignment), /*is_zeroed*/False); 113} 114 115static void* DRD_(memalign)(ThreadId tid, SizeT align, SizeT n) 116{ 117 return DRD_(new_block)(tid, n, align, /*is_zeroed*/False); 118} 119 120static void* DRD_(calloc)(ThreadId tid, SizeT nmemb, SizeT size1) 121{ 122 return DRD_(new_block)(tid, nmemb*size1, VG_(clo_alignment), 123 /*is_zeroed*/True); 124} 125 126static __inline__ void DRD_(handle_free)(ThreadId tid, Addr p) 127{ 128 DRD_Chunk* mc; 129 130 DRD_(s_cmalloc_n_frees)++; 131 132 mc = VG_(HT_remove)(DRD_(s_malloc_list), (UWord)p); 133 if (mc == NULL) 134 { 135 tl_assert(0); 136 } 137 else 138 { 139 tl_assert(p == mc->data); 140 if (mc->size > 0) 141 DRD_(s_stop_using_mem_callback)(mc->data, mc->size); 142 VG_(cli_free)((void*)p); 143 VG_(free)(mc); 144 } 145} 146 147static void DRD_(free)(ThreadId tid, void* p) 148{ 149 DRD_(handle_free)(tid, (Addr)p); 150} 151 152static void* DRD_(realloc)(ThreadId tid, void* p_old, SizeT new_size) 153{ 154 DRD_Chunk* mc; 155 void* p_new; 156 SizeT old_size; 157 158 DRD_(s_cmalloc_n_frees) ++; 159 DRD_(s_cmalloc_n_mallocs) ++; 160 DRD_(s_cmalloc_bs_mallocd) += new_size; 161 162 /* Remove the old block */ 163 mc = VG_(HT_remove)(DRD_(s_malloc_list), (UWord)p_old); 164 if (mc == NULL) { 165 tl_assert(0); 166 return NULL; 167 } 168 169 old_size = mc->size; 170 171 if (old_size == new_size) 172 { 173 /* size unchanged */ 174 mc->where = VG_(record_ExeContext)(tid, 0); 175 p_new = p_old; 176 177 } 178 else if (old_size > new_size) 179 { 180 /* new size is smaller */ 181 DRD_(s_stop_using_mem_callback)(mc->data + new_size, old_size); 182 mc->size = new_size; 183 mc->where = VG_(record_ExeContext)(tid, 0); 184 p_new = p_old; 185 186 } 187 else 188 { 189 /* new size is bigger */ 190 /* Get new memory */ 191 const Addr a_new = (Addr)VG_(cli_malloc)(VG_(clo_alignment), new_size); 192 193 if (a_new) 194 { 195 /* Copy from old to new */ 196 VG_(memcpy)((void*)a_new, p_old, mc->size); 197 198 /* Free old memory */ 199 DRD_(s_stop_using_mem_callback)(mc->data, mc->size); 200 VG_(free)(mc); 201 202 // Allocate a new chunk. 203 mc = DRD_(create_chunk)(tid, a_new, new_size); 204 DRD_(s_start_using_mem_callback)(a_new, a_new + new_size, 205 0/*ec_uniq*/); 206 } 207 else 208 { 209 /* Allocation failed -- leave original block untouched. */ 210 } 211 212 p_new = (void*)a_new; 213 } 214 215 // Now insert the new mc (with a possibly new 'data' field) into 216 // malloc_list. If this realloc() did not increase the memory size, we 217 // will have removed and then re-added mc unnecessarily. But that's ok 218 // because shrinking a block with realloc() is (presumably) much rarer 219 // than growing it, and this way simplifies the growing case. 220 VG_(HT_add_node)(DRD_(s_malloc_list), mc); 221 222 return p_new; 223} 224 225static void* DRD_(__builtin_new)(ThreadId tid, SizeT n) 226{ 227 void* const result = DRD_(new_block)(tid, n, VG_(clo_alignment), 228 /*is_zeroed*/False); 229 //VG_(message)(Vg_DebugMsg, "__builtin_new(%d, %d) = %p", tid, n, result); 230 return result; 231} 232 233static void DRD_(__builtin_delete)(ThreadId tid, void* p) 234{ 235 //VG_(message)(Vg_DebugMsg, "__builtin_delete(%d, %p)", tid, p); 236 DRD_(handle_free)(tid, (Addr)p); 237} 238 239static void* DRD_(__builtin_vec_new)(ThreadId tid, SizeT n) 240{ 241 return DRD_(new_block)(tid, n, VG_(clo_alignment), /*is_zeroed*/False); 242} 243 244static void DRD_(__builtin_vec_delete)(ThreadId tid, void* p) 245{ 246 DRD_(handle_free)(tid, (Addr)p); 247} 248 249static SizeT DRD_(malloc_usable_size) ( ThreadId tid, void* p ) 250{ 251 DRD_Chunk *mc = VG_(HT_lookup)( DRD_(s_malloc_list), (UWord)p ); 252 253 // There may be slop, but pretend there isn't because only the asked-for 254 // area will have been shadowed properly. 255 return ( mc ? mc->size : 0 ); 256} 257 258void DRD_(register_malloc_wrappers)(const StartUsingMem start_callback, 259 const StopUsingMem stop_callback) 260{ 261 tl_assert(DRD_(s_malloc_list) == 0); 262 DRD_(s_malloc_list) = VG_(HT_construct)("drd_malloc_list"); // a big prime 263 tl_assert(DRD_(s_malloc_list) != 0); 264 tl_assert(start_callback); 265 tl_assert(stop_callback); 266 267 DRD_(s_start_using_mem_callback) = start_callback; 268 DRD_(s_stop_using_mem_callback) = stop_callback; 269 270 VG_(needs_malloc_replacement)(DRD_(malloc), 271 DRD_(__builtin_new), 272 DRD_(__builtin_vec_new), 273 DRD_(memalign), 274 DRD_(calloc), 275 DRD_(free), 276 DRD_(__builtin_delete), 277 DRD_(__builtin_vec_delete), 278 DRD_(realloc), 279 DRD_(malloc_usable_size), 280 0); 281} 282 283Bool DRD_(heap_addrinfo)(Addr const a, 284 Addr* const data, 285 SizeT* const size, 286 ExeContext** const where) 287{ 288 DRD_Chunk* mc; 289 290 tl_assert(data); 291 tl_assert(size); 292 tl_assert(where); 293 294 VG_(HT_ResetIter)(DRD_(s_malloc_list)); 295 while ((mc = VG_(HT_Next)(DRD_(s_malloc_list)))) 296 { 297 if (mc->data <= a && a < mc->data + mc->size) 298 { 299 *data = mc->data; 300 *size = mc->size; 301 *where = mc->where; 302 return True; 303 } 304 } 305 return False; 306} 307 308/*------------------------------------------------------------*/ 309/*--- Statistics printing ---*/ 310/*------------------------------------------------------------*/ 311 312void DRD_(print_malloc_stats)(void) 313{ 314 DRD_Chunk* mc; 315 SizeT nblocks = 0; 316 SizeT nbytes = 0; 317 318 if (VG_(clo_verbosity) == 0) 319 return; 320 if (VG_(clo_xml)) 321 return; 322 323 /* Count memory still in use. */ 324 VG_(HT_ResetIter)(DRD_(s_malloc_list)); 325 while ((mc = VG_(HT_Next)(DRD_(s_malloc_list)))) 326 { 327 nblocks++; 328 nbytes += mc->size; 329 } 330 331 VG_(message)(Vg_DebugMsg, 332 "malloc/free: in use at exit: %lu bytes in %lu blocks.", 333 nbytes, nblocks); 334 VG_(message)(Vg_DebugMsg, 335 "malloc/free: %lu allocs, %lu frees, %lu bytes allocated.", 336 DRD_(s_cmalloc_n_mallocs), 337 DRD_(s_cmalloc_n_frees), DRD_(s_cmalloc_bs_mallocd)); 338 if (VG_(clo_verbosity) > 1) 339 VG_(message)(Vg_DebugMsg, " "); 340} 341 342/*--------------------------------------------------------------------*/ 343/*--- end ---*/ 344/*--------------------------------------------------------------------*/ 345