1#include <time.h>
2#include <stdlib.h>
3#include <stdint.h>
4#include <assert.h>
5#include <string.h>
6#include <stdio.h>
7
8#include "../memcheck.h"
9
10// Test VALGRIND_CREATE_MEMPOOL_EXT features, the VALGRIND_MEMPOOL_METAPOOL and
11// VALGRIND_MEMPOOL_AUTO_FREE flags.
12// Also show that without these, having a custom allocator that:
13// - Allocates a MEMPOOL
14// - Uses ITSELF to get large blocks to populate the pool (so these are marked
15//   as MALLOCLIKE blocks)
16// - Then passes out MALLOCLIKE blocks out of these pool blocks
17// Was not previously supported by the 'loose model' for mempools in memcheck
18// because it spotted these (correctly) as overlapping blocks (test case 3
19// below).
20// The VALGRIND_MEMPOOL_METAPOOL says not to treat these as overlaps.
21//
22// Also, when one of these metapool blocks is freed, memcheck will not auto-free
23// the MALLOCLIKE blocks allocated from the meta-pool, and report them as leaks.
24// When VALGRIND_MEMPOOL_AUTO_FREE is passed, no such leaks are reported.
25// This is for custom allocators that destroy a pool without freeing the objects
26// allocated from it, because that is the defined behaviour of the allocator.
27
28struct pool
29{
30  size_t allocated;
31  size_t used;
32  uint8_t *buf;
33};
34
35struct cell
36{
37  struct cell *next;
38  char x[16 - sizeof(void*)];
39};
40
41static struct pool _PlainPool, *PlainPool = &_PlainPool;
42static struct pool _MetaPool,  *MetaPool  = &_MetaPool;
43
44#define N 10
45#define POOL_BLOCK_SIZE   4096
46#define NOISE_SIZE        256
47
48// For easy testing, the plain mempool uses N allocations, the
49// metapool 2 * N (so 10 reported leaks are from the plain pool, 20 must be
50// from the metapool).
51
52static int    MetaPoolFlags = 0;
53static int    CleanupBeforeExit = 0;
54static int    GenerateNoise = 0;
55static int    NoiseCounter = 0;
56
57static struct cell *cells_plain[2 * N];
58static struct cell *cells_meta[2 * N];
59
60static unsigned char *noise[3 * N];
61
62static char   PlainBlock[POOL_BLOCK_SIZE];
63static char   MetaBlock[POOL_BLOCK_SIZE];
64
65void create_meta_pool (void)
66{
67   VALGRIND_CREATE_MEMPOOL_EXT(MetaPool, 0, 0, MetaPoolFlags);
68   VALGRIND_MEMPOOL_ALLOC(MetaPool, MetaBlock, POOL_BLOCK_SIZE);
69
70   MetaPool->buf = (uint8_t *) MetaBlock;
71   MetaPool->allocated = POOL_BLOCK_SIZE;
72   MetaPool->used = 0;
73
74   /* A pool-block is expected to have metadata, and the core of
75      valgrind sees a MALLOCLIKE_BLOCK that starts at the same address
76      as a MEMPOOLBLOCK as a MEMPOOLBLOCK, hence never as a leak.
77      Introduce such some simulated metadata.
78   */
79
80   MetaPool->buf  += sizeof(uint8_t);
81   MetaPool->used += sizeof(uint8_t);
82}
83
84static void create_plain_pool (void)
85{
86   VALGRIND_CREATE_MEMPOOL(PlainPool, 0, 0);
87
88   PlainPool->buf = (uint8_t *) PlainBlock;
89   PlainPool->allocated = POOL_BLOCK_SIZE;
90   PlainPool->used = 0;
91
92   /* Same overhead */
93   PlainPool->buf  += sizeof(uint8_t);
94   PlainPool->used += sizeof(uint8_t);
95}
96
97static void *allocate_meta_style (struct pool *p, size_t n)
98{
99  void *a = p->buf + p->used;
100  assert(p->used + n < p->allocated);
101
102  // Simulate a custom allocator that allocates memory either directly for
103  // the application or for a custom memory pool: All are marked as MALLOCLIKE.
104  VALGRIND_MALLOCLIKE_BLOCK(a, n, 0, 0);
105  p->used += n;
106
107  return a;
108}
109
110static void *allocate_plain_style (struct pool *p, size_t n)
111{
112  void *a = p->buf + p->used;
113  assert(p->used + n < p->allocated);
114
115  // And this is custom allocator that knows that it is allocating from a pool.
116  VALGRIND_MEMPOOL_ALLOC(p, a, n);
117  p->used += n;
118
119  return a;
120}
121
122/* flags */
123
124static void set_flags ( int n )
125{
126  switch (n) {
127     // Case 0: No special flags. VALGRIND_CREATE_MEMPOOL_EXT is same as
128     // VALGRIND_CREATE_MEMPOOL.
129     // When mempools are destroyed, the METAPOOL leaks because auto-free is
130     // missing. Must show 2*N (20) leaks.
131     // The VALGRIND_MEMPOOL_ALLOC items from the plain pool are automatically
132     // destroyed. CleanupBeforeExit means the metapool is freed and destroyed
133     // (simulating an app that cleans up before it exits), and when false it
134     // simply exits with the pool unaltered.
135     case 0:
136        MetaPoolFlags     = 0;
137        CleanupBeforeExit = 1;
138        break;
139
140     // Case 1: VALGRIND_MEMPOOL_METAPOOL, no auto-free.
141     // Without explicit free, these MALLOCLIKE_BLOCK blocks are considered
142     // leaks. So this case should show same as case 0: 20 leaks.
143     case 1:
144        MetaPoolFlags     = VALGRIND_MEMPOOL_METAPOOL;
145        CleanupBeforeExit = 1;
146        break;
147
148     // Same as before, but now the MALLOCLIKE blocks are auto-freed.
149     // Must show 0 leaks.
150     case 2:
151        MetaPoolFlags = VALGRIND_MEMPOOL_METAPOOL | VALGRIND_MEMPOOL_AUTO_FREE;
152        CleanupBeforeExit = 1;
153        break;
154
155     case 3: // Note: this is incorrect behaviour, and aborts valgrind.
156        // (so it is not exercised during regression testing).
157        // Just auto-free, not marked with meta pool flag.
158        // This is an error, and will cause valgrind to abort when the pool
159        // is created.
160        MetaPoolFlags     = VALGRIND_MEMPOOL_AUTO_FREE;
161        CleanupBeforeExit = 1;
162        break;
163
164     case 4:
165        // No auto-free, no cleanup. Leaves overlapping blocks detected
166        // by valgrind, but those are ignored because of the METAPOOL.
167        // So, no crash, no problems, but 20 leaks.
168        MetaPoolFlags     = VALGRIND_MEMPOOL_METAPOOL;
169        CleanupBeforeExit = 0;
170        break;
171
172     case 5:
173        // Main reason for the VALGRIND_MEMPOOL_METAPOOL flags: When not
174        // specified, and the application has a memorypool that has MALLOC_LIKE
175        // overlapping allocations, that leaves block(s) that overlap.
176        // Causes a fatal error.
177        // The METAPOOL allows the overlap. Test must show that without that
178        // flag, a fatal error occurs.
179        MetaPoolFlags     = 0;
180        CleanupBeforeExit = 0;
181        break;
182
183     case 6:
184        // Test the VG_(HT_remove_at_Iter)() function, which removes a chunk
185        // from a hashlist without the need to reset the iterator. The pool
186        // is auto_freed, and the best test for the function (besides the ones
187        // already done above) is by allocating lots of other chunks that are
188        // NOT part of the pool so the MC_Alloc lists contain other stuff.
189	// That will make the iterator find stuff AND skip stuff.
190        MetaPoolFlags     = VALGRIND_MEMPOOL_METAPOOL | VALGRIND_MEMPOOL_AUTO_FREE;
191        CleanupBeforeExit = 1;
192        GenerateNoise     = 1;
193        break;
194
195     default:
196        assert(0);
197  }
198}
199
200static void GenerateNoisyBit (void)
201{
202   // In case the HT_remove_at_Iter messes up the administration, the wrong
203   // blocks may be deleted from the list, making access to these noise-blocks
204   // invalid. So fill 256-byte blocks with easily tested contents.
205
206   noise[NoiseCounter] = malloc(NOISE_SIZE);
207   assert(noise[NoiseCounter] != NULL);
208   memset(noise[NoiseCounter],(unsigned char) (NoiseCounter % 256), NOISE_SIZE);
209   NoiseCounter++;
210}
211
212static void CheckNoiseContents (void)
213{
214   int i;
215
216   for (i = 0; i < NoiseCounter; i++) {
217      unsigned char Check = (unsigned char) ( i % 256);
218      int j;
219
220      for (j = 0; j < NOISE_SIZE; j++) {
221         assert(noise[i][j] == Check);
222      }
223   }
224}
225
226int main( int argc, char** argv )
227{
228   int arg;
229   size_t i;
230
231   assert(argc == 2 || argc == 3);
232   assert(argv[1]);
233   assert(strlen(argv[1]) == 1);
234   assert(argv[1][0] >= '0' && argv[1][0] <= '9');
235   arg = atoi( argv[1] );
236   set_flags( arg );
237
238   create_plain_pool();
239   create_meta_pool();
240
241   // N plain allocs
242   for (i = 0; i < N; ++i) {
243      cells_plain[i] = allocate_plain_style(PlainPool,sizeof(struct cell));
244
245      if (GenerateNoise)
246         GenerateNoisyBit();
247   }
248
249   // 2*N meta allocs
250   for (i = 0; i < 2 * N; ++i) {
251      cells_meta[i] = allocate_meta_style(MetaPool,sizeof(struct cell));
252
253      if (GenerateNoise)
254         GenerateNoisyBit();
255   }
256
257   // Leak the memory from the pools by losing the pointers.
258   for (i = 0; i < N; ++i) {
259      cells_plain[i] = NULL;
260   }
261
262   for (i = 0; i < 2 * N; ++i) {
263      cells_meta[i] = NULL;
264   }
265
266   if (GenerateNoise)
267      CheckNoiseContents();
268
269   // This must free MALLOCLIKE allocations from the pool when
270   // VALGRIND_MEMPOOL_AUTO_FREE
271   // is set for the pool and report leaks when not.
272
273   if (CleanupBeforeExit) {
274      VALGRIND_MEMPOOL_FREE(MetaPool, MetaBlock);
275
276      if (GenerateNoise)
277         CheckNoiseContents();
278
279       VALGRIND_DESTROY_MEMPOOL(MetaPool);
280
281      if (GenerateNoise)
282         CheckNoiseContents();
283
284   }
285
286   // Cleanup.
287   VALGRIND_DESTROY_MEMPOOL(PlainPool);
288
289   if (GenerateNoise)
290      CheckNoiseContents();
291
292   // Try to trigger an error in the bookkeeping by freeing the noise bits.
293   // Valgrind should report no leaks, and zero memory in use. If the
294   // new HT_remove_at_Iter function would corrupt the bookkeeping in any
295   // way, this should bring it out!
296   if (GenerateNoise) {
297      for (i = 0; i < NoiseCounter; i++)
298         free(noise[i]);
299   }
300
301
302  // Perf test
303   if (argc == 3) {
304      struct pool perf_plain_pool;
305      void *perf_plain_block;
306      struct pool perf_meta_pool;
307      void *perf_meta_block;
308      size_t pool_block_size;
309      int n;
310      int nr_elts = atoi( argv[2] );
311      time_t dnow;
312#define tprintf(...) (dnow = time(NULL),          \
313                      printf(__VA_ARGS__),        \
314                      printf(" %s", ctime(&dnow)))
315
316      pool_block_size = nr_elts * sizeof(struct cell) + sizeof(uint8_t) + 1;
317
318      // Create perf meta pool
319      VALGRIND_CREATE_MEMPOOL_EXT
320         (&perf_meta_pool, 0, 0,
321          VALGRIND_MEMPOOL_METAPOOL | VALGRIND_MEMPOOL_AUTO_FREE);
322      perf_meta_block = malloc(pool_block_size);
323
324      VALGRIND_MEMPOOL_ALLOC(&perf_meta_pool, perf_meta_block,
325                             pool_block_size);
326
327      perf_meta_pool.buf = (uint8_t *) perf_meta_block;
328      perf_meta_pool.allocated = pool_block_size;
329      perf_meta_pool.used = 0;
330
331
332      perf_meta_pool.buf  += sizeof(uint8_t);
333      perf_meta_pool.used += sizeof(uint8_t);
334
335      // Create perf plain pool
336      VALGRIND_CREATE_MEMPOOL(&perf_plain_pool, 0, 0);
337      perf_plain_block = malloc(pool_block_size);
338
339      perf_plain_pool.buf = (uint8_t *) perf_plain_block;
340      perf_plain_pool.allocated = pool_block_size;;
341      perf_plain_pool.used = 0;
342
343      perf_plain_pool.buf  += sizeof(uint8_t);
344      perf_plain_pool.used += sizeof(uint8_t);
345
346      tprintf("allocating %d elts", nr_elts);
347      for (n = 0; n < nr_elts; n++) {
348         (void) allocate_meta_style (&perf_meta_pool, sizeof(struct cell));
349         (void) allocate_plain_style (&perf_plain_pool, sizeof(struct cell));
350      }
351
352      tprintf("freeing mempool");
353      VALGRIND_MEMPOOL_FREE(&perf_meta_pool, perf_meta_block);
354      tprintf("destroying mempool");
355      VALGRIND_DESTROY_MEMPOOL(&perf_meta_pool);
356      tprintf("done");
357
358   }
359   return 0;
360}
361