1
/* ``The contents of this file are subject to the Erlang Public License,
2
* Version 1.1, (the "License"); you may not use this file except in
3
* compliance with the License. You should have received a copy of the
4
* Erlang Public License along with this software. If not, it can be
5
* retrieved via the world wide web at http://www.erlang.org/.
7
* Software distributed under the License is distributed on an "AS IS"
8
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
9
* the License for the specific language governing rights and limitations
12
* The Initial Developer of the Original Code is Ericsson Utvecklings AB.
13
* Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
14
* AB. All Rights Reserved.''
23
* Author: Rickard Green
26
/* Headers to include ... */
32
#include "erl_memory_trace_block_table.h"
36
#undef REALLY_HARD_DEBUG
39
# define REALLY_HARD_DEBUG 0
42
# define REALLY_HARD_DEBUG 0
45
/* Some system specific defines ... */
46
#if defined(__WIN32__) && !defined(__GNUC__)
47
# define INLINE __forceinline
50
# define INLINE __inline__
56
/* Our own assert() ... */
58
#define ASSERT(A) ((void) ((A) ? 1 : assert_failed(__FILE__, __LINE__, #A)))
60
static int assert_failed(char *f, int l, char *a)
62
fprintf(stderr, "%s:%d: Assertion failed: %s\n", f, l, a);
68
#define ASSERT(A) ((void) 1)
72
#define EMTBT_BLOCKS_PER_POOL 1000
74
typedef struct emtbt_block_pool_ {
75
struct emtbt_block_pool_ *next;
76
emtbt_block blocks[1];
80
void * (*alloc)(size_t);
81
void * (*realloc)(void *, size_t);
89
int current_size_index;
91
emtbt_block ** buckets;
94
/* Fixed size allocation of blocks */
95
emtbt_block_pool *block_pools;
96
emtbt_block *free_blocks;
102
static emtbt_block null_blk = {0};
104
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
110
static void check_table(emtbt_table *table);
114
block_alloc_new_pool(emtbt_table *tab)
117
emtbt_block_pool *poolp;
119
size = sizeof(emtbt_block_pool) - sizeof(emtbt_block);
120
size += tab->blocks_per_pool*sizeof(emtbt_block);
122
poolp = (*tab->alloc)(size);
128
poolp->next = tab->block_pools;
129
tab->block_pools = poolp;
131
blks = (emtbt_block *) poolp->blocks;
133
for (i = 1; i < tab->blocks_per_pool - 1; i++)
134
blks[i].next = &blks[i + 1];
135
blks[tab->blocks_per_pool - 1].next = NULL;
136
tab->free_blocks = &blks[1];
143
static INLINE emtbt_block *
144
block_alloc(emtbt_table *tab)
151
if (tab->free_blocks) {
152
res = tab->free_blocks;
153
tab->free_blocks = tab->free_blocks->next;
156
res = block_alloc_new_pool(tab);
160
res->next = ((emtbt_block *) 0xfffffff0);
161
res->prev = ((emtbt_block *) 0xfffffff0);
162
res->bucket = ((emtbt_block **) 0xfffffff0);
173
block_free(emtbt_table *tab, emtbt_block *bp)
180
bp->next = tab->free_blocks;
181
tab->free_blocks = bp;
190
#define PRIME0 ((usgnd_int_32) 268438039)
191
#define PRIME1 ((usgnd_int_32) 268440479)
192
#define PRIME2 ((usgnd_int_32) 268439161)
193
#define PRIME3 ((usgnd_int_32) 268437017)
195
#define MK_HASH(H, P, IS64) \
199
(H) += ((P) >> 8) & 0xff; \
201
(H) += ((P) >> 16) & 0xff; \
203
(H) += ((P) >> 24) & 0xff; \
206
(H) += ((P) >> 32) & 0xff; \
208
(H) += ((P) >> 40) & 0xff; \
210
(H) += ((P) >> 48) & 0xff; \
212
(H) += ((P) >> 56) & 0xff; \
217
static const int table_sizes[] = {
241
check_table(emtbt_table *table)
244
emtbt_block *block, *prev_block;
247
block = table->blocks;
248
ASSERT(!block || !block->prev);
252
MK_HASH(hash, block->pointer, table->is_64_bit);
253
ASSERT(hash == block->hash);
254
ASSERT(block->bucket - table->buckets
255
== hash % table->no_of_buckets);
256
ASSERT(!prev_block || prev_block == block->prev);
260
ASSERT(table->no_blocks >= no_blocks);
263
ASSERT(table->no_blocks == no_blocks);
265
#if REALLY_HARD_DEBUG
268
for (i = 0; i < table->no_of_buckets; i++) {
269
int bucket_end_found;
270
emtbt_block **bucket;
271
if (!table->buckets[i])
273
bucket_end_found = 0;
274
bucket = &table->buckets[i];
275
for (block = table->blocks; block; block = block->next) {
276
if (block->bucket == bucket) {
277
if (!block->prev || block->prev->bucket != bucket)
278
ASSERT(*bucket == block);
279
if (!block->next || block->next->bucket != bucket)
283
ASSERT(bucket_end_found);
293
link_block(emtbt_table *table, emtbt_block **bucket, emtbt_block *block)
297
block->bucket = bucket;
299
block->next = *bucket;
300
block->prev = (*bucket)->prev;
302
block->prev->next = block;
304
table->blocks = block;
305
block->next->prev = block;
308
block->next = table->blocks;
311
table->blocks->prev = block;
312
table->blocks = block;
313
table->used_buckets++;
326
resize_table(emtbt_table *table, int new_no_of_buckets)
333
emtbt_block **buckets;
335
if (new_no_of_buckets < table->no_of_buckets) {
336
/* shrink never fails */
337
buckets = (emtbt_block **) (*table->realloc)(table->buckets,
338
(sizeof(emtbt_block *)
339
* new_no_of_buckets));
343
else if (new_no_of_buckets > table->no_of_buckets) {
344
(*table->free)((void *) table->buckets);
345
buckets = (emtbt_block **) (*table->alloc)((sizeof(emtbt_block *)
346
* new_no_of_buckets));
353
table->buckets = buckets;
354
table->no_of_buckets = new_no_of_buckets;
355
table->max_used_buckets = (4*new_no_of_buckets)/5;
356
table->min_used_buckets = new_no_of_buckets/5;
357
table->used_buckets = 0;
360
org_no_blocks = table->no_blocks;
363
table->no_blocks = 0;
366
for (i = 0; i < new_no_of_buckets; i++)
369
block = table->blocks;
370
table->blocks = NULL;
373
emtbt_block *next_block = block->next;
374
link_block(table,&table->buckets[block->hash%new_no_of_buckets],block);
378
ASSERT(org_no_blocks == table->no_blocks);
384
grow_table(emtbt_table *table)
386
if (table->current_size_index < sizeof(table_sizes)/sizeof(int)) {
388
table->current_size_index++;
389
new_size = table_sizes[table->current_size_index];
390
ASSERT(new_size > 0);
391
return resize_table(table, new_size);
397
shrink_table(emtbt_table *table)
399
if (table->current_size_index > 0) {
401
table->current_size_index--;
402
new_size = table_sizes[table->current_size_index];
403
ASSERT(new_size > 0);
404
(void) resize_table(table, new_size);
408
static INLINE emtbt_block *
409
peek_block(emtbt_table *table, usgnd_int_max ptr)
411
emtbt_block **bucket;
415
MK_HASH(hash, ptr, table->is_64_bit);
417
bucket = &table->buckets[hash % table->no_of_buckets];
422
while (block->bucket == bucket) {
424
if (block->pointer == ptr)
434
insert_block(emtbt_table *table, emtbt_block *block)
436
emtbt_block **bucket;
437
emtbt_block *tmp_block;
445
if (table->used_buckets >= table->max_used_buckets) {
446
if(!grow_table(table))
452
MK_HASH(hash, p, table->is_64_bit);
455
bucket = &table->buckets[hash % table->no_of_buckets];
458
while (tmp_block->bucket == bucket) {
459
if (tmp_block->pointer == p)
461
if (!tmp_block->next)
463
tmp_block = tmp_block->next;
467
link_block(table, bucket, block);
469
ASSERT(block == peek_block(table, p));
476
delete_block(emtbt_table *table, emtbt_block *block)
478
emtbt_block **bucket;
487
bucket = block->bucket;
491
block->prev->next = block->next;
493
table->blocks = block->next;
496
block->next->prev = block->prev;
498
if (block == *bucket) {
499
ASSERT(!block->prev || block->prev->bucket != bucket);
500
if (block->next && block->next->bucket == bucket)
501
*bucket = block->next;
503
ASSERT(table->used_buckets > 0);
505
table->used_buckets--;
510
block->next = ((emtbt_block *) 0xfffffff0);
511
block->prev = ((emtbt_block *) 0xfffffff0);
512
block->bucket = ((emtbt_block **) 0xfffffff0);
515
ASSERT(table->no_blocks > 0);
518
if (table->used_buckets < table->min_used_buckets)
527
static INLINE emtbt_block *
528
fetch_block(emtbt_table *table, usgnd_int_max ptr)
532
block = peek_block(table, ptr);
533
delete_block(table, block);
538
const char *emtbt_error_string(int error)
541
case EMTBT_ALLOC_XBLK_ERROR:
542
return "Allocation to an already existing block";
543
case EMTBT_REALLOC_NOBLK_ERROR:
544
return "Reallocation of non-existing block";
545
case EMTBT_REALLOC_XBLK_ERROR:
546
return "Reallocation to an already existing block";
547
case EMTBT_REALLOC_BLK_TYPE_MISMATCH:
548
return "Block types mismatch when reallocating";
549
case EMTBT_FREE_NOBLK_ERROR:
550
return "Deallocation of non-existing block";
551
case EMTBT_FREE_BLK_TYPE_MISMATCH:
552
return "Block types mismatch when deallocating";
553
case EMTBT_INTERNAL_ERROR:
554
return "Block table internal error";
564
emtbt_new_table(int is_64_bit,
565
void * (*alloc)(size_t),
566
void * (*realloc)(void *, size_t),
567
void (*free)(void *))
569
emtbt_table *tab = (*alloc)(sizeof(emtbt_table));
572
tab->realloc = realloc;
574
tab->is_64_bit = is_64_bit;
576
tab->no_of_buckets = 0;
577
tab->max_used_buckets = 0;
578
tab->min_used_buckets = 0;
579
tab->used_buckets = 0;
580
tab->current_size_index = 0;
584
tab->block_pools = NULL;
585
tab->free_blocks = NULL;
586
tab->blocks_per_pool = EMTBT_BLOCKS_PER_POOL;
593
emtbt_destroy_table(emtbt_table *tab)
595
void (*freep)(void *);
596
emtbt_block_pool *poolp1, *poolp2;
600
/* Free block pools */
601
poolp1 = tab->block_pools;
604
poolp1 = poolp1->next;
605
(*freep)((void *) poolp2);
609
(*freep)((void *) tab->buckets);
611
(*freep)((void *) tab);
615
#define CP_BLK(TO, FROM) \
617
(TO)->time.secs = (FROM)->time.secs; \
618
(TO)->time.usecs = (FROM)->time.usecs; \
619
(TO)->type = (FROM)->type; \
620
(TO)->pointer = (FROM)->pointer; \
621
(TO)->size = (FROM)->size; \
625
emtbt_alloc_op(emtbt_table *tab, emtp_operation *op)
630
blk = block_alloc(tab);
634
blk->time.secs = op->time.secs;
635
blk->time.usecs = op->time.usecs;
636
blk->type = op->u.block.type;
637
blk->pointer = op->u.block.new_ptr;
638
blk->size = op->u.block.new_size;
640
res = insert_block(tab, blk);
644
return EMTBT_ALLOC_XBLK_ERROR;
649
emtbt_realloc_op(emtbt_table *tab, emtp_operation *op, emtbt_block *old_blk)
654
if (!op->u.block.new_size) {
657
blk = fetch_block(tab, op->u.block.prev_ptr);
659
return EMTBT_REALLOC_NOBLK_ERROR;
661
CP_BLK(old_blk, blk);
662
block_free(tab, blk);
666
if (!op->u.block.new_ptr) {
667
/* failed operation */
668
if (!op->u.block.prev_ptr)
669
CP_BLK(old_blk, &null_blk);
671
blk = peek_block(tab, op->u.block.prev_ptr);
673
return EMTBT_REALLOC_NOBLK_ERROR;
674
CP_BLK(old_blk, blk);
676
if (blk->type != op->u.block.type)
677
return EMTBT_REALLOC_BLK_TYPE_MISMATCH;
681
else if (!op->u.block.prev_ptr) {
684
CP_BLK(old_blk, &null_blk);
685
blk = block_alloc(tab);
688
blk->type = op->u.block.type;
689
blk->pointer = op->u.block.new_ptr;
690
blk->time.secs = op->time.secs;
691
blk->time.usecs = op->time.usecs;
692
blk->size = op->u.block.new_size;
694
res = insert_block(tab, blk);
698
return EMTBT_REALLOC_XBLK_ERROR;
700
else if (op->u.block.new_ptr == op->u.block.prev_ptr) {
702
blk = peek_block(tab, op->u.block.prev_ptr);
704
return EMTBT_REALLOC_NOBLK_ERROR;
705
CP_BLK(old_blk, blk);
706
blk->time.secs = op->time.secs;
707
blk->time.usecs = op->time.usecs;
708
blk->size = op->u.block.new_size;
710
if (blk->type != op->u.block.type)
711
return EMTBT_REALLOC_BLK_TYPE_MISMATCH;
716
blk = fetch_block(tab, op->u.block.prev_ptr);
718
return EMTBT_REALLOC_NOBLK_ERROR;
719
CP_BLK(old_blk, blk);
720
blk->time.secs = op->time.secs;
721
blk->time.usecs = op->time.usecs;
722
blk->pointer = op->u.block.new_ptr;
723
blk->size = op->u.block.new_size;
724
res = insert_block(tab, blk);
728
return EMTBT_REALLOC_XBLK_ERROR;
730
if (blk->type != op->u.block.type)
731
return EMTBT_REALLOC_BLK_TYPE_MISMATCH;
741
emtbt_free_op(emtbt_table *tab, emtp_operation *op, emtbt_block *old_blk)
745
if (!op->u.block.prev_ptr)
746
CP_BLK(old_blk, &null_blk);
749
blk = fetch_block(tab, op->u.block.prev_ptr);
751
return EMTBT_FREE_NOBLK_ERROR;
753
CP_BLK(old_blk, blk);
754
block_free(tab, blk);
756
if (blk->type != op->u.block.type)
757
return EMTBT_FREE_BLK_TYPE_MISMATCH;