Home | History | Annotate | Download | only in drd
      1 /* -*- mode: C; c-basic-offset: 3; indent-tabs-mode: nil; -*- */
      2 /*
      3   This file is part of drd, a thread error detector.
      4 
      5   Copyright (C) 2006-2011 Bart Van Assche <bvanassche (at) 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. */
     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  */
     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 
    110 static void handle_free(ThreadId tid, void* p)
    111 {
    112    Bool success;
    113 
    114    tl_assert(p);
    115    success = DRD_(freelike_block)(tid, (Addr)p, True);
    116    tl_assert(success);
    117 }
    118 
    119 /**
    120  * Remove the information that was stored by DRD_(malloclike_block)() about
    121  * a memory block.
    122  */
    123 Bool DRD_(freelike_block)(const ThreadId tid, const Addr p, const Bool dealloc)
    124 {
    125    DRD_Chunk* mc;
    126 
    127    tl_assert(p);
    128 
    129    s_cmalloc_n_frees++;
    130 
    131    mc = VG_(HT_lookup)(s_malloc_list, (UWord)p);
    132    if (mc)
    133    {
    134       tl_assert(p == mc->data);
    135       if (dealloc)
    136 	 VG_(cli_free)((void*)p);
    137       if (mc->size > 0)
    138          s_stop_using_mem_callback(mc->data, mc->size);
    139       VG_(HT_remove)(s_malloc_list, (UWord)p);
    140       VG_(free)(mc);
    141       return True;
    142    }
    143    return False;
    144 }
    145 
    146 /** Wrapper for malloc(). */
    147 static void* drd_malloc(ThreadId tid, SizeT n)
    148 {
    149    return new_block(tid, n, VG_(clo_alignment), /*is_zeroed*/False);
    150 }
    151 
    152 /** Wrapper for memalign(). */
    153 static void* drd_memalign(ThreadId tid, SizeT align, SizeT n)
    154 {
    155    return new_block(tid, n, align, /*is_zeroed*/False);
    156 }
    157 
    158 /** Wrapper for calloc(). */
    159 static void* drd_calloc(ThreadId tid, SizeT nmemb, SizeT size1)
    160 {
    161    return new_block(tid, nmemb*size1, VG_(clo_alignment),
    162                           /*is_zeroed*/True);
    163 }
    164 
    165 /** Wrapper for free(). */
    166 static void drd_free(ThreadId tid, void* p)
    167 {
    168    handle_free(tid, p);
    169 }
    170 
    171 /**
    172  * Wrapper for realloc(). Returns a pointer to the new block of memory, or
    173  * NULL if no new block could not be allocated. Notes:
    174  * - realloc(NULL, size) has the same effect as malloc(size).
    175  * - realloc(p, 0) has the same effect as free(p).
    176  * - success is not guaranteed even if the requested size is smaller than the
    177  *   allocated size.
    178 */
    179 static void* drd_realloc(ThreadId tid, void* p_old, SizeT new_size)
    180 {
    181    DRD_Chunk* mc;
    182    void*      p_new;
    183    SizeT      old_size;
    184 
    185    if (! p_old)
    186       return drd_malloc(tid, new_size);
    187 
    188    if (new_size == 0)
    189    {
    190       drd_free(tid, p_old);
    191       return NULL;
    192    }
    193 
    194    s_cmalloc_n_mallocs++;
    195    s_cmalloc_n_frees++;
    196    s_cmalloc_bs_mallocd += new_size;
    197 
    198    mc = VG_(HT_lookup)(s_malloc_list, (UWord)p_old);
    199    if (mc == NULL)
    200    {
    201       tl_assert(0);
    202       return NULL;
    203    }
    204 
    205    old_size = mc->size;
    206 
    207    if (old_size == new_size)
    208    {
    209       /* size unchanged */
    210       mc->where = VG_(record_ExeContext)(tid, 0);
    211       p_new = p_old;
    212    }
    213    else if (new_size < old_size)
    214    {
    215       /* new size is smaller but nonzero */
    216       s_stop_using_mem_callback(mc->data + new_size, old_size - new_size);
    217       mc->size = new_size;
    218       mc->where = VG_(record_ExeContext)(tid, 0);
    219       p_new = p_old;
    220    }
    221    else
    222    {
    223       /* new size is bigger */
    224       p_new = VG_(cli_malloc)(VG_(clo_alignment), new_size);
    225 
    226       if (p_new)
    227       {
    228          /* Copy from old to new. */
    229          VG_(memcpy)(p_new, p_old, mc->size);
    230 
    231          /* Free old memory. */
    232          VG_(cli_free)(p_old);
    233          if (mc->size > 0)
    234             s_stop_using_mem_callback(mc->data, mc->size);
    235          VG_(HT_remove)(s_malloc_list, (UWord)p_old);
    236 
    237          /* Update state information. */
    238          mc->data  = (Addr)p_new;
    239          mc->size  = new_size;
    240          mc->where = VG_(record_ExeContext)(tid, 0);
    241          VG_(HT_add_node)(s_malloc_list, mc);
    242          s_start_using_mem_callback((Addr)p_new, new_size, 0/*ec_uniq*/);
    243       }
    244       else
    245       {
    246          /* Allocation failed -- leave original block untouched. */
    247       }
    248    }
    249 
    250    return p_new;
    251 }
    252 
    253 /** Wrapper for __builtin_new(). */
    254 static void* drd___builtin_new(ThreadId tid, SizeT n)
    255 {
    256    return new_block(tid, n, VG_(clo_alignment), /*is_zeroed*/False);
    257 }
    258 
    259 /** Wrapper for __builtin_delete(). */
    260 static void drd___builtin_delete(ThreadId tid, void* p)
    261 {
    262    handle_free(tid, p);
    263 }
    264 
    265 /** Wrapper for __builtin_vec_new(). */
    266 static void* drd___builtin_vec_new(ThreadId tid, SizeT n)
    267 {
    268    return new_block(tid, n, VG_(clo_alignment), /*is_zeroed*/False);
    269 }
    270 
    271 /** Wrapper for __builtin_vec_delete(). */
    272 static void drd___builtin_vec_delete(ThreadId tid, void* p)
    273 {
    274    handle_free(tid, p);
    275 }
    276 
    277 /**
    278  * Wrapper for malloc_usable_size() / malloc_size(). This function takes
    279  * a pointer to a block allocated by `malloc' and returns the amount of space
    280  * that is available in the block. This may or may not be more than the size
    281  * requested from `malloc', due to alignment or minimum size constraints.
    282  */
    283 static SizeT drd_malloc_usable_size(ThreadId tid, void* p)
    284 {
    285    DRD_Chunk* mc;
    286 
    287    mc = VG_(HT_lookup)(s_malloc_list, (UWord)p);
    288 
    289    return mc ? mc->size : 0;
    290 }
    291 
    292 void DRD_(register_malloc_wrappers)(const StartUsingMem start_callback,
    293                                     const StopUsingMem stop_callback)
    294 {
    295    tl_assert(s_malloc_list == 0);
    296    s_malloc_list = VG_(HT_construct)("drd_malloc_list");
    297    tl_assert(s_malloc_list);
    298    tl_assert(start_callback);
    299    tl_assert(stop_callback);
    300 
    301    s_start_using_mem_callback = start_callback;
    302    s_stop_using_mem_callback  = stop_callback;
    303 
    304    VG_(needs_malloc_replacement)(drd_malloc,
    305                                  drd___builtin_new,
    306                                  drd___builtin_vec_new,
    307                                  drd_memalign,
    308                                  drd_calloc,
    309                                  drd_free,
    310                                  drd___builtin_delete,
    311                                  drd___builtin_vec_delete,
    312                                  drd_realloc,
    313                                  drd_malloc_usable_size,
    314                                  0);
    315 }
    316 
    317 Bool DRD_(heap_addrinfo)(Addr const a,
    318                          Addr* const data,
    319                          SizeT* const size,
    320                          ExeContext** const where)
    321 {
    322    DRD_Chunk* mc;
    323 
    324    tl_assert(data);
    325    tl_assert(size);
    326    tl_assert(where);
    327 
    328    VG_(HT_ResetIter)(s_malloc_list);
    329    while ((mc = VG_(HT_Next)(s_malloc_list)))
    330    {
    331       if (mc->data <= a && a < mc->data + mc->size)
    332       {
    333          *data  = mc->data;
    334          *size  = mc->size;
    335          *where = mc->where;
    336          return True;
    337       }
    338    }
    339    return False;
    340 }
    341 
    342 /*------------------------------------------------------------*/
    343 /*--- Statistics printing                                  ---*/
    344 /*------------------------------------------------------------*/
    345 
    346 void DRD_(print_malloc_stats)(void)
    347 {
    348    DRD_Chunk* mc;
    349    SizeT     nblocks = 0;
    350    SizeT     nbytes  = 0;
    351 
    352    if (VG_(clo_verbosity) == 0)
    353       return;
    354    if (VG_(clo_xml))
    355       return;
    356 
    357    /* Count memory still in use. */
    358    VG_(HT_ResetIter)(s_malloc_list);
    359    while ((mc = VG_(HT_Next)(s_malloc_list)))
    360    {
    361       nblocks++;
    362       nbytes += mc->size;
    363    }
    364 
    365    VG_(message)(Vg_DebugMsg,
    366                 "malloc/free: in use at exit: %lu bytes in %lu blocks.\n",
    367                 nbytes, nblocks);
    368    VG_(message)(Vg_DebugMsg,
    369                 "malloc/free: %lu allocs, %lu frees, %lu bytes allocated.\n",
    370                 s_cmalloc_n_mallocs,
    371                 s_cmalloc_n_frees, s_cmalloc_bs_mallocd);
    372    if (VG_(clo_verbosity) > 1)
    373       VG_(message)(Vg_DebugMsg, " \n");
    374 }
    375 
    376 /*--------------------------------------------------------------------*/
    377 /*--- end                                                          ---*/
    378 /*--------------------------------------------------------------------*/
    379