drd_malloc_wrappers.c revision bedfd237fbdc80d0c917cfcb85a94b5561c92633
1/* -*- mode: C; c-basic-offset: 3; -*- */
2/*
3  This file is part of drd, a thread error detector.
4
5  Copyright (C) 2006-2009 Bart Van Assche <bart.vanassche@gmail.com>.
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
43typedef struct _DRD_Chunk {
44   struct _DRD_Chunk* next;
45   Addr          data;            // ptr to actual block
46   SizeT         size : (sizeof(UWord)*8)-2; //size requested; 30 or 62 bits
47   ExeContext*   where;           // where it was allocated
48} DRD_Chunk;
49
50
51/* Local variables. */
52
53static StartUsingMem DRD_(s_start_using_mem_callback);
54static StopUsingMem  DRD_(s_stop_using_mem_callback);
55/* Stats ... */
56static SizeT DRD_(s_cmalloc_n_mallocs)  = 0;
57static SizeT DRD_(s_cmalloc_n_frees)    = 0;
58static SizeT DRD_(s_cmalloc_bs_mallocd) = 0;
59/* Record malloc'd blocks. */
60static VgHashTable DRD_(s_malloc_list) = NULL;
61
62
63/*------------------------------------------------------------*/
64/*--- Tracking malloc'd and free'd blocks                  ---*/
65/*------------------------------------------------------------*/
66
67/** Allocate its shadow chunk, put it on the appropriate list. */
68static DRD_Chunk* DRD_(create_chunk)(ThreadId tid, Addr p, SizeT size)
69{
70   DRD_Chunk* mc = VG_(malloc)("drd.malloc_wrappers.cDC.1",
71                               sizeof(DRD_Chunk));
72   mc->data      = p;
73   mc->size      = size;
74   mc->where     = VG_(record_ExeContext)(tid, 0);
75
76   return mc;
77}
78
79/*------------------------------------------------------------*/
80/*--- client_malloc(), etc                                 ---*/
81/*------------------------------------------------------------*/
82
83/* Allocate memory and note change in memory available */
84static
85__inline__
86void* DRD_(new_block)(ThreadId tid,
87                      SizeT size, SizeT align,
88                      Bool is_zeroed)
89{
90   Addr p;
91
92   DRD_(s_cmalloc_n_mallocs) ++;
93
94   // Allocate and zero
95   p = (Addr)VG_(cli_malloc)(align, size);
96   if (!p) {
97      return NULL;
98   }
99   if (is_zeroed) VG_(memset)((void*)p, 0, size);
100   DRD_(s_start_using_mem_callback)(p, p + size, 0/*ec_uniq*/);
101
102   // Only update this stat if allocation succeeded.
103   DRD_(s_cmalloc_bs_mallocd) += size;
104
105   VG_(HT_add_node)(DRD_(s_malloc_list), DRD_(create_chunk)(tid, p, size));
106
107   return (void*)p;
108}
109
110static void* DRD_(malloc)(ThreadId tid, SizeT n)
111{
112   return DRD_(new_block)(tid, n, VG_(clo_alignment), /*is_zeroed*/False);
113}
114
115static void* DRD_(memalign)(ThreadId tid, SizeT align, SizeT n)
116{
117   return DRD_(new_block)(tid, n, align, /*is_zeroed*/False);
118}
119
120static void* DRD_(calloc)(ThreadId tid, SizeT nmemb, SizeT size1)
121{
122   return DRD_(new_block)(tid, nmemb*size1, VG_(clo_alignment),
123                          /*is_zeroed*/True);
124}
125
126static __inline__ void DRD_(handle_free)(ThreadId tid, Addr p)
127{
128   DRD_Chunk* mc;
129
130   DRD_(s_cmalloc_n_frees)++;
131
132   mc = VG_(HT_remove)(DRD_(s_malloc_list), (UWord)p);
133   if (mc == NULL)
134   {
135      tl_assert(0);
136   }
137   else
138   {
139      tl_assert(p == mc->data);
140      if (mc->size > 0)
141         DRD_(s_stop_using_mem_callback)(mc->data, mc->size);
142      VG_(cli_free)((void*)p);
143      VG_(free)(mc);
144   }
145}
146
147static void DRD_(free)(ThreadId tid, void* p)
148{
149   DRD_(handle_free)(tid, (Addr)p);
150}
151
152static void* DRD_(realloc)(ThreadId tid, void* p_old, SizeT new_size)
153{
154   DRD_Chunk* mc;
155   void*     p_new;
156   SizeT     old_size;
157
158   DRD_(s_cmalloc_n_frees) ++;
159   DRD_(s_cmalloc_n_mallocs) ++;
160   DRD_(s_cmalloc_bs_mallocd) += new_size;
161
162   /* Remove the old block */
163   mc = VG_(HT_remove)(DRD_(s_malloc_list), (UWord)p_old);
164   if (mc == NULL) {
165      tl_assert(0);
166      return NULL;
167   }
168
169   old_size = mc->size;
170
171   if (old_size == new_size)
172   {
173      /* size unchanged */
174      mc->where = VG_(record_ExeContext)(tid, 0);
175      p_new = p_old;
176
177   }
178   else if (old_size > new_size)
179   {
180      /* new size is smaller */
181      DRD_(s_stop_using_mem_callback)(mc->data + new_size, old_size);
182      mc->size = new_size;
183      mc->where = VG_(record_ExeContext)(tid, 0);
184      p_new = p_old;
185
186   }
187   else
188   {
189      /* new size is bigger */
190      /* Get new memory */
191      const Addr a_new = (Addr)VG_(cli_malloc)(VG_(clo_alignment), new_size);
192
193      if (a_new)
194      {
195         /* Copy from old to new */
196         VG_(memcpy)((void*)a_new, p_old, mc->size);
197
198         /* Free old memory */
199         DRD_(s_stop_using_mem_callback)(mc->data, mc->size);
200         VG_(free)(mc);
201
202         // Allocate a new chunk.
203         mc = DRD_(create_chunk)(tid, a_new, new_size);
204         DRD_(s_start_using_mem_callback)(a_new, a_new + new_size,
205                                          0/*ec_uniq*/);
206      }
207      else
208      {
209         /* Allocation failed -- leave original block untouched. */
210      }
211
212      p_new = (void*)a_new;
213   }
214
215   // Now insert the new mc (with a possibly new 'data' field) into
216   // malloc_list.  If this realloc() did not increase the memory size, we
217   // will have removed and then re-added mc unnecessarily.  But that's ok
218   // because shrinking a block with realloc() is (presumably) much rarer
219   // than growing it, and this way simplifies the growing case.
220   VG_(HT_add_node)(DRD_(s_malloc_list), mc);
221
222   return p_new;
223}
224
225static void* DRD_(__builtin_new)(ThreadId tid, SizeT n)
226{
227   void* const result = DRD_(new_block)(tid, n, VG_(clo_alignment),
228                                        /*is_zeroed*/False);
229   //VG_(message)(Vg_DebugMsg, "__builtin_new(%d, %d) = %p", tid, n, result);
230   return result;
231}
232
233static void DRD_(__builtin_delete)(ThreadId tid, void* p)
234{
235   //VG_(message)(Vg_DebugMsg, "__builtin_delete(%d, %p)", tid, p);
236   DRD_(handle_free)(tid, (Addr)p);
237}
238
239static void* DRD_(__builtin_vec_new)(ThreadId tid, SizeT n)
240{
241   return DRD_(new_block)(tid, n, VG_(clo_alignment), /*is_zeroed*/False);
242}
243
244static void DRD_(__builtin_vec_delete)(ThreadId tid, void* p)
245{
246   DRD_(handle_free)(tid, (Addr)p);
247}
248
249static SizeT DRD_(malloc_usable_size) ( ThreadId tid, void* p )
250{
251   DRD_Chunk *mc = VG_(HT_lookup)( DRD_(s_malloc_list), (UWord)p );
252
253   // There may be slop, but pretend there isn't because only the asked-for
254   // area will have been shadowed properly.
255   return ( mc ? mc->size : 0 );
256}
257
258void DRD_(register_malloc_wrappers)(const StartUsingMem start_callback,
259                                    const StopUsingMem stop_callback)
260{
261   tl_assert(DRD_(s_malloc_list) == 0);
262   DRD_(s_malloc_list) = VG_(HT_construct)("drd_malloc_list");   // a big prime
263   tl_assert(DRD_(s_malloc_list) != 0);
264   tl_assert(start_callback);
265   tl_assert(stop_callback);
266
267   DRD_(s_start_using_mem_callback) = start_callback;
268   DRD_(s_stop_using_mem_callback)  = stop_callback;
269
270   VG_(needs_malloc_replacement)(DRD_(malloc),
271                                 DRD_(__builtin_new),
272                                 DRD_(__builtin_vec_new),
273                                 DRD_(memalign),
274                                 DRD_(calloc),
275                                 DRD_(free),
276                                 DRD_(__builtin_delete),
277                                 DRD_(__builtin_vec_delete),
278                                 DRD_(realloc),
279                                 DRD_(malloc_usable_size),
280                                 0);
281}
282
283Bool DRD_(heap_addrinfo)(Addr const a,
284                         Addr* const data,
285                         SizeT* const size,
286                         ExeContext** const where)
287{
288   DRD_Chunk* mc;
289
290   tl_assert(data);
291   tl_assert(size);
292   tl_assert(where);
293
294   VG_(HT_ResetIter)(DRD_(s_malloc_list));
295   while ((mc = VG_(HT_Next)(DRD_(s_malloc_list))))
296   {
297      if (mc->data <= a && a < mc->data + mc->size)
298      {
299         *data  = mc->data;
300         *size  = mc->size;
301         *where = mc->where;
302         return True;
303      }
304   }
305   return False;
306}
307
308/*------------------------------------------------------------*/
309/*--- Statistics printing                                  ---*/
310/*------------------------------------------------------------*/
311
312void DRD_(print_malloc_stats)(void)
313{
314   DRD_Chunk* mc;
315   SizeT     nblocks = 0;
316   SizeT     nbytes  = 0;
317
318   if (VG_(clo_verbosity) == 0)
319      return;
320   if (VG_(clo_xml))
321      return;
322
323   /* Count memory still in use. */
324   VG_(HT_ResetIter)(DRD_(s_malloc_list));
325   while ((mc = VG_(HT_Next)(DRD_(s_malloc_list))))
326   {
327      nblocks++;
328      nbytes += mc->size;
329   }
330
331   VG_(message)(Vg_DebugMsg,
332                "malloc/free: in use at exit: %lu bytes in %lu blocks.",
333                nbytes, nblocks);
334   VG_(message)(Vg_DebugMsg,
335                "malloc/free: %lu allocs, %lu frees, %lu bytes allocated.",
336                DRD_(s_cmalloc_n_mallocs),
337                DRD_(s_cmalloc_n_frees), DRD_(s_cmalloc_bs_mallocd));
338   if (VG_(clo_verbosity) > 1)
339      VG_(message)(Vg_DebugMsg, " ");
340}
341
342/*--------------------------------------------------------------------*/
343/*--- end                                                          ---*/
344/*--------------------------------------------------------------------*/
345