• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
28 struct pool
29 {
30   size_t allocated;
31   size_t used;
32   uint8_t *buf;
33 };
34 
35 struct cell
36 {
37   struct cell *next;
38   char x[16 - sizeof(void*)];
39 };
40 
41 static struct pool _PlainPool, *PlainPool = &_PlainPool;
42 static 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 
52 static int    MetaPoolFlags = 0;
53 static int    CleanupBeforeExit = 0;
54 static int    GenerateNoise = 0;
55 static int    NoiseCounter = 0;
56 
57 static struct cell *cells_plain[2 * N];
58 static struct cell *cells_meta[2 * N];
59 
60 static unsigned char *noise[3 * N];
61 
62 static char   PlainBlock[POOL_BLOCK_SIZE];
63 static char   MetaBlock[POOL_BLOCK_SIZE];
64 
create_meta_pool(void)65 void 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 
create_plain_pool(void)84 static 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 
allocate_meta_style(struct pool * p,size_t n)97 static 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 
allocate_plain_style(struct pool * p,size_t n)110 static 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 
set_flags(int n)124 static 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 
GenerateNoisyBit(void)200 static 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 
CheckNoiseContents(void)212 static 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 
main(int argc,char ** argv)226 int 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