blob: c74fff7735db5e08cf77bb7ae98b14c945c98180 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 "wspace_verify.h"
#include "wspace_chunk.h"
#include "wspace_mark_sweep.h"
#include "../utils/vector_block.h"
#include "gc_ms.h"
#include "../gen/gen.h"
#include "../finalizer_weakref/finalizer_weakref.h"
#ifdef SSPACE_VERIFY
#define VERIFY_CARD_SIZE_BYTES_SHIFT 12
#define VERIFY_CARD_SIZE_BYTES (1 << VERIFY_CARD_SIZE_BYTES_SHIFT)
#define VERIFY_CARD_LOW_MASK (VERIFY_CARD_SIZE_BYTES - 1)
#define VERIFY_CARD_HIGH_MASK (~VERIFY_CARD_LOW_MASK)
#define VERIFY_MAX_OBJ_SIZE_BYTES (1 << (32-VERIFY_CARD_SIZE_BYTES_SHIFT))
typedef struct Verify_Card {
SpinLock lock;
Vector_Block *block;
} Verify_Card;
typedef unsigned int Obj_Addr;
static GC *gc_in_verify = NULL;
static Verify_Card *alloc_verify_cards = NULL;
static Verify_Card *mark_verify_cards = NULL;
static POINTER_SIZE_INT card_num = 0;
static volatile POINTER_SIZE_INT alloc_obj_num = 0;
static volatile POINTER_SIZE_INT live_obj_in_mark = 0;
static volatile POINTER_SIZE_INT live_obj_in_fix = 0;
void wspace_verify_init(GC *gc)
{
gc_in_verify = gc;
Wspace *wspace = gc_get_wspace(gc);
POINTER_SIZE_INT space_size = space_committed_size((Space*)wspace);
card_num = space_size >> VERIFY_CARD_SIZE_BYTES_SHIFT;
POINTER_SIZE_INT cards_size = sizeof(Verify_Card) * card_num;
alloc_verify_cards = (Verify_Card*)STD_MALLOC(cards_size);
memset(alloc_verify_cards, 0, cards_size);
mark_verify_cards = (Verify_Card*)STD_MALLOC(cards_size);
memset(mark_verify_cards, 0, cards_size);
}
static Obj_Addr compose_obj_addr(unsigned int offset, unsigned int size)
{
assert(size < VERIFY_MAX_OBJ_SIZE_BYTES);
return offset | (size << VERIFY_CARD_SIZE_BYTES_SHIFT);
}
static void *decompose_obj_addr(Obj_Addr obj_addr, POINTER_SIZE_INT card_index, unsigned int & size)
{
assert(card_index < card_num);
POINTER_SIZE_INT card_offset = obj_addr & VERIFY_CARD_LOW_MASK;
POINTER_SIZE_INT heap_offset = VERIFY_CARD_SIZE_BYTES * card_index + card_offset;
size = (obj_addr & VERIFY_CARD_HIGH_MASK) >> VERIFY_CARD_SIZE_BYTES_SHIFT;
assert(size < VERIFY_MAX_OBJ_SIZE_BYTES);
return (void*)(heap_offset + (POINTER_SIZE_INT)gc_heap_base(gc_in_verify));
}
static Boolean obj_addr_overlapped(Obj_Addr addr1, Obj_Addr addr2)
{
unsigned int offset1 = addr1 & VERIFY_CARD_LOW_MASK;
unsigned int size1 = (addr1 & VERIFY_CARD_HIGH_MASK) >> VERIFY_CARD_SIZE_BYTES_SHIFT;
unsigned int ceiling1 = offset1 + size1;
unsigned int offset2 = addr2 & VERIFY_CARD_LOW_MASK;
unsigned int size2 = (addr2 & VERIFY_CARD_HIGH_MASK) >> VERIFY_CARD_SIZE_BYTES_SHIFT;
unsigned int ceiling2 = offset2 + size2;
unsigned int reason = 0;
if(offset1 == offset2)
reason = 1;
if((offset1 < offset2) && (ceiling1 > offset2))
reason = 2;
if((offset2 < offset1) && (ceiling2 > offset1))
reason = 3;
if(!reason)
return FALSE;
printf("\nreason: %d\nold offset: %x size: %d\nnew offset: %x size: %d", reason, (POINTER_SIZE_INT)offset1, size1, (POINTER_SIZE_INT)offset2, size2);
return TRUE;
}
static Vector_Block *create_vector_block(unsigned int size)
{
Vector_Block *block = (Vector_Block*)STD_MALLOC(size);
vector_block_init(block, size);
return block;
}
static void verify_card_get_block(Verify_Card *card)
{
lock(card->lock);
if(card->block){
unlock(card->lock);
return;
}
card->block = create_vector_block(VECTOR_BLOCK_DATA_SIZE_BYTES);
unlock(card->lock);
}
void wspace_verify_alloc(void *addr, unsigned int size)
{
assert(address_belongs_to_gc_heap(addr, gc_in_verify));
atomic_inc32(&alloc_obj_num);
unsigned int heap_offset = (unsigned int)((POINTER_SIZE_INT)addr - (POINTER_SIZE_INT)gc_heap_base(gc_in_verify));
unsigned int card_offset = heap_offset & VERIFY_CARD_LOW_MASK;
Verify_Card *card = &alloc_verify_cards[heap_offset >> VERIFY_CARD_SIZE_BYTES_SHIFT];
verify_card_get_block(card);
Vector_Block *block = card->block;
Obj_Addr obj_addr = compose_obj_addr(card_offset, size);
lock(card->lock);
Obj_Addr *p_addr = block->head;
while(p_addr < block->tail){
assert(!obj_addr_overlapped(obj_addr, *p_addr));
p_addr++;
}
vector_block_add_entry(block, obj_addr);
unlock(card->lock);
}
/* size is rounded up size */
static Boolean obj_position_is_correct(void *addr, unsigned int size)
{
Chunk_Header *chunk = NULL;
if(size <= SUPER_OBJ_THRESHOLD)
chunk = NORMAL_CHUNK_HEADER(addr);
else
chunk = ABNORMAL_CHUNK_HEADER(addr);
if(chunk->slot_size != size) return FALSE;
if(((POINTER_SIZE_INT)addr - (POINTER_SIZE_INT)chunk->base) % size != 0) return FALSE;
return TRUE;
}
static void wspace_verify_weakref(Partial_Reveal_Object *p_obj)
{
WeakReferenceType type = special_reference_type(p_obj);
if(type == NOT_REFERENCE) return;
REF *p_referent_field = obj_get_referent_field(p_obj);
Partial_Reveal_Object *p_referent = read_slot(p_referent_field);
if (!p_referent) return;
unsigned int size = vm_object_size(p_referent);
if(size <= SUPER_OBJ_THRESHOLD){
Wspace *wspace = gc_get_wspace(gc_in_verify);
Size_Segment *size_seg = wspace_get_size_seg(wspace, size);
size = NORMAL_SIZE_ROUNDUP(size, size_seg);
}
assert(obj_position_is_correct(p_referent, size));
}
static void mark_card_add_entry(void *addr, unsigned int size)
{
assert(address_belongs_to_gc_heap(addr, gc_in_verify));
unsigned int heap_offset = (unsigned int)((POINTER_SIZE_INT)addr - (POINTER_SIZE_INT)gc_heap_base(gc_in_verify));
unsigned int card_offset = heap_offset & VERIFY_CARD_LOW_MASK;
Verify_Card *card = &mark_verify_cards[heap_offset >> VERIFY_CARD_SIZE_BYTES_SHIFT];
verify_card_get_block(card);
Vector_Block *block = card->block;
if(size <= SUPER_OBJ_THRESHOLD){
Wspace *wspace = gc_get_wspace(gc_in_verify);
Size_Segment *size_seg = wspace_get_size_seg(wspace, size);
size = NORMAL_SIZE_ROUNDUP(size, size_seg);
}
assert(obj_position_is_correct(addr, size));
Obj_Addr obj_addr = compose_obj_addr(card_offset, size);
lock(card->lock);
Obj_Addr *p_addr = block->head;
while(p_addr < block->tail){
assert(!obj_addr_overlapped(obj_addr, *p_addr));
p_addr++;
}
vector_block_add_entry(block, obj_addr);
unlock(card->lock);
}
/* size is real size of obj */
void wspace_record_mark(void *addr, unsigned int size)
{
atomic_inc32(&live_obj_in_mark);
mark_card_add_entry(addr, size);
}
static void verify_mark(void *addr, unsigned int size, Boolean destructively)
{
assert(address_belongs_to_gc_heap(addr, gc_in_verify));
unsigned int heap_offset = (unsigned int)((POINTER_SIZE_INT)addr - (POINTER_SIZE_INT)gc_heap_base(gc_in_verify));
unsigned int card_offset = heap_offset & VERIFY_CARD_LOW_MASK;
Verify_Card *card = &mark_verify_cards[heap_offset >> VERIFY_CARD_SIZE_BYTES_SHIFT];
Vector_Block *block = card->block;
assert(block);
Obj_Addr obj_addr = compose_obj_addr(card_offset, size);
Obj_Addr *p_addr = block->head;
while(p_addr < block->tail){
if(obj_addr == *p_addr){
if(destructively)
*p_addr = 0;
break;
}
p_addr++;
}
assert(p_addr < block->tail);
}
void wspace_modify_mark_in_compact(void *new_addr, void *old_addr, unsigned int size)
{
/* Verify the old addr and remove it in the according mark card */
verify_mark(old_addr, size, TRUE);
/* Add new_addr into mark card */
mark_card_add_entry(new_addr, size);
}
void wspace_verify_fix_in_compact(void)
{
atomic_inc32(&live_obj_in_fix);
}
static void check_and_clear_mark_cards(void)
{
for(POINTER_SIZE_INT i=0; i<card_num; i++){
Vector_Block *block = mark_verify_cards[i].block;
if(!block)
continue;
Obj_Addr *p_addr = block->head;
while(p_addr < block->tail){
if(*p_addr){
unsigned int size = 0;
void *addr = NULL;
addr = decompose_obj_addr(*p_addr, i, size);
printf("Extra mark obj: %x size: %d\n", (POINTER_SIZE_INT)addr, size);
}
p_addr++;
}
vector_block_clear(block);
}
}
static void clear_alloc_cards(void)
{
for(POINTER_SIZE_INT i=0; i<card_num; i++){
Verify_Card *card = &alloc_verify_cards[i];
if(card->block)
vector_block_clear(card->block);
}
}
static void summarize_sweep_verify(GC *gc)
{
POINTER_SIZE_INT live_obj_num = 0;
for(unsigned int i=0; i<gc->num_collectors; ++i){
live_obj_num += gc->collectors[i]->live_obj_num;
}
printf("Live obj in sweeping: %d\n", live_obj_num);
}
void wspace_verify_free_area(POINTER_SIZE_INT *start, POINTER_SIZE_INT size)
{
POINTER_SIZE_INT *p_value = start;
assert(!(size % BYTES_PER_WORD));
size /= BYTES_PER_WORD;
while(size--)
assert(!*p_value++);
}
static POINTER_SIZE_INT wspace_live_obj_num(Wspace *wspace, Boolean gc_finished)
{
Chunk_Header *chunk = (Chunk_Header*)space_heap_start((Space*)wspace);
Chunk_Header *wspace_ceiling = (Chunk_Header*)space_heap_end((Space*)wspace);
POINTER_SIZE_INT live_num = 0;
for(; chunk < wspace_ceiling; chunk = (Chunk_Header*)CHUNK_END(chunk)){
/* chunk is free before GC */
if(chunk->status & CHUNK_FREE){
assert((gc_finished && chunk->status==CHUNK_FREE)
|| (!gc_finished && chunk->status==(CHUNK_FREE|CHUNK_TO_MERGE)));
continue;
}
if(chunk->status & CHUNK_ABNORMAL){
assert(chunk->status == CHUNK_ABNORMAL);
assert(chunk->slot_size > SUPER_OBJ_THRESHOLD);
Partial_Reveal_Object *obj = (Partial_Reveal_Object*)chunk->base;
assert(chunk->slot_size == vm_object_size(obj));
assert(get_obj_info_raw(obj) & SUPER_OBJ_MASK);
}
/* chunk is used as a normal or abnormal one in which there are live objects */
unsigned int slot_num = chunk->slot_num;
POINTER_SIZE_INT *table = chunk->table;
POINTER_SIZE_INT live_num_in_chunk = 0;
unsigned int word_index = 0;
for(unsigned int i=0; i<slot_num; ++i){
unsigned int color_index = COLOR_BITS_PER_OBJ * i;
word_index = color_index / BITS_PER_WORD;
void *p_obj = slot_index_to_addr(chunk, i);
if(table[word_index] & (cur_alloc_color << (color_index % BITS_PER_WORD))){
++live_num_in_chunk;
verify_mark(p_obj, chunk->slot_size, gc_finished);
if(gc_finished){
wspace_verify_alloc(p_obj, chunk->slot_size);
wspace_verify_weakref((Partial_Reveal_Object*)p_obj);
}
}
}
live_num += live_num_in_chunk;
}
return live_num;
}
static void allocator_verify_local_chunks(Allocator *allocator)
{
Wspace *wspace = gc_get_wspace(allocator->gc);
Size_Segment **size_segs = wspace->size_segments;
Chunk_Header ***local_chunks = allocator->local_chunks;
for(unsigned int i = SIZE_SEGMENT_NUM; i--;){
if(!size_segs[i]->local_alloc){
assert(!local_chunks[i]);
continue;
}
Chunk_Header **chunks = local_chunks[i];
assert(chunks);
for(unsigned int j = size_segs[i]->chunk_num; j--;){
assert(!chunks[j]);
}
}
}
static void gc_verify_allocator_local_chunks(GC *gc)
{
if(major_is_marksweep()){
Mutator *mutator = gc->mutator_list;
while(mutator){
allocator_verify_local_chunks((Allocator*)mutator);
mutator = mutator->next;
}
}
if(collect_is_major())
for(unsigned int i = gc->num_collectors; i--;){
allocator_verify_local_chunks((Allocator*)gc->collectors[i]);
}
}
void wspace_verify_before_collection(GC *gc)
{
printf("Allocated obj: %d\n", alloc_obj_num);
alloc_obj_num = 0;
}
void wspace_verify_after_sweep(GC *gc)
{
printf("Live obj in marking: %d\n", live_obj_in_mark);
live_obj_in_mark = 0;
summarize_sweep_verify(gc);
Wspace *wspace = gc_get_wspace(gc);
POINTER_SIZE_INT total_live_obj = wspace_live_obj_num(wspace, FALSE);
printf("Live obj after sweep: %d\n", total_live_obj);
}
void wspace_verify_after_collection(GC *gc)
{
printf("Live obj in fixing: %d\n", live_obj_in_fix);
live_obj_in_fix = 0;
clear_alloc_cards();
Wspace *wspace = gc_get_wspace(gc);
POINTER_SIZE_INT total_live_obj = wspace_live_obj_num(wspace, TRUE);
printf("Live obj after collection: %d\n", total_live_obj);
check_and_clear_mark_cards();
gc_verify_allocator_local_chunks(gc);
}
/*
void wspace_verify_super_obj(GC *gc)
{
Wspace *wspace = gc_get_wspace(gc);
Chunk_Header *chunk = (Chunk_Header*)space_heap_start((Space*)wspace);
Chunk_Header *wspace_ceiling = (Chunk_Header*)space_heap_end((Space*)wspace);
for(; chunk < wspace_ceiling; chunk = (Chunk_Header*)CHUNK_END(chunk)){
if(chunk->status & CHUNK_ABNORMAL){
assert(chunk->status == CHUNK_ABNORMAL);
assert(chunk->slot_size > SUPER_OBJ_THRESHOLD);
Partial_Reveal_Object *obj = (Partial_Reveal_Object*)chunk->base;
assert(chunk->slot_size == vm_object_size(obj));
assert(get_obj_info_raw(obj) & SUPER_OBJ_MASK);
}
}
}
*/
/* wspace verify marking with vtable marking in advance */
Wspace *wspace_in_verifier;
static Pool *trace_pool = NULL;
static Vector_Block *trace_stack = NULL;
POINTER_SIZE_INT live_obj_in_verify_marking = 0;
static Boolean obj_mark_in_vtable(GC *gc, Partial_Reveal_Object *obj)
{
assert(address_belongs_to_gc_heap(obj, gc));
assert((vm_object_size(obj) <= SUPER_OBJ_THRESHOLD) || (get_obj_info_raw(obj) & SUPER_OBJ_MASK));
Boolean marked = obj_mark_in_vt(obj);
#ifdef SSPACE_VERIFY
if(marked) live_obj_in_verify_marking++;
#endif
return marked;
}
static void tracestack_push(void *p_obj)
{
vector_stack_push(trace_stack, (POINTER_SIZE_INT)p_obj);
if( !vector_stack_is_full(trace_stack)) return;
pool_put_entry(trace_pool, trace_stack);
trace_stack = free_task_pool_get_entry(&gc_metadata);
assert(trace_stack);
}
static FORCE_INLINE void scan_slot(GC *gc, REF *p_ref)
{
Partial_Reveal_Object *p_obj = read_slot(p_ref);
if( p_obj == NULL) return;
if(obj_belongs_to_space(p_obj, (Space*)wspace_in_verifier) && obj_mark_in_vtable(gc, p_obj))
tracestack_push(p_obj);
return;
}
static FORCE_INLINE void scan_object(GC *gc, Partial_Reveal_Object *p_obj)
{
if(!object_has_ref_field(p_obj) ) return;
REF *p_ref;
if (object_is_array(p_obj)) { /* scan array object */
Partial_Reveal_Array *array = (Partial_Reveal_Array*)p_obj;
unsigned int array_length = array->array_len;
p_ref = (REF*)((POINTER_SIZE_INT)array + (int)array_first_element_offset(array));
for (unsigned int i = 0; i < array_length; i++) {
scan_slot(gc, p_ref+i);
}
} else { /* scan non-array object */
unsigned int num_refs = object_ref_field_num(p_obj);
int *ref_iterator = object_ref_iterator_init(p_obj);
for(unsigned int i=0; i<num_refs; i++){
p_ref = object_ref_iterator_get(ref_iterator+i, p_obj);
scan_slot(gc, p_ref);
}
#ifndef BUILD_IN_REFERENT
//scan_weak_reference(collector, p_obj, scan_slot);
#endif
}
}
static void trace_object(GC *gc, Partial_Reveal_Object *p_obj)
{
scan_object(gc, p_obj);
while( !vector_stack_is_empty(trace_stack)){
p_obj = (Partial_Reveal_Object *)vector_stack_pop(trace_stack);
scan_object(gc, p_obj);
}
}
void wspace_verify_vtable_mark(GC *gc)
{
wspace_in_verifier = gc_get_wspace(gc);
GC_Metadata *metadata = gc->metadata;
Pool *rootset_pool = metadata->gc_rootset_pool;
trace_stack = free_task_pool_get_entry(metadata);
trace_pool = sync_pool_create();
pool_iterator_init(rootset_pool);
Vector_Block *root_set = pool_iterator_next(rootset_pool);
while(root_set){
POINTER_SIZE_INT *iter = vector_block_iterator_init(root_set);
while(!vector_block_iterator_end(root_set, iter)){
REF *p_ref = (REF*)*iter;
iter = vector_block_iterator_advance(root_set, iter);
Partial_Reveal_Object *p_obj = read_slot(p_ref);
assert(p_obj!=NULL);
if(obj_belongs_to_space(p_obj, (Space*)wspace_in_verifier) && obj_mark_in_vtable(gc, p_obj))
tracestack_push(p_obj);
}
root_set = pool_iterator_next(metadata->gc_rootset_pool);
}
/* put back the last trace_stack task */
pool_put_entry(trace_pool, trace_stack);
/* second step: iterate over the mark tasks and scan objects */
/* get a task buf for the mark stack */
trace_stack = free_task_pool_get_entry(metadata);
Vector_Block *mark_task = pool_get_entry(trace_pool);
while(mark_task){
POINTER_SIZE_INT *iter = vector_block_iterator_init(mark_task);
while(!vector_block_iterator_end(mark_task, iter)){
Partial_Reveal_Object *p_obj = (Partial_Reveal_Object*)*iter;
iter = vector_block_iterator_advance(mark_task, iter);
trace_object(gc, p_obj);
}
/* run out one task, put back to the pool and grab another task */
vector_stack_clear(mark_task);
pool_put_entry(metadata->free_task_pool, mark_task);
mark_task = pool_get_entry(trace_pool);
}
/* put back the last mark stack to the free pool */
vector_stack_clear(trace_stack);
pool_put_entry(metadata->free_task_pool, trace_stack);
trace_stack = NULL;
sync_pool_destruct(trace_pool);
trace_pool = NULL;
printf("Live obj in vtable marking: %d\n", live_obj_in_verify_marking);
live_obj_in_verify_marking = 0;
}
#endif
#ifdef SSPACE_TIME
inline uint64 tsc()
{
__asm _emit 0x0F;
__asm _emit 0x31
}
#define CPU_HZ 3000000 // per ms
static uint64 gc_start_time;
static uint64 mark_start_time;
static uint64 sweep_start_time;
static uint64 compact_start_time;
static uint64 fix_start_time;
static uint64 merge_start_time;
void wspace_gc_time(GC *gc, Boolean before_gc)
{
if(before_gc){
gc_start_time = tsc();
mark_start_time = gc_start_time;
} else {
uint64 end_time = tsc();
assert(end_time > gc_start_time);
printf("\n\nGC %d time: %dms\n\n", gc->num_collections, (end_time-gc_start_time) / CPU_HZ);
}
}
void wspace_mark_time(Boolean before_mark)
{
assert(before_mark == FALSE);
if(before_mark){
mark_start_time = tsc();
} else {
uint64 end_time = tsc();
assert(end_time > mark_start_time);
printf("\nMark time: %dms\n", (end_time-mark_start_time) / CPU_HZ);
sweep_start_time = end_time;
}
}
void wspace_sweep_time(Boolean before_sweep, Boolean wspace_need_compact)
{
assert(before_sweep == FALSE);
if(before_sweep){
sweep_start_time = tsc();
} else {
uint64 end_time = tsc();
assert(end_time > sweep_start_time);
printf("\nSweep time: %dms\n", (end_time-sweep_start_time) / CPU_HZ);
if(wspace_need_compact)
compact_start_time = end_time;
else
merge_start_time = end_time;
}
}
void wspace_compact_time(Boolean before_compact)
{
assert(before_compact == FALSE);
if(before_compact){
compact_start_time = tsc();
} else {
uint64 end_time = tsc();
assert(end_time > compact_start_time);
printf("\nCompact time: %dms\n", (end_time-compact_start_time) / CPU_HZ);
fix_start_time = end_time;
}
}
void wspace_fix_time(Boolean before_fix)
{
assert(before_fix == FALSE);
if(before_fix){
fix_start_time = tsc();
} else {
uint64 end_time = tsc();
assert(end_time > fix_start_time);
printf("\nFix time: %dms\n", (end_time-fix_start_time) / CPU_HZ);
merge_start_time = end_time;
}
}
void wspace_merge_time(Boolean before_merge)
{
assert(before_merge == FALSE);
if(before_merge){
merge_start_time = tsc();
} else {
uint64 end_time = tsc();
assert(end_time > merge_start_time);
printf("\nMerge time: %dms\n\n", (end_time-merge_start_time) / CPU_HZ);
}
}
#endif