blob: 7f5c6c3b4e2dd16a1e90243a48f560036501e4f9 [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.
*/
/**
* @file
* Verify stack enumeration code by conservative scanning the stack
*/
#ifdef _DEBUG
/*
* BEWARE! This code is used in _DEBUG configuration only
*/
#include <set>
#define LOG_DOMAIN "verify.rse"
#include "cxxlog.h"
#include "open/gc.h"
#include "open/vm_properties.h"
#include "root_set_enum_internal.h"
#include "verify_stack_enumeration.h"
bool verify_stack_enumeration_flag = false; // true if verification is enabled
int verify_stack_enumeration_period = 1024; // verify each Nth safepoint
int verify_stack_enumeration_counter = 0; // safepoint counter
static int counter = 0; // verification scans counter
static void *heap_base = NULL;
static void *heap_ceiling = NULL;
static void** stack_top = NULL;
static void** stack_end = NULL;
static std::set<void**> roots;
static int size = 0;
static void register_root(void** root)
{
// register only stack roots
if (stack_top < root && root < stack_end) {
TRACE2("verify.rse.root", "registered stack root " << *root << " at " << root);
++size;
roots.insert(root);
}
}
typedef void (*add_root_set_entry_func)(Managed_Object_Handle *, Boolean);
static void add_root_set_entry(Managed_Object_Handle *ref, Boolean is_pinned)
{
TRACE2("verify.rse.root", "root " << *ref << " at " << ref);
register_root(ref);
}
typedef void (*add_weak_root_set_entry_func)(Managed_Object_Handle *ref, Boolean is_pinned,Boolean is_short_weak);
static void add_weak_root_set_entry(Managed_Object_Handle *ref, Boolean is_pinned,Boolean is_short_weak)
{
TRACE2("verify.rse.root", "weak root " << *ref << " at " << ref);
register_root(ref);
}
typedef void (*add_compressed_root_set_entry_func)(U_32 *ref, Boolean is_pinned);
static void add_compressed_root_set_entry(U_32 *ref, Boolean is_pinned)
{
TRACE("compressed root " << *ref << " at " << ref << ", not verified");
}
typedef void (*add_root_set_entry_interior_pointer_func)(void **ref, int offset, Boolean is_pinned);
static void add_root_set_entry_interior_pointer (void **ref, int offset, Boolean is_pinned)
{
TRACE("interior root " << *ref << "(-" << offset << ") at " << ref);
register_root(ref);
}
static bool pointer_looks_like_root(void* pointer)
{
// check just the pointer bounds for now
return ((pointer > heap_base) && (pointer < heap_ceiling));
}
void verify_stack_enumeration() {
// XXX: workaround to avoid infinite recursion
// due to suspend_enable() in vm_gc_lock_enum()
verify_stack_enumeration_flag = false;
// grab gc lock or block in suspend-enabled mode
vm_gc_lock_enum();
// we are guaranteed that noone will start
// enumeration while we are holding gc lock
TRACE("--- verify_stack_enumeration started [" << counter << "]---");
// cache heap boundaries
if (NULL == heap_base) { heap_base = gc_heap_base_address(); }
if (NULL == heap_ceiling) { heap_ceiling = gc_heap_ceiling_address(); }
// GC must be prevented from happening while we hijack the GC functions
assert(!hythread_is_suspend_enabled());
// save away the GC function pointer
add_root_set_entry_func gc_add_root_set_entry_saved
= gc_add_root_set_entry;
add_weak_root_set_entry_func gc_add_weak_root_set_entry_saved
= gc_add_weak_root_set_entry;
add_compressed_root_set_entry_func gc_add_compressed_root_set_entry_saved
= gc_add_compressed_root_set_entry;
add_root_set_entry_interior_pointer_func gc_add_root_set_entry_interior_pointer_saved
= gc_add_root_set_entry_interior_pointer;
// hijack to debug callback
gc_add_root_set_entry = &add_root_set_entry;
gc_add_weak_root_set_entry = &add_weak_root_set_entry;
gc_add_compressed_root_set_entry = &add_compressed_root_set_entry;
gc_add_root_set_entry_interior_pointer = &add_root_set_entry_interior_pointer;
assert(roots.empty());
// find out stack boundaries
VM_thread* thread = p_TLS_vmthread;
void* local;
stack_top = &local;
stack_end = thread->stack_end;
assert((((POINTER_SIZE_INT)stack_top) & (sizeof(void*)-1)) == 0
&& "pointer must be aligned");
assert((((POINTER_SIZE_INT)stack_end) & (sizeof(void*)-1)) == 0
&& "pointer must be aligned");
assert(stack_top < stack_end);
// NOTE: we assume thread enumeration happens on *this* thread
vm_enumerate_thread(p_TLS_vmthread);
// scan the stack
TRACE(" stack is from " << stack_top << " to " << stack_end);
void** word = stack_top;
std::set<void**>::iterator i;
while (word < stack_end) {
if (pointer_looks_like_root(*word)) {
i = roots.find(word);
if (i != roots.end()) {
TRACE2("verify.rse.root",
"pointer " << *word << " at " << word << " was enumerated");
// drop the root
roots.erase(i);
} else {
TRACE2("verify.rse.bug",
"pointer " << *word << " at " << word << " was not enumerated");
}
}
++word;
}
i = roots.begin();
while (i != roots.end()) {
if (*i) {
void** root = *i;
TRACE2("verify.rse.bug", "the root " << *root << " at " << root
<< " was not found during conservative stack scan");
}
++i;
}
// restore the GC function pointers
gc_add_root_set_entry = gc_add_root_set_entry_saved;
gc_add_weak_root_set_entry = gc_add_weak_root_set_entry_saved;
gc_add_compressed_root_set_entry = gc_add_compressed_root_set_entry_saved;
gc_add_root_set_entry_interior_pointer = gc_add_root_set_entry_interior_pointer_saved;
// free memory
roots.clear();
size = 0;
TRACE("--- completed verify_stack_enumeration [" << counter++ << "]---");
vm_gc_unlock_enum();
// XXX: workaround to avoid infinite recursion
// switch back to verification enabled
// mode after releasing gc lock.
verify_stack_enumeration_flag = true;
}
// Let it be literate :)
static const char* num_suffix(int n) {
if ((n/10)%10 == 1) return "th"; // 10-19
if (n%10 == 1) return "st"; // 1, 21, 31, ... 91
if (n%10 == 2) return "nd"; // 2, 22, 32, ... 92
if (n%10 == 3) return "rd"; // 3, 23, 33, ... 93
return "th";
}
void initialize_verify_stack_enumeration()
{
verify_stack_enumeration_flag = vm_property_get_boolean("verify.rse", false, VM_PROPERTIES);
if (verify_stack_enumeration_flag) {
INFO("verify stack enumeration mode");
int n = vm_property_get_integer("verify.rse.after", 0, VM_PROPERTIES);
if (n > 0) verify_stack_enumeration_counter = n;
INFO(">verify after " << verify_stack_enumeration_counter);
n = vm_property_get_integer("verify.rse.period", 0, VM_PROPERTIES);
if (n > 0) verify_stack_enumeration_period = n;
INFO(">verify each " << verify_stack_enumeration_period
<< num_suffix(verify_stack_enumeration_period) << " iteration");
}
}
#endif // _DEBUG