Home | History | Annotate | Download | only in drd
      1 /*
      2   This file is part of drd, a thread error detector.
      3 
      4   Copyright (C) 2006-2017 Bart Van Assche <bvanassche (at) 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. */
     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  */
     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 
    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  */
    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(). */
    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(). */
    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(). */
    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(). */
    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 */
    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(). */
    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(). */
    259 static void drd___builtin_delete(ThreadId tid, void* p)
    260 {
    261    handle_free(tid, p);
    262 }
    263 
    264 /** Wrapper for __builtin_vec_new(). */
    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(). */
    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  */
    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 
    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 
    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 
    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