Home | History | Annotate | Download | only in tests
      1 /* Copyright (c) 2006, Google Inc.
      2  * All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 // A test for low_level_alloc.cc
     32 
     33 #include <stdio.h>
     34 #include <map>
     35 #include "base/low_level_alloc.h"
     36 #include "base/logging.h"
     37 #include <gperftools/malloc_hook.h>
     38 
     39 using std::map;
     40 
     41 // a block of memory obtained from the allocator
     42 struct BlockDesc {
     43   char *ptr;      // pointer to memory
     44   int len;        // number of bytes
     45   int fill;       // filled with data starting with this
     46 };
     47 
     48 // Check that the pattern placed in the block d
     49 // by RandomizeBlockDesc is still there.
     50 static void CheckBlockDesc(const BlockDesc &d) {
     51   for (int i = 0; i != d.len; i++) {
     52     CHECK((d.ptr[i] & 0xff) == ((d.fill + i) & 0xff));
     53   }
     54 }
     55 
     56 // Fill the block "*d" with a pattern
     57 // starting with a random byte.
     58 static void RandomizeBlockDesc(BlockDesc *d) {
     59   d->fill = rand() & 0xff;
     60   for (int i = 0; i != d->len; i++) {
     61     d->ptr[i] = (d->fill + i) & 0xff;
     62   }
     63 }
     64 
     65 // Use to indicate to the malloc hooks that
     66 // this calls is from LowLevelAlloc.
     67 static bool using_low_level_alloc = false;
     68 
     69 // n times, toss a coin, and based on the outcome
     70 // either allocate a new block or deallocate an old block.
     71 // New blocks are placed in a map with a random key
     72 // and initialized with RandomizeBlockDesc().
     73 // If keys conflict, the older block is freed.
     74 // Old blocks are always checked with CheckBlockDesc()
     75 // before being freed.  At the end of the run,
     76 // all remaining allocated blocks are freed.
     77 // If use_new_arena is true, use a fresh arena, and then delete it.
     78 // If call_malloc_hook is true and user_arena is true,
     79 // allocations and deallocations are reported via the MallocHook
     80 // interface.
     81 static void Test(bool use_new_arena, bool call_malloc_hook, int n) {
     82   typedef map<int, BlockDesc> AllocMap;
     83   AllocMap allocated;
     84   AllocMap::iterator it;
     85   BlockDesc block_desc;
     86   int rnd;
     87   LowLevelAlloc::Arena *arena = 0;
     88   if (use_new_arena) {
     89     int32 flags = call_malloc_hook?  LowLevelAlloc::kCallMallocHook :  0;
     90     arena = LowLevelAlloc::NewArena(flags, LowLevelAlloc::DefaultArena());
     91   }
     92   for (int i = 0; i != n; i++) {
     93     if (i != 0 && i % 10000 == 0) {
     94       printf(".");
     95       fflush(stdout);
     96     }
     97 
     98     switch(rand() & 1) {      // toss a coin
     99     case 0:     // coin came up heads: add a block
    100       using_low_level_alloc = true;
    101       block_desc.len = rand() & 0x3fff;
    102       block_desc.ptr =
    103         reinterpret_cast<char *>(
    104                         arena == 0
    105                         ? LowLevelAlloc::Alloc(block_desc.len)
    106                         : LowLevelAlloc::AllocWithArena(block_desc.len, arena));
    107       using_low_level_alloc = false;
    108       RandomizeBlockDesc(&block_desc);
    109       rnd = rand();
    110       it = allocated.find(rnd);
    111       if (it != allocated.end()) {
    112         CheckBlockDesc(it->second);
    113         using_low_level_alloc = true;
    114         LowLevelAlloc::Free(it->second.ptr);
    115         using_low_level_alloc = false;
    116         it->second = block_desc;
    117       } else {
    118         allocated[rnd] = block_desc;
    119       }
    120       break;
    121     case 1:     // coin came up tails: remove a block
    122       it = allocated.begin();
    123       if (it != allocated.end()) {
    124         CheckBlockDesc(it->second);
    125         using_low_level_alloc = true;
    126         LowLevelAlloc::Free(it->second.ptr);
    127         using_low_level_alloc = false;
    128         allocated.erase(it);
    129       }
    130       break;
    131     }
    132   }
    133   // remove all remaniing blocks
    134   while ((it = allocated.begin()) != allocated.end()) {
    135     CheckBlockDesc(it->second);
    136     using_low_level_alloc = true;
    137     LowLevelAlloc::Free(it->second.ptr);
    138     using_low_level_alloc = false;
    139     allocated.erase(it);
    140   }
    141   if (use_new_arena) {
    142     CHECK(LowLevelAlloc::DeleteArena(arena));
    143   }
    144 }
    145 
    146 // used for counting allocates and frees
    147 static int32 allocates;
    148 static int32 frees;
    149 
    150 // called on each alloc if kCallMallocHook specified
    151 static void AllocHook(const void *p, size_t size) {
    152   if (using_low_level_alloc) {
    153     allocates++;
    154   }
    155 }
    156 
    157 // called on each free if kCallMallocHook specified
    158 static void FreeHook(const void *p) {
    159   if (using_low_level_alloc) {
    160     frees++;
    161   }
    162 }
    163 
    164 int main(int argc, char *argv[]) {
    165   // This is needed by maybe_threads_unittest.sh, which parses argv[0]
    166   // to figure out what directory low_level_alloc_unittest is in.
    167   if (argc != 1) {
    168     fprintf(stderr, "USAGE: %s\n", argv[0]);
    169     return 1;
    170   }
    171 
    172   CHECK(MallocHook::AddNewHook(&AllocHook));
    173   CHECK(MallocHook::AddDeleteHook(&FreeHook));
    174   CHECK_EQ(allocates, 0);
    175   CHECK_EQ(frees, 0);
    176   Test(false, false, 50000);
    177   CHECK_NE(allocates, 0);   // default arena calls hooks
    178   CHECK_NE(frees, 0);
    179   for (int i = 0; i != 16; i++) {
    180     bool call_hooks = ((i & 1) == 1);
    181     allocates = 0;
    182     frees = 0;
    183     Test(true, call_hooks, 15000);
    184     if (call_hooks) {
    185       CHECK_GT(allocates, 5000); // arena calls hooks
    186       CHECK_GT(frees, 5000);
    187     } else {
    188       CHECK_EQ(allocates, 0);    // arena doesn't call hooks
    189       CHECK_EQ(frees, 0);
    190     }
    191   }
    192   printf("\nPASS\n");
    193   CHECK(MallocHook::RemoveNewHook(&AllocHook));
    194   CHECK(MallocHook::RemoveDeleteHook(&FreeHook));
    195   return 0;
    196 }
    197