1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
|
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "../memcheck.h"
/*
Incompetent memory management
Simulate what happens when trying to manage a dense brancy cyclic
structure with refcounting.
*/
static int bytes, blocks;
struct node {
struct node *l, *r;
int ref;
};
static void ref(struct node *n)
{
if (n)
n->ref++;
}
static void unref(struct node *n)
{
if (n && --n->ref == 0) {
bytes -= sizeof(*n);
blocks--;
free(n);
}
}
static struct node *alloc()
{
struct node *n = malloc(sizeof(*n));
n->l = n->r = NULL;
n->ref = 0;
bytes += sizeof(*n);
blocks++;
return n;
}
static void assign(struct node **np, struct node *n)
{
ref(n);
unref(*np);
*np = n;
}
static int rrange(int range)
{
long long ret = rand() * (long long)range;
return (int)(ret / RAND_MAX);
}
#define ITER 100000
#define N 1000
static struct node *nodes[N];
static struct node *mk()
{
int idx = rrange(N);
struct node *n;
/*
50% recycle
25% alloc new
25% return NULL
*/
if (rand() > RAND_MAX/2) {
/* recycle existing block */
n = nodes[idx];
} else if (rand() > RAND_MAX/2) {
/* alloc new block */
n = alloc();
assign(&n->l, mk());
assign(&n->r, mk());
} else {
/* no block */
n = NULL;
}
assign(&nodes[idx], n);
return n;
}
int main()
{
int i;
long base_definite, base_dubious, base_reachable, base_suppressed;
long definite, dubious, reachable, suppressed;
int total;
/* we require these longs to have same size as a machine word */
assert(sizeof(long) == sizeof(void*));
/* get a baseline in case the runtime allocated some memory */
VALGRIND_DO_LEAK_CHECK;
base_definite = base_dubious = base_reachable = base_suppressed = 0;
VALGRIND_COUNT_LEAKS(base_definite, base_dubious,
base_reachable, base_suppressed);
for(i = 0; i < ITER; i++) {
mk();
if ((i % (ITER/10)) == 0) {
if (0)
printf("%d living blocks, %d bytes\n",
blocks, bytes);
VALGRIND_DO_LEAK_CHECK;
}
}
/* "free all memory" */
for(i = 0; i < N; i++)
assign(&nodes[i], NULL);
if (0)
printf("FINISHED: %d living blocks, %d bytes\n",
blocks, bytes);
VALGRIND_DO_LEAK_CHECK;
/* Shouldn't be necessary, but COUNT_LEAKS doesn't define its
result values */
definite = dubious = reachable = suppressed = 0;
VALGRIND_COUNT_LEAKS(definite, dubious, reachable, suppressed);
definite -= base_definite;
dubious -= base_dubious;
reachable -= base_reachable;
suppressed -= base_suppressed;
total = definite+dubious+reachable+suppressed;
if (0)
printf("leaks: definite %d, dubious %d, reachable %d, suppressed %d = %d\n",
(int)definite, (int)dubious, (int)reachable, (int)suppressed, total);
if (reachable != 0)
printf("FAILED: I freed everything, "
"but there's still %d bytes reachable\n",
(int)reachable);
else if (total != bytes)
printf("FAILED: I count %d bytes, leakcheck says %d\n",
bytes, total);
else
printf("PASS\n");
return 0;
}
|