| // 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; |
| } |