From nobody Mon Sep 17 00:00:00 2001 From: Vera Volynets Date: Thu, 13 Jul 2006 19:06:19 +0400 Subject: [PATCH] Checks correctness of gc work. This is GC debug ifrastructure. It checks condition that the structure of object reference graph stays exactly the same during collection. Therefore number of strongly reachable live objects and live bytes before and after GC should stay unchanged. Run vm with option: -Dvm.verify.gc=true in command line. --- d9d2f53581754029e051b91bd6bbfdecf10e34fb vm/vmcore/include/environment.h | 3 vm/vmcore/include/gc_debug.h | 24 + vm/vmcore/src/gc/gc_debug.cpp | 417 +++++++++++++++++++++ vm/vmcore/src/gc/stop_the_world_root_set_enum.cpp | 12 + vm/vmcore/src/init/vm.cpp | 3 vm/vmcore/src/init/vm_main.cpp | 7 6 files changed, 462 insertions(+), 4 deletions(-) mode change 100644 => 100755 vm/vmcore/include/environment.h create mode 100755 vm/vmcore/include/gc_debug.h create mode 100755 vm/vmcore/src/gc/gc_debug.cpp d9d2f53581754029e051b91bd6bbfdecf10e34fb diff --git a/vm/vmcore/include/environment.h b/vm/vmcore/include/environment.h old mode 100644 new mode 100755 index 03ba4d6..a77efe1 --- a/vm/vmcore/include/environment.h +++ b/vm/vmcore/include/environment.h @@ -52,7 +52,8 @@ struct Global_Env { bool use_large_pages; // 20040109 Use large pages for class-related data such as vtables. bool verify_all; // psrebriy 20050815 Verify all classes including loaded by bootstrap class loader bool pin_interned_strings; // if true, interned strings are never moved - + bool debug_gc; // if true, trace the heap before and after stop-the-world phases + // // preloaded strings // diff --git a/vm/vmcore/include/gc_debug.h b/vm/vmcore/include/gc_debug.h new file mode 100755 index 0000000..abb46f5 --- /dev/null +++ b/vm/vmcore/include/gc_debug.h @@ -0,0 +1,24 @@ +/* + * Copyright 2005-2006 The Apache Software Foundation or its licensors, as applicable. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _DEBUG_GC_H +#define _DEBUG_GC_H + +void debug_gc_setup(); +void debug_gc_trace_heap(bool); +void debug_gc_enumerate_roots(void); +#endif // _DEBUG_GC_H + diff --git a/vm/vmcore/src/gc/gc_debug.cpp b/vm/vmcore/src/gc/gc_debug.cpp new file mode 100755 index 0000000..e34f42c --- /dev/null +++ b/vm/vmcore/src/gc/gc_debug.cpp @@ -0,0 +1,417 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as applicable. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "open/gc.h" +#include "open/vm_gc.h" +#include "open/vm.h" +#include "finalize.h" + +#define LOG_DOMAIN "gc.debug" +#include "cxxlog.h" +#include "gc_debug.h" + +#include +#include +#include +#include + +///////////////////////////////////////////// +int debug_num_roots_added = 0; +unsigned int num_of_live_objects = 0; +unsigned int live_bytes = 0; +unsigned int num_of_live_finalizable_objects = 0; +unsigned int live_finalizable_bytes = 0; +typedef unsigned char byte; +typedef POINTER_SIZE_INT pint; +pint heap_base, heap_ceiling; +static bool debug_gc_verify; +byte *mark_bits; +unsigned mark_bits_size; +unsigned gc_number = 0; + +#define OBJECT_ALIGNMENT_DOUBLE 8 +#define ALIGN(x, alignment) ((((pint)(x)) + alignment - 1) & (~(alignment-1))) +#define ALIGNED(x, alignment) (((((pint)(x)) & (alignment-1))) == 0) + +struct Object_Info { + const char *name; + bool is_array; + bool has_slots; + int size; // either object size or array element size + int* offsets; +}; + +struct Object { + VTable_Handle vt; //pointer to VT + uint32 lockword; +}; + +struct Array { + VTable *vt; + uint32 lockword; + uint32 length; +}; + +struct Object_Print { + void* obj; + const char* name; +}; + +static void (*real_gc_add_root_set_entry)(Managed_Object_Handle* root, Boolean pinned); +static void (*real_gc_add_finalizable_root_set_entry)(Managed_Object_Handle* root, Boolean pinned); +static void (*real_gc_class_prepared)(Class_Handle, VTable_Handle); + +static std::map infomap; +static std::vector debug_root_set; +static std::vector finalizable_root_set; +static std::stack debug_stack; +static std::list fingerprint; + +static int +array_length(Managed_Object_Handle p) { + Array* array = (Array*)p; + return array->length; +} + +Object_Info* obj_oi(Object* obj) { + TRACE2("debug.vtable", "object " << obj << " has vt_key: "<< obj->vt<< " and info: " << infomap[obj->vt]) + return infomap[obj->vt]; +} + +static int +object_size (Object* obj) { + Object_Info* oi = obj_oi(obj); + // align to GC_OBJECT_ALIGNMENT bytes + if (oi->is_array) { + if (oi->size > 4) { + int s = (oi->size * array_length(obj) + 16); + assert(ALIGNED(s,OBJECT_ALIGNMENT_DOUBLE)); + return s; + } + return ALIGN(oi->size * array_length(obj) + 12, GC_OBJECT_ALIGNMENT); + } else { + assert(ALIGNED(oi->size, GC_OBJECT_ALIGNMENT)); + return oi->size; + } +} + +static int *build_slot_offset_array(Class_Handle ch) +{ + unsigned num_ref_fields = 0; + unsigned num_fields = class_num_instance_fields_recursive(ch); + unsigned i; + for (i = 0; i < num_fields; i++) { + Field_Handle fh = class_get_instance_field_recursive(ch, i); + if (field_is_reference(fh)) { + num_ref_fields++; + } + } + + if (0 == num_ref_fields) return NULL; + + // malloc up the array if we need one. + int* ref_array = (int*) malloc((num_ref_fields+1) * sizeof(int)); + + int* p = ref_array; + for (i = 0; i < num_fields; i++) { + Field_Handle fh = class_get_instance_field_recursive(ch, i); + if (field_is_reference(fh)) { + *p = field_get_offset(fh); + p++; + } + } + + // It is 0 delimited. + *p = 0; + if (class_is_reference(ch)) { + // remove weak referent field from traced slot offset array + + int offset = class_get_referent_offset(ch); + TRACE2("debug.class", "class name: " << class_get_name(ch) << " offset from build...: " << offset); + int* poff = ref_array; + while (*poff && *poff != offset) { + poff++; + } + + if (*poff == offset) { + while (*(poff+1)) { + *poff = *(poff+1); + poff++; + } + *poff = 0; + } + } + + return ref_array; +} + +void subs_gc_add_root_set_entry(Managed_Object_Handle *d_ref, Boolean is_pinned) { + TRACE2("debug.roots", "subs_gc_add_root_set_entry"); + if(*d_ref == NULL) return; + debug_num_roots_added++; + debug_root_set.push_back((Object **)d_ref); + void* obj = *d_ref; + TRACE2("debug.roots", "root: " << d_ref << " object: " << obj << " num: " << debug_num_roots_added); + assert((pint)obj < heap_ceiling && (pint)obj >= heap_base); +} + +void +subs_gc_add_finalizable_root_set_entry(Managed_Object_Handle *d_ref, Boolean is_pinned) { + TRACE2("debug.roots", "subs_gc_finalizable_add_root_set_entry"); + if(*d_ref == NULL) return; + finalizable_root_set.push_back((Object **)d_ref); + void* obj = *d_ref; +// TRACE2("debug.roots", "finalizable_root: " << d_ref << " object: " << obj); + assert((pint)obj < heap_ceiling && (pint)obj >= heap_base); +} + +void subs_gc_class_prepared(Class_Handle ch, VTable_Handle vth) { + + Object_Info* oi = new Object_Info; + infomap[vth] = oi; + assert(oi == infomap[vth]); + TRACE2("debug.vtable", "table key (vt): " <name = class_get_name(ch); + + if (class_is_array(ch)) { + oi->is_array = true; + oi->size = class_element_size(ch); + oi->has_slots = !class_is_non_ref_array(ch); + assert(NULL == oi->offsets); + } else { + oi->is_array = false; + oi->size =ALIGN(class_get_boxed_data_size(ch), GC_OBJECT_ALIGNMENT); + assert(ALIGNED(oi->size, GC_OBJECT_ALIGNMENT)); + oi->offsets = build_slot_offset_array(ch); + oi->has_slots = (oi->offsets != NULL); + } + real_gc_class_prepared(ch,vth); +} + +void +debug_gc_setup() { + + // substituted gc functions + real_gc_add_root_set_entry = gc_add_root_set_entry; + gc_add_root_set_entry = subs_gc_add_root_set_entry; + real_gc_class_prepared = gc_class_prepared; + gc_class_prepared = subs_gc_class_prepared; + + real_gc_add_finalizable_root_set_entry = gc_add_finalizable_root_set_entry; + gc_add_finalizable_root_set_entry = subs_gc_add_finalizable_root_set_entry; + + heap_ceiling = (pint) gc_heap_ceiling_address(); + heap_base = (pint) gc_heap_base_address(); + +} + +static bool +mark_object(void *obj) { + assert((pint)obj < heap_ceiling && (pint)obj >= heap_base); + unsigned offset = (pint)obj - (pint)heap_base; + unsigned bitnum = offset / GC_OBJECT_ALIGNMENT; + unsigned index = bitnum / 8; + unsigned mask = 1 << (bitnum % 8); + assert(index < mark_bits_size); + bool unmarked = ((mark_bits[index] & mask) == 0); + mark_bits[index] = (mark_bits[index] | mask); + return unmarked; +} + +void initialize_global_variables() { + num_of_live_objects = 0; + live_bytes = 0; + debug_num_roots_added = 0; + num_of_live_finalizable_objects = 0; + live_finalizable_bytes = 0; +} + +static void +trace(Object *obj) { + TRACE2("debug.obj", "object to trace: " << obj); + assert(NULL != obj); + Object_Info *oi = obj_oi(obj); + TRACE2("debug.obj", "class name: " << oi->name <<" is array: "<is_array<< " has slots: " << oi->has_slots <<" offsets: " <offsets); + if (!oi->has_slots) return; + if (oi->is_array) { + int len = array_length(obj); + int i; + byte** elem = (byte**)((byte*)obj + 12); + for (i = 0; i < len; i++, elem += 1){ + if (NULL == *elem) continue; + if(mark_object(*elem)) { + TRACE2("debug.obj", "object " << (void*)obj << " points to " << (void*)*elem); + debug_stack.push((Object *)*elem); + } + } + } else { + int* poff; + for (poff = oi->offsets; *poff; poff++) { + byte** field = (byte**) ((byte*)obj + *poff); + if (NULL == *field) continue; + TRACE2("debug.obj", "poff " << poff << " *poff " << *poff); + TRACE2("debug.obj", "field " << (void*)field << " points to " << (void*)*field); + if(mark_object(*field)) { + debug_stack.push((Object *)(*field)); + } + } + } +} + +static void +debug_gc_trace_object(Object *obj) { + TRACE2("debug.obj", "simple object: " << obj); + assert((pint)obj < heap_ceiling && (pint)obj >= heap_base); + Object_Info *oi = obj_oi(obj); + num_of_live_objects += 1; + live_bytes += object_size(obj); + + + if (debug_gc_verify) { + Object_Print op = fingerprint.front(); + fingerprint.pop_front(); + if (op.name != oi->name) { + DIE2("gc.debug", "expected object: " << op.name + << " was at: " << op.obj << ", got: " + << oi->name << " at " << (void*)obj); + } + } else { + Object_Print op; + op.obj = (void*)obj; + op.name = oi->name; + fingerprint.push_back(op); + } + trace(obj); +} + +static void +debug_gc_trace_finalizable_object(Object *obj) { + num_of_live_finalizable_objects += 1; + live_finalizable_bytes += object_size(obj); + trace(obj); +} + +void +debug_gc_trace_heap(bool verify) { + TRACE2("debug.roots", "******************************* trace heap ******************************"); + //enumerate objects to be finalized before each trace + TRACE2("debug.roots", "number of objects in finalizable_root_set is before clear: " << finalizable_root_set.size()); + finalizable_root_set.clear(); + vm_enumerate_objects_to_be_finalized(); + TRACE2("debug.roots", "number of objects in finalizable_root_set after update enumeration is: " << finalizable_root_set.size()); + TRACE2("debug.roots", "number of objects in debug_root_set is: " << debug_root_set.size()); + + initialize_global_variables(); + assert(debug_stack.empty()); + debug_gc_verify = verify; + if (!debug_gc_verify) { + fingerprint.clear(); + gc_number++; + } + + Object *obj; + + if (NULL == mark_bits) { + // 4 is lowest object alignment + // 8 is bits in byte + mark_bits_size = (heap_ceiling - heap_base) / 4 / 8 + 1; + mark_bits = (byte*) malloc(mark_bits_size); assert(mark_bits); + LOG2("gc.debug", "allocated " << mark_bits_size/1024 << "k for debug gc markbits"); + } + + memset(mark_bits, 0 , mark_bits_size); + + std::vector::iterator i; + int k = 0; + for (i = debug_root_set.begin(); i != debug_root_set.end(); i++, k++) { + void *root = *i; + obj = *(*i); + if(obj == NULL) continue; + TRACE2("debug.roots", "*** debug_root_set root: " << root << " object: "<< obj <<" root number: " << k); + assert((pint)obj < heap_ceiling && (pint)obj >= heap_base); + if(mark_object(obj)) { + debug_gc_trace_object(obj); + while (!debug_stack.empty()) { + obj = debug_stack.top(); + debug_stack.pop(); + debug_gc_trace_object(obj); + } + } + } + + + std::vector::iterator j; + k = 0; + for (j = finalizable_root_set.begin(); j != finalizable_root_set.end(); j++, k++) { + void *root = *j; + obj = *(*j); + if(obj == NULL) continue; + TRACE2("debug.roots", "^^^ finalized_root_set root: " << root << " object: "<< obj <<" root number: " << k); + assert((pint)obj < heap_ceiling && (pint)obj >= heap_base); + if(mark_object(obj)) { + debug_gc_trace_finalizable_object(obj); + while (!debug_stack.empty()) { + obj = debug_stack.top(); + debug_stack.pop(); + debug_gc_trace_finalizable_object(obj); + } + } + } + + if(!debug_gc_verify) { + INFO("GC[" << gc_number - 1 << "] live objects: " << num_of_live_objects << " live bytes: " << live_bytes << " BEFORE GC"); + INFO("GC[" << gc_number - 1 << "] live finalizable objects: " << num_of_live_finalizable_objects << " live finalizable bytes: " << live_finalizable_bytes << " BEFORE GC"); + } + else { + INFO("GC[" << gc_number - 1 << "] live objects: " << num_of_live_objects << " live bytes: " << live_bytes << " AFTER GC"); + INFO("GC[" << gc_number - 1 << "] finalizable live objects: " << num_of_live_finalizable_objects << " finalizable live bytes: " << live_finalizable_bytes << " AFTER GC"); + } + + if(debug_gc_verify) { + debug_root_set.clear(); + } + +} + +void debug_gc_enumerate_roots() { + int root_number = 0; + int fin_root_number = 0; + Object *obj = NULL; + TRACE2("debug.roots", "number of objects in finalizable_root_set before enumeration to gc is: " << finalizable_root_set.size()); + TRACE2("debug.roots", "number of objects in debug_root_set before enumeration to gc is: " << debug_root_set.size()); + std::vector::iterator i; + for (i = debug_root_set.begin(); i != debug_root_set.end(); i++, root_number++) { + real_gc_add_root_set_entry((Managed_Object_Handle*)*i, FALSE); + TRACE2("debug.roots", "root passed to gc : " << *i << " " << (Object *)*(*i)); + } + INFO2("gc.debug", root_number << " roots passed down to real GC"); + + std::vector::iterator j; + for (j = finalizable_root_set.begin(); j != finalizable_root_set.end(); j++, fin_root_number++) { + real_gc_add_finalizable_root_set_entry((Managed_Object_Handle*)*j, FALSE); + TRACE2("debug.roots", " finalizable root passed to real gc : " << *j << " " << (Object *)*(*j)); + } + + INFO2("gc.debug", fin_root_number << " finalizable roots passed down to real GC"); +} diff --git a/vm/vmcore/src/gc/stop_the_world_root_set_enum.cpp b/vm/vmcore/src/gc/stop_the_world_root_set_enum.cpp index 305c3cb..65d287a 100644 --- a/vm/vmcore/src/gc/stop_the_world_root_set_enum.cpp +++ b/vm/vmcore/src/gc/stop_the_world_root_set_enum.cpp @@ -27,7 +27,9 @@ #include "unloading.h" #include "thread_manager.h" #include "interpreter.h" #include "finalize.h" - +#include "open/vm_util.h" // VM_Global_State +#include "environment.h" // Global_Env +#include "gc_debug.h" // debug_gc ////////// M E A S U R E M E N T of thread suspension time/////////// apr_time_t _start_time, _end_time; @@ -144,7 +146,10 @@ vm_enumerate_root_set_all_threads() assert(!tmn_is_suspend_enabled()); // vm_gc_unlock_enum expects suspend enabled, enable it here - + if (VM_Global_State::loader_env->debug_gc ) { + debug_gc_trace_heap(false); + debug_gc_enumerate_roots(); + } } //vm_enumerate_root_set_all_threads @@ -153,6 +158,9 @@ void vm_resume_threads_after() { TRACE2("vm.gc", "vm_resume_threads_after()"); // Check that we are still enumerating the universe and formally mark the end of it. + if (VM_Global_State::loader_env->debug_gc ) { + debug_gc_trace_heap(true); + } assert(get_global_safepoint_status() == enumerate_the_universe); global_safepoint_status = nill; assert(p_the_safepoint_control_thread == p_TLS_vmthread); diff --git a/vm/vmcore/src/init/vm.cpp b/vm/vmcore/src/init/vm.cpp index 1cde1a1..049f541 100644 --- a/vm/vmcore/src/init/vm.cpp +++ b/vm/vmcore/src/init/vm.cpp @@ -251,7 +251,8 @@ #endif // PLATFORM_POSIX true, FALSE}, {"vm.bootclasspath.appendclasspath", "Append classpath to the bootclasspath", true, FALSE}, - + {"vm.verify.gc", "Turn on gc_debug.", + true, FALSE}, // Non-boolean properties below. (sorted for convenience) {"vm.boot.library.path", "List of directories which contain additional dynamic libraries to load into VM"}, {"vm.boot.class.path", "Virtual machine bootclasspath"}, diff --git a/vm/vmcore/src/init/vm_main.cpp b/vm/vmcore/src/init/vm_main.cpp index e03e674..b057f4b 100644 --- a/vm/vmcore/src/init/vm_main.cpp +++ b/vm/vmcore/src/init/vm_main.cpp @@ -42,6 +42,7 @@ #include "dll_jit_intf.h" #include "dll_gc.h" #include "em_intf.h" #include "port_filepath.h" +#include "gc_debug.h" union Scalar_Arg { int i; @@ -354,6 +355,8 @@ #endif VM_Global_State::loader_env->pin_interned_strings = (bool)vm_get_property_value_boolean("vm.pin_interned_strings", FALSE); + VM_Global_State::loader_env->debug_gc = + (bool)vm_get_property_value_boolean("vm.verify.gc", FALSE); initialize_verify_stack_enumeration(); @@ -382,6 +385,10 @@ #endif // _WINDOWS // Initialize memory allocation vm_init_mem_alloc(); gc_init(); + if (VM_Global_State::loader_env->debug_gc) { + debug_gc_setup(); + } + gc_thread_init(&p_TLS_vmthread->_gc_private_information); // Prepares to load natives -- 1.3.GIT