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
39using std::map;
40
41// a block of memory obtained from the allocator
42struct 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.
50static 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.
58static 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.
67static 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.
81static 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
147static int32 allocates;
148static int32 frees;
149
150// called on each alloc if kCallMallocHook specified
151static 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
158static void FreeHook(const void *p) {
159  if (using_low_level_alloc) {
160    frees++;
161  }
162}
163
164int 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