blob: ff95b2c2939e50573ac8db5b49037ef94b54e748 [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.
*/
/**
* @author Salikh Zakirov
*/
/*
* JVMTI heap API
*/
#include "cxxlog.h"
#include "port_mutex.h"
#include "jvmti_direct.h"
#include "jvmti_utils.h"
#include "open/gc.h"
#include "open/vm_gc.h"
#include "open/hythread_ext.h"
#include "object_layout.h"
#include "thread_manager.h"
#include "suspend_checker.h"
#include "vm_arrays.h"
#include "object_handles.h"
#include "vm_log.h"
// private module headers
#include "jvmti_heap.h"
#include "jvmti_roots.h"
#include "jvmti_tags.h"
#include "jvmti_trace.h"
// FIXME: use TLS to store ti_env
TIEnv* global_ti_env;
/*
* Get Tag
*
* Retrieve the tag associated with an object. The tag is a long
* value typically used to store a unique identifier or pointer to
* object information. The tag is set with SetTag. Objects for
* which no tags have been set return a tag value of zero.
*
* OPTIONAL Functionality
*/
jvmtiError JNICALL
jvmtiGetTag(jvmtiEnv* env,
jobject object,
jlong* tag_ptr)
{
TRACE2("jvmti.heap", "GetTag called");
SuspendEnabledChecker sec;
/*
* Check given env & current phase.
*/
jvmtiPhase phases[] = {JVMTI_PHASE_START, JVMTI_PHASE_LIVE};
CHECK_EVERYTHING();
TIEnv* ti_env = reinterpret_cast<TIEnv *>(env);
if (!ti_env->posessed_capabilities.can_tag_objects)
return JVMTI_ERROR_MUST_POSSESS_CAPABILITY;
if (!tag_ptr)
return JVMTI_ERROR_NULL_POINTER;
if (!is_jobject_valid(object))
return JVMTI_ERROR_INVALID_OBJECT;
if (ti_env->tags == NULL) {
*tag_ptr = 0;
return JVMTI_ERROR_NONE;
}
tmn_suspend_disable(); // ----vv
Managed_Object_Handle obj = object->object;
*tag_ptr = ti_env->tags->get(obj);
tmn_suspend_enable(); // ----^^
return JVMTI_ERROR_NONE;
}
/*
* Set Tag
*
* Set the tag associated with an object. The tag is a long value
* typically used to store a unique identifier or pointer to object
* information. The tag is visible with GetTag.
*
* OPTIONAL Functionality
*/
jvmtiError JNICALL
jvmtiSetTag(jvmtiEnv* env,
jobject object,
jlong tag)
{
TRACE2("jvmti.heap", "SetTag called");
SuspendEnabledChecker sec;
/*
* Check given env & current phase.
*/
jvmtiPhase phases[] = {JVMTI_PHASE_START, JVMTI_PHASE_LIVE};
CHECK_EVERYTHING();
TIEnv* ti_env = reinterpret_cast<TIEnv *>(env);
if (!ti_env->posessed_capabilities.can_tag_objects)
return JVMTI_ERROR_MUST_POSSESS_CAPABILITY;
if (!is_jobject_valid(object))
return JVMTI_ERROR_INVALID_OBJECT;
if (ti_env->tags == NULL) {
port_mutex_lock(&ti_env->environment_data_lock);
if (ti_env->tags == NULL) {
ti_env->tags = new TITags;
}
port_mutex_unlock(&ti_env->environment_data_lock);
}
if (ti_env->tags == NULL) {
return JVMTI_ERROR_OUT_OF_MEMORY;
}
tmn_suspend_disable(); // ----vv
Managed_Object_Handle obj = object->object;
ti_env->tags->set(obj, tag);
tmn_suspend_enable(); // ----^^
return JVMTI_ERROR_NONE;
}
/*
* Force Garbage Collection
*
* Force the VM to perform a garbage collection. The garbage
* collection is as complete as possible. This function does
* not cause finalizers to be run. This function does not return
* until the garbage collection is finished.
*
* REQUIRED Functionality
*/
jvmtiError JNICALL
jvmtiForceGarbageCollection(jvmtiEnv* env)
{
TRACE2("jvmti.heap", "ForceGarbageCollection called");
SuspendEnabledChecker sec;
/*
* Check given env & current phase.
*/
jvmtiPhase phases[] = {JVMTI_PHASE_LIVE};
CHECK_EVERYTHING();
gc_force_gc();
return JVMTI_ERROR_NONE;
}
static jvmtiError allocate_iteration_state(TIEnv* ti_env)
{
assert(NULL == ti_env->iteration_state);
ti_env->iteration_state = new TIIterationState;
TIIterationState* state = ti_env->iteration_state;
if (NULL == state) {
return JVMTI_ERROR_OUT_OF_MEMORY;
}
memset(state, 0, sizeof(TIIterationState));
// we trust in correct values of global values
// Global_Env::heap_base and Global_Env::heap_end
assert((UDATA)ti_env->vm->vm_env->heap_base == (UDATA)gc_heap_base_address());
assert((UDATA)ti_env->vm->vm_env->heap_end == (UDATA)gc_heap_ceiling_address());
state->markbits_size = ((UDATA)ti_env->vm->vm_env->heap_end
- (UDATA)ti_env->vm->vm_env->heap_base) / GC_OBJECT_ALIGNMENT / 8;
state->markbits = new unsigned char[state->markbits_size];
if (state->markbits == NULL) {
delete state;
ti_env->iteration_state = NULL;
return JVMTI_ERROR_OUT_OF_MEMORY;
}
state->markstack = new std::stack<ManagedObject*>;
if (state->markstack == NULL) {
delete[] state->markbits;
delete state;
ti_env->iteration_state = NULL;
return JVMTI_ERROR_OUT_OF_MEMORY;
}
memset(state->markbits, 0, state->markbits_size);
return JVMTI_ERROR_NONE;
}
static void free_iteration_state(TIEnv* ti_env)
{
assert(ti_env->iteration_state);
assert(ti_env->iteration_state->markstack);
assert(ti_env->iteration_state->markbits);
delete ti_env->iteration_state->markstack;
delete[] ti_env->iteration_state->markbits;
delete ti_env->iteration_state;
ti_env->iteration_state = NULL;
}
/*
* Iterate Over Objects Reachable From Object
*
* This function iterates over all objects that are directly and
* indirectly reachable from the specified object.
*
* OPTIONAL Functionality
*/
jvmtiError JNICALL
jvmtiIterateOverObjectsReachableFromObject(jvmtiEnv* env,
jobject object,
jvmtiObjectReferenceCallback object_ref_callback,
void* user_data)
{
TRACE2("jvmti.heap", "IterateOverObjectsReachableFromObject called");
SuspendEnabledChecker sec;
/*
* Check given env & current phase.
*/
jvmtiPhase phases[] = {JVMTI_PHASE_LIVE};
CHECK_EVERYTHING();
TIEnv* ti_env = reinterpret_cast<TIEnv *>(env);
if (!ti_env->posessed_capabilities.can_tag_objects)
return JVMTI_ERROR_MUST_POSSESS_CAPABILITY;
if (!object_ref_callback)
return JVMTI_ERROR_NULL_POINTER;
if (!is_jobject_valid(object))
return JVMTI_ERROR_INVALID_OBJECT;
hythread_global_lock();
jvmtiError r;
r = allocate_iteration_state(ti_env);
if (r != JVMTI_ERROR_NONE) {
hythread_global_unlock();
return r;
}
hythread_suspend_disable(); // to keep assertions happy
hythread_iterator_t iterator;
hythread_suspend_all(&iterator, NULL);
global_ti_env = ti_env; // FIXME: use TLS to store TIEnv pointer
ti_env->iteration_state->user_data = user_data;
ti_env->iteration_state->object_ref_callback = object_ref_callback;
ti_env->iteration_state->markstack->push((ManagedObject*)object->object);
ti_trace_heap(ti_env);
free_iteration_state(ti_env);
TRACE2("ti.iterate", "iteration complete");
hythread_resume_all(NULL);
hythread_suspend_enable();
hythread_global_unlock();
return JVMTI_ERROR_NONE;
}
static void ti_iterate_reachable(TIEnv* ti_env,
hythread_iterator_t iterator)
{
// enumerate roots
ti_enumerate_roots(ti_env, iterator);
// check if we don't need to trace heap
if (ti_env->iteration_state->abort
|| ti_env->iteration_state->object_ref_callback == NULL) return;
ti_trace_heap(ti_env);
}
/*
* Iterate Over Reachable Objects
*
* This function iterates over the root objects and all objects
* that are directly and indirectly reachable from the root
* objects. The root objects comprise the set of system classes,
* JNI globals, references from thread stacks, and other objects
* used as roots for the purposes of garbage collection.
*
* OPTIONAL Functionality
*/
jvmtiError JNICALL
jvmtiIterateOverReachableObjects(jvmtiEnv* env,
jvmtiHeapRootCallback heap_root_callback,
jvmtiStackReferenceCallback stack_ref_callback,
jvmtiObjectReferenceCallback object_ref_callback,
void* user_data)
{
TRACE2("jvmti.heap", "IterateOverReachableObjects called");
SuspendEnabledChecker sec;
/*
* Check given env & current phase.
*/
jvmtiPhase phases[] = {JVMTI_PHASE_LIVE};
CHECK_EVERYTHING();
TIEnv* ti_env = reinterpret_cast<TIEnv *>(env);
if (!ti_env->posessed_capabilities.can_tag_objects)
return JVMTI_ERROR_MUST_POSSESS_CAPABILITY;
hythread_global_lock();
jvmtiError r;
r = allocate_iteration_state(ti_env);
if (r != JVMTI_ERROR_NONE) {
hythread_global_unlock();
return r;
}
hythread_suspend_disable(); // to keep assertions happy
hythread_iterator_t iterator;
hythread_suspend_all(&iterator, NULL);
global_ti_env = ti_env; // FIXME: use TLS to store TIEnv pointer
ti_env->iteration_state->user_data = user_data;
ti_env->iteration_state->heap_root_callback = heap_root_callback;
ti_env->iteration_state->stack_ref_callback = stack_ref_callback;
ti_env->iteration_state->object_ref_callback = object_ref_callback;
ti_iterate_reachable(ti_env, iterator);
free_iteration_state(ti_env);
TRACE2("ti.iterate", "iteration complete");
hythread_resume_all(NULL);
hythread_suspend_enable();
hythread_global_unlock();
return JVMTI_ERROR_NONE;
}
bool vm_iterate_object(Managed_Object_Handle obj)
{
TIEnv* ti_env = global_ti_env; // FIXME: use TLS to store ti_env
TRACE2("vm.iterate", "vm_iterate_object " << (ManagedObject*)obj);
// Note: It is ok for ti_env->tags to be NULL at this point.
jlong class_tag = ti_get_object_class_tag(ti_env, obj);
tag_pair** tp = ti_get_object_tptr(obj);
jlong tag = (*tp != NULL ? (*tp)->tag : 0);
jlong size = ti_get_object_size(ti_env, obj);
TIIterationState *state = ti_env->iteration_state;
assert(state);
void* user_data = state->user_data;
jvmtiHeapObjectCallback heap_object_callback
= state->heap_object_callback;
if (state->class_filter != NULL &&
((ManagedObject*)obj)->vt()->clss != state->class_filter) {
// do not run user callback if we were
// asked to iterate instances of specified class
return true;
}
if (state->object_filter == JVMTI_HEAP_OBJECT_UNTAGGED && tag != 0) {
// do not run user callback for tagged objects
// if we were asked for untagged objects only
return true;
}
// in JVMTI_HEAP_OBJECT_TAGGED case, we should only get tagged objects
assert(!(state->object_filter == JVMTI_HEAP_OBJECT_TAGGED && tag == 0));
jvmtiIterationControl r =
heap_object_callback(class_tag, size, &tag, user_data);
// update tag value or remove tag
ti_env->tags->update(obj, tag, tp);
// return true to continue iteration, false to terminate it
return (JVMTI_ITERATION_CONTINUE == r);
}
/*
* Iterate Over Heap
*
* Iterate over all objects in the heap. This includes both
* reachable and unreachable objects.
*
* OPTIONAL Functionality
*/
jvmtiError JNICALL
jvmtiIterateOverHeap(jvmtiEnv* env,
jvmtiHeapObjectFilter object_filter,
jvmtiHeapObjectCallback heap_object_callback,
void* user_data)
{
TRACE2("jvmti.heap", "IterateOverHeap called");
SuspendEnabledChecker sec;
/*
* Check given env & current phase.
*/
jvmtiPhase phases[] = {JVMTI_PHASE_LIVE};
CHECK_EVERYTHING();
TIEnv* ti_env = reinterpret_cast<TIEnv *>(env);
if (!ti_env->posessed_capabilities.can_tag_objects)
return JVMTI_ERROR_MUST_POSSESS_CAPABILITY;
if (object_filter != JVMTI_HEAP_OBJECT_TAGGED
&& object_filter != JVMTI_HEAP_OBJECT_UNTAGGED
&& object_filter != JVMTI_HEAP_OBJECT_EITHER)
return JVMTI_ERROR_ILLEGAL_ARGUMENT;
// According to JVMTI specification, if callback is NULL,
// we must not process untagged objects.
// Since the iteration is visible to user only through callback,
// doing nothing will satisfy specification.
if (!heap_object_callback)
return JVMTI_ERROR_NONE;
// heap iteration requires stop-the-world
hythread_global_lock();
assert(NULL == ti_env->iteration_state);
ti_env->iteration_state = new TIIterationState;
TIIterationState* state = ti_env->iteration_state;
if (NULL == state) {
hythread_global_unlock();
return JVMTI_ERROR_OUT_OF_MEMORY;
}
memset(state, 0, sizeof(TIIterationState));
hythread_suspend_disable(); // to keep assertions happy
hythread_iterator_t iterator;
hythread_suspend_all(&iterator, NULL);
TRACE2("ti.iterate", "suspended all threads");
global_ti_env = ti_env; // FIXME: use TLS to store TIEnv pointer
state->user_data = user_data;
state->heap_object_callback = heap_object_callback;
state->object_filter = object_filter;
if (JVMTI_HEAP_OBJECT_TAGGED == object_filter) {
// we iterate tagged objects directly from tags structure
if (ti_env->tags)
ti_env->tags->iterate();
} else {
// iterating untagged objects requires full heap iteration
gc_iterate_heap();
}
delete ti_env->iteration_state;
ti_env->iteration_state = NULL;
TRACE2("ti.iterate", "iteration complete");
hythread_resume_all(NULL);
hythread_suspend_enable();
hythread_global_unlock();
return JVMTI_ERROR_NONE;
}
/*
* Iterate Over Instances Of Class
*
* Iterate over all objects in the heap that are instances of
* the specified class. This includes both reachable and
* unreachable objects.
*
* OPTIONAL Functionality
*/
jvmtiError JNICALL
jvmtiIterateOverInstancesOfClass(jvmtiEnv* env,
jclass klass,
jvmtiHeapObjectFilter object_filter,
jvmtiHeapObjectCallback heap_object_callback,
void* user_data)
{
TRACE2("jvmti.heap", "IterateOverInstancesOfClass called");
SuspendEnabledChecker sec;
/*
* Check given env & current phase.
*/
jvmtiPhase phases[] = {JVMTI_PHASE_LIVE};
CHECK_EVERYTHING();
TIEnv* ti_env = reinterpret_cast<TIEnv *>(env);
if (!ti_env->posessed_capabilities.can_tag_objects)
return JVMTI_ERROR_MUST_POSSESS_CAPABILITY;
if (!is_jclass_valid(klass))
return JVMTI_ERROR_INVALID_CLASS;
if (object_filter != JVMTI_HEAP_OBJECT_TAGGED
&& object_filter != JVMTI_HEAP_OBJECT_UNTAGGED
&& object_filter != JVMTI_HEAP_OBJECT_EITHER)
return JVMTI_ERROR_ILLEGAL_ARGUMENT;
// According to JVMTI specification, if callback is NULL,
// we must not process untagged objects.
// Since the iteration is visible to user only through callback,
// doing nothing will satisfy specification.
if (!heap_object_callback)
return JVMTI_ERROR_NONE;
// heap iteration requires stop-the-world
TRACE2("ti.iterate", "acquire tm lock");
hythread_global_lock();
TRACE2("ti.iterate", "got tm lock");
assert(NULL == ti_env->iteration_state);
ti_env->iteration_state = new TIIterationState;
TIIterationState* state = ti_env->iteration_state;
if (NULL == state) {
hythread_global_unlock();
return JVMTI_ERROR_OUT_OF_MEMORY;
}
memset(state, 0, sizeof(TIIterationState));
hythread_suspend_disable(); // to keep assertions happy
hythread_iterator_t iterator;
hythread_suspend_all(&iterator, NULL);
TRACE2("ti.iterate", "suspended all threads");
global_ti_env = ti_env; // FIXME: use TLS to store TIEnv pointer
state->user_data = user_data;
state->heap_object_callback = heap_object_callback;
state->object_filter = object_filter;
state->class_filter = jclass_to_struct_Class(klass);
if (JVMTI_HEAP_OBJECT_TAGGED == object_filter) {
// we iterate tagged objects directly from tags structure
ti_env->tags->iterate();
} else {
// iterating untagged objects requires full heap iteration
gc_iterate_heap();
}
delete ti_env->iteration_state;
ti_env->iteration_state = NULL;
TRACE2("ti.iterate", "iteration complete");
hythread_resume_all(NULL);
hythread_suspend_enable();
hythread_global_unlock();
return JVMTI_ERROR_NONE;
}
/*
* Get Objects With Tags
*
* Return objects in the heap with the specified tags. The format
* is parallel arrays of objects and tags.
*
* OPTIONAL Functionality
*/
jvmtiError JNICALL
jvmtiGetObjectsWithTags(jvmtiEnv* env,
jint tag_count,
const jlong* tags,
jint* count_ptr,
jobject** object_result_ptr,
jlong** tag_result_ptr)
{
TRACE2("jvmti.heap", "GetObjectsWithTags called");
SuspendEnabledChecker sec;
/*
* Check given env & current phase.
*/
jvmtiPhase phases[] = {JVMTI_PHASE_LIVE};
CHECK_EVERYTHING();
TIEnv* ti_env = reinterpret_cast<TIEnv *>(env);
if (!ti_env->posessed_capabilities.can_tag_objects)
return JVMTI_ERROR_MUST_POSSESS_CAPABILITY;
if (count_ptr == NULL || tags == NULL ||
(object_result_ptr == NULL && tag_result_ptr == NULL))
return JVMTI_ERROR_NULL_POINTER;
if (tag_count <= 0)
return JVMTI_ERROR_ILLEGAL_ARGUMENT;
std::set<jlong> tag_set;
std::list<tag_pair> objects;
jvmtiError error = JVMTI_ERROR_NONE;
int i;
for (i = 0; i < tag_count; i++) {
if (tags[i] == 0)
return JVMTI_ERROR_ILLEGAL_ARGUMENT;
tag_set.insert(tags[i]);
}
hythread_suspend_disable(); // ---------------vv
ti_env->tags->get_objects_with_tags(tag_set, objects);
int count = (int)objects.size();
*count_ptr = count;
if (count == 0) {
// set output args to NULL
// for compatibility
if (object_result_ptr)
*object_result_ptr = NULL;
if (tag_result_ptr)
*tag_result_ptr = NULL;
} else {
assert(count > 0);
if (object_result_ptr != NULL) {
jvmtiError r = _allocate(count * sizeof(jobject),
(unsigned char **)object_result_ptr);
if (r == JVMTI_ERROR_NONE) {
memset(*object_result_ptr, 0, count * sizeof(jobject));
std::list<tag_pair>::iterator o;
for (i = 0, o = objects.begin(); o != objects.end(); o++, i++) {
jobject jobj = oh_allocate_local_handle();
if (jobj) jobj->object = (ManagedObject*)o->obj;
else error = JVMTI_ERROR_OUT_OF_MEMORY;
(*object_result_ptr)[i] = jobj;
}
} else {
error = r;
}
if (r != JVMTI_ERROR_NONE && (*object_result_ptr)) {
_deallocate((unsigned char *)*object_result_ptr);
*object_result_ptr = NULL;
}
}
if (tag_result_ptr != NULL && error == JVMTI_ERROR_NONE) {
jvmtiError r = _allocate(count * sizeof(jlong),
(unsigned char **)tag_result_ptr);
if (JVMTI_ERROR_NONE == r) {
memset(*tag_result_ptr, 0, count * sizeof(jlong));
std::list<tag_pair>::iterator o;
for (i = 0, o = objects.begin(); o != objects.end(); o++, i++) {
(*tag_result_ptr)[i] = o->tag;
}
} else {
error = r;
}
}
}
hythread_suspend_enable(); // ---------------^^
return error;
}