#include "defs.h"
#include "gdb_obstack.h"
#include "bcache.h"
#include "gdb_string.h"
#include <stddef.h>
#include <stdlib.h>
struct bstring
{
struct bstring *next;
size_t length;
union
{
char data[1];
double dummy;
}
d;
};
struct bcache
{
struct obstack cache;
void *pool;
unsigned int num_buckets;
struct bstring **bucket;
unsigned long unique_count;
long total_count;
long unique_size;
long total_size;
long structure_size;
};
unsigned long
hash(const void *addr, int length)
{
const unsigned char *k, *e;
unsigned long h;
k = (const unsigned char *)addr;
e = k+length;
for (h=0; k< e;++k)
{
h *=16777619;
h ^= *k;
}
return (h);
}
#define CHAIN_LENGTH_THRESHOLD (5)
static void
expand_hash_table (struct bcache *bcache)
{
static unsigned long sizes[] = {
1021, 2053, 4099, 8191, 16381, 32771,
65537, 131071, 262144, 524287, 1048573, 2097143,
4194301, 8388617, 16777213, 33554467, 67108859, 134217757,
268435459, 536870923, 1073741827, 2147483659UL
};
unsigned int new_num_buckets;
struct bstring **new_buckets;
unsigned int i;
new_num_buckets = bcache->num_buckets * 2;
for (i = 0; i < (sizeof (sizes) / sizeof (sizes[0])); i++)
if (sizes[i] > bcache->num_buckets)
{
new_num_buckets = sizes[i];
break;
}
{
size_t new_size = new_num_buckets * sizeof (new_buckets[0]);
new_buckets = (struct bstring **) xmmalloc (bcache->pool, new_size);
memset (new_buckets, 0, new_size);
bcache->structure_size -= (bcache->num_buckets
* sizeof (bcache->bucket[0]));
bcache->structure_size += new_size;
}
for (i = 0; i < bcache->num_buckets; i++)
{
struct bstring *s, *next;
for (s = bcache->bucket[i]; s; s = next)
{
struct bstring **new_bucket;
next = s->next;
new_bucket = &new_buckets[(hash (&s->d.data, s->length)
% new_num_buckets)];
s->next = *new_bucket;
*new_bucket = s;
}
}
if (bcache->bucket)
xmfree (bcache->pool, bcache->bucket);
bcache->bucket = new_buckets;
bcache->num_buckets = new_num_buckets;
}
#define BSTRING_SIZE(n) (offsetof (struct bstring, d.data) + (n))
void *
bcache (const void *addr, int length, struct bcache *bcache)
{
int hash_index;
struct bstring *s;
if (bcache->unique_count >= bcache->num_buckets * CHAIN_LENGTH_THRESHOLD)
expand_hash_table (bcache);
bcache->total_count++;
bcache->total_size += length;
hash_index = hash (addr, length) % bcache->num_buckets;
for (s = bcache->bucket[hash_index]; s; s = s->next)
if (s->length == length
&& ! memcmp (&s->d.data, addr, length))
return &s->d.data;
{
struct bstring *new
= obstack_alloc (&bcache->cache, BSTRING_SIZE (length));
memcpy (&new->d.data, addr, length);
new->length = length;
new->next = bcache->bucket[hash_index];
bcache->bucket[hash_index] = new;
bcache->unique_count++;
bcache->unique_size += length;
bcache->structure_size += BSTRING_SIZE (length);
return &new->d.data;
}
}
void
bcache_specify_allocation_with_arg
(struct bcache *b, void * (* alloc) (void *, long),
void (* free) (void *, void *), void *arg)
{
obstack_specify_allocation_with_arg (&b->cache, 0, 0, alloc, free, arg);
}
void
bcache_specify_allocation
(struct bcache *b, void * (* alloc) (void *, long),
void (* free) (void *, void *))
{
obstack_specify_allocation (&b->cache, 0, 0, alloc, free);
}
struct bcache *
bcache_xmalloc (void *pool)
{
struct bcache *b = (struct bcache *) xmcalloc (pool, 1, sizeof (struct bcache));
b->pool = pool;
obstack_specify_allocation_with_arg (&b->cache, 0, 0, xmmalloc, xmfree, pool);
return b;
}
void
bcache_xfree (struct bcache *bcache)
{
if (bcache == NULL)
return;
obstack_free (&bcache->cache, 0);
xmfree (bcache->pool, bcache->bucket);
xmfree (bcache->pool, bcache);
}
static int
compare_ints (const void *ap, const void *bp)
{
return * (int *) ap - * (int *) bp;
}
static void
print_percentage (int portion, int total)
{
if (total == 0)
printf_filtered ("(not applicable)\n");
else
printf_filtered ("%3d%%\n", portion * 100 / total);
}
void
print_bcache_statistics (struct bcache *c, char *type)
{
int occupied_buckets;
int max_chain_length;
int median_chain_length;
{
unsigned int b;
int *chain_length
= (int *) alloca (c->num_buckets * sizeof (*chain_length));
occupied_buckets = 0;
for (b = 0; b < c->num_buckets; b++)
{
struct bstring *s = c->bucket[b];
chain_length[b] = 0;
if (s)
{
occupied_buckets++;
while (s)
{
chain_length[b]++;
s = s->next;
}
}
}
qsort (chain_length, c->num_buckets, sizeof (chain_length[0]),
compare_ints);
if (c->num_buckets > 0)
{
max_chain_length = chain_length[c->num_buckets - 1];
median_chain_length = chain_length[c->num_buckets / 2];
}
else
{
max_chain_length = 0;
median_chain_length = 0;
}
}
printf_filtered (" Cached '%s' statistics:\n", type);
printf_filtered (" Total object count: %ld\n", c->total_count);
printf_filtered (" Unique object count: %lu\n", c->unique_count);
printf_filtered (" Percentage of duplicates, by count: ");
print_percentage (c->total_count - c->unique_count, c->total_count);
printf_filtered ("\n");
printf_filtered (" Total object size: %ld\n", c->total_size);
printf_filtered (" Unique object size: %ld\n", c->unique_size);
printf_filtered (" Percentage of duplicates, by size: ");
print_percentage (c->total_size - c->unique_size, c->total_size);
printf_filtered ("\n");
printf_filtered (" Total memory used by bcache, including overhead: %ld\n",
c->structure_size);
printf_filtered (" Percentage memory overhead: ");
print_percentage (c->structure_size - c->unique_size, c->unique_size);
printf_filtered (" Net memory savings: ");
print_percentage (c->total_size - c->structure_size, c->total_size);
printf_filtered ("\n");
printf_filtered (" Hash table size: %3d\n", c->num_buckets);
printf_filtered (" Hash table population: ");
print_percentage (occupied_buckets, c->num_buckets);
printf_filtered (" Median hash chain length: %3d\n",
median_chain_length);
printf_filtered (" Average hash chain length: ");
if (c->num_buckets > 0)
printf_filtered ("%3lu\n", c->unique_count / c->num_buckets);
else
printf_filtered ("(not applicable)\n");
printf_filtered (" Maximum hash chain length: %3d\n", max_chain_length);
printf_filtered ("\n");
}
int
bcache_memory_used (struct bcache *bcache)
{
return obstack_memory_used (&bcache->cache);
}