blob: 615c6432404df87a01f831aeff990d87bb5b141b [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.
*/
/*
* JVMTI heap API
*/
#define LOG_DOMAIN "ti.roots"
#include "cxxlog.h"
// global headers
#include "open/gc.h"
#include "open/vm_gc.h"
#include "open/hythread_ext.h"
#include "jit_import_rt.h"
// VM headers
#include "jvmti_support.h"
#include "jthread.h"
#include "Class.h"
#include "cci.h"
#include "classloader.h"
#include "finalize.h"
#include "interpreter.h"
#include "jit_intf_cpp.h"
#include "m2n.h"
#include "object_handles.h"
#include "object_layout.h"
#include "root_set_enum_internal.h"
#include "stack_iterator.h"
#include "suspend_checker.h"
#include "thread_manager.h"
#include "vm_arrays.h"
// JVMTI headers
#include "jvmti_direct.h"
#include "jvmti_utils.h"
// private headers
#include "jvmti_heap.h"
#include "jvmti_tags.h"
#include "jvmti_trace.h"
/**
* calls stack root object callback.
*
* @param root is location of the root pointer (or interior pointer, or heap
* offset).
* @param obj is value of the root (differs from *root in case of interior
* pointers).
*/
void vm_ti_enumerate_stack_root(
jvmtiEnv* env,
void* root, Managed_Object_Handle obj,
jvmtiHeapRootKind root_kind,
int depth,
jmethodID method,
int slot)
{
// JVMTI does not care about NULL roots
if (NULL == obj) return;
TIEnv* ti_env = (TIEnv*)env;
TIIterationState *state = ti_env->iteration_state;
assert(state);
if (state->abort) {
// user requested iteration abort, so ignore root
return;
}
jvmtiIterationControl r = JVMTI_ITERATION_CONTINUE;
if (state->stack_ref_callback) {
tag_pair **tp = ti_get_object_tptr(obj);
jlong tag = (*tp != NULL ? (*tp)->tag : 0);
jlong class_tag = ti_get_object_class_tag(ti_env, obj);
jlong size = ti_get_object_size(ti_env, obj);
void* user_data = state->user_data;
jlong thread_tag = state->thread_tag;
r = state->stack_ref_callback(root_kind, class_tag, size, &tag,
thread_tag, depth, method, slot, user_data);
ti_env->tags->update(obj, tag, tp);
}
if (JVMTI_ITERATION_ABORT == r) {
state->abort = true;
} else if (JVMTI_ITERATION_CONTINUE == r) {
// push the reference to the mark stack for later tracing
assert(state->markstack);
if (ti_mark_object(obj, state)) {
state->markstack->push((ManagedObject*)obj);
}
}
}
/**
* calls heap root object callback.
*
* @param root is location of the root pointer (or interior pointer, or heap offset).
* @param obj is value of the root (differs from *root in case of interior pointers).
*/
void vm_ti_enumerate_heap_root(
jvmtiEnv* env,
void* root,
Managed_Object_Handle obj,
jvmtiHeapRootKind root_kind)
{
// JVMTI does not care about NULL roots
if (NULL == obj) return;
TIEnv* ti_env = (TIEnv*)env;
TIIterationState *state = ti_env->iteration_state;
assert(state);
if (state->abort) {
// user requested iteration abort, so ignore root
return;
}
jvmtiIterationControl r = JVMTI_ITERATION_CONTINUE;
if (state->heap_root_callback) {
tag_pair** tp = ti_get_object_tptr(obj);
jlong tag = ((*tp) != NULL ? (*tp)->tag : 0);
jlong class_tag = ti_get_object_class_tag(ti_env, obj);
jlong size = ti_get_object_size(ti_env, obj);
void* user_data = state->user_data;
r = state->heap_root_callback(root_kind, class_tag, size, &tag, user_data);
ti_env->tags->update(obj, tag, tp);
}
if (JVMTI_ITERATION_ABORT == r) {
state->abort = true;
} else if (JVMTI_ITERATION_CONTINUE == r) {
// push the reference to the mark stack for later tracing
assert(state->markstack);
if (ti_mark_object(obj, state)) {
state->markstack->push((ManagedObject*)obj);
}
}
}
/**
* calls root object callback, taking some information
* from TIIterationState.
*
* @param root is location of the root pointer (or interior pointer, or heap offset).
* @param obj is value of the root (differs from *root in case of interior pointers).
*/
static void ti_enumerate_root(void* root, Managed_Object_Handle obj)
{
TIEnv* ti_env = global_ti_env; // FIXME: load ti_env from TLS
TIIterationState *state = ti_env->iteration_state;
assert(state);
if (JVMTI_HEAP_ROOT_STACK_LOCAL == state->root_kind
|| JVMTI_HEAP_ROOT_JNI_LOCAL == state->root_kind) {
jint depth = (jint)state->depth;
jmethodID method = state->method;
jint slot = (jint)(((UDATA)state->frame_base - (UDATA)root)/sizeof(void*));
vm_ti_enumerate_stack_root((jvmtiEnv*)ti_env,
root, obj, state->root_kind,
depth, method, slot);
} else {
vm_ti_enumerate_heap_root((jvmtiEnv*)ti_env,
root, obj,
state->root_kind);
}
}
////////////////////////////////////////
// hijacked enumeration functions
//
static void ti_add_root_set_entry(
Managed_Object_Handle *root,
Boolean UNREF pinned)
{
TRACE2("ti.root", "ti root " << root << " -> " << *root);
ti_enumerate_root(root, *root);
}
static void ti_add_weak_root_set_entry(
Managed_Object_Handle *root,
Boolean UNREF pinned,
Boolean UNREF short_weak)
{
TRACE2("ti.root", "ti root " << root << " -> " << *root);
// XXX: should weak roots be enumerated?
ti_enumerate_root(root, *root);
}
static void ti_add_root_set_entry_interior_pointer(
void **slot,
int offset,
Boolean UNREF pinned)
{
Managed_Object_Handle obj = (Managed_Object_Handle)
((UDATA)*slot - offset);
ti_enumerate_root(slot, obj);
}
static void ti_add_compressed_root_set_entry(
U_32 *ref,
Boolean UNREF pinned)
{
assert(REFS_IS_COMPRESSED_MODE);
#ifndef REFS_USE_UNCOMPRESSED
Managed_Object_Handle obj = (Managed_Object_Handle)
uncompress_compressed_reference(*ref);
ti_enumerate_root(ref, obj);
#endif // REFS_USE_UNCOMPRESSED
}
//
// hijacked enumeration functions
////////////////////////////////////////
static void ti_enumerate_globals(TIEnv* ti_env)
{
// this function reimplements the function
// vm_enumerate_root_set_global_refs()
TIIterationState *state = ti_env->iteration_state;
state->root_kind = JVMTI_HEAP_ROOT_OTHER;
// Static fields of all classes
vm_enumerate_static_fields();
vm_enumerate_objects_to_be_finalized();
vm_enumerate_references_to_enqueue();
state->root_kind = JVMTI_HEAP_ROOT_JNI_GLOBAL;
oh_enumerate_global_handles();
state->root_kind = JVMTI_HEAP_ROOT_OTHER;
vm_enumerate_interned_strings();
state->root_kind = JVMTI_HEAP_ROOT_MONITOR;
extern void vm_enumerate_root_set_mon_arrays();
vm_enumerate_root_set_mon_arrays();
state->root_kind = JVMTI_HEAP_ROOT_SYSTEM_CLASS;
ClassLoader::gc_enumerate();
}
static void ti_enumerate_thread_not_on_stack(TIEnv* ti_env, VM_thread* thread)
{
TIIterationState *state = ti_env->iteration_state;
state->root_kind = JVMTI_HEAP_ROOT_THREAD;
assert(thread);
if (thread->thread_exception.exc_object != NULL) {
vm_enumerate_root_reference((void **)&(thread->thread_exception.exc_object), FALSE);
}
if (thread->thread_exception.exc_cause != NULL) {
vm_enumerate_root_reference((void **)&(thread->thread_exception.exc_cause), FALSE);
}
if (thread->jvmti_thread.p_exception_object_ti != NULL) {
vm_enumerate_root_reference((void **)&(thread->jvmti_thread.p_exception_object_ti), FALSE);
}
if (thread->native_handles)
((NativeObjectHandles*)(thread->native_handles))->enumerate();
if (thread->gc_frames) {
((GcFrame*)(thread->gc_frames))->enumerate();
}
}
static void ti_enumerate_thread_stack(TIEnv* ti_env, StackIterator* si)
{
ASSERT_NO_INTERPRETER
TIIterationState *state = ti_env->iteration_state;
state->depth = 0;
while (!si_is_past_end(si)) {
CodeChunkInfo* cci = si_get_code_chunk_info(si);
if (cci) {
state->method = (jmethodID)cci->get_method();
state->root_kind = JVMTI_HEAP_ROOT_STACK_LOCAL;
// FIXME: set up frame base (platform dependent!)
cci->get_jit()->get_root_set_from_stack_frame(cci->get_method(), 0, si_get_jit_context(si));
} else {
state->method = (jmethodID)m2n_get_method(si_get_m2n(si));
state->root_kind = JVMTI_HEAP_ROOT_JNI_LOCAL;
oh_enumerate_handles(m2n_get_local_handles(si_get_m2n(si)));
}
state->depth += 1;
si_goto_previous(si);
}
si_free(si);
}
void jitted_ti_enumerate_thread(jvmtiEnv *env, VM_thread *thread)
{
StackIterator* si;
si = si_create_from_native(thread);
ti_enumerate_thread_stack((TIEnv*)env, si);
// Enumerate references associated with a thread that are not stored on the thread's stack.
ti_enumerate_thread_not_on_stack((TIEnv*)env, thread);
}
static void ti_enumerate_thread(TIEnv *ti_env, VM_thread* thread)
{
TIIterationState *state = ti_env->iteration_state;
state->root_kind = JVMTI_HEAP_ROOT_THREAD;
state->thread_tag = ti_env->tags->get(
(Managed_Object_Handle)
jthread_self()->object);
if (interpreter_enabled()) {
interpreter.interpreter_ti_enumerate_thread((jvmtiEnv*)ti_env, thread);
} else {
jitted_ti_enumerate_thread((jvmtiEnv*)ti_env, thread);
}
}
void ti_enumerate_roots(TIEnv *ti_env, hythread_iterator_t iterator)
{
TRACE2("ti.trace", "enumerating roots");
// FIXME: weird function table manipulations
void (*save_gc_add_root_set_entry)
(Managed_Object_Handle *ref, Boolean pinned);
void (*save_gc_add_weak_root_set_entry)
(Managed_Object_Handle *ref1, Boolean pinned, Boolean short_weak);
void (*save_gc_add_root_set_entry_interior_pointer)
(void **slot, int offset, Boolean pinned);
void (*save_gc_add_compressed_root_set_entry)
(U_32 *ref, Boolean pinned);
// save away old values
save_gc_add_root_set_entry =
gc_add_root_set_entry;
save_gc_add_weak_root_set_entry =
gc_add_weak_root_set_entry;
save_gc_add_root_set_entry_interior_pointer =
gc_add_root_set_entry_interior_pointer;
save_gc_add_compressed_root_set_entry =
gc_add_compressed_root_set_entry;
// hijack ti enumeration functions
gc_add_root_set_entry =
ti_add_root_set_entry;
gc_add_weak_root_set_entry =
ti_add_weak_root_set_entry;
gc_add_root_set_entry_interior_pointer =
ti_add_root_set_entry_interior_pointer;
gc_add_compressed_root_set_entry =
ti_add_compressed_root_set_entry;
// Run through list of active threads and enumerate each one of them.
hythread_t tm_thread = hythread_iterator_next(&iterator);
while (tm_thread && !ti_env->iteration_state->abort) {
vm_thread_t thread = jthread_get_vm_thread(tm_thread);
if (thread)
ti_enumerate_thread(ti_env, thread);
tm_thread = hythread_iterator_next(&iterator);
}
// finally, process all the global refs
ti_enumerate_globals(ti_env);
// restore original enumeration functions
gc_add_root_set_entry =
save_gc_add_root_set_entry;
gc_add_weak_root_set_entry =
save_gc_add_weak_root_set_entry;
gc_add_root_set_entry_interior_pointer =
save_gc_add_root_set_entry_interior_pointer;
gc_add_compressed_root_set_entry =
save_gc_add_compressed_root_set_entry;
TRACE2("ti.trace", "completed root enumeration");
}