8317636: Improve heap walking API tests to verify correctness of field indexes
Reviewed-by: cjplummer, sspitsyn
This commit is contained in:
parent
fd3042a04b
commit
f31957e6a1
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8317636
|
||||
* @summary The test verifies heap walking API (FollowReferences) reports
|
||||
* field indeces in correct order (as described by jvmtiHeapReferenceInfoField spec).
|
||||
* For simplification only primitive field callback is tested
|
||||
* and all fields in the test classes are 'int'.
|
||||
* Field IDs are not reported to the callback, so the test uses field values
|
||||
* to distinguish between fields, so all field values in the test classes should be unique.
|
||||
* @run main/othervm/native -agentlib:FieldIndicesTest FieldIndicesTest
|
||||
*/
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
|
||||
// Test class hierarchy is based on the example described in the spec.
|
||||
// Extra fields added to improve coverage.
|
||||
interface I0 {
|
||||
int p = 10;
|
||||
// extra fields
|
||||
public int p5 = 11;
|
||||
int p6 = 12;
|
||||
public int p1 = 13;
|
||||
}
|
||||
|
||||
interface I1 extends I0 {
|
||||
int x = 20;
|
||||
// extra fields
|
||||
int x1 = 21;
|
||||
public int x2 = 22;
|
||||
}
|
||||
|
||||
interface I2 extends I0 {
|
||||
int y = 30;
|
||||
// extra fields
|
||||
int y9 = 31;
|
||||
public int y4 = 32;
|
||||
public int y3 = 33;
|
||||
}
|
||||
|
||||
class C1 implements I1 {
|
||||
public static int a = 40;
|
||||
private int b = 41;
|
||||
// extra fields
|
||||
private int a1 = 42;
|
||||
protected static int b7 = 43;
|
||||
static int b2 = 44;
|
||||
final protected int a3 = 45;
|
||||
static int a2 = 46;
|
||||
public int b1 = 47;
|
||||
}
|
||||
|
||||
class C2 extends C1 implements I2 {
|
||||
static int q = 60;
|
||||
final int r = 61;
|
||||
// extra fields
|
||||
private int q11 = 61;
|
||||
final static int q9 = 62;
|
||||
static int q2 = 63;
|
||||
final protected int r3 = 64;
|
||||
public int r7 = 65;
|
||||
}
|
||||
|
||||
public class FieldIndicesTest {
|
||||
static {
|
||||
System.loadLibrary("FieldIndicesTest");
|
||||
}
|
||||
|
||||
private static native void prepare(Object testObject);
|
||||
|
||||
private static native void test(Object rootObject);
|
||||
|
||||
private static native boolean testFailed();
|
||||
|
||||
private static void prepare(String name, Object testObject) {
|
||||
System.out.println(">>prepare(" + name + ")");
|
||||
prepare(testObject);
|
||||
System.out.println("<<prepare(" + name + ")");
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
private static void test(String name, Object rootObject) {
|
||||
System.out.println(">>test(" + name + ")");
|
||||
test(rootObject);
|
||||
System.out.println("<<test(" + name + ")");
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
public static void main(String argv[]) {
|
||||
C1 obj1 = new C1();
|
||||
C2 obj2 = new C2();
|
||||
|
||||
prepare("obj1", obj1);
|
||||
prepare("obj2", obj2);
|
||||
|
||||
test("obj1", obj1);
|
||||
test("obj2", obj2);
|
||||
|
||||
Reference.reachabilityFence(obj1);
|
||||
Reference.reachabilityFence(obj2);
|
||||
|
||||
if (testFailed()) {
|
||||
throw new RuntimeException("Test failed. See log for details");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,515 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 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 <string.h>
|
||||
#include <stdint.h>
|
||||
#include "jvmti.h"
|
||||
#include "jvmti_common.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
static jvmtiEnv* jvmti = nullptr;
|
||||
static bool test_failed = false;
|
||||
|
||||
static void* allocate(JNIEnv* env, jlong size) {
|
||||
unsigned char* result = nullptr;
|
||||
check_jvmti_status(env,
|
||||
jvmti->Allocate(size, &result),
|
||||
"Allocate failed");
|
||||
return result;
|
||||
}
|
||||
|
||||
static void deallocate(JNIEnv* env, void* mem) {
|
||||
check_jvmti_status(env,
|
||||
jvmti->Deallocate((unsigned char*)mem),
|
||||
"Deallocate failed");
|
||||
}
|
||||
|
||||
// Converts JNI class name signature to simple name (in place).
|
||||
static void sig2name(char* str) {
|
||||
size_t len = strlen(str);
|
||||
if (len >=2 && str[0] == 'L' && str[len-1] == ';') {
|
||||
len -=2;
|
||||
memmove(str, str+1, len);
|
||||
str[len] = '\0';
|
||||
}
|
||||
// Replace '/' with '.'.
|
||||
for (char* pos = str; (pos = strchr(pos, '/')) != nullptr; ) {
|
||||
*pos = '.';
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_static_field(JNIEnv* env, jclass klass, jfieldID fid) {
|
||||
enum {
|
||||
ACC_STATIC = 0x0008
|
||||
};
|
||||
|
||||
jint access_flags = 0;
|
||||
check_jvmti_status(env,
|
||||
jvmti->GetFieldModifiers(klass, fid, &access_flags),
|
||||
"GetFieldModifiers failed");
|
||||
return (access_flags & ACC_STATIC) != 0;
|
||||
}
|
||||
|
||||
static void verify_int_field(JNIEnv* env, jclass klass, jfieldID fid) {
|
||||
char* name = nullptr;
|
||||
char* sig = nullptr;
|
||||
check_jvmti_status(env,
|
||||
jvmti->GetFieldName(klass, fid, &name, &sig, nullptr),
|
||||
"GetFieldName failed");
|
||||
|
||||
if (strcmp(sig, "I") != 0) {
|
||||
printf("ERROR: field '%s' is not int ('%s')\n", name, sig);
|
||||
fflush(nullptr);
|
||||
fatal(env, "unexpected field type");
|
||||
}
|
||||
|
||||
deallocate(env, name);
|
||||
deallocate(env, sig);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Per jvmtiHeapReferenceInfoField spec (reference information for
|
||||
JVMTI_HEAP_REFERENCE_FIELD and JVMTI_HEAP_REFERENCE_STATIC_FIELD references.):
|
||||
If the referrer object is not an interface, then the field indices are determined as follows:
|
||||
- make a list of all the fields in C and its superclasses,
|
||||
starting with all the fields in java.lang.Object and ending with all the fields in C.
|
||||
- Within this list, put the fields for a given class in the order returned by GetClassFields.
|
||||
- Assign the fields in this list indices n, n+1, ..., in order,
|
||||
where n is the count of the fields in all the interfaces implemented by C.
|
||||
Note that C implements all interfaces directly implemented by its superclasses;
|
||||
as well as all superinterfaces of these interfaces.
|
||||
If the referrer object is an interface, then the field indices are determined as follows:
|
||||
- make a list of the fields directly declared in I.
|
||||
- Within this list, put the fields in the order returned by GetClassFields.
|
||||
- Assign the fields in this list indices n, n+1, ..., in order,
|
||||
where n is the count of the fields in all the superinterfaces of I.
|
||||
|
||||
'Klass' struct contains all required data to calculate field indices.
|
||||
Also contains static field values.
|
||||
For each test class, the 'Klass' struct is created and a pointer to it is set as the jclass's tag.
|
||||
*/
|
||||
|
||||
struct Klass {
|
||||
jclass klass;
|
||||
char* name;
|
||||
Klass* super_klass;
|
||||
|
||||
struct Field {
|
||||
jfieldID id;
|
||||
char* name;
|
||||
|
||||
// Field value for static fields (0 for instance fields).
|
||||
// All fields in the test classes are 'int'.
|
||||
jint value;
|
||||
|
||||
void init(JNIEnv* env, jclass klass, jfieldID fid);
|
||||
};
|
||||
|
||||
// Fields of the class and its superclasses
|
||||
// as described in jvmtiHeapReferenceInfoField spec.
|
||||
Field* fields;
|
||||
jint field_count;
|
||||
|
||||
// Interfaces implemented by this klass, superclasses and superinterfaces.
|
||||
Klass** interfaces;
|
||||
jint interface_count;
|
||||
|
||||
// Number of fields in all implemented interfaces.
|
||||
jint interface_field_count;
|
||||
|
||||
static Klass* explore(JNIEnv* env, jclass klass);
|
||||
|
||||
private:
|
||||
// Initializes fields, field_count.
|
||||
void explore_fields(JNIEnv* env);
|
||||
// Initializes interfaces, interface_count.
|
||||
void explore_interfaces(JNIEnv* env);
|
||||
|
||||
void print() const;
|
||||
};
|
||||
|
||||
/*
|
||||
For each test object, the 'Object' struct is created and a pointer to it is set as the jobject's tag.
|
||||
*/
|
||||
struct Object {
|
||||
Klass* klass;
|
||||
// Values of instance fields (0 for static fields).
|
||||
// Size of the array == klass->field_count.
|
||||
jint* field_values;
|
||||
|
||||
static Object* explore(JNIEnv* env, jobject obj);
|
||||
};
|
||||
|
||||
|
||||
void Klass::Field::init(JNIEnv* env, jclass klass, jfieldID fid) {
|
||||
id = fid;
|
||||
check_jvmti_status(env,
|
||||
jvmti->GetFieldName(klass, fid, &name, nullptr, nullptr),
|
||||
"GetFieldName failed");
|
||||
if (is_static_field(env, klass, fid)) {
|
||||
verify_int_field(env, klass, fid);
|
||||
value = env->GetStaticIntField(klass, fid);
|
||||
} else {
|
||||
value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Klass::explore_fields(JNIEnv* env) {
|
||||
jint this_count;
|
||||
jfieldID* this_fields;
|
||||
check_jvmti_status(env,
|
||||
jvmti->GetClassFields(klass, &this_count, &this_fields),
|
||||
"GetClassFields failed");
|
||||
|
||||
jint super_count = super_klass != nullptr ? super_klass->field_count : 0;
|
||||
|
||||
fields = (Field*)allocate(env, sizeof(Field) * (super_count + this_count));
|
||||
field_count = 0;
|
||||
|
||||
if (super_klass != 0) {
|
||||
// super_klass->fields already contains fields from all superclasses in the required order.
|
||||
for (int i = 0; i < super_count; i++) {
|
||||
fields[field_count++].init(env, super_klass->klass, super_klass->fields[i].id);
|
||||
}
|
||||
}
|
||||
|
||||
// Add field of this class to the end of the list.
|
||||
for (int i = 0; i < this_count; i++) {
|
||||
fields[field_count++].init(env, klass, this_fields[i]);
|
||||
}
|
||||
deallocate(env, this_fields);
|
||||
}
|
||||
|
||||
|
||||
// Calculates maximum number of implemented interfaces of the klass and its superinterfaces.
|
||||
static jint get_max_interface_count(JNIEnv* env, jclass klass) {
|
||||
jint interface_count;
|
||||
jclass* interfaces;
|
||||
check_jvmti_status(env,
|
||||
jvmti->GetImplementedInterfaces(klass, &interface_count, &interfaces),
|
||||
"GetImplementedInterfaces failed");
|
||||
|
||||
jint result = interface_count;
|
||||
// interfaces implemented by superinterfaces
|
||||
for (jint i = 0; i < interface_count; i++) {
|
||||
result += get_max_interface_count(env, interfaces[i]);
|
||||
}
|
||||
|
||||
deallocate(env, interfaces);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Explores all interfaces implemented by 'klass', sorts out duplicates,
|
||||
// and stores the interfaces in the 'arr' starting from 'index'.
|
||||
// Returns number of the interfaces added.
|
||||
static jint fill_interfaces(Klass** arr, jint index, JNIEnv* env, jclass klass) {
|
||||
jint interface_count;
|
||||
jclass* interfaces;
|
||||
check_jvmti_status(env,
|
||||
jvmti->GetImplementedInterfaces(klass, &interface_count, &interfaces),
|
||||
"GetImplementedInterfaces failed");
|
||||
|
||||
jint count = 0;
|
||||
for (jint i = 0; i < interface_count; i++) {
|
||||
// Skip interface if it's already in the array
|
||||
// (i.e. implemented by another superclass/superinterface).
|
||||
bool dup = false;
|
||||
for (jint j = 0; j < index; j++) {
|
||||
if (env->IsSameObject(arr[j]->klass, interfaces[i]) == JNI_TRUE) {
|
||||
dup = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (dup) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add the interface.
|
||||
arr[index + count] = Klass::explore(env, interfaces[i]);
|
||||
count++;
|
||||
|
||||
// And explore its superinterfaces.
|
||||
count += fill_interfaces(arr, index + count, env, interfaces[i]);
|
||||
}
|
||||
|
||||
deallocate(env, interfaces);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void Klass::explore_interfaces(JNIEnv* env) {
|
||||
jint max_count = get_max_interface_count(env, klass);
|
||||
if (super_klass != nullptr) {
|
||||
max_count += super_klass->interface_count;
|
||||
}
|
||||
|
||||
// Allocate array for maximum possible count.
|
||||
interfaces = (Klass**)allocate(env, sizeof(Klass*) * max_count);
|
||||
|
||||
interface_count = 0;
|
||||
if (super_klass != nullptr) {
|
||||
// Add all interfaces implemented by super_klass first.
|
||||
interface_count = super_klass->interface_count;
|
||||
memcpy(interfaces, super_klass->interfaces, sizeof(Klass*) * super_klass->interface_count);
|
||||
}
|
||||
|
||||
// Interfaces implemented by the klass.
|
||||
interface_count += fill_interfaces(interfaces, interface_count, env, klass);
|
||||
}
|
||||
|
||||
void Klass::print() const {
|
||||
printf("Explored klass: %s, super: %s\n",
|
||||
name, super_klass == nullptr ? nullptr : super_klass->name);
|
||||
printf(" interfaces (%d):\n", (int)interface_count);
|
||||
for (jint i = 0; i < interface_count; i++) {
|
||||
printf(" %d: %s\n", (int)i, interfaces[i]->name);
|
||||
}
|
||||
printf(" fields (%d):\n", (int)field_count);
|
||||
for (jint i = 0; i < field_count; i++) {
|
||||
printf(" %d: %s (value = %d)\n",
|
||||
(int)i, fields[i].name, (int)fields[i].value);
|
||||
}
|
||||
printf(" interface_field_count: %d\n", (int)interface_field_count);
|
||||
}
|
||||
|
||||
Klass* Klass::explore(JNIEnv* env, jclass klass) {
|
||||
jlong tag = 0;
|
||||
check_jvmti_status(env,
|
||||
jvmti->GetTag(klass, &tag),
|
||||
"GetTag failed");
|
||||
if (tag != 0) { // already explored
|
||||
return (Klass*)tag;
|
||||
}
|
||||
|
||||
Klass* result = (Klass*)allocate(env, sizeof(Klass));
|
||||
|
||||
result->klass = (jclass)env->NewGlobalRef(klass);
|
||||
|
||||
check_jvmti_status(env,
|
||||
jvmti->GetClassSignature(klass, &result->name, nullptr),
|
||||
"GetClassSignature failed");
|
||||
sig2name(result->name);
|
||||
|
||||
// Explore superclass first.
|
||||
jclass super_klass = env->GetSuperclass(klass);
|
||||
result->super_klass = super_klass == nullptr ? nullptr : Klass::explore(env, super_klass);
|
||||
|
||||
result->explore_fields(env);
|
||||
|
||||
result->explore_interfaces(env);
|
||||
|
||||
// Calculate interface_field_count.
|
||||
result->interface_field_count = 0;
|
||||
for (jint i = 0; i < result->interface_count; i++) {
|
||||
result->interface_field_count += result->interfaces[i]->field_count;
|
||||
}
|
||||
|
||||
check_jvmti_status(env,
|
||||
jvmti->SetTag(klass, (jlong)result),
|
||||
"SetTag failed");
|
||||
|
||||
result->print();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Object* Object::explore(JNIEnv* env, jobject obj) {
|
||||
jlong tag = 0;
|
||||
check_jvmti_status(env,
|
||||
jvmti->GetTag(obj, &tag),
|
||||
"GetTag failed");
|
||||
if (tag != 0) { // already explored
|
||||
return (Object*)tag;
|
||||
}
|
||||
|
||||
jclass obj_klass = env->GetObjectClass(obj);
|
||||
Klass* klass = Klass::explore(env, obj_klass);
|
||||
jint* values = (jint*)allocate(env, sizeof(jint) * klass->field_count);
|
||||
|
||||
for (jint i = 0; i < klass->field_count; i++) {
|
||||
jfieldID fid = klass->fields[i].id;
|
||||
if (is_static_field(env, obj_klass, fid)) {
|
||||
values[i] = 0;
|
||||
} else {
|
||||
verify_int_field(env, obj_klass, fid);
|
||||
values[i] = env->GetIntField(obj, fid);
|
||||
}
|
||||
}
|
||||
|
||||
Object* result = (Object*)allocate(env, sizeof(Object));
|
||||
result->klass = klass;
|
||||
result->field_values = values;
|
||||
|
||||
check_jvmti_status(env,
|
||||
jvmti->SetTag(obj, (jlong)result),
|
||||
"SetTag failed");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
|
||||
jint res = jvm->GetEnv((void **)&jvmti, JVMTI_VERSION_1_1);
|
||||
if (res != JNI_OK || jvmti == nullptr) {
|
||||
printf("jvm->GetEnv failed\n");
|
||||
fflush(nullptr);
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
jvmtiCapabilities caps;
|
||||
memset(&caps, 0, sizeof(caps));
|
||||
caps.can_tag_objects = 1;
|
||||
jvmtiError err = jvmti->AddCapabilities(&caps);
|
||||
if (err != JVMTI_ERROR_NONE) {
|
||||
printf("AddCapabilities failed: %s (%d)\n", TranslateError(err), err);
|
||||
fflush(nullptr);
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
return JNI_OK;
|
||||
}
|
||||
|
||||
|
||||
static bool check_index_bounds(jint index, Klass* klass) {
|
||||
if (index < klass->interface_field_count) {
|
||||
printf("ERROR: field_index is too small (%d < %d)\n",
|
||||
(int)index, (int)klass->interface_field_count);
|
||||
test_failed = true;
|
||||
return false;
|
||||
}
|
||||
if (index >= klass->interface_field_count + klass->field_count) {
|
||||
printf("ERROR: field_index is too big (%d >= %d)\n",
|
||||
(int)index, (int)(klass->interface_field_count + klass->field_count));
|
||||
test_failed = true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static char* get_field_name(Klass* klass, jint index) {
|
||||
index -= klass->interface_field_count;
|
||||
if (index < 0 || index >= klass->field_count) {
|
||||
return nullptr;
|
||||
}
|
||||
return klass->fields[index].name;
|
||||
}
|
||||
|
||||
|
||||
jint JNICALL primitiveFieldCallback(
|
||||
jvmtiHeapReferenceKind reference_kind,
|
||||
const jvmtiHeapReferenceInfo* reference_info,
|
||||
jlong class_tag,
|
||||
jlong* tag_ptr,
|
||||
jvalue value,
|
||||
jvmtiPrimitiveType value_type,
|
||||
void* user_data)
|
||||
{
|
||||
if (*tag_ptr == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
jint index = reference_info->field.index;
|
||||
jint int_value = value.i;
|
||||
if (value_type != JVMTI_PRIMITIVE_TYPE_INT) {
|
||||
printf("ERROR: unexpected value type in primitiveFieldCallback: '%c'\n", (char)value_type);
|
||||
test_failed = true;
|
||||
int_value = -1;
|
||||
}
|
||||
|
||||
if (reference_kind == JVMTI_HEAP_REFERENCE_FIELD) {
|
||||
Object* obj = (Object*)(*tag_ptr);
|
||||
Klass* klass = obj->klass;
|
||||
printf("primitiveFieldCallback(JVMTI_HEAP_REFERENCE_FIELD): "
|
||||
"klass=%s, index=%d, type=%c, value=%d\n",
|
||||
klass->name, index,
|
||||
(int)value_type, (int)value.i);
|
||||
if (check_index_bounds(index, klass)) {
|
||||
jint expected_value = obj->field_values[index - klass->interface_field_count];
|
||||
if (int_value != expected_value) {
|
||||
printf(" ERROR: wrong instance value: (%d, expected %d)\n",
|
||||
(int)int_value, (int)expected_value);
|
||||
test_failed = true;
|
||||
} else {
|
||||
printf(" OK: field %s.%s, value %d\n",
|
||||
klass->name, get_field_name(klass, index), (int)int_value);
|
||||
}
|
||||
}
|
||||
} else if (reference_kind == JVMTI_HEAP_REFERENCE_STATIC_FIELD) {
|
||||
Klass* klass = (Klass*)(*tag_ptr);
|
||||
printf("primitiveFieldCallback(JVMTI_HEAP_REFERENCE_STATIC_FIELD): "
|
||||
"klass=%s, index=%d, type=%c, value=%d\n",
|
||||
klass->name, index,
|
||||
(int)value_type, (int)value.i);
|
||||
if (check_index_bounds(index, klass)) {
|
||||
jint expected_value = klass->fields[index - klass->interface_field_count].value;
|
||||
if (int_value != expected_value) {
|
||||
printf(" ERROR: wrong static value: (%d, expected %d)\n\n\n",
|
||||
(int)int_value, (int)expected_value);
|
||||
test_failed = true;
|
||||
} else {
|
||||
printf(" OK: field %s.%s, value %d\n",
|
||||
klass->name, get_field_name(klass, index), (int)int_value);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
printf("ERROR: unexpected reference_kind in primitiveFieldCallback: %d\n", (int)reference_kind);
|
||||
test_failed = true;
|
||||
}
|
||||
|
||||
fflush(nullptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_FieldIndicesTest_prepare(JNIEnv *env, jclass cls, jobject testObj) {
|
||||
Object::explore(env, testObj);
|
||||
fflush(nullptr);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_FieldIndicesTest_test(JNIEnv *env, jclass cls, jobject rootObject) {
|
||||
jvmtiHeapCallbacks heapCallbacks;
|
||||
memset(&heapCallbacks, 0, sizeof(heapCallbacks));
|
||||
|
||||
heapCallbacks.primitive_field_callback = primitiveFieldCallback;
|
||||
|
||||
check_jvmti_status(env,
|
||||
jvmti->FollowReferences(JVMTI_HEAP_FILTER_UNTAGGED, // heap_filter
|
||||
nullptr, // class
|
||||
rootObject, // initial_object
|
||||
&heapCallbacks,
|
||||
nullptr),
|
||||
"FollowReferences failed");
|
||||
fflush(nullptr);
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_FieldIndicesTest_testFailed(JNIEnv *env, jclass cls) {
|
||||
return test_failed ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user