1 /* -*- mode: C; c-basic-offset: 3; indent-tabs-mode: nil; -*- */
3 This file is part of drd, a thread error detector.
5 Copyright (C) 2006-2011 Bart Van Assche <bvanassche@acm.org>.
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.
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.
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
22 The GNU General Public License is contained in the file COPYING.
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"
41 /* Local type definitions. */
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.
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
56 /* Local variables. */
58 static StartUsingMem s_start_using_mem_callback;
59 static StopUsingMem s_stop_using_mem_callback;
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;
68 /* Function definitions. */
70 /** Allocate client memory memory and update the hash map. */
71 static void* new_block(ThreadId tid, SizeT size, SizeT align, Bool is_zeroed)
75 p = VG_(cli_malloc)(align, size);
79 VG_(memset)(p, 0, size);
81 DRD_(malloclike_block)(tid, (Addr)p, size);
87 * Store information about a memory block that has been allocated by
88 * malloc() or a malloc() replacement in the hash map.
90 void DRD_(malloclike_block)(const ThreadId tid, const Addr p, const SizeT size)
97 s_start_using_mem_callback(p, size, 0/*ec_uniq*/);
99 s_cmalloc_n_mallocs++;
100 // Only update this stat if allocation succeeded.
101 s_cmalloc_bs_mallocd += size;
103 mc = VG_(malloc)("drd.malloc_wrappers.cDC.1", sizeof(DRD_Chunk));
106 mc->where = VG_(record_ExeContext)(tid, 0);
107 VG_(HT_add_node)(s_malloc_list, mc);
110 static void handle_free(ThreadId tid, void* p)
115 success = DRD_(freelike_block)(tid, (Addr)p, True);
120 * Remove the information that was stored by DRD_(malloclike_block)() about
123 Bool DRD_(freelike_block)(const ThreadId tid, const Addr p, const Bool dealloc)
131 mc = VG_(HT_lookup)(s_malloc_list, (UWord)p);
134 tl_assert(p == mc->data);
136 VG_(cli_free)((void*)p);
138 s_stop_using_mem_callback(mc->data, mc->size);
139 VG_(HT_remove)(s_malloc_list, (UWord)p);
146 /** Wrapper for malloc(). */
147 static void* drd_malloc(ThreadId tid, SizeT n)
149 return new_block(tid, n, VG_(clo_alignment), /*is_zeroed*/False);
152 /** Wrapper for memalign(). */
153 static void* drd_memalign(ThreadId tid, SizeT align, SizeT n)
155 return new_block(tid, n, align, /*is_zeroed*/False);
158 /** Wrapper for calloc(). */
159 static void* drd_calloc(ThreadId tid, SizeT nmemb, SizeT size1)
161 return new_block(tid, nmemb*size1, VG_(clo_alignment),
165 /** Wrapper for free(). */
166 static void drd_free(ThreadId tid, void* p)
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
179 static void* drd_realloc(ThreadId tid, void* p_old, SizeT new_size)
186 return drd_malloc(tid, new_size);
190 drd_free(tid, p_old);
194 s_cmalloc_n_mallocs++;
196 s_cmalloc_bs_mallocd += new_size;
198 mc = VG_(HT_lookup)(s_malloc_list, (UWord)p_old);
207 if (old_size == new_size)
210 mc->where = VG_(record_ExeContext)(tid, 0);
213 else if (new_size < old_size)
215 /* new size is smaller but nonzero */
216 s_stop_using_mem_callback(mc->data + new_size, old_size - new_size);
218 mc->where = VG_(record_ExeContext)(tid, 0);
223 /* new size is bigger */
224 p_new = VG_(cli_malloc)(VG_(clo_alignment), new_size);
228 /* Copy from old to new. */
229 VG_(memcpy)(p_new, p_old, mc->size);
231 /* Free old memory. */
232 VG_(cli_free)(p_old);
234 s_stop_using_mem_callback(mc->data, mc->size);
235 VG_(HT_remove)(s_malloc_list, (UWord)p_old);
237 /* Update state information. */
238 mc->data = (Addr)p_new;
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*/);
246 /* Allocation failed -- leave original block untouched. */
253 /** Wrapper for __builtin_new(). */
254 static void* drd___builtin_new(ThreadId tid, SizeT n)
256 return new_block(tid, n, VG_(clo_alignment), /*is_zeroed*/False);
259 /** Wrapper for __builtin_delete(). */
260 static void drd___builtin_delete(ThreadId tid, void* p)
265 /** Wrapper for __builtin_vec_new(). */
266 static void* drd___builtin_vec_new(ThreadId tid, SizeT n)
268 return new_block(tid, n, VG_(clo_alignment), /*is_zeroed*/False);
271 /** Wrapper for __builtin_vec_delete(). */
272 static void drd___builtin_vec_delete(ThreadId tid, void* p)
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.
283 static SizeT drd_malloc_usable_size(ThreadId tid, void* p)
287 mc = VG_(HT_lookup)(s_malloc_list, (UWord)p);
289 return mc ? mc->size : 0;
292 void DRD_(register_malloc_wrappers)(const StartUsingMem start_callback,
293 const StopUsingMem stop_callback)
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);
301 s_start_using_mem_callback = start_callback;
302 s_stop_using_mem_callback = stop_callback;
304 VG_(needs_malloc_replacement)(drd_malloc,
306 drd___builtin_vec_new,
310 drd___builtin_delete,
311 drd___builtin_vec_delete,
313 drd_malloc_usable_size,
317 Bool DRD_(heap_addrinfo)(Addr const a,
320 ExeContext** const where)
328 VG_(HT_ResetIter)(s_malloc_list);
329 while ((mc = VG_(HT_Next)(s_malloc_list)))
331 if (mc->data <= a && a < mc->data + mc->size)
342 /*------------------------------------------------------------*/
343 /*--- Statistics printing ---*/
344 /*------------------------------------------------------------*/
346 void DRD_(print_malloc_stats)(void)
352 if (VG_(clo_verbosity) == 0)
357 /* Count memory still in use. */
358 VG_(HT_ResetIter)(s_malloc_list);
359 while ((mc = VG_(HT_Next)(s_malloc_list)))
365 VG_(message)(Vg_DebugMsg,
366 "malloc/free: in use at exit: %lu bytes in %lu blocks.\n",
368 VG_(message)(Vg_DebugMsg,
369 "malloc/free: %lu allocs, %lu frees, %lu bytes allocated.\n",
371 s_cmalloc_n_frees, s_cmalloc_bs_mallocd);
372 if (VG_(clo_verbosity) > 1)
373 VG_(message)(Vg_DebugMsg, " \n");
376 /*--------------------------------------------------------------------*/
378 /*--------------------------------------------------------------------*/