// Performance test for the leak checker from bug #191182.
// Nb: it must be run with --leak-resolution=high to show the quadratic
// behaviour caused by the large number of loss records.
// By Philippe Waroquiers.
//
// On my machine, before being fixed, building the loss record list took about
// 36 seconds, and sorting + printing it took about 20 seconds. After being
// fixed it took about 2 seconds, and the leak checking was only a small
// fraction of that. --njn
#include <stdlib.h>
#include <strings.h>
#include <stdio.h>
#include <math.h>
/* parameters */
/* we will create stack_fan_out ^ stack_depth different call stacks */
int stack_fan_out = 15;
int stack_depth = 4;
/* for each call stack, allocate malloc_fan blocks */
int malloc_fan = 4;
/* for each call stack, allocate data structures having malloc_depth
indirection at each malloc-ed level */
int malloc_depth = 2;
/* in addition to the pointer needed to maintain the levels; some more
bytes are allocated simulating the data stored in the data structure */
int malloc_data = 5;
/* every n top blocks, 1 block and all its children will be freed instead of
being kept */
int free_every_n = 2;
/* every n release block operation, 1 block and its children will be leaked */
int leak_every_n = 250;
struct Chunk {
struct Chunk* child;
char s[];
};
struct Chunk** topblocks;
int freetop = 0;
/* statistics */
long total_malloced = 0;
int blocknr = 0;
int blockfreed = 0;
int blockleaked = 0;
int total_stacks = 0;
int releaseop = 0;
void free_chunks (struct Chunk ** mem)
{
if (*mem == 0)
return;
free_chunks ((&(*mem)->child));
blockfreed++;
free (*mem);
*mem = 0;
}
void release (struct Chunk ** mem)
{
releaseop++;
if (releaseop % leak_every_n == 0) {
blockleaked++;
*mem = 0; // lose the pointer without free-ing the blocks
} else {
free_chunks (mem);
}
}
void call_stack (int level)
{
int call_fan_out = 1;
if (level == stack_depth) {
int sz = sizeof(struct Chunk*) + malloc_data;
int d;
int f;
for (f = 0; f < malloc_fan; f++) {
struct Chunk *new = NULL; // shut gcc up
struct Chunk *prev = NULL;
for (d = 0; d < malloc_depth; d++) {
new = malloc (sz);
total_malloced += sz;
blocknr++;
new->child = prev;
prev = new;
}
topblocks[freetop] = new;
if (freetop % free_every_n == 0) {
release (&topblocks[freetop]);
}
freetop++;
}
total_stacks++;
} else {
/* Nb: don't common these up into a loop! We need different code
locations to exercise the problem. */
call_stack (level + 1);
if (call_fan_out == stack_fan_out) return;
call_fan_out++;
call_stack (level + 1);
if (call_fan_out == stack_fan_out) return;
call_fan_out++;
call_stack (level + 1);
if (call_fan_out == stack_fan_out) return;
call_fan_out++;
call_stack (level + 1);
if (call_fan_out == stack_fan_out) return;
call_fan_out++;
call_stack (level + 1);
if (call_fan_out == stack_fan_out) return;
call_fan_out++;
call_stack (level + 1);
if (call_fan_out == stack_fan_out) return;
call_fan_out++;
call_stack (level + 1);
if (call_fan_out == stack_fan_out) return;
call_fan_out++;
call_stack (level + 1);
if (call_fan_out == stack_fan_out) return;
call_fan_out++;
call_stack (level + 1);
if (call_fan_out == stack_fan_out) return;
call_fan_out++;
call_stack (level + 1);
if (call_fan_out == stack_fan_out) return;
call_fan_out++;
call_stack (level + 1);
if (call_fan_out == stack_fan_out) return;
call_fan_out++;
call_stack (level + 1);
if (call_fan_out == stack_fan_out) return;
call_fan_out++;
call_stack (level + 1);
if (call_fan_out == stack_fan_out) return;
call_fan_out++;
call_stack (level + 1);
if (call_fan_out == stack_fan_out) return;
call_fan_out++;
call_stack (level + 1);
if (call_fan_out == stack_fan_out) return;
call_fan_out++;
call_stack (level + 1);
if (call_fan_out == stack_fan_out) return;
call_fan_out++;
call_stack (level + 1);
if (call_fan_out == stack_fan_out) return;
call_fan_out++;
call_stack (level + 1);
if (call_fan_out == stack_fan_out) return;
call_fan_out++;
call_stack (level + 1);
if (call_fan_out == stack_fan_out) return;
call_fan_out++;
call_stack (level + 1);
if (call_fan_out == stack_fan_out) return;
call_fan_out++;
printf ("maximum stack_fan_out exceeded\n");
}
}
int main()
{
int d;
int stacks = 1;
for (d = 0; d < stack_depth; d++)
stacks *= stack_fan_out;
printf ("will generate %d different stacks\n", stacks);
topblocks = malloc(sizeof(struct Chunk*) * stacks * malloc_fan);
call_stack (0);
printf ("total stacks %d\n", total_stacks);
printf ("total bytes malloc-ed: %ld\n", total_malloced);
printf ("total blocks malloc-ed: %d\n", blocknr);
printf ("total blocks free-ed: %d\n", blockfreed);
printf ("total blocks leak-ed: %d\n", blockleaked);
return 0;
}