|
@@ -20,39 +20,58 @@
|
|
|
* DEALINGS IN THE SOFTWARE.
|
|
|
*/
|
|
|
#include "hl.h"
|
|
|
-
|
|
|
-HL_PRIM void hl_gc_set_track( void *f );
|
|
|
+#include <stdio.h>
|
|
|
|
|
|
static int track_depth = 10;
|
|
|
|
|
|
+#ifdef HL_TRACK_ENABLE
|
|
|
+hl_track_info hl_track = {0};
|
|
|
+#endif
|
|
|
+
|
|
|
+typedef enum {
|
|
|
+ KALLOC,
|
|
|
+ KCAST,
|
|
|
+ KDYNFIELD,
|
|
|
+ KDYNCALL,
|
|
|
+ _KLAST
|
|
|
+} bucket_kind;
|
|
|
+
|
|
|
typedef struct {
|
|
|
hl_type *t;
|
|
|
- int stack_count;
|
|
|
void **stack;
|
|
|
- int alloc_count;
|
|
|
- int total_size;
|
|
|
+ int stack_count;
|
|
|
+ int hit_count;
|
|
|
+ int info;
|
|
|
} bucket;
|
|
|
|
|
|
-static unsigned int *hashes = NULL;
|
|
|
-static bucket *buckets = NULL;
|
|
|
-static int bcount = 0;
|
|
|
-static int max_buckets = 0;
|
|
|
+typedef struct {
|
|
|
+ unsigned int *hashes;
|
|
|
+ bucket *buckets;
|
|
|
+ int bcount;
|
|
|
+ int max_buckets;
|
|
|
+ unsigned int prev_hash;
|
|
|
+ unsigned int prev_hash2;
|
|
|
+ bucket *prev_b;
|
|
|
+ bucket *prev_b2;
|
|
|
+} bucket_list;
|
|
|
+
|
|
|
+static bucket_list all_data[_KLAST] = {0};
|
|
|
static hl_mutex *track_lock = NULL;
|
|
|
|
|
|
int hl_internal_capture_stack( void **stack, int size );
|
|
|
|
|
|
-static bucket *bucket_find_insert( unsigned int hash, void **stack, int count ) {
|
|
|
+static bucket *bucket_find_insert( bucket_list *data, unsigned int hash, void **stack, int count ) {
|
|
|
int min = 0, mid;
|
|
|
- int max = bcount;
|
|
|
+ int max = data->bcount;
|
|
|
bucket *b;
|
|
|
while( min < max ) {
|
|
|
mid = (min + max) >> 1;
|
|
|
- if( hashes[mid] < hash )
|
|
|
+ if( data->hashes[mid] < hash )
|
|
|
min = mid + 1;
|
|
|
- else if( hashes[mid] > hash )
|
|
|
+ else if( data->hashes[mid] > hash )
|
|
|
max = mid;
|
|
|
else {
|
|
|
- b = buckets + mid;
|
|
|
+ b = data->buckets + mid;
|
|
|
if( b->stack_count != count ) {
|
|
|
if( b->stack_count < count )
|
|
|
min = mid + 1;
|
|
@@ -74,20 +93,20 @@ static bucket *bucket_find_insert( unsigned int hash, void **stack, int count )
|
|
|
}
|
|
|
}
|
|
|
mid = (min + max) >> 1;
|
|
|
- if( bcount == max_buckets ) {
|
|
|
- int nbuckets = max_buckets ? max_buckets << 1 : 256;
|
|
|
+ if( data->bcount == data->max_buckets ) {
|
|
|
+ int nbuckets = data->max_buckets ? data->max_buckets << 1 : 256;
|
|
|
bucket *bnew = (bucket*)malloc(sizeof(bucket)*nbuckets);
|
|
|
unsigned int *hnew = (unsigned int*)malloc(sizeof(int)*nbuckets);
|
|
|
- memcpy(bnew,buckets,bcount*sizeof(bucket));
|
|
|
- memcpy(hnew,hashes,bcount*sizeof(int));
|
|
|
- free(buckets);
|
|
|
- free(hashes);
|
|
|
- buckets = bnew;
|
|
|
- hashes = hnew;
|
|
|
- max_buckets = nbuckets;
|
|
|
+ memcpy(bnew,data->buckets,data->bcount*sizeof(bucket));
|
|
|
+ memcpy(hnew,data->hashes,data->bcount*sizeof(int));
|
|
|
+ free(data->buckets);
|
|
|
+ free(data->hashes);
|
|
|
+ data->buckets = bnew;
|
|
|
+ data->hashes = hnew;
|
|
|
+ data->max_buckets = nbuckets;
|
|
|
}
|
|
|
- b = buckets + mid;
|
|
|
- if( hashes[mid] == hash && b->stack_count == count ) {
|
|
|
+ b = data->buckets + mid;
|
|
|
+ if( data->hashes[mid] == hash && b->stack_count == count ) {
|
|
|
int i;
|
|
|
for(i=0;i<count;i++)
|
|
|
if( b->stack[i] != stack[i] )
|
|
@@ -95,32 +114,32 @@ static bucket *bucket_find_insert( unsigned int hash, void **stack, int count )
|
|
|
if( i == count )
|
|
|
return b;
|
|
|
}
|
|
|
- memmove(buckets + (mid + 1), buckets + mid, (bcount - mid) * sizeof(bucket));
|
|
|
- memmove(hashes + (mid + 1), hashes + mid, (bcount - mid) * sizeof(int));
|
|
|
+ memmove(data->buckets + (mid + 1), data->buckets + mid, (data->bcount - mid) * sizeof(bucket));
|
|
|
+ memmove(data->hashes + (mid + 1), data->hashes + mid, (data->bcount - mid) * sizeof(int));
|
|
|
memset(b, 0, sizeof(bucket));
|
|
|
b->stack = malloc(sizeof(void*)*count);
|
|
|
memcpy(b->stack, stack, sizeof(void*)*count);
|
|
|
b->stack_count = count;
|
|
|
- hashes[mid] = hash;
|
|
|
- bcount++;
|
|
|
+ data->hashes[mid] = hash;
|
|
|
+ data->bcount++;
|
|
|
return b;
|
|
|
}
|
|
|
|
|
|
static void init_lock() {
|
|
|
hl_thread_info *tinf = hl_get_thread();
|
|
|
- tinf->exc_flags |= HL_TRACK_DISABLE;
|
|
|
+ int flags = tinf->flags;
|
|
|
+ tinf->flags &= ~(HL_TRACK_ALLOC<<HL_TREAD_TRACK_SHIFT);
|
|
|
track_lock = hl_mutex_alloc(true);
|
|
|
hl_add_root(&track_lock);
|
|
|
- tinf->exc_flags &= ~HL_TRACK_DISABLE;
|
|
|
+ tinf->flags = flags;
|
|
|
}
|
|
|
|
|
|
-static void on_alloc( hl_type *t, int size, int flags, void *ptr ) {
|
|
|
- static unsigned int prev_hash = 0, prev_hash2 = 0;
|
|
|
- static bucket *prev_b = NULL, *prev_b2 = NULL;
|
|
|
+static bucket *fetch_bucket( bucket_kind kind ) {
|
|
|
int count, i;
|
|
|
unsigned int hash;
|
|
|
- bucket *b;
|
|
|
hl_thread_info *tinf = hl_get_thread();
|
|
|
+ bucket_list *data = &all_data[kind];
|
|
|
+ bucket *b;
|
|
|
if( track_lock == NULL ) init_lock();
|
|
|
count = hl_internal_capture_stack(tinf->exc_stack_trace,track_depth);
|
|
|
hash = -count;
|
|
@@ -128,79 +147,146 @@ static void on_alloc( hl_type *t, int size, int flags, void *ptr ) {
|
|
|
hash = (hash * 31) + (((unsigned int)(int_val)tinf->exc_stack_trace[i]) >> 1);
|
|
|
// look for bucket
|
|
|
hl_mutex_acquire(track_lock);
|
|
|
- if( hash == prev_hash && prev_b!=NULL ) {
|
|
|
- b = prev_b;
|
|
|
- } else if( hash == prev_hash2 && prev_b2!=NULL ) {
|
|
|
- b = prev_b2;
|
|
|
+ if( hash == data->prev_hash && data->prev_b ) {
|
|
|
+ b = data->prev_b;
|
|
|
+ } else if( hash == data->prev_hash2 && data->prev_b2 ) {
|
|
|
+ b = data->prev_b2;
|
|
|
} else {
|
|
|
- b = bucket_find_insert(hash, tinf->exc_stack_trace, count);
|
|
|
- prev_hash2 = prev_hash;
|
|
|
- prev_b2 = prev_b;
|
|
|
- prev_hash = hash;
|
|
|
- prev_b = b;
|
|
|
+ b = bucket_find_insert(data, hash, tinf->exc_stack_trace, count);
|
|
|
+ data->prev_hash2 = data->prev_hash;
|
|
|
+ data->prev_b2 = data->prev_b;
|
|
|
+ data->prev_hash = hash;
|
|
|
+ data->prev_b = b;
|
|
|
}
|
|
|
+ return b;
|
|
|
+}
|
|
|
+
|
|
|
+static void on_alloc( hl_type *t, int size, int flags, void *ptr ) {
|
|
|
+ bucket *b = fetch_bucket(KALLOC);
|
|
|
b->t = t;
|
|
|
- b->alloc_count++;
|
|
|
- b->total_size += size;
|
|
|
+ b->hit_count++;
|
|
|
+ b->info += size;
|
|
|
hl_mutex_release(track_lock);
|
|
|
}
|
|
|
|
|
|
-HL_PRIM void hl_track_init() {
|
|
|
- hl_gc_set_track(on_alloc);
|
|
|
+static void on_cast( hl_type *t1, hl_type *t2 ) {
|
|
|
+ bucket *b = fetch_bucket(KCAST);
|
|
|
+ b->t = t1;
|
|
|
+ b->hit_count++;
|
|
|
+ b->info = t2->kind;
|
|
|
+ hl_mutex_release(track_lock);
|
|
|
}
|
|
|
|
|
|
-HL_PRIM void hl_track_stop() {
|
|
|
- hl_gc_set_track(NULL);
|
|
|
+static void on_dynfield( vdynamic *d, int hfield ) {
|
|
|
+ bucket *b = fetch_bucket(KDYNFIELD);
|
|
|
+ b->t = d?d->t:&hlt_dyn;
|
|
|
+ b->hit_count++;
|
|
|
+ b->info = hfield;
|
|
|
+ hl_mutex_release(track_lock);
|
|
|
+}
|
|
|
+
|
|
|
+static void on_dyncall( vdynamic *d, int hfield ) {
|
|
|
+ bucket *b = fetch_bucket(KDYNCALL);
|
|
|
+ b->t = d?d->t:&hlt_dyn;
|
|
|
+ b->hit_count++;
|
|
|
+ b->info = hfield;
|
|
|
+ hl_mutex_release(track_lock);
|
|
|
+}
|
|
|
+
|
|
|
+HL_PRIM void hl_track_init() {
|
|
|
+#ifdef HL_TRACK_ENABLE
|
|
|
+ char *env = getenv("HL_TRACK");
|
|
|
+ if( env )
|
|
|
+ hl_track.flags = atoi(env);
|
|
|
+ hl_track.on_alloc = on_alloc;
|
|
|
+ hl_track.on_cast = on_cast;
|
|
|
+ hl_track.on_dynfield = on_dynfield;
|
|
|
+ hl_track.on_dyncall = on_dyncall;
|
|
|
+#endif
|
|
|
}
|
|
|
|
|
|
HL_PRIM void hl_track_lock( bool lock ) {
|
|
|
+#ifdef HL_TRACK_ENABLE
|
|
|
if( !track_lock ) init_lock();
|
|
|
if( lock )
|
|
|
hl_mutex_acquire(track_lock);
|
|
|
else
|
|
|
hl_mutex_release(track_lock);
|
|
|
+#endif
|
|
|
}
|
|
|
|
|
|
HL_PRIM int hl_track_count( int *depth ) {
|
|
|
- int value;
|
|
|
- value = bcount;
|
|
|
+ int value = 0;
|
|
|
+ int i;
|
|
|
+ for(i=0;i<_KLAST;i++)
|
|
|
+ value += all_data[i].bcount;
|
|
|
*depth = track_depth;
|
|
|
return value;
|
|
|
}
|
|
|
|
|
|
-HL_PRIM void hl_track_entry( int id, hl_type **t, int *allocs, int *size, varray *stack ) {
|
|
|
- bucket *b = buckets + id;
|
|
|
+HL_PRIM int hl_track_entry( int id, hl_type **t, int *count, int *info, varray *stack ) {
|
|
|
+ static bucket_list *cur = NULL;
|
|
|
+ static int prev_id = -10;
|
|
|
+ static int count_before = 0;
|
|
|
+ bucket *b = NULL;
|
|
|
+ if( id == prev_id + 1 ) {
|
|
|
+ if( id - count_before == cur->bcount ) {
|
|
|
+ if( cur - all_data == _KLAST ) return -1;
|
|
|
+ count_before += cur->bcount;
|
|
|
+ cur++;
|
|
|
+ }
|
|
|
+ b = cur->buckets + (id - count_before);
|
|
|
+ prev_id++;
|
|
|
+ } else {
|
|
|
+ int i;
|
|
|
+ count_before = 0;
|
|
|
+ for(i=0;i<_KLAST;i++) {
|
|
|
+ bucket_list *data = &all_data[i];
|
|
|
+ if( id - count_before < data->bcount ) break;
|
|
|
+ count_before += data->bcount;
|
|
|
+ }
|
|
|
+ if( i == _KLAST ) return -1; // out of range
|
|
|
+ prev_id = id;
|
|
|
+ cur = &all_data[i];
|
|
|
+ b = cur->buckets;
|
|
|
+ }
|
|
|
*t = b->t;
|
|
|
- *allocs = b->alloc_count;
|
|
|
- *size = b->total_size;
|
|
|
+ *count = b->hit_count;
|
|
|
+ *info = b->info;
|
|
|
stack->size = b->stack_count;
|
|
|
memcpy(hl_aptr(stack,void*), b->stack, b->stack_count * sizeof(void*));
|
|
|
+ return (cur - all_data);
|
|
|
}
|
|
|
|
|
|
-HL_PRIM bool hl_track_enabled() {
|
|
|
- hl_thread_info *t = hl_get_thread();
|
|
|
- return t && (t->exc_flags & HL_TRACK_DISABLE) == 0;
|
|
|
+HL_PRIM int hl_track_get_bits( bool thread ) {
|
|
|
+# ifdef HL_TRACK_ENABLE
|
|
|
+ return (thread ? (hl_get_thread()->flags>>HL_TREAD_TRACK_SHIFT) : hl_track.flags) & HL_TRACK_MASK;
|
|
|
+# else
|
|
|
+ return 0;
|
|
|
+# endif
|
|
|
}
|
|
|
|
|
|
-HL_PRIM void hl_track_enable( bool b ) {
|
|
|
- hl_thread_info *t = hl_get_thread();
|
|
|
- if( t ) {
|
|
|
- if( !b )
|
|
|
- t->exc_flags |= HL_TRACK_DISABLE;
|
|
|
- else
|
|
|
- t->exc_flags &= ~HL_TRACK_DISABLE;
|
|
|
+HL_PRIM void hl_track_set_bits( int flags, bool thread ) {
|
|
|
+# ifdef HL_TRACK_ENABLE
|
|
|
+ if( thread ) {
|
|
|
+ hl_thread_info *t = hl_get_thread();
|
|
|
+ if( t ) t->flags = (t->flags & ~(HL_TRACK_MASK<<HL_TREAD_TRACK_SHIFT)) | ((flags & HL_TRACK_MASK) << HL_TREAD_TRACK_SHIFT);
|
|
|
+ } else {
|
|
|
+ hl_track.flags = (hl_track.flags & ~HL_TRACK_MASK) | (flags & HL_TRACK_MASK);
|
|
|
}
|
|
|
+# endif
|
|
|
}
|
|
|
|
|
|
HL_PRIM void hl_track_reset() {
|
|
|
- bcount = 0;
|
|
|
+ int i;
|
|
|
+ for(i=0;i<_KLAST;i++)
|
|
|
+ all_data[i].bcount = 0;
|
|
|
}
|
|
|
|
|
|
DEFINE_PRIM(_VOID, track_init, _NO_ARG);
|
|
|
-DEFINE_PRIM(_VOID, track_stop, _NO_ARG);
|
|
|
DEFINE_PRIM(_I32, track_count, _REF(_I32));
|
|
|
-DEFINE_PRIM(_VOID, track_entry, _I32 _REF(_TYPE) _REF(_I32) _REF(_I32) _ARR);
|
|
|
+DEFINE_PRIM(_I32, track_entry, _I32 _REF(_TYPE) _REF(_I32) _REF(_I32) _ARR);
|
|
|
DEFINE_PRIM(_VOID, track_lock, _BOOL);
|
|
|
-DEFINE_PRIM(_VOID, track_enable, _BOOL);
|
|
|
-DEFINE_PRIM(_BOOL, track_enabled, _NO_ARG);
|
|
|
+DEFINE_PRIM(_I32, track_get_bits, _BOOL);
|
|
|
+DEFINE_PRIM(_VOID, track_set_bits, _I32 _BOOL);
|
|
|
DEFINE_PRIM(_VOID, track_reset, _NO_ARG);
|