59ac4c2629
Reviewed-by: sspitsyn, erikj
468 lines
14 KiB
C
468 lines
14 KiB
C
/*
|
|
* Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <memory.h>
|
|
|
|
#include "jni.h"
|
|
#include "jvmti.h"
|
|
#include "agent_common.h"
|
|
|
|
static jvmtiEnv *jvmti;
|
|
static jint dummy_user_data;
|
|
static jboolean user_data_error_flag = JNI_FALSE;
|
|
|
|
/*
|
|
* Default callbacks
|
|
*/
|
|
|
|
static jvmtiEventObjectFree object_free_callback;
|
|
|
|
static void JNICALL default_object_free(jvmtiEnv *env, jlong tag) {
|
|
if (object_free_callback != NULL) {
|
|
(*object_free_callback)(env, tag);
|
|
}
|
|
}
|
|
|
|
static jvmtiIterationControl JNICALL default_heap_object_callback
|
|
(jlong class_tag, jlong size, jlong* tag_ptr, void* user_data)
|
|
{
|
|
if (user_data != &dummy_user_data && user_data_error_flag == JNI_FALSE) {
|
|
user_data_error_flag = JNI_TRUE;
|
|
fprintf(stderr, "WARNING: (default) unexpected value of user_data\n");
|
|
}
|
|
return JVMTI_ITERATION_ABORT;
|
|
}
|
|
|
|
static jvmtiHeapObjectCallback heap_object_callback = default_heap_object_callback;
|
|
static jvmtiHeapRootCallback heap_root_callback = NULL;
|
|
static jvmtiStackReferenceCallback stack_ref_callback = NULL;
|
|
static jvmtiObjectReferenceCallback object_ref_callback = NULL;
|
|
|
|
/*
|
|
* Basic tagging functions
|
|
*/
|
|
|
|
JNIEXPORT jint JNICALL Java_nsk_share_jvmti_unit_Heap_setTag0
|
|
(JNIEnv *env, jclass cls, jobject o, jlong tag)
|
|
{
|
|
return (*jvmti)->SetTag(jvmti, o, tag);
|
|
}
|
|
|
|
JNIEXPORT jlong JNICALL Java_nsk_share_jvmti_unit_Heap_getTag0
|
|
(JNIEnv *env, jclass cls, jobject o)
|
|
{
|
|
jlong tag;
|
|
jvmtiError err = (*jvmti)->GetTag(jvmti, o, &tag);
|
|
if (err != JVMTI_ERROR_NONE) {
|
|
fprintf(stderr, "ERORR: GetTag failed: JVMTI error=%d\n", err);
|
|
return 0;
|
|
}
|
|
return tag;
|
|
}
|
|
|
|
JNIEXPORT jlong JNICALL Java_nsk_share_jvmti_unit_Heap_getObjectSize
|
|
(JNIEnv *env, jclass cls, jobject o)
|
|
{
|
|
jlong size;
|
|
jvmtiError err = (*jvmti)->GetObjectSize(jvmti, o, &size);
|
|
if (err != JVMTI_ERROR_NONE) {
|
|
fprintf(stderr, "ERORR: GetObjectSize failed: JVMTI error=%d\n", err);
|
|
return 0;
|
|
}
|
|
return size;
|
|
}
|
|
|
|
/*
|
|
* Iterations functions
|
|
*/
|
|
|
|
JNIEXPORT jint JNICALL Java_nsk_share_jvmti_unit_Heap_iterateOverHeap0
|
|
(JNIEnv *env, jclass cls, jint filter_kind)
|
|
{
|
|
if (heap_object_callback == default_heap_object_callback) {
|
|
fprintf(stderr, "WARNING: default heap_object_callback set\n");
|
|
}
|
|
user_data_error_flag = JNI_FALSE;
|
|
return (*jvmti)->IterateOverHeap(jvmti,
|
|
filter_kind, heap_object_callback, &dummy_user_data);
|
|
}
|
|
|
|
JNIEXPORT jint JNICALL Java_nsk_share_jvmti_unit_Heap_iterateOverInstancesOfClass0
|
|
(JNIEnv *env, jclass this_cls, jclass target_cls, jint filter_kind)
|
|
{
|
|
if (heap_object_callback == default_heap_object_callback) {
|
|
fprintf(stderr, "WARNING: default heap_object_callback set\n");
|
|
}
|
|
user_data_error_flag = JNI_FALSE;
|
|
return (*jvmti)->IterateOverInstancesOfClass(jvmti, target_cls,
|
|
filter_kind, heap_object_callback, &dummy_user_data);
|
|
}
|
|
|
|
JNIEXPORT jint JNICALL Java_nsk_share_jvmti_unit_Heap_iterateOverReachableObjects0
|
|
(JNIEnv *env, jclass this_cls)
|
|
{
|
|
jvmtiError err;
|
|
user_data_error_flag = JNI_FALSE;
|
|
err = (*jvmti)->IterateOverReachableObjects(jvmti, heap_root_callback,
|
|
stack_ref_callback, object_ref_callback, &dummy_user_data);
|
|
if (err != JVMTI_ERROR_NONE) {
|
|
fprintf(stderr, "IterateOverReachableObjects failed: jvmti error=%d", err);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
JNIEXPORT jint JNICALL Java_nsk_share_jvmti_unit_Heap_iterateOverObjectsReachableFromObject0
|
|
(JNIEnv *env, jclass this_cls, jobject o)
|
|
{
|
|
jvmtiError err;
|
|
user_data_error_flag = JNI_FALSE;
|
|
err = (*jvmti)->IterateOverObjectsReachableFromObject(jvmti, o,
|
|
object_ref_callback, &dummy_user_data);
|
|
if (err != JVMTI_ERROR_NONE) {
|
|
fprintf(stderr, "IterateOverObjectsReachableFromObject failed: jvmti error=%d", err);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
|
|
/*
|
|
* GetObjectsWithTags tests
|
|
*/
|
|
|
|
static jobject object_results_ref;
|
|
static jobject tag_results_ref;
|
|
|
|
JNIEXPORT jlongArray JNICALL Java_nsk_share_jvmti_unit_Heap_tagResults
|
|
(JNIEnv *env, jclass this_cls)
|
|
{
|
|
return (jlongArray)tag_results_ref;
|
|
}
|
|
|
|
JNIEXPORT jobjectArray JNICALL Java_nsk_share_jvmti_unit_Heap_objectResults
|
|
(JNIEnv *env, jclass this_cls)
|
|
{
|
|
return (jobjectArray)object_results_ref;
|
|
}
|
|
|
|
JNIEXPORT jint JNICALL Java_nsk_share_jvmti_unit_Heap_getObjectsWithTags
|
|
(JNIEnv *env, jclass this_cls, jint count, jlongArray array)
|
|
{
|
|
jlong *tags;
|
|
jint i;
|
|
jvmtiError err;
|
|
jobject *object_results;
|
|
jlong *tag_results;
|
|
jclass cls;
|
|
jobjectArray object_array;
|
|
jlongArray tag_array;
|
|
|
|
/* get rid of any arrays that we are holding from a previous call */
|
|
|
|
if (object_results_ref != NULL) {
|
|
(*env)->DeleteGlobalRef(env, object_results_ref);
|
|
object_results_ref = NULL;
|
|
}
|
|
if (tag_results_ref != NULL) {
|
|
(*env)->DeleteGlobalRef(env, tag_results_ref);
|
|
tag_results_ref = NULL;
|
|
}
|
|
|
|
/* copy input list-of-tags from java into C */
|
|
|
|
tags = (jlong*)malloc(count * sizeof(jlong));
|
|
(*env)->GetLongArrayRegion(env, array, 0, count, tags);
|
|
|
|
err = (*jvmti)->GetObjectsWithTags(jvmti, count, tags,
|
|
&count, &object_results, &tag_results);
|
|
|
|
/* free the input argument that we malloced */
|
|
free(tags);
|
|
if (err != JVMTI_ERROR_NONE) {
|
|
fprintf(stderr, "ERROR: GetObjectsWithTags failed: %d\n", err);
|
|
return err;
|
|
}
|
|
|
|
/* copy output from C arrays into Java arrays */
|
|
|
|
cls = (*env)->FindClass(env, "java/lang/Object");
|
|
|
|
object_array = (*env)->NewObjectArray(env, count, cls, NULL);
|
|
tag_array = (*env)->NewLongArray(env, count);
|
|
|
|
for (i=0; i<count; i++) {
|
|
(*env)->SetObjectArrayElement(env, object_array, i, object_results[i]);
|
|
}
|
|
(*env)->SetLongArrayRegion(env, tag_array, 0, count, tag_results);
|
|
|
|
/* create JNI global refs as the current refs are local */
|
|
|
|
object_results_ref = (*env)->NewGlobalRef(env, object_array);
|
|
tag_results_ref = (*env)->NewGlobalRef(env, tag_array);
|
|
|
|
(*jvmti)->Deallocate(jvmti, (unsigned char*)object_results);
|
|
(*jvmti)->Deallocate(jvmti, (unsigned char*)tag_results);
|
|
|
|
return count;
|
|
}
|
|
|
|
/************* Basic Iteration Tests **************/
|
|
|
|
static jint object_count;
|
|
|
|
static jvmtiIterationControl JNICALL tagged_object_count_callback
|
|
(jlong class_tag, jlong size, jlong* tag_ptr, void* user_data)
|
|
{
|
|
if (user_data != &dummy_user_data && user_data_error_flag == JNI_FALSE) {
|
|
user_data_error_flag = JNI_TRUE;
|
|
fprintf(stderr, "WARNING: (tagged) unexpected value of user_data\n");
|
|
}
|
|
if (*tag_ptr != 0) {
|
|
object_count++;
|
|
}
|
|
return JVMTI_ITERATION_CONTINUE;
|
|
}
|
|
|
|
static jvmtiIterationControl JNICALL total_object_count_callback
|
|
(jlong class_tag, jlong size, jlong* tag_ptr, void* user_data)
|
|
{
|
|
if (user_data != &dummy_user_data && user_data_error_flag == JNI_FALSE) {
|
|
user_data_error_flag = JNI_TRUE;
|
|
fprintf(stderr, "WARNING: (total) unexpected value of user_data\n");
|
|
}
|
|
if (*tag_ptr != 0) {
|
|
object_count++;
|
|
}
|
|
return JVMTI_ITERATION_CONTINUE;
|
|
}
|
|
|
|
|
|
JNIEXPORT void JNICALL Java_nsk_share_jvmti_unit_Heap_setTaggedObjectCountCallback
|
|
(JNIEnv *env, jclass cls)
|
|
{
|
|
heap_object_callback = tagged_object_count_callback;
|
|
object_count = 0;
|
|
}
|
|
|
|
JNIEXPORT void JNICALL Java_nsk_share_jvmti_unit_Heap_setTotalObjectCountCallback
|
|
(JNIEnv *env, jclass cls)
|
|
{
|
|
heap_object_callback = total_object_count_callback;
|
|
object_count = 0;
|
|
}
|
|
|
|
JNIEXPORT jint JNICALL Java_nsk_share_jvmti_unit_Heap_getObjectCount
|
|
(JNIEnv *env, jclass cls)
|
|
{
|
|
return object_count;
|
|
}
|
|
|
|
JNIEXPORT void JNICALL Java_nsk_share_jvmti_unit_Heap_zeroObjectCount
|
|
(JNIEnv *env, jclass cls)
|
|
{
|
|
object_count = 0;
|
|
}
|
|
|
|
|
|
/************* Basic Iteration Tests *************/
|
|
|
|
static jvmtiIterationControl JNICALL klass_tag_test_callback
|
|
(jlong class_tag, jlong size, jlong* tag_ptr, void* user_data)
|
|
{
|
|
if (user_data != &dummy_user_data && user_data_error_flag == JNI_FALSE) {
|
|
user_data_error_flag = JNI_TRUE;
|
|
fprintf(stderr, "WARNING: (klass) unexpected value of user_data\n");
|
|
}
|
|
if (class_tag != 0) {
|
|
*tag_ptr = class_tag;
|
|
}
|
|
return JVMTI_ITERATION_CONTINUE;
|
|
}
|
|
|
|
JNIEXPORT void JNICALL Java_nsk_share_jvmti_unit_Heap_setKlassTagTestCallback
|
|
(JNIEnv *env, jclass cls)
|
|
{
|
|
heap_object_callback = klass_tag_test_callback;
|
|
}
|
|
|
|
/************* Heap Walking Tests *************/
|
|
|
|
JNIEXPORT jobject JNICALL Java_nsk_share_jvmti_unit_Heap_newGlobalRef
|
|
(JNIEnv *env, jclass cls, jobject o)
|
|
{
|
|
return (*env)->NewGlobalRef(env, o);
|
|
}
|
|
|
|
static jvmtiIterationControl JNICALL simple_heap_root_callback
|
|
(jvmtiHeapRootKind root_kind, jlong class_tag, jlong size,
|
|
jlong* tag_ptr, void* user_data)
|
|
{
|
|
if (user_data != &dummy_user_data && user_data_error_flag == JNI_FALSE) {
|
|
user_data_error_flag = JNI_TRUE;
|
|
fprintf(stderr, "WARNING: (heap) unexpected value of user_data\n");
|
|
}
|
|
*tag_ptr = root_kind;
|
|
return JVMTI_ITERATION_CONTINUE;
|
|
}
|
|
|
|
static jvmtiIterationControl JNICALL simple_stack_ref_callback
|
|
(jvmtiHeapRootKind root_kind, jlong class_tag, jlong size, jlong* tag_ptr,
|
|
jlong thread_tag, jint depth, jmethodID method, jint slot, void* user_data)
|
|
{
|
|
if (root_kind == JVMTI_HEAP_ROOT_STACK_LOCAL) {
|
|
if (method == NULL) {
|
|
fprintf(stderr, "WARNING: jmethodID missing for STACK_LOCAL\n");
|
|
}
|
|
}
|
|
if (user_data != &dummy_user_data && user_data_error_flag == JNI_FALSE) {
|
|
user_data_error_flag = JNI_TRUE;
|
|
fprintf(stderr, "WARNING: (stack) unexpected value of user_data\n");
|
|
}
|
|
*tag_ptr = thread_tag;
|
|
return JVMTI_ITERATION_CONTINUE;
|
|
}
|
|
|
|
static jvmtiIterationControl JNICALL simple_object_ref_callback
|
|
(jvmtiObjectReferenceKind reference_kind, jlong class_tag, jlong size, jlong* tag_ptr,
|
|
jlong referrer_tag, jint referrer_index, void* user_data)
|
|
{
|
|
if (user_data != &dummy_user_data && user_data_error_flag == JNI_FALSE) {
|
|
user_data_error_flag = JNI_TRUE;
|
|
fprintf(stderr, "WARNING: (object) unexpected value of user_data\n");
|
|
}
|
|
*tag_ptr = 777;
|
|
return JVMTI_ITERATION_CONTINUE;
|
|
}
|
|
|
|
|
|
JNIEXPORT void JNICALL Java_nsk_share_jvmti_unit_Heap_setHeapRootCallback
|
|
(JNIEnv *env, jclass cls)
|
|
{
|
|
heap_root_callback = simple_heap_root_callback;
|
|
stack_ref_callback = NULL;
|
|
object_ref_callback = NULL;
|
|
}
|
|
|
|
JNIEXPORT void JNICALL Java_nsk_share_jvmti_unit_Heap_setStackRefCallback
|
|
(JNIEnv *env, jclass cls)
|
|
{
|
|
heap_root_callback = NULL;
|
|
stack_ref_callback = simple_stack_ref_callback;
|
|
object_ref_callback = NULL;
|
|
}
|
|
|
|
JNIEXPORT void JNICALL Java_nsk_share_jvmti_unit_Heap_setObjectRefCallback
|
|
(JNIEnv *env, jclass cls)
|
|
{
|
|
heap_root_callback = NULL;
|
|
stack_ref_callback = NULL;
|
|
object_ref_callback = simple_object_ref_callback;
|
|
}
|
|
|
|
|
|
|
|
/*************** OBJECT_FREE tests ************/
|
|
|
|
static jint object_free_count;
|
|
|
|
static void JNICALL
|
|
object_free_count_callback(jvmtiEnv *env, jlong tag) {
|
|
if (tag == 0) {
|
|
fprintf(stderr, "WARNING: OBJECT_FREE event called with tag 0!!!\n");
|
|
}
|
|
object_free_count++;
|
|
}
|
|
|
|
JNIEXPORT void JNICALL Java_nsk_share_jvmti_unit_Heap_setObjectFreeCallback
|
|
(JNIEnv *env, jclass cls)
|
|
{
|
|
object_free_callback = object_free_count_callback;
|
|
object_free_count = 0;
|
|
}
|
|
|
|
JNIEXPORT jint JNICALL Java_nsk_share_jvmti_unit_Heap_getObjectFreeCount
|
|
(JNIEnv *env, jclass cls)
|
|
{
|
|
return object_free_count;
|
|
}
|
|
|
|
JNIEXPORT void JNICALL Java_nsk_share_jvmti_unit_Heap_zeroObjectFreeCount
|
|
(JNIEnv *env, jclass cls)
|
|
{
|
|
object_free_count = 0;
|
|
}
|
|
|
|
/*
|
|
* Agent_Initialize - add capabilities and enables OBJECT_FREE event
|
|
*/
|
|
jint Agent_Initialize(JavaVM *vm, char *options, void *reserved)
|
|
{
|
|
jint rc;
|
|
jvmtiError err;
|
|
jvmtiCapabilities capabilities;
|
|
jvmtiEventCallbacks callbacks;
|
|
|
|
/* get JVMTI environment */
|
|
|
|
rc = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION);
|
|
if (rc != JNI_OK) {
|
|
fprintf(stderr, "Unable to create jvmtiEnv, GetEnv failed, error=%d\n", rc);
|
|
return -1;
|
|
}
|
|
|
|
|
|
/* add annotate object capability */
|
|
err = (*jvmti)->GetCapabilities(jvmti, &capabilities);
|
|
if (err != JVMTI_ERROR_NONE) {
|
|
fprintf(stderr, "GetCapabilities failed, error=%d\n", err);
|
|
}
|
|
capabilities.can_tag_objects = 1;
|
|
capabilities.can_generate_object_free_events = 1;
|
|
err = (*jvmti)->AddCapabilities(jvmti, &capabilities);
|
|
|
|
if (err != JVMTI_ERROR_NONE) {
|
|
fprintf(stderr, "AddCapabilities failed, error=%d\n", err);
|
|
return -1;
|
|
}
|
|
|
|
|
|
/* enable OBJECT_FREE events */
|
|
err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_OBJECT_FREE, NULL);
|
|
if (err != JVMTI_ERROR_NONE) {
|
|
fprintf(stderr, "SetEventNotificationMode failed, error=%d\n", err);
|
|
return -1;
|
|
}
|
|
|
|
|
|
memset(&callbacks, 0, sizeof(callbacks));
|
|
callbacks.ObjectFree = default_object_free;
|
|
err = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks));
|
|
if (err != JVMTI_ERROR_NONE) {
|
|
fprintf(stderr, "SetEventCallbacks failed, error=%d\n", err);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|