浏览代码

updated tracking api:
allow tracking cast and dynamic calls
only track if flags/HL_TRACK set (close #215)

ncannasse 6 年之前
父节点
当前提交
1d67541791
共有 7 个文件被更改,包括 219 次插入101 次删除
  1. 2 20
      src/alloc.c
  2. 1 1
      src/debugger.c
  3. 34 2
      src/hl.h
  4. 8 0
      src/std/cast.c
  5. 7 7
      src/std/error.c
  6. 10 0
      src/std/obj.c
  7. 157 71
      src/std/track.c

+ 2 - 20
src/alloc.c

@@ -113,9 +113,6 @@ struct _gc_pheader {
 	unsigned char *sizes;
 	unsigned char *bmp;
 	gc_pheader *next_page;
-#ifdef HL_TRACK_ALLOC
-	int *alloc_hashes;
-#endif
 #ifdef GC_DEBUG
 	int page_id;
 #endif
@@ -145,7 +142,6 @@ static int gc_free_blocks[GC_ALL_PAGES] = {0};
 static gc_pheader *gc_free_pages[GC_ALL_PAGES] = {NULL};
 static gc_pheader *gc_level1_null[1<<GC_LEVEL1_BITS] = {NULL};
 static gc_pheader **hl_gc_page_map[1<<GC_LEVEL0_BITS] = {NULL};
-static void (*gc_track_callback)(hl_type *,int,int,void*) = NULL;
 
 static struct {
 	int count;
@@ -219,10 +215,6 @@ static void gc_global_lock( bool lock ) {
 }
 #endif
 
-HL_PRIM void hl_gc_set_track( void *f ) {
-	gc_track_callback = f;
-}
-
 HL_PRIM void hl_add_root( void *r ) {
 	gc_global_lock(true);
 	if( gc_roots_count == gc_roots_max ) {
@@ -270,6 +262,7 @@ HL_API void hl_register_thread( void *stack_top ) {
 	memset(t, 0, sizeof(hl_thread_info));
 	t->thread_id = hl_thread_id();
 	t->stack_top = stack_top;
+	t->flags = HL_TRACK_MASK << HL_TREAD_TRACK_SHIFT;
 	current_thread = t;
 	hl_add_root(&t->exc_value);
 	hl_add_root(&t->exc_handler);
@@ -465,10 +458,6 @@ retry:
 		start_pos += p->max_blocks;
 		MZERO(p->sizes,p->max_blocks);
 	}
-#	ifdef HL_TRACK_ALLOC
-	p->alloc_hashes = (int*)malloc(sizeof(int) * p->max_blocks);
-	MZERO(p->alloc_hashes,p->max_blocks * sizeof(int));
-#	endif
 	m = start_pos % block;
 	if( m ) start_pos += block - m;
 	p->first_block = start_pos / block;
@@ -538,9 +527,6 @@ alloc_fixed:
 			if( ptr[i] != 0xDD )
 				hl_fatal("assert");
 	}
-#	endif
-#	ifdef HL_TRACK_ALLOC
-	p->alloc_hashes[p->next_block] = hl_get_stack_hash();
 #	endif
 	p->next_block++;
 	gc_stats.total_allocated += p->block_size;
@@ -648,9 +634,6 @@ alloc_var:
 	} else {
 		p->free_blocks = p->max_blocks - (p->next_block + nblocks);
 	}
-#	ifdef HL_TRACK_ALLOC
-	p->alloc_hashes[p->next_block] = hl_get_stack_hash();
-#	endif
 	if( nblocks > 1 ) MZERO(p->sizes + p->next_block, nblocks);
 	p->sizes[p->next_block] = (unsigned char)nblocks;
 	p->next_block += nblocks;
@@ -715,8 +698,7 @@ void *hl_gc_alloc_gen( hl_type *t, int size, int flags ) {
 	memset((char*)ptr+(allocated - HL_WSIZE),0xEE,HL_WSIZE);
 #	endif
 	gc_global_lock(false);
-	if( gc_track_callback && (current_thread->exc_flags&HL_TRACK_DISABLE) == 0 )
-		((void (*)(hl_type *,int,int,void*))gc_track_callback)(t,size,flags,ptr);
+	hl_track_call(HL_TRACK_ALLOC, on_alloc(t,size,flags,ptr));
 	return ptr;
 }
 

+ 1 - 1
src/debugger.c

@@ -59,7 +59,7 @@ static void hl_debug_loop( hl_module *m ) {
 	flags |= 4;
 	loop = true;
 #	endif
-	hl_get_thread()->exc_flags |= HL_THREAD_INVISIBLE;
+	hl_get_thread()->flags |= HL_THREAD_INVISIBLE;
 	do {
 		int i;
 		vbyte cmd;

+ 34 - 2
src/hl.h

@@ -127,6 +127,10 @@
 #	define HL_DEBUG
 #endif
 
+#ifndef HL_CONSOLE
+#	define HL_TRACK_ENABLE
+#endif
+
 #ifndef HL_NO_THREADS
 #	define HL_THREADS
 #	ifdef HL_VCC
@@ -821,8 +825,14 @@ struct _hl_trap_ctx {
 #define HL_EXC_RETHROW		1
 #define HL_EXC_CATCH_ALL	2
 #define HL_EXC_IS_THROW		4
-#define HL_TRACK_DISABLE	8
 #define HL_THREAD_INVISIBLE	16
+#define HL_TREAD_TRACK_SHIFT 5
+
+#define HL_TRACK_ALLOC		1
+#define HL_TRACK_CAST		2
+#define HL_TRACK_DYNFIELD	4
+#define HL_TRACK_DYNCALL	8
+#define HL_TRACK_MASK		(HL_TRACK_ALLOC | HL_TRACK_CAST | HL_TRACK_DYNFIELD | HL_TRACK_DYNCALL)
 
 typedef struct {
 	int thread_id;
@@ -835,7 +845,7 @@ typedef struct {
 	hl_trap_ctx *trap_uncaught;
 	vclosure *exc_handler;
 	vdynamic *exc_value;
-	int exc_flags;
+	int flags;
 	int exc_stack_count;
 	// extra
 	jmp_buf gc_regs;
@@ -844,6 +854,28 @@ typedef struct {
 
 HL_API hl_thread_info *hl_get_thread();
 
+#ifdef HL_TRACK_ENABLE
+
+typedef struct {
+	int flags;
+	void (*on_alloc)(hl_type *,int,int,void*);
+	void (*on_cast)(hl_type *, hl_type*);
+	void (*on_dynfield)( vdynamic *, int );
+	void (*on_dyncall)( vdynamic *, int );
+} hl_track_info;
+
+#define hl_is_tracking(flag) ((hl_track.flags&(flag)) && (hl_get_thread()->flags & (flag<<HL_TREAD_TRACK_SHIFT)))
+#define hl_track_call(flag,call) if( hl_is_tracking(flag) ) hl_track.call
+
+HL_API hl_track_info hl_track;
+
+#else 
+
+#define hl_is_tracking(_) false
+#define hl_track_call(a,b)
+
+#endif
+
 C_FUNCTION_END
 
 #endif

+ 8 - 0
src/std/cast.c

@@ -76,6 +76,7 @@ HL_PRIM vdynamic *hl_make_dyn( void *data, hl_type *t ) {
 
 
 HL_PRIM int hl_dyn_casti( void *data, hl_type *t, hl_type *to ) {
+	hl_track_call(HL_TRACK_CAST, on_cast(t,to));
 	if( t->kind == HDYN ) {
 		vdynamic *v = *((vdynamic**)data);
 		if( v == NULL ) return 0;
@@ -109,6 +110,7 @@ HL_PRIM int hl_dyn_casti( void *data, hl_type *t, hl_type *to ) {
 }
 
 HL_PRIM void *hl_dyn_castp( void *data, hl_type *t, hl_type *to ) {
+	hl_track_call(HL_TRACK_CAST, on_cast(t,to));
 	if( to->kind == HDYN && hl_is_dynamic(t) )
 		return *(vdynamic**)data;
 	if( t->kind == HDYN || t->kind == HNULL ) {
@@ -234,6 +236,7 @@ HL_PRIM void *hl_dyn_castp( void *data, hl_type *t, hl_type *to ) {
 }
 
 HL_PRIM double hl_dyn_castd( void *data, hl_type *t ) {
+	hl_track_call(HL_TRACK_CAST, on_cast(t,&hlt_f64));
 	if( t->kind == HDYN ) {
 		vdynamic *v = *((vdynamic**)data);
 		if( v == NULL ) return 0;
@@ -267,6 +270,7 @@ HL_PRIM double hl_dyn_castd( void *data, hl_type *t ) {
 }
 
 HL_PRIM float hl_dyn_castf( void *data, hl_type *t ) {
+	hl_track_call(HL_TRACK_CAST, on_cast(t,&hlt_f32));
 	if( t->kind == HDYN ) {
 		vdynamic *v = *((vdynamic**)data);
 		if( v == NULL ) return 0;
@@ -320,6 +324,7 @@ HL_PRIM int hl_ptr_compare( vdynamic *a, vdynamic *b ) {
 }
 
 HL_PRIM int hl_dyn_compare( vdynamic *a, vdynamic *b ) {
+	hl_track_call(HL_TRACK_CAST, on_cast(a?a->t:&hlt_dyn,b?b->t:&hlt_dyn));
 	if( a == b )
 		return 0;
 	if( a == NULL )
@@ -370,6 +375,7 @@ HL_PRIM int hl_dyn_compare( vdynamic *a, vdynamic *b ) {
 }
 
 HL_PRIM void hl_write_dyn( void *data, hl_type *t, vdynamic *v ) {
+	hl_track_call(HL_TRACK_CAST, on_cast(v?v->t:&hlt_dyn,t));
 	switch( t->kind ) {
 	case HUI8:
 		*(unsigned char*)data = (unsigned char)hl_dyn_casti(&v,&hlt_dyn,t);
@@ -396,6 +402,7 @@ HL_PRIM void hl_write_dyn( void *data, hl_type *t, vdynamic *v ) {
 }
 
 HL_PRIM vdynamic* hl_value_cast( vdynamic *v, hl_type *t ) {
+	hl_track_call(HL_TRACK_CAST, on_cast(v?v->t:&hlt_dyn,t));
 	if( t->kind == HDYN || v == NULL || hl_safe_cast(v->t,t) )
 		return v;
 	invalid_cast(v->t,t);
@@ -442,6 +449,7 @@ static bool is_number( hl_type *t ) {
 HL_PRIM vdynamic *hl_dyn_op( int op, vdynamic *a, vdynamic *b ) {
 	static uchar *op_names[] = { USTR("+"), USTR("-"), USTR("*"), USTR("%"), USTR("/"), USTR("<<"), USTR(">>"), USTR(">>>"), USTR("&"), USTR("|"), USTR("^") };
 	if( op < 0 || op >= OpLast ) hl_error("Invalid op %d",op);
+	hl_track_call(HL_TRACK_CAST, on_cast(a?a->t:&hlt_dyn,b?b->t:&hlt_dyn));
 	if( !a && !b ) return op == OP_DIV || op == OP_MOD ? hl_dynf64(hl_nan()) : NULL;
 	if( (!a || is_number(a->t)) && (!b || is_number(b->t)) ) {
 		switch( op ) {

+ 7 - 7
src/std/error.c

@@ -89,19 +89,19 @@ HL_PRIM void hl_throw( vdynamic *v ) {
 	hl_trap_ctx *trap = t->trap_current;
 	bool was_rethrow = false;
 	bool call_handler = false;
-	if( t->exc_flags & HL_EXC_RETHROW ) {
+	if( t->flags & HL_EXC_RETHROW ) {
 		was_rethrow = true;
-		t->exc_flags &= ~HL_EXC_RETHROW;
+		t->flags &= ~HL_EXC_RETHROW;
 	} else
 		t->exc_stack_count = capture_stack_func(t->exc_stack_trace, HL_EXC_MAX_STACK);
 	t->exc_value = v;
 	t->trap_current = trap->prev;
-	call_handler = (t->exc_flags&HL_EXC_CATCH_ALL) || trap == t->trap_uncaught || t->trap_current == NULL;
-	if( (t->exc_flags&HL_EXC_CATCH_ALL) || break_on_trap(t,trap,v) ) {
+	call_handler = (t->flags&HL_EXC_CATCH_ALL) || trap == t->trap_uncaught || t->trap_current == NULL;
+	if( (t->flags&HL_EXC_CATCH_ALL) || break_on_trap(t,trap,v) ) {
 		if( trap == t->trap_uncaught ) t->trap_uncaught = NULL;
-		t->exc_flags |= HL_EXC_IS_THROW;
+		t->flags |= HL_EXC_IS_THROW;
 		hl_debug_break();
-		t->exc_flags &= ~HL_EXC_IS_THROW;
+		t->flags &= ~HL_EXC_IS_THROW;
 	}
 	if( t->exc_handler && call_handler ) hl_dyn_call(t->exc_handler,&v,1);
 	if( throw_jump == NULL ) throw_jump = longjmp;
@@ -153,7 +153,7 @@ HL_PRIM varray *hl_exception_stack() {
 }
 
 HL_PRIM void hl_rethrow( vdynamic *v ) {
-	hl_get_thread()->exc_flags |= HL_EXC_RETHROW;
+	hl_get_thread()->flags |= HL_EXC_RETHROW;
 	hl_throw(v);
 }
 

+ 10 - 0
src/std/obj.c

@@ -770,6 +770,7 @@ static vdynamic *hl_obj_lookup_extra( vdynamic *d, int hfield ) {
 
 HL_PRIM int hl_dyn_geti( vdynamic *d, int hfield, hl_type *t ) {
 	hl_type *ft;
+	hl_track_call(HL_TRACK_DYNFIELD, on_dynfield(d,hfield));
 	void *addr = hl_obj_lookup(d,hfield,&ft);
 	if( !addr ) {
 		d = hl_obj_lookup_extra(d,hfield);
@@ -795,6 +796,7 @@ HL_PRIM int hl_dyn_geti( vdynamic *d, int hfield, hl_type *t ) {
 
 HL_PRIM float hl_dyn_getf( vdynamic *d, int hfield ) {
 	hl_type *ft;
+	hl_track_call(HL_TRACK_DYNFIELD, on_dynfield(d,hfield));
 	void *addr = hl_obj_lookup(d,hfield,&ft);
 	if( !addr ) {
 		d = hl_obj_lookup_extra(d,hfield);
@@ -805,6 +807,7 @@ HL_PRIM float hl_dyn_getf( vdynamic *d, int hfield ) {
 
 HL_PRIM double hl_dyn_getd( vdynamic *d, int hfield ) {
 	hl_type *ft;
+	hl_track_call(HL_TRACK_DYNFIELD, on_dynfield(d,hfield));
 	void *addr = hl_obj_lookup(d,hfield,&ft);
 	if( !addr ) {
 		d = hl_obj_lookup_extra(d,hfield);
@@ -815,6 +818,7 @@ HL_PRIM double hl_dyn_getd( vdynamic *d, int hfield ) {
 
 HL_PRIM void *hl_dyn_getp( vdynamic *d, int hfield, hl_type *t ) {
 	hl_type *ft;
+	hl_track_call(HL_TRACK_DYNFIELD, on_dynfield(d,hfield));
 	void *addr = hl_obj_lookup(d,hfield,&ft);
 	if( !addr ) {
 		d = hl_obj_lookup_extra(d,hfield);
@@ -874,6 +878,7 @@ static void *hl_obj_lookup_set( vdynamic *d, int hfield, hl_type *t, hl_type **f
 
 HL_PRIM void hl_dyn_seti( vdynamic *d, int hfield, hl_type *t, int value ) {
 	hl_type *ft = NULL;
+	hl_track_call(HL_TRACK_DYNFIELD, on_dynfield(d,hfield));
 	void *addr = hl_obj_lookup_set(d,hfield,t,&ft);
 	switch( ft->kind ) {
 	case HUI8:
@@ -907,6 +912,7 @@ HL_PRIM void hl_dyn_seti( vdynamic *d, int hfield, hl_type *t, int value ) {
 
 HL_PRIM void hl_dyn_setf( vdynamic *d, int hfield, float value ) {
 	hl_type *t = NULL;
+	hl_track_call(HL_TRACK_DYNFIELD, on_dynfield(d,hfield));
 	void *addr = hl_obj_lookup_set(d,hfield,&hlt_f32,&t);
 	if( t->kind == HF32 )
 		*(float*)addr = value;
@@ -920,6 +926,7 @@ HL_PRIM void hl_dyn_setf( vdynamic *d, int hfield, float value ) {
 
 HL_PRIM void hl_dyn_setd( vdynamic *d, int hfield, double value ) {
 	hl_type *t = NULL;
+	hl_track_call(HL_TRACK_DYNFIELD, on_dynfield(d,hfield));
 	void *addr = hl_obj_lookup_set(d,hfield,&hlt_f64,&t);
 	if( t->kind == HF64 )
 		*(double*)addr = value;
@@ -933,6 +940,7 @@ HL_PRIM void hl_dyn_setd( vdynamic *d, int hfield, double value ) {
 
 HL_PRIM void hl_dyn_setp( vdynamic *d, int hfield, hl_type *t, void *value ) {
 	hl_type *ft = NULL;
+	hl_track_call(HL_TRACK_DYNFIELD, on_dynfield(d,hfield));
 	void *addr = hl_obj_lookup_set(d,hfield,t,&ft);
 	if( hl_same_type(t,ft) || (hl_is_ptr(ft) && value == NULL) )
 		*(void**)addr = value;
@@ -1140,3 +1148,5 @@ DEFINE_PRIM(_ARR, obj_fields, _DYN);
 DEFINE_PRIM(_DYN, obj_copy, _DYN);
 DEFINE_PRIM(_DYN, get_virtual_value, _DYN);
 DEFINE_PRIM(_I32, hash, _BYTES);
+DEFINE_PRIM(_BYTES, field_name, _I32);
+

+ 157 - 71
src/std/track.c

@@ -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);