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 (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 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 */ 124 Bool 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(). */ 145 static 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(). */ 151 static 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(). */ 157 static 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(). */ 164 static 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 */ 177 static 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(). */ 252 static 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(). */ 258 static void drd___builtin_delete(ThreadId tid, void* p) 259 { 260 handle_free(tid, p); 261 } 262 263 /** Wrapper for __builtin_vec_new(). */ 264 static 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(). */ 270 static 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 */ 281 static 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 290 void 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 315 Bool 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 344 void 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