• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2   This file is part of drd, a thread error detector.
3 
4   Copyright (C) 2006-2015 Bart Van Assche <bvanassche@acm.org>.
5 
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.
10 
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.
15 
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
19   02111-1307, USA.
20 
21   The GNU General Public License is contained in the file COPYING.
22 */
23 
24 
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"
38 
39 
40 /* Local type definitions. */
41 
42 /**
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.
46  */
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
52 } DRD_Chunk;
53 
54 
55 /* Local variables. */
56 
57 static StartUsingMem s_start_using_mem_callback;
58 static StopUsingMem  s_stop_using_mem_callback;
59 /* Statistics. */
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;
65 
66 
67 /* Function definitions. */
68 
69 /** Allocate client memory memory and update the hash map. */
new_block(ThreadId tid,SizeT size,SizeT align,Bool is_zeroed)70 static void* new_block(ThreadId tid, SizeT size, SizeT align, Bool is_zeroed)
71 {
72    void* p;
73 
74    p = VG_(cli_malloc)(align, size);
75    if (!p)
76       return NULL;
77    if (is_zeroed)
78       VG_(memset)(p, 0, size);
79 
80    DRD_(malloclike_block)(tid, (Addr)p, size);
81 
82    return p;
83 }
84 
85 /**
86  * Store information about a memory block that has been allocated by
87  * malloc() or a malloc() replacement in the hash map.
88  */
DRD_(malloclike_block)89 void DRD_(malloclike_block)(const ThreadId tid, const Addr p, const SizeT size)
90 {
91    DRD_Chunk* mc;
92 
93    tl_assert(p);
94 
95    if (size > 0)
96       s_start_using_mem_callback(p, size, 0/*ec_uniq*/);
97 
98    s_cmalloc_n_mallocs++;
99    // Only update this stat if allocation succeeded.
100    s_cmalloc_bs_mallocd += size;
101 
102    mc = VG_(malloc)("drd.malloc_wrappers.cDC.1", sizeof(DRD_Chunk));
103    mc->data  = p;
104    mc->size  = size;
105    mc->where = VG_(record_ExeContext)(tid, 0);
106    VG_(HT_add_node)(s_malloc_list, mc);
107 }
108 
handle_free(ThreadId tid,void * p)109 static void handle_free(ThreadId tid, void* p)
110 {
111    Bool success;
112 
113    tl_assert(p);
114    success = DRD_(freelike_block)(tid, (Addr)p, True);
115    tl_assert(success);
116 }
117 
118 /**
119  * Remove the information that was stored by DRD_(malloclike_block)() about
120  * a memory block.
121  */
DRD_(freelike_block)122 Bool DRD_(freelike_block)(const ThreadId tid, const Addr p, const Bool dealloc)
123 {
124    DRD_Chunk* mc;
125 
126    tl_assert(p);
127 
128    s_cmalloc_n_frees++;
129 
130    mc = VG_(HT_lookup)(s_malloc_list, (UWord)p);
131    if (mc)
132    {
133       tl_assert(p == mc->data);
134       if (mc->size > 0)
135          s_stop_using_mem_callback(mc->data, mc->size);
136       if (dealloc)
137 	 VG_(cli_free)((void*)p);
138       VG_(HT_remove)(s_malloc_list, (UWord)p);
139       VG_(free)(mc);
140       return True;
141    }
142    return False;
143 }
144 
145 /** Wrapper for malloc(). */
drd_malloc(ThreadId tid,SizeT n)146 static void* drd_malloc(ThreadId tid, SizeT n)
147 {
148    return new_block(tid, n, VG_(clo_alignment), /*is_zeroed*/False);
149 }
150 
151 /** Wrapper for memalign(). */
drd_memalign(ThreadId tid,SizeT align,SizeT n)152 static void* drd_memalign(ThreadId tid, SizeT align, SizeT n)
153 {
154    return new_block(tid, n, align, /*is_zeroed*/False);
155 }
156 
157 /** Wrapper for calloc(). */
drd_calloc(ThreadId tid,SizeT nmemb,SizeT size1)158 static void* drd_calloc(ThreadId tid, SizeT nmemb, SizeT size1)
159 {
160    return new_block(tid, nmemb*size1, VG_(clo_alignment),
161                           /*is_zeroed*/True);
162 }
163 
164 /** Wrapper for free(). */
drd_free(ThreadId tid,void * p)165 static void drd_free(ThreadId tid, void* p)
166 {
167    handle_free(tid, p);
168 }
169 
170 /**
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
176  *   allocated size.
177 */
drd_realloc(ThreadId tid,void * p_old,SizeT new_size)178 static void* drd_realloc(ThreadId tid, void* p_old, SizeT new_size)
179 {
180    DRD_Chunk* mc;
181    void*      p_new;
182    SizeT      old_size;
183 
184    if (! p_old)
185       return drd_malloc(tid, new_size);
186 
187    if (new_size == 0)
188    {
189       drd_free(tid, p_old);
190       return NULL;
191    }
192 
193    s_cmalloc_n_mallocs++;
194    s_cmalloc_n_frees++;
195    s_cmalloc_bs_mallocd += new_size;
196 
197    mc = VG_(HT_lookup)(s_malloc_list, (UWord)p_old);
198    if (mc == NULL)
199    {
200       tl_assert(0);
201       return NULL;
202    }
203 
204    old_size = mc->size;
205 
206    if (old_size == new_size)
207    {
208       /* size unchanged */
209       mc->where = VG_(record_ExeContext)(tid, 0);
210       p_new = p_old;
211    }
212    else if (new_size < old_size)
213    {
214       /* new size is smaller but nonzero */
215       s_stop_using_mem_callback(mc->data + new_size, old_size - new_size);
216       mc->size = new_size;
217       mc->where = VG_(record_ExeContext)(tid, 0);
218       p_new = p_old;
219    }
220    else
221    {
222       /* new size is bigger */
223       p_new = VG_(cli_malloc)(VG_(clo_alignment), new_size);
224 
225       if (p_new)
226       {
227          /* Copy from old to new. */
228          VG_(memcpy)(p_new, p_old, mc->size);
229 
230          /* Free old memory. */
231          if (mc->size > 0)
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);
235 
236          /* Update state information. */
237          mc->data  = (Addr)p_new;
238          mc->size  = new_size;
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*/);
242       }
243       else
244       {
245          /* Allocation failed -- leave original block untouched. */
246       }
247    }
248 
249    return p_new;
250 }
251 
252 /** Wrapper for __builtin_new(). */
drd___builtin_new(ThreadId tid,SizeT n)253 static void* drd___builtin_new(ThreadId tid, SizeT n)
254 {
255    return new_block(tid, n, VG_(clo_alignment), /*is_zeroed*/False);
256 }
257 
258 /** Wrapper for __builtin_delete(). */
drd___builtin_delete(ThreadId tid,void * p)259 static void drd___builtin_delete(ThreadId tid, void* p)
260 {
261    handle_free(tid, p);
262 }
263 
264 /** Wrapper for __builtin_vec_new(). */
drd___builtin_vec_new(ThreadId tid,SizeT n)265 static void* drd___builtin_vec_new(ThreadId tid, SizeT n)
266 {
267    return new_block(tid, n, VG_(clo_alignment), /*is_zeroed*/False);
268 }
269 
270 /** Wrapper for __builtin_vec_delete(). */
drd___builtin_vec_delete(ThreadId tid,void * p)271 static void drd___builtin_vec_delete(ThreadId tid, void* p)
272 {
273    handle_free(tid, p);
274 }
275 
276 /**
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.
281  */
drd_malloc_usable_size(ThreadId tid,void * p)282 static SizeT drd_malloc_usable_size(ThreadId tid, void* p)
283 {
284    DRD_Chunk* mc;
285 
286    mc = VG_(HT_lookup)(s_malloc_list, (UWord)p);
287 
288    return mc ? mc->size : 0;
289 }
290 
DRD_(register_malloc_wrappers)291 void DRD_(register_malloc_wrappers)(const StartUsingMem start_callback,
292                                     const StopUsingMem stop_callback)
293 {
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);
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