From 31551e73fd9739152b74bab7d590d047d8a51e42 Mon Sep 17 00:00:00 2001 From: Egor Sheremetov Date: Tue, 20 Jun 2023 16:22:35 +0200 Subject: [PATCH] Added check of invariance of objects' topology before and after compaction --- runtime/Makefile | 4 +- runtime/gc.c | 113 +++++++++++++++++++++++++++++++++++---- runtime/gc.h | 4 +- runtime/runtime.c | 4 +- runtime/runtime_common.h | 3 +- 5 files changed, 112 insertions(+), 16 deletions(-) diff --git a/runtime/Makefile b/runtime/Makefile index 72187e258..3ac2b7eaa 100644 --- a/runtime/Makefile +++ b/runtime/Makefile @@ -1,11 +1,11 @@ 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 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 - $(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 $(CC) $(FLAGS) -c virt_stack.c diff --git a/runtime/gc.c b/runtime/gc.c index 049fc77cb..8c197bb16 100644 --- a/runtime/gc.c +++ b/runtime/gc.c @@ -16,7 +16,7 @@ static const size_t INIT_HEAP_SIZE = MINIMUM_HEAP_CAPACITY; -#ifdef DEBUG_VERSION +#ifdef FULL_INVARIANT_CHECKS size_t cur_id = 0; #endif @@ -49,7 +49,7 @@ void handler (int sig) { } void *alloc (size_t size) { -#ifdef DEBUG_VERSION +#ifdef FULL_INVARIANT_CHECKS ++cur_id; #endif size = BYTES_TO_WORDS(size); @@ -61,6 +61,83 @@ void *alloc (size_t size) { 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) { if (heap.current + size <= heap.end) { void *p = (void *)heap.current; @@ -73,8 +150,23 @@ void *gc_alloc_on_existing_heap (size_t size) { void *gc_alloc (size_t size) { mark_phase(); +#ifdef FULL_INVARIANT_CHECKS + FILE *heap_before_compaction = print_objects_traversal(1); +#endif 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); } @@ -112,13 +204,13 @@ void compact_phase (size_t additional_size) { update_references(&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 heap.begin = mremap( heap.begin, WORDS_TO_BYTES(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) { perror("ERROR: compact_phase: mremap failed\n"); @@ -268,7 +360,7 @@ void mark (void *obj) { // while the queue is non-empty void *cur_obj = queue_dequeue(&q_head_iter); 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); !field_is_done_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); } -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) { lama_type type = get_type_header_ptr(header_ptr); @@ -595,7 +690,7 @@ void *alloc_string (int len) { void *alloc_array (int len) { data *obj = alloc(array_size(len)); obj->data_header = ARRAY_TAG | (len << 3); -#ifdef DEBUG_VERSION +#ifdef FULL_INVARIANT_CHECKS obj->id = cur_id; #endif obj->forward_address = 0; @@ -605,7 +700,7 @@ void *alloc_array (int len) { void *alloc_sexp (int members) { sexp *obj = alloc(sexp_size(members)); obj->sexp_header = obj->contents.data_header = SEXP_TAG | (members << 3); -#ifdef DEBUG_VERSION +#ifdef FULL_INVARIANT_CHECKS obj->contents.id = cur_id; #endif obj->contents.forward_address = 0; @@ -616,7 +711,7 @@ void *alloc_sexp (int members) { void *alloc_closure (int captured) { data *obj = alloc(closure_size(captured)); obj->data_header = CLOSURE_TAG | (captured << 3); -#ifdef DEBUG_VERSION +#ifdef FULL_INVARIANT_CHECKS obj->id = cur_id; #endif obj->forward_address = 0; diff --git a/runtime/gc.h b/runtime/gc.h index 064bcfb58..6c248260b 100644 --- a/runtime/gc.h +++ b/runtime/gc.h @@ -18,7 +18,7 @@ #ifdef DEBUG_VERSION # define MINIMUM_HEAP_CAPACITY (8) #else -# define MINIMUM_HEAP_CAPACITY (1 << 25) +# define MINIMUM_HEAP_CAPACITY (1 << 10) #endif #include @@ -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 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) -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_end_of_obj (void *header_ptr); diff --git a/runtime/runtime.c b/runtime/runtime.c index f439fa4ad..9a19fe8bb 100644 --- a/runtime/runtime.c +++ b/runtime/runtime.c @@ -503,7 +503,7 @@ extern void *Lsubstring (void *subj, int p, int l) { extern struct re_pattern_buffer *Lregexp (char *regexp) { 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)); @@ -1020,7 +1020,7 @@ extern int Btag (void *d, int t, int n) { && LEN(r->data_header) == UNBOX(n)); #else 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)); #endif } diff --git a/runtime/runtime_common.h b/runtime/runtime_common.h index 664b9e9e0..f5e9527f6 100644 --- a/runtime/runtime_common.h +++ b/runtime/runtime_common.h @@ -4,6 +4,7 @@ // this flag makes GC behavior a bit different for testing purposes. //#define DEBUG_VERSION +//#define FULL_INVARIANT_CHECKS #define STRING_TAG 0x00000001 //# define STRING_TAG 0x00000000 @@ -48,7 +49,7 @@ typedef struct { // other utility info (i.e., size for array, number of fields for s-expression) int data_header; -#ifdef DEBUG_VERSION +#ifdef FULL_INVARIANT_CHECKS size_t id; #endif