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 (at) 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 */ 48 typedef 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 58 static StartUsingMem s_start_using_mem_callback; 59 static StopUsingMem s_stop_using_mem_callback; 60 /* Statistics. */ 61 static SizeT s_cmalloc_n_mallocs = 0; 62 static SizeT s_cmalloc_n_frees = 0; 63 static SizeT s_cmalloc_bs_mallocd = 0; 64 /* Record malloc'd blocks. */ 65 static VgHashTable s_malloc_list = NULL; 66 67 68 /* Function definitions. */ 69 70 /** Allocate client memory memory and update the hash map. */ 71 static 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 */ 90 void 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 110 static 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 */ 123 Bool 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(). */ 147 static 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(). */ 153 static 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(). */ 159 static 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(). */ 166 static 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 */ 179 static 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(). */ 254 static 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(). */ 260 static void drd___builtin_delete(ThreadId tid, void* p) 261 { 262 handle_free(tid, p); 263 } 264 265 /** Wrapper for __builtin_vec_new(). */ 266 static 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(). */ 272 static 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 */ 283 static 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 292 void 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 317 Bool 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 346 void 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