#include "defs.h"
#include "gdb_obstack.h"
#include "bcache.h"
#include "gdb_string.h"
#include "gdb_assert.h"
#include <stddef.h>
#include <stdlib.h>
struct bstring
{
struct bstring *next;
unsigned short length;
unsigned short half_hash;
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 expand_count;
unsigned long expand_hash_count;
unsigned long half_hash_miss_count;
};
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;
bcache->expand_count++;
bcache->expand_hash_count += bcache->unique_count;
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))
static void *
bcache_data (const void *addr, int length, struct bcache *bcache)
{
unsigned long full_hash;
unsigned short half_hash;
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;
full_hash = hash (addr, length);
half_hash = (full_hash >> 16);
hash_index = full_hash % bcache->num_buckets;
for (s = bcache->bucket[hash_index]; s; s = s->next)
{
if (s->half_hash == half_hash)
{
if (s->length == length
&& ! memcmp (&s->d.data, addr, length))
return &s->d.data;
else
bcache->half_hash_miss_count++;
}
}
{
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];
new->half_hash = half_hash;
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 *, size_t),
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) (size_t),
void (* free) (void *))
{
obstack_specify_allocation (&b->cache, 0, 0, alloc, free);
}
void *
deprecated_bcache (const void *addr, int length, struct bcache *bcache)
{
return bcache_data (addr, length, bcache);
}
const void *
bcache (const void *addr, int length, struct bcache *bcache)
{
return bcache_data (addr, length, bcache);
}
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", (int) (portion * 100.0 / total));
}
void
print_bcache_statistics (struct bcache *c, char *type)
{
int occupied_buckets;
int max_chain_length;
int median_chain_length;
int max_entry_size;
int median_entry_size;
{
unsigned int b;
int *chain_length = XCALLOC (c->num_buckets + 1, int);
int *entry_size = XCALLOC (c->unique_count + 1, int);
int stringi = 0;
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)
{
gdb_assert (b < c->num_buckets);
chain_length[b]++;
gdb_assert (stringi < c->unique_count);
entry_size[stringi++] = s->length;
s = s->next;
}
}
}
qsort (chain_length, c->num_buckets, sizeof (chain_length[0]),
compare_ints);
qsort (entry_size, c->unique_count, sizeof (entry_size[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;
}
if (c->unique_count > 0)
{
max_entry_size = entry_size[c->unique_count - 1];
median_entry_size = entry_size[c->unique_count / 2];
}
else
{
max_entry_size = 0;
median_entry_size = 0;
}
xfree (chain_length);
xfree (entry_size);
}
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 (_(" Max entry size: %d\n"), max_entry_size);
printf_filtered (_(" Average entry size: "));
if (c->unique_count > 0)
printf_filtered ("%ld\n", c->unique_size / c->unique_count);
else
printf_filtered (_("(not applicable)\n"));
printf_filtered (_(" Median entry size: %d\n"), median_entry_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 expands: %lu\n"),
c->expand_count);
printf_filtered (_(" Hash table hashes: %lu\n"),
c->total_count + c->expand_hash_count);
printf_filtered (_(" Half hash misses: %lu\n"),
c->half_hash_miss_count);
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);
}