2 This file is part of drd, a thread error detector.
4 Copyright (C) 2006-2017 Bart Van Assche <bvanassche@acm.org>.
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 License, or (at your option) any later version.
11 This program is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21 The GNU General Public License is contained in the file COPYING.
25 #include "drd_malloc_wrappers.h"
26 #include "drd_thread.h"
27 #include "pub_tool_basics.h"
28 #include "pub_tool_execontext.h"
29 #include "pub_tool_hashtable.h"
30 #include "pub_tool_libcassert.h"
31 #include "pub_tool_libcbase.h"
32 #include "pub_tool_libcprint.h"
33 #include "pub_tool_mallocfree.h"
34 #include "pub_tool_options.h"
35 #include "pub_tool_replacemalloc.h"
36 #include "pub_tool_threadstate.h"
37 #include "pub_tool_tooliface.h"
40 /* Local type definitions. */
43 * Node with per-allocation information that will be stored in a hash map.
44 * As specified in <pub_tool_hashtable.h>, the first member must be a pointer
45 * and the second member must be an UWord.
47 typedef struct _DRD_Chunk
{
48 struct _DRD_Chunk
* next
;
49 UWord data
; // pointer to actual block
50 SizeT size
; // size requested
51 ExeContext
* where
; // where it was allocated
55 /* Local variables. */
57 static StartUsingMem s_start_using_mem_callback
;
58 static StopUsingMem s_stop_using_mem_callback
;
60 static SizeT s_cmalloc_n_mallocs
= 0;
61 static SizeT s_cmalloc_n_frees
= 0;
62 static SizeT s_cmalloc_bs_mallocd
= 0;
63 /* Record malloc'd blocks. */
64 static VgHashTable
*s_malloc_list
= NULL
;
67 /* Function definitions. */
69 /** Allocate client memory memory and update the hash map. */
70 static void* new_block(ThreadId tid
, SizeT size
, SizeT align
, Bool is_zeroed
)
74 p
= VG_(cli_malloc
)(align
, size
);
78 VG_(memset
)(p
, 0, size
);
80 DRD_(malloclike_block
)(tid
, (Addr
)p
, size
);
86 * Store information about a memory block that has been allocated by
87 * malloc() or a malloc() replacement in the hash map.
89 void DRD_(malloclike_block
)(const ThreadId tid
, const Addr p
, const SizeT size
)
96 s_start_using_mem_callback(p
, size
, 0/*ec_uniq*/);
98 s_cmalloc_n_mallocs
++;
99 // Only update this stat if allocation succeeded.
100 s_cmalloc_bs_mallocd
+= size
;
102 mc
= VG_(malloc
)("drd.malloc_wrappers.cDC.1", sizeof(DRD_Chunk
));
105 mc
->where
= VG_(record_ExeContext
)(tid
, 0);
106 VG_(HT_add_node
)(s_malloc_list
, mc
);
109 static void handle_free(ThreadId tid
, void* p
)
114 success
= DRD_(freelike_block
)(tid
, (Addr
)p
, True
);
119 * Remove the information that was stored by DRD_(malloclike_block)() about
122 Bool
DRD_(freelike_block
)(const ThreadId tid
, const Addr p
, const Bool dealloc
)
130 mc
= VG_(HT_lookup
)(s_malloc_list
, (UWord
)p
);
133 tl_assert(p
== mc
->data
);
135 s_stop_using_mem_callback(mc
->data
, mc
->size
);
137 VG_(cli_free
)((void*)p
);
138 VG_(HT_remove
)(s_malloc_list
, (UWord
)p
);
145 /** Wrapper for malloc(). */
146 static void* drd_malloc(ThreadId tid
, SizeT n
)
148 return new_block(tid
, n
, VG_(clo_alignment
), /*is_zeroed*/False
);
151 /** Wrapper for memalign(). */
152 static void* drd_memalign(ThreadId tid
, SizeT align
, SizeT n
)
154 return new_block(tid
, n
, align
, /*is_zeroed*/False
);
157 /** Wrapper for calloc(). */
158 static void* drd_calloc(ThreadId tid
, SizeT nmemb
, SizeT size1
)
160 return new_block(tid
, nmemb
*size1
, VG_(clo_alignment
),
164 /** Wrapper for free(). */
165 static void drd_free(ThreadId tid
, void* p
)
171 * Wrapper for realloc(). Returns a pointer to the new block of memory, or
172 * NULL if no new block could not be allocated. Notes:
173 * - realloc(NULL, size) has the same effect as malloc(size).
174 * - realloc(p, 0) has the same effect as free(p).
175 * - success is not guaranteed even if the requested size is smaller than the
178 static void* drd_realloc(ThreadId tid
, void* p_old
, SizeT new_size
)
185 return drd_malloc(tid
, new_size
);
189 drd_free(tid
, p_old
);
193 s_cmalloc_n_mallocs
++;
195 s_cmalloc_bs_mallocd
+= new_size
;
197 mc
= VG_(HT_lookup
)(s_malloc_list
, (UWord
)p_old
);
206 if (old_size
== new_size
)
209 mc
->where
= VG_(record_ExeContext
)(tid
, 0);
212 else if (new_size
< old_size
)
214 /* new size is smaller but nonzero */
215 s_stop_using_mem_callback(mc
->data
+ new_size
, old_size
- new_size
);
217 mc
->where
= VG_(record_ExeContext
)(tid
, 0);
222 /* new size is bigger */
223 p_new
= VG_(cli_malloc
)(VG_(clo_alignment
), new_size
);
227 /* Copy from old to new. */
228 VG_(memcpy
)(p_new
, p_old
, mc
->size
);
230 /* Free old memory. */
232 s_stop_using_mem_callback(mc
->data
, mc
->size
);
233 VG_(cli_free
)(p_old
);
234 VG_(HT_remove
)(s_malloc_list
, (UWord
)p_old
);
236 /* Update state information. */
237 mc
->data
= (Addr
)p_new
;
239 mc
->where
= VG_(record_ExeContext
)(tid
, 0);
240 VG_(HT_add_node
)(s_malloc_list
, mc
);
241 s_start_using_mem_callback((Addr
)p_new
, new_size
, 0/*ec_uniq*/);
245 /* Allocation failed -- leave original block untouched. */
252 /** Wrapper for __builtin_new(). */
253 static void* drd___builtin_new(ThreadId tid
, SizeT n
)
255 return new_block(tid
, n
, VG_(clo_alignment
), /*is_zeroed*/False
);
258 /** Wrapper for __builtin_delete(). */
259 static void drd___builtin_delete(ThreadId tid
, void* p
)
264 /** Wrapper for __builtin_vec_new(). */
265 static void* drd___builtin_vec_new(ThreadId tid
, SizeT n
)
267 return new_block(tid
, n
, VG_(clo_alignment
), /*is_zeroed*/False
);
270 /** Wrapper for __builtin_vec_delete(). */
271 static void drd___builtin_vec_delete(ThreadId tid
, void* p
)
277 * Wrapper for malloc_usable_size() / malloc_size(). This function takes
278 * a pointer to a block allocated by `malloc' and returns the amount of space
279 * that is available in the block. This may or may not be more than the size
280 * requested from `malloc', due to alignment or minimum size constraints.
282 static SizeT
drd_malloc_usable_size(ThreadId tid
, void* p
)
286 mc
= VG_(HT_lookup
)(s_malloc_list
, (UWord
)p
);
288 return mc
? mc
->size
: 0;
291 void DRD_(register_malloc_wrappers
)(const StartUsingMem start_callback
,
292 const StopUsingMem stop_callback
)
294 tl_assert(s_malloc_list
== 0);
295 s_malloc_list
= VG_(HT_construct
)("drd_malloc_list");
296 tl_assert(start_callback
);
297 tl_assert(stop_callback
);
299 s_start_using_mem_callback
= start_callback
;
300 s_stop_using_mem_callback
= stop_callback
;
302 VG_(needs_malloc_replacement
)(drd_malloc
,
304 drd___builtin_vec_new
,
308 drd___builtin_delete
,
309 drd___builtin_vec_delete
,
311 drd_malloc_usable_size
,
315 Bool
DRD_(heap_addrinfo
)(Addr
const a
,
318 ExeContext
** const where
)
326 VG_(HT_ResetIter
)(s_malloc_list
);
327 while ((mc
= VG_(HT_Next
)(s_malloc_list
)))
329 if (mc
->data
<= a
&& a
< mc
->data
+ mc
->size
)
340 /*------------------------------------------------------------*/
341 /*--- Statistics printing ---*/
342 /*------------------------------------------------------------*/
344 void DRD_(print_malloc_stats
)(void)
350 if (VG_(clo_verbosity
) == 0)
355 /* Count memory still in use. */
356 VG_(HT_ResetIter
)(s_malloc_list
);
357 while ((mc
= VG_(HT_Next
)(s_malloc_list
)))
363 VG_(message
)(Vg_DebugMsg
,
364 "malloc/free: in use at exit: %lu bytes in %lu blocks.\n",
366 VG_(message
)(Vg_DebugMsg
,
367 "malloc/free: %lu allocs, %lu frees, %lu bytes allocated.\n",
369 s_cmalloc_n_frees
, s_cmalloc_bs_mallocd
);
370 if (VG_(clo_verbosity
) > 1)
371 VG_(message
)(Vg_DebugMsg
, " \n");
374 /*--------------------------------------------------------------------*/
376 /*--------------------------------------------------------------------*/