syswrap openat2 for all linux arches
[valgrind.git] / drd / drd_malloc_wrappers.c
blob72e906181809ff1808b519b99017759f2edfe563
1 /*
2 This file is part of drd, a thread error detector.
4 Copyright (C) 2006-2020 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, see <http://www.gnu.org/licenses/>.
19 The GNU General Public License is contained in the file COPYING.
23 #include "drd_malloc_wrappers.h"
24 #include "drd_thread.h"
25 #include "pub_tool_basics.h"
26 #include "pub_tool_execontext.h"
27 #include "pub_tool_hashtable.h"
28 #include "pub_tool_libcassert.h"
29 #include "pub_tool_libcbase.h"
30 #include "pub_tool_libcprint.h"
31 #include "pub_tool_mallocfree.h"
32 #include "pub_tool_options.h"
33 #include "pub_tool_replacemalloc.h"
34 #include "pub_tool_threadstate.h"
35 #include "pub_tool_tooliface.h"
38 /* Local type definitions. */
40 /**
41 * Node with per-allocation information that will be stored in a hash map.
42 * As specified in <pub_tool_hashtable.h>, the first member must be a pointer
43 * and the second member must be an UWord.
45 typedef struct _DRD_Chunk {
46 struct _DRD_Chunk* next;
47 UWord data; // pointer to actual block
48 SizeT size; // size requested
49 ExeContext* where; // where it was allocated
50 } DRD_Chunk;
53 /* Local variables. */
55 static StartUsingMem s_start_using_mem_callback;
56 static StopUsingMem s_stop_using_mem_callback;
57 /* Statistics. */
58 static SizeT s_cmalloc_n_mallocs = 0;
59 static SizeT s_cmalloc_n_frees = 0;
60 static SizeT s_cmalloc_bs_mallocd = 0;
61 /* Record malloc'd blocks. */
62 static VgHashTable *s_malloc_list = NULL;
65 /* Function definitions. */
67 /** Allocate client memory memory and update the hash map. */
68 static void* new_block(ThreadId tid, SizeT size, SizeT align, Bool is_zeroed)
70 void* p;
72 p = VG_(cli_malloc)(align, size);
73 if (!p)
74 return NULL;
75 if (is_zeroed)
76 VG_(memset)(p, 0, size);
78 DRD_(malloclike_block)(tid, (Addr)p, size);
80 return p;
83 /**
84 * Store information about a memory block that has been allocated by
85 * malloc() or a malloc() replacement in the hash map.
87 void DRD_(malloclike_block)(const ThreadId tid, const Addr p, const SizeT size)
89 DRD_Chunk* mc;
91 tl_assert(p);
93 if (size > 0)
94 s_start_using_mem_callback(p, size, 0/*ec_uniq*/);
96 s_cmalloc_n_mallocs++;
97 // Only update this stat if allocation succeeded.
98 s_cmalloc_bs_mallocd += size;
100 mc = VG_(malloc)("drd.malloc_wrappers.cDC.1", sizeof(DRD_Chunk));
101 mc->data = p;
102 mc->size = size;
103 mc->where = VG_(record_ExeContext)(tid, 0);
104 VG_(HT_add_node)(s_malloc_list, mc);
107 static void handle_free(ThreadId tid, void* p)
109 Bool success;
111 tl_assert(p);
112 success = DRD_(freelike_block)(tid, (Addr)p, True);
113 tl_assert(success);
117 * Remove the information that was stored by DRD_(malloclike_block)() about
118 * a memory block.
120 Bool DRD_(freelike_block)(const ThreadId tid, const Addr p, const Bool dealloc)
122 DRD_Chunk* mc;
124 tl_assert(p);
126 s_cmalloc_n_frees++;
128 mc = VG_(HT_lookup)(s_malloc_list, (UWord)p);
129 if (mc)
131 tl_assert(p == mc->data);
132 if (mc->size > 0)
133 s_stop_using_mem_callback(mc->data, mc->size);
134 if (dealloc)
135 VG_(cli_free)((void*)p);
136 VG_(HT_remove)(s_malloc_list, (UWord)p);
137 VG_(free)(mc);
138 return True;
140 return False;
143 /** Wrapper for malloc(). */
144 static void* drd_malloc(ThreadId tid, SizeT n)
146 return new_block(tid, n, VG_(clo_alignment), /*is_zeroed*/False);
149 /** Wrapper for memalign(). */
150 static void* drd_memalign(ThreadId tid, SizeT align, SizeT orig_alignT, SizeT n)
152 return new_block(tid, n, align, /*is_zeroed*/False);
155 /** Wrapper for calloc(). */
156 static void* drd_calloc(ThreadId tid, SizeT nmemb, SizeT size1)
158 return new_block(tid, nmemb*size1, VG_(clo_alignment),
159 /*is_zeroed*/True);
162 /** Wrapper for free(). */
163 static void drd_free(ThreadId tid, void* p)
165 handle_free(tid, p);
169 * Wrapper for realloc(). Returns a pointer to the new block of memory, or
170 * NULL if no new block could not be allocated. Notes:
171 * - realloc(NULL, size) has the same effect as malloc(size).
172 * - realloc(p, 0) has the same effect as free(p).
173 * - success is not guaranteed even if the requested size is smaller than the
174 * allocated size.
176 static void* drd_realloc(ThreadId tid, void* p_old, SizeT new_size)
178 DRD_Chunk* mc;
179 void* p_new;
180 SizeT old_size;
182 if (! p_old)
183 return drd_malloc(tid, new_size);
185 if (new_size == 0)
187 if (VG_(clo_realloc_zero_bytes_frees) == True)
189 drd_free(tid, p_old);
190 return NULL;
192 new_size = 1;
195 s_cmalloc_n_mallocs++;
196 s_cmalloc_n_frees++;
197 s_cmalloc_bs_mallocd += new_size;
199 mc = VG_(HT_lookup)(s_malloc_list, (UWord)p_old);
200 if (mc == NULL)
202 tl_assert(0);
203 return NULL;
206 old_size = mc->size;
208 if (old_size == new_size)
210 /* size unchanged */
211 mc->where = VG_(record_ExeContext)(tid, 0);
212 p_new = p_old;
214 else if (new_size < old_size)
216 /* new size is smaller but nonzero */
217 s_stop_using_mem_callback(mc->data + new_size, old_size - new_size);
218 mc->size = new_size;
219 mc->where = VG_(record_ExeContext)(tid, 0);
220 p_new = p_old;
222 else
224 /* new size is bigger */
225 p_new = VG_(cli_malloc)(VG_(clo_alignment), new_size);
227 if (p_new)
229 /* Copy from old to new. */
230 VG_(memcpy)(p_new, p_old, mc->size);
232 /* Free old memory. */
233 if (mc->size > 0)
234 s_stop_using_mem_callback(mc->data, mc->size);
235 VG_(cli_free)(p_old);
236 VG_(HT_remove)(s_malloc_list, (UWord)p_old);
238 /* Update state information. */
239 mc->data = (Addr)p_new;
240 mc->size = new_size;
241 mc->where = VG_(record_ExeContext)(tid, 0);
242 VG_(HT_add_node)(s_malloc_list, mc);
243 s_start_using_mem_callback((Addr)p_new, new_size, 0/*ec_uniq*/);
245 else
247 /* Allocation failed -- leave original block untouched. */
251 return p_new;
254 /** Wrapper for __builtin_new(). */
255 static void* drd___builtin_new(ThreadId tid, SizeT n)
257 return new_block(tid, n, VG_(clo_alignment), /*is_zeroed*/False);
260 /** Wrapper for __builtin_new_aligned(). */
261 static void* drd___builtin_new_aligned(ThreadId tid, SizeT n, SizeT align, SizeT orig_align)
263 return new_block(tid, n, align, /*is_zeroed*/False);
266 /** Wrapper for __builtin_delete(). */
267 static void drd___builtin_delete(ThreadId tid, void* p)
269 handle_free(tid, p);
272 /** Wrapper for __builtin_delete_aligned(). */
273 static void drd___builtin_delete_aligned(ThreadId tid, void* p, SizeT align)
275 handle_free(tid, p);
278 /** Wrapper for __builtin_vec_new(). */
279 static void* drd___builtin_vec_new(ThreadId tid, SizeT n)
281 return new_block(tid, n, VG_(clo_alignment), /*is_zeroed*/False);
284 /** Wrapper for __builtin_vec_new_aligned(). */
285 static void* drd___builtin_vec_new_aligned(ThreadId tid, SizeT n, SizeT align, SizeT orig_align)
287 return new_block(tid, n, align, /*is_zeroed*/False);
290 /** Wrapper for __builtin_vec_delete(). */
291 static void drd___builtin_vec_delete(ThreadId tid, void* p)
293 handle_free(tid, p);
296 /** Wrapper for __builtin_vec_delete_aligned(). */
297 static void drd___builtin_vec_delete_aligned(ThreadId tid, void* p, SizeT align)
299 handle_free(tid, p);
303 * Wrapper for malloc_usable_size() / malloc_size(). This function takes
304 * a pointer to a block allocated by `malloc' and returns the amount of space
305 * that is available in the block. This may or may not be more than the size
306 * requested from `malloc', due to alignment or minimum size constraints.
308 static SizeT drd_malloc_usable_size(ThreadId tid, void* p)
310 DRD_Chunk* mc;
312 mc = VG_(HT_lookup)(s_malloc_list, (UWord)p);
314 return mc ? mc->size : 0;
317 void DRD_(register_malloc_wrappers)(const StartUsingMem start_callback,
318 const StopUsingMem stop_callback)
320 tl_assert(s_malloc_list == 0);
321 s_malloc_list = VG_(HT_construct)("drd_malloc_list");
322 tl_assert(start_callback);
323 tl_assert(stop_callback);
325 s_start_using_mem_callback = start_callback;
326 s_stop_using_mem_callback = stop_callback;
328 VG_(needs_malloc_replacement)(drd_malloc,
329 drd___builtin_new,
330 drd___builtin_new_aligned,
331 drd___builtin_vec_new,
332 drd___builtin_vec_new_aligned,
333 drd_memalign,
334 drd_calloc,
335 drd_free,
336 drd___builtin_delete,
337 drd___builtin_delete_aligned,
338 drd___builtin_vec_delete,
339 drd___builtin_vec_delete_aligned,
340 drd_realloc,
341 drd_malloc_usable_size,
345 Bool DRD_(heap_addrinfo)(Addr const a,
346 Addr* const data,
347 SizeT* const size,
348 ExeContext** const where)
350 DRD_Chunk* mc;
352 tl_assert(data);
353 tl_assert(size);
354 tl_assert(where);
356 VG_(HT_ResetIter)(s_malloc_list);
357 while ((mc = VG_(HT_Next)(s_malloc_list)))
359 if (mc->data <= a && a < mc->data + mc->size)
361 *data = mc->data;
362 *size = mc->size;
363 *where = mc->where;
364 return True;
367 return False;
370 /*------------------------------------------------------------*/
371 /*--- Statistics printing ---*/
372 /*------------------------------------------------------------*/
374 void DRD_(print_malloc_stats)(void)
376 DRD_Chunk* mc;
377 SizeT nblocks = 0;
378 SizeT nbytes = 0;
380 if (VG_(clo_verbosity) == 0)
381 return;
382 if (VG_(clo_xml))
383 return;
385 /* Count memory still in use. */
386 VG_(HT_ResetIter)(s_malloc_list);
387 while ((mc = VG_(HT_Next)(s_malloc_list)))
389 nblocks++;
390 nbytes += mc->size;
393 VG_(message)(Vg_DebugMsg,
394 "malloc/free: in use at exit: %lu bytes in %lu blocks.\n",
395 nbytes, nblocks);
396 VG_(message)(Vg_DebugMsg,
397 "malloc/free: %lu allocs, %lu frees, %lu bytes allocated.\n",
398 s_cmalloc_n_mallocs,
399 s_cmalloc_n_frees, s_cmalloc_bs_mallocd);
400 if (VG_(clo_verbosity) > 1)
401 VG_(message)(Vg_DebugMsg, " \n");
404 /*--------------------------------------------------------------------*/
405 /*--- end ---*/
406 /*--------------------------------------------------------------------*/