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@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. */
new_block(ThreadId tid,SizeT size,SizeT align,Bool is_zeroed)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 */
DRD_(malloclike_block)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
handle_free(ThreadId tid,void * p)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 */
DRD_(freelike_block)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(). */
drd_malloc(ThreadId tid,SizeT n)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(). */
drd_memalign(ThreadId tid,SizeT align,SizeT n)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(). */
drd_calloc(ThreadId tid,SizeT nmemb,SizeT size1)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(). */
drd_free(ThreadId tid,void * p)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 */
drd_realloc(ThreadId tid,void * p_old,SizeT new_size)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(). */
drd___builtin_new(ThreadId tid,SizeT n)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(). */
drd___builtin_delete(ThreadId tid,void * p)258 static void drd___builtin_delete(ThreadId tid, void* p)
259 {
260 handle_free(tid, p);
261 }
262
263 /** Wrapper for __builtin_vec_new(). */
drd___builtin_vec_new(ThreadId tid,SizeT n)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(). */
drd___builtin_vec_delete(ThreadId tid,void * p)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 */
drd_malloc_usable_size(ThreadId tid,void * p)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
DRD_(register_malloc_wrappers)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
DRD_(heap_addrinfo)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
DRD_(print_malloc_stats)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