mirror of
https://github.com/ProgramSnail/Lama.git
synced 2025-12-07 07:18:48 +00:00
Added check of invariance of objects' topology before and after compaction
This commit is contained in:
parent
11d496405b
commit
31551e73fd
5 changed files with 112 additions and 16 deletions
|
|
@ -1,11 +1,11 @@
|
||||||
CC=gcc
|
CC=gcc
|
||||||
FLAGS=-m32 -g2 -fstack-protector-all
|
FLAGS=-m32 -g2 -fstack-protector-all -DFULL_INVARIANT_CHECKS
|
||||||
|
|
||||||
all: gc_runtime.o gc.o runtime.o
|
all: gc_runtime.o gc.o runtime.o
|
||||||
ar rc runtime.a gc_runtime.o runtime.o gc.o
|
ar rc runtime.a gc_runtime.o runtime.o gc.o
|
||||||
|
|
||||||
test.o: gc.c gc.h gc_runtime.s runtime.c runtime.h runtime_common.h virt_stack.c virt_stack.h test_main.c test_util.s
|
test.o: gc.c gc.h gc_runtime.s runtime.c runtime.h runtime_common.h virt_stack.c virt_stack.h test_main.c test_util.s
|
||||||
$(CC) -o test.o -DDEBUG_VERSION $(FLAGS) gc.c gc_runtime.s virt_stack.c runtime.c test_main.c test_util.s
|
$(CC) -o test.o -DDEBUG_VERSION $(FLAGS) gc.c gc_runtime.s virt_stack.c ext_arr.c runtime.c test_main.c test_util.s
|
||||||
|
|
||||||
virt_stack.o: virt_stack.h virt_stack.c
|
virt_stack.o: virt_stack.h virt_stack.c
|
||||||
$(CC) $(FLAGS) -c virt_stack.c
|
$(CC) $(FLAGS) -c virt_stack.c
|
||||||
|
|
|
||||||
113
runtime/gc.c
113
runtime/gc.c
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
static const size_t INIT_HEAP_SIZE = MINIMUM_HEAP_CAPACITY;
|
static const size_t INIT_HEAP_SIZE = MINIMUM_HEAP_CAPACITY;
|
||||||
|
|
||||||
#ifdef DEBUG_VERSION
|
#ifdef FULL_INVARIANT_CHECKS
|
||||||
size_t cur_id = 0;
|
size_t cur_id = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -49,7 +49,7 @@ void handler (int sig) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void *alloc (size_t size) {
|
void *alloc (size_t size) {
|
||||||
#ifdef DEBUG_VERSION
|
#ifdef FULL_INVARIANT_CHECKS
|
||||||
++cur_id;
|
++cur_id;
|
||||||
#endif
|
#endif
|
||||||
size = BYTES_TO_WORDS(size);
|
size = BYTES_TO_WORDS(size);
|
||||||
|
|
@ -61,6 +61,83 @@ void *alloc (size_t size) {
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef FULL_INVARIANT_CHECKS
|
||||||
|
|
||||||
|
// precondition: obj_content is a valid address pointing to the content of an object
|
||||||
|
static void objects_dfs(FILE *f, void *obj_content) {
|
||||||
|
void *obj_header = get_obj_header_ptr(obj_content);
|
||||||
|
data *obj_data = TO_DATA(obj_content);
|
||||||
|
// internal mark-bit for this dfs, should be recovered by the caller
|
||||||
|
if ((obj_data->forward_address & 2) != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// set this bit as 1
|
||||||
|
obj_data->forward_address |= 2;
|
||||||
|
fprintf(f, "%zu ", obj_data->id);
|
||||||
|
// first cycle: print object's fields
|
||||||
|
for (obj_field_iterator field_it = ptr_field_begin_iterator(obj_header);
|
||||||
|
!field_is_done_iterator(&field_it);
|
||||||
|
obj_next_field_iterator(&field_it)) {
|
||||||
|
size_t field_value = *(size_t *) field_it.cur_field;
|
||||||
|
if (is_valid_heap_pointer((size_t *) field_value)) {
|
||||||
|
fprintf(f, "%zu ", TO_DATA(field_value)->id);
|
||||||
|
} else {
|
||||||
|
fprintf(f, "%d ", UNBOX(field_value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf(f, "\n");
|
||||||
|
for (obj_field_iterator field_it = ptr_field_begin_iterator(obj_header);
|
||||||
|
!field_is_done_iterator(&field_it);
|
||||||
|
obj_next_field_iterator(&field_it)) {
|
||||||
|
size_t field_value = *(size_t *) field_it.cur_field;
|
||||||
|
if (is_valid_heap_pointer((size_t *) field_value)) {
|
||||||
|
objects_dfs(f, (void*) field_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *print_objects_traversal(bool marked) {
|
||||||
|
FILE *f = tmpfile();
|
||||||
|
for (heap_iterator it = heap_begin_iterator();
|
||||||
|
!heap_is_done_iterator(&it);
|
||||||
|
heap_next_obj_iterator(&it)) {
|
||||||
|
void *obj_header = it.current;
|
||||||
|
data *obj_data = TO_DATA(get_object_content_ptr(obj_header));
|
||||||
|
if ((obj_data->forward_address & 1) == marked) {
|
||||||
|
objects_dfs(f, get_object_content_ptr(obj_header));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// resetting bit that represent mark-bit for this internal dfs-traversal
|
||||||
|
for (heap_iterator it = heap_begin_iterator();
|
||||||
|
!heap_is_done_iterator(&it);
|
||||||
|
heap_next_obj_iterator(&it)) {
|
||||||
|
void *obj_header = it.current;
|
||||||
|
data *obj_data = TO_DATA(get_object_content_ptr(obj_header));
|
||||||
|
obj_data->forward_address &= (~2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int files_cmp(FILE *f1, FILE *f2) {
|
||||||
|
int symbol1, symbol2;
|
||||||
|
int position = 0;
|
||||||
|
|
||||||
|
while ((symbol1 = fgetc(f1)) != EOF && (symbol2 = fgetc(f2)) != EOF) {
|
||||||
|
if (symbol1 != symbol2) {
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
++position;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (symbol1 != EOF || symbol2 != EOF) {
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
void *gc_alloc_on_existing_heap (size_t size) {
|
void *gc_alloc_on_existing_heap (size_t size) {
|
||||||
if (heap.current + size <= heap.end) {
|
if (heap.current + size <= heap.end) {
|
||||||
void *p = (void *)heap.current;
|
void *p = (void *)heap.current;
|
||||||
|
|
@ -73,8 +150,23 @@ void *gc_alloc_on_existing_heap (size_t size) {
|
||||||
|
|
||||||
void *gc_alloc (size_t size) {
|
void *gc_alloc (size_t size) {
|
||||||
mark_phase();
|
mark_phase();
|
||||||
|
#ifdef FULL_INVARIANT_CHECKS
|
||||||
|
FILE *heap_before_compaction = print_objects_traversal(1);
|
||||||
|
#endif
|
||||||
|
|
||||||
compact_phase(size);
|
compact_phase(size);
|
||||||
|
#ifdef FULL_INVARIANT_CHECKS
|
||||||
|
FILE *heap_after_compaction = print_objects_traversal(0);
|
||||||
|
|
||||||
|
int pos = files_cmp(heap_before_compaction, heap_after_compaction);
|
||||||
|
if (pos >= 0) { // position of difference is found
|
||||||
|
fprintf(stderr, "GC invariant is broken\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(heap_before_compaction);
|
||||||
|
fclose(heap_after_compaction);
|
||||||
|
#endif
|
||||||
|
|
||||||
return gc_alloc_on_existing_heap(size);
|
return gc_alloc_on_existing_heap(size);
|
||||||
}
|
}
|
||||||
|
|
@ -112,13 +204,13 @@ void compact_phase (size_t additional_size) {
|
||||||
update_references(&old_heap);
|
update_references(&old_heap);
|
||||||
physically_relocate(&old_heap);
|
physically_relocate(&old_heap);
|
||||||
|
|
||||||
// shrink it if possible, otherwise this code won'test_small_tree_compaction do anything, in both cases references
|
// shrink it if possible, otherwise this code won't do anything, in both cases references
|
||||||
// will remain valid
|
// will remain valid
|
||||||
heap.begin = mremap(
|
heap.begin = mremap(
|
||||||
heap.begin,
|
heap.begin,
|
||||||
WORDS_TO_BYTES(heap.size),
|
WORDS_TO_BYTES(heap.size),
|
||||||
WORDS_TO_BYTES(next_heap_size),
|
WORDS_TO_BYTES(next_heap_size),
|
||||||
0 // in this case we don't set MREMAP_MAYMOVE because it shouldn'test_small_tree_compaction move :)
|
0 // in this case we don't set MREMAP_MAYMOVE because it shouldn't move :)
|
||||||
);
|
);
|
||||||
if (heap.begin == MAP_FAILED) {
|
if (heap.begin == MAP_FAILED) {
|
||||||
perror("ERROR: compact_phase: mremap failed\n");
|
perror("ERROR: compact_phase: mremap failed\n");
|
||||||
|
|
@ -268,7 +360,7 @@ void mark (void *obj) {
|
||||||
// while the queue is non-empty
|
// while the queue is non-empty
|
||||||
void *cur_obj = queue_dequeue(&q_head_iter);
|
void *cur_obj = queue_dequeue(&q_head_iter);
|
||||||
mark_object(cur_obj);
|
mark_object(cur_obj);
|
||||||
void *header_ptr = get_obj_header_ptr(cur_obj, get_type_row_ptr(cur_obj));
|
void *header_ptr = get_obj_header_ptr(cur_obj);
|
||||||
for (obj_field_iterator ptr_field_it = ptr_field_begin_iterator(header_ptr);
|
for (obj_field_iterator ptr_field_it = ptr_field_begin_iterator(header_ptr);
|
||||||
!field_is_done_iterator(&ptr_field_it);
|
!field_is_done_iterator(&ptr_field_it);
|
||||||
obj_next_ptr_field_iterator(&ptr_field_it)) {
|
obj_next_ptr_field_iterator(&ptr_field_it)) {
|
||||||
|
|
@ -559,7 +651,10 @@ bool field_is_done_iterator (obj_field_iterator *it) {
|
||||||
return it->cur_field >= get_end_of_obj(it->obj_ptr);
|
return it->cur_field >= get_end_of_obj(it->obj_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *get_obj_header_ptr (void *ptr, lama_type type) { return ptr - get_header_size(type); }
|
void *get_obj_header_ptr (void *ptr) {
|
||||||
|
lama_type type = get_type_row_ptr(ptr);
|
||||||
|
return ptr - get_header_size(type);
|
||||||
|
}
|
||||||
|
|
||||||
void *get_object_content_ptr (void *header_ptr) {
|
void *get_object_content_ptr (void *header_ptr) {
|
||||||
lama_type type = get_type_header_ptr(header_ptr);
|
lama_type type = get_type_header_ptr(header_ptr);
|
||||||
|
|
@ -595,7 +690,7 @@ void *alloc_string (int len) {
|
||||||
void *alloc_array (int len) {
|
void *alloc_array (int len) {
|
||||||
data *obj = alloc(array_size(len));
|
data *obj = alloc(array_size(len));
|
||||||
obj->data_header = ARRAY_TAG | (len << 3);
|
obj->data_header = ARRAY_TAG | (len << 3);
|
||||||
#ifdef DEBUG_VERSION
|
#ifdef FULL_INVARIANT_CHECKS
|
||||||
obj->id = cur_id;
|
obj->id = cur_id;
|
||||||
#endif
|
#endif
|
||||||
obj->forward_address = 0;
|
obj->forward_address = 0;
|
||||||
|
|
@ -605,7 +700,7 @@ void *alloc_array (int len) {
|
||||||
void *alloc_sexp (int members) {
|
void *alloc_sexp (int members) {
|
||||||
sexp *obj = alloc(sexp_size(members));
|
sexp *obj = alloc(sexp_size(members));
|
||||||
obj->sexp_header = obj->contents.data_header = SEXP_TAG | (members << 3);
|
obj->sexp_header = obj->contents.data_header = SEXP_TAG | (members << 3);
|
||||||
#ifdef DEBUG_VERSION
|
#ifdef FULL_INVARIANT_CHECKS
|
||||||
obj->contents.id = cur_id;
|
obj->contents.id = cur_id;
|
||||||
#endif
|
#endif
|
||||||
obj->contents.forward_address = 0;
|
obj->contents.forward_address = 0;
|
||||||
|
|
@ -616,7 +711,7 @@ void *alloc_sexp (int members) {
|
||||||
void *alloc_closure (int captured) {
|
void *alloc_closure (int captured) {
|
||||||
data *obj = alloc(closure_size(captured));
|
data *obj = alloc(closure_size(captured));
|
||||||
obj->data_header = CLOSURE_TAG | (captured << 3);
|
obj->data_header = CLOSURE_TAG | (captured << 3);
|
||||||
#ifdef DEBUG_VERSION
|
#ifdef FULL_INVARIANT_CHECKS
|
||||||
obj->id = cur_id;
|
obj->id = cur_id;
|
||||||
#endif
|
#endif
|
||||||
obj->forward_address = 0;
|
obj->forward_address = 0;
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
#ifdef DEBUG_VERSION
|
#ifdef DEBUG_VERSION
|
||||||
# define MINIMUM_HEAP_CAPACITY (8)
|
# define MINIMUM_HEAP_CAPACITY (8)
|
||||||
#else
|
#else
|
||||||
# define MINIMUM_HEAP_CAPACITY (1 << 25)
|
# define MINIMUM_HEAP_CAPACITY (1 << 10)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
@ -193,7 +193,7 @@ void obj_next_ptr_field_iterator (obj_field_iterator *it);
|
||||||
// returns if we are done iterating over fields of the object
|
// returns if we are done iterating over fields of the object
|
||||||
bool field_is_done_iterator (obj_field_iterator *it);
|
bool field_is_done_iterator (obj_field_iterator *it);
|
||||||
// ptr is pointer to the actual object content, returns pointer to the very beginning of the object (header)
|
// ptr is pointer to the actual object content, returns pointer to the very beginning of the object (header)
|
||||||
void *get_obj_header_ptr (void *ptr, lama_type type);
|
void *get_obj_header_ptr (void *ptr);
|
||||||
void *get_object_content_ptr (void *header_ptr);
|
void *get_object_content_ptr (void *header_ptr);
|
||||||
void *get_end_of_obj (void *header_ptr);
|
void *get_end_of_obj (void *header_ptr);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -503,7 +503,7 @@ extern void *Lsubstring (void *subj, int p, int l) {
|
||||||
extern struct re_pattern_buffer *Lregexp (char *regexp) {
|
extern struct re_pattern_buffer *Lregexp (char *regexp) {
|
||||||
regex_t *b = (regex_t *)malloc(sizeof(regex_t));
|
regex_t *b = (regex_t *)malloc(sizeof(regex_t));
|
||||||
|
|
||||||
/* printf ("regexp: %s,\test_small_tree_compaction%x\n", regexp, b); */
|
/* printf ("regexp: %s,\t%x\n", regexp, b); */
|
||||||
|
|
||||||
memset(b, 0, sizeof(regex_t));
|
memset(b, 0, sizeof(regex_t));
|
||||||
|
|
||||||
|
|
@ -1020,7 +1020,7 @@ extern int Btag (void *d, int t, int n) {
|
||||||
&& LEN(r->data_header) == UNBOX(n));
|
&& LEN(r->data_header) == UNBOX(n));
|
||||||
#else
|
#else
|
||||||
return BOX(TAG(r->data_header) == SEXP_TAG
|
return BOX(TAG(r->data_header) == SEXP_TAG
|
||||||
&& GET_SEXP_TAG(TO_SEXP(d)->data_header) == UNBOX(test_small_tree_compaction)
|
&& GET_SEXP_TAG(TO_SEXP(d)->data_header) == UNBOX(t)
|
||||||
&& LEN(r->data_header) == UNBOX(n));
|
&& LEN(r->data_header) == UNBOX(n));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
// this flag makes GC behavior a bit different for testing purposes.
|
// this flag makes GC behavior a bit different for testing purposes.
|
||||||
//#define DEBUG_VERSION
|
//#define DEBUG_VERSION
|
||||||
|
//#define FULL_INVARIANT_CHECKS
|
||||||
|
|
||||||
#define STRING_TAG 0x00000001
|
#define STRING_TAG 0x00000001
|
||||||
//# define STRING_TAG 0x00000000
|
//# define STRING_TAG 0x00000000
|
||||||
|
|
@ -48,7 +49,7 @@ typedef struct {
|
||||||
// other utility info (i.e., size for array, number of fields for s-expression)
|
// other utility info (i.e., size for array, number of fields for s-expression)
|
||||||
int data_header;
|
int data_header;
|
||||||
|
|
||||||
#ifdef DEBUG_VERSION
|
#ifdef FULL_INVARIANT_CHECKS
|
||||||
size_t id;
|
size_t id;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue