# HG changeset patch # User mdoerr # Date 1564392643 -7200 # Mon Jul 29 11:30:43 2019 +0200 # Node ID d8efa9dda604ac8380f8952da482298b9aa25801 # Parent 0a8436eda2fa81c9cf87f6a84b631b3deda0fe51 8227680: FastJNIAccessors: Check for JVMTI field access event requests at runtime Summary: Check JvmtiExport::_field_access_count != 0 at runtime Reviewed-by: dholmes, eosterlund, bulasevich diff --git a/src/hotspot/cpu/aarch64/jniFastGetField_aarch64.cpp b/src/hotspot/cpu/aarch64/jniFastGetField_aarch64.cpp --- a/src/hotspot/cpu/aarch64/jniFastGetField_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/jniFastGetField_aarch64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2019, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -79,33 +79,57 @@ Address safepoint_counter_addr(rcounter_addr, offset); __ ldrw(rcounter, safepoint_counter_addr); __ tbnz(rcounter, 0, slow); - __ eor(robj, c_rarg1, rcounter); - __ eor(robj, robj, rcounter); // obj, since - // robj ^ rcounter ^ rcounter == robj - // robj is address dependent on rcounter. + if (!UseBarriersForVolatile) { + // Field may be volatile. See other usages of this flag. + __ membar(MacroAssembler::AnyAny); + __ mov(robj, c_rarg1); + } else if (JvmtiExport::can_post_field_access()) { + // Using barrier to order wrt. JVMTI check and load of result. + __ membar(Assembler::LoadLoad); + __ mov(robj, c_rarg1); + } else { + // Using address dependency to order wrt. load of result. + __ eor(robj, c_rarg1, rcounter); + __ eor(robj, robj, rcounter); // obj, since + // robj ^ rcounter ^ rcounter == robj + // robj is address dependent on rcounter. + } + + if (JvmtiExport::can_post_field_access()) { + // Check to see if a field access watch has been set before we + // take the fast path. + unsigned long offset2; + __ adrp(result, + ExternalAddress((address) JvmtiExport::get_field_access_count_addr()), + offset2); + __ ldrw(result, Address(result, offset2)); + __ cbnzw(result, slow); + } + + // Both robj and rscratch1 are clobbered by try_resolve_jobject_in_native. BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler(); bs->try_resolve_jobject_in_native(masm, c_rarg0, robj, rscratch1, slow); __ lsr(roffset, c_rarg2, 2); // offset + __ add(result, robj, roffset); assert(count < LIST_CAPACITY, "LIST_CAPACITY too small"); speculative_load_pclist[count] = __ pc(); // Used by the segfault handler + // Using acquire: Order JVMTI check and load of result wrt. succeeding check + // (LoadStore for volatile field). switch (type) { - case T_BOOLEAN: __ ldrb (result, Address(robj, roffset)); break; - case T_BYTE: __ ldrsb (result, Address(robj, roffset)); break; - case T_CHAR: __ ldrh (result, Address(robj, roffset)); break; - case T_SHORT: __ ldrsh (result, Address(robj, roffset)); break; - case T_FLOAT: __ ldrw (result, Address(robj, roffset)); break; - case T_INT: __ ldrsw (result, Address(robj, roffset)); break; + case T_BOOLEAN: __ ldarb(result, result); break; + case T_BYTE: __ ldarb(result, result); __ sxtb(result, result); break; + case T_CHAR: __ ldarh(result, result); break; + case T_SHORT: __ ldarh(result, result); __ sxth(result, result); break; + case T_FLOAT: __ ldarw(result, result); break; + case T_INT: __ ldarw(result, result); __ sxtw(result, result); break; case T_DOUBLE: - case T_LONG: __ ldr (result, Address(robj, roffset)); break; + case T_LONG: __ ldar (result, result); break; default: ShouldNotReachHere(); } - // counter_addr is address dependent on result. - __ eor(rcounter_addr, rcounter_addr, result); - __ eor(rcounter_addr, rcounter_addr, result); __ ldrw(rscratch1, safepoint_counter_addr); __ cmpw(rcounter, rscratch1); __ br (Assembler::NE, slow); diff --git a/src/hotspot/cpu/arm/jniFastGetField_arm.cpp b/src/hotspot/cpu/arm/jniFastGetField_arm.cpp --- a/src/hotspot/cpu/arm/jniFastGetField_arm.cpp +++ b/src/hotspot/cpu/arm/jniFastGetField_arm.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2019, 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 @@ -32,7 +32,7 @@ #define __ masm-> -#define BUFFER_SIZE 96 +#define BUFFER_SIZE 120 address JNI_FastGetField::generate_fast_get_int_field0(BasicType type) { const char* name = NULL; @@ -99,10 +99,10 @@ CodeBuffer cbuf(blob); MacroAssembler* masm = new MacroAssembler(&cbuf); fast_entry = __ pc(); + Label slow_case; // Safepoint check InlinedAddress safepoint_counter_addr(SafepointSynchronize::safepoint_counter_addr()); - Label slow_case; __ ldr_literal(Rsafepoint_counter_addr, safepoint_counter_addr); __ push(RegisterSet(R0, R3)); // save incoming arguments for slow case @@ -112,9 +112,21 @@ __ bic(R1, R1, JNIHandles::weak_tag_mask); - // Address dependency restricts memory access ordering. It's cheaper than explicit LoadLoad barrier - __ andr(Rtmp1, Rsafept_cnt, (unsigned)1); - __ ldr(Robj, Address(R1, Rtmp1)); + if (JvmtiExport::can_post_field_access()) { + // Using barrier to order wrt. JVMTI check and load of result. + __ membar(MacroAssembler::Membar_mask_bits(MacroAssembler::LoadLoad), Rtmp1); + + // Check to see if a field access watch has been set before we + // take the fast path. + __ ldr_global_s32(Rtmp1, (address)JvmtiExport::get_field_access_count_addr()); + __ cbnz(Rtmp1, slow_case); + + __ ldr(Robj, Address(R1)); + } else { + // Address dependency restricts memory access ordering. It's cheaper than explicit LoadLoad barrier + __ andr(Rtmp1, Rsafept_cnt, (unsigned)1); + __ ldr(Robj, Address(R1, Rtmp1)); + } Address field_addr; if (type != T_BOOLEAN @@ -170,20 +182,18 @@ ShouldNotReachHere(); } - // Address dependency restricts memory access ordering. It's cheaper than explicit LoadLoad barrier + __ ldr_literal(Rsafepoint_counter_addr, safepoint_counter_addr); #ifdef __ABI_HARD__ if (type == T_FLOAT || type == T_DOUBLE) { - __ ldr_literal(Rsafepoint_counter_addr, safepoint_counter_addr); __ fmrrd(Rres, Rres_hi, D0); - __ eor(Rtmp2, Rres, Rres); - __ ldr_s32(Rsafept_cnt2, Address(Rsafepoint_counter_addr, Rtmp2)); - } else + } #endif // __ABI_HARD__ - { - __ ldr_literal(Rsafepoint_counter_addr, safepoint_counter_addr); - __ eor(Rtmp2, Rres, Rres); - __ ldr_s32(Rsafept_cnt2, Address(Rsafepoint_counter_addr, Rtmp2)); - } + + // Order JVMTI check and load of result wrt. succeeding check + // (LoadStore for volatile field). + __ membar(MacroAssembler::Membar_mask_bits(MacroAssembler::LoadLoad | MacroAssembler::LoadStore), Rtmp2); + + __ ldr_s32(Rsafept_cnt2, Address(Rsafepoint_counter_addr)); __ cmp(Rsafept_cnt2, Rsafept_cnt); // discards saved R0 R1 R2 R3 __ add(SP, SP, 4 * wordSize, eq); diff --git a/src/hotspot/cpu/sparc/jniFastGetField_sparc.cpp b/src/hotspot/cpu/sparc/jniFastGetField_sparc.cpp --- a/src/hotspot/cpu/sparc/jniFastGetField_sparc.cpp +++ b/src/hotspot/cpu/sparc/jniFastGetField_sparc.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2019, 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 @@ -70,6 +70,15 @@ __ andcc (G4, 1, G0); __ br (Assembler::notZero, false, Assembler::pn, label1); __ delayed()->srl (O2, 2, O4); + + if (JvmtiExport::can_post_field_access()) { + // Check to see if a field access watch has been set before we + // take the fast path. + AddressLiteral get_field_access_count_addr(JvmtiExport::get_field_access_count_addr()); + __ load_contents(get_field_access_count_addr, O5); + __ cmp_and_br_short(O5, 0, Assembler::notEqual, Assembler::pn, label1); + } + __ mov(O1, O5); // Both O5 and G3 are clobbered by try_resolve_jobject_in_native. @@ -153,6 +162,15 @@ __ andcc (G4, 1, G0); __ br (Assembler::notZero, false, Assembler::pn, label1); __ delayed()->srl (O2, 2, O4); + + if (JvmtiExport::can_post_field_access()) { + // Check to see if a field access watch has been set before we + // take the fast path. + AddressLiteral get_field_access_count_addr(JvmtiExport::get_field_access_count_addr()); + __ load_contents(get_field_access_count_addr, O5); + __ cmp_and_br_short(O5, 0, Assembler::notEqual, Assembler::pn, label1); + } + __ mov(O1, O5); // Both O5 and G1 are clobbered by try_resolve_jobject_in_native. @@ -211,6 +229,15 @@ __ andcc (G4, 1, G0); __ br (Assembler::notZero, false, Assembler::pn, label1); __ delayed()->srl (O2, 2, O4); + + if (JvmtiExport::can_post_field_access()) { + // Check to see if a field access watch has been set before we + // take the fast path. + AddressLiteral get_field_access_count_addr(JvmtiExport::get_field_access_count_addr()); + __ load_contents(get_field_access_count_addr, O5); + __ cmp_and_br_short(O5, 0, Assembler::notEqual, Assembler::pn, label1); + } + __ mov(O1, O5); // Both O5 and G3 are clobbered by try_resolve_jobject_in_native. diff --git a/src/hotspot/cpu/x86/jniFastGetField_x86_32.cpp b/src/hotspot/cpu/x86/jniFastGetField_x86_32.cpp --- a/src/hotspot/cpu/x86/jniFastGetField_x86_32.cpp +++ b/src/hotspot/cpu/x86/jniFastGetField_x86_32.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2019, 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 @@ -75,6 +75,14 @@ __ mov32 (rcx, counter); __ testb (rcx, 1); __ jcc (Assembler::notZero, slow); + + if (JvmtiExport::can_post_field_access()) { + // Check to see if a field access watch has been set before we + // take the fast path. + __ cmp32(ExternalAddress((address) JvmtiExport::get_field_access_count_addr()), 0); + __ jcc(Assembler::notZero, slow); + } + __ mov(rax, rcx); __ andptr(rax, 1); // rax, must end up 0 __ movptr(rdx, Address(rsp, rax, Address::times_1, 2*wordSize)); @@ -188,6 +196,14 @@ __ mov32 (rcx, counter); __ testb (rcx, 1); __ jcc (Assembler::notZero, slow); + + if (JvmtiExport::can_post_field_access()) { + // Check to see if a field access watch has been set before we + // take the fast path. + __ cmp32(ExternalAddress((address) JvmtiExport::get_field_access_count_addr()), 0); + __ jcc(Assembler::notZero, slow); + } + __ mov(rax, rcx); __ andptr(rax, 1); // rax, must end up 0 __ movptr(rdx, Address(rsp, rax, Address::times_1, 3*wordSize)); @@ -272,6 +288,14 @@ __ mov32 (rcx, counter); __ testb (rcx, 1); __ jcc (Assembler::notZero, slow); + + if (JvmtiExport::can_post_field_access()) { + // Check to see if a field access watch has been set before we + // take the fast path. + __ cmp32(ExternalAddress((address) JvmtiExport::get_field_access_count_addr()), 0); + __ jcc(Assembler::notZero, slow); + } + __ mov(rax, rcx); __ andptr(rax, 1); // rax, must end up 0 __ movptr(rdx, Address(rsp, rax, Address::times_1, 2*wordSize)); diff --git a/src/hotspot/cpu/x86/jniFastGetField_x86_64.cpp b/src/hotspot/cpu/x86/jniFastGetField_x86_64.cpp --- a/src/hotspot/cpu/x86/jniFastGetField_x86_64.cpp +++ b/src/hotspot/cpu/x86/jniFastGetField_x86_64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2019, 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 @@ -41,11 +41,10 @@ // c_rarg1: obj // c_rarg2: jfield id -static const Register rtmp = r8; -static const Register robj = r9; -static const Register rcounter = r10; -static const Register roffset = r11; -static const Register rcounter_addr = r11; +static const Register rtmp = rax; // r8 == c_rarg2 on Windows +static const Register robj = r9; +static const Register roffset = r10; +static const Register rcounter = r11; // Warning: do not use rip relative addressing after the first counter load // since that may scratch r10! @@ -74,6 +73,15 @@ __ mov (robj, c_rarg1); __ testb (rcounter, 1); __ jcc (Assembler::notZero, slow); + + if (JvmtiExport::can_post_field_access()) { + // Check to see if a field access watch has been set before we + // take the fast path. + assert_different_registers(rscratch1, robj, rcounter); // cmp32 clobbers rscratch1! + __ cmp32(ExternalAddress((address) JvmtiExport::get_field_access_count_addr()), 0); + __ jcc(Assembler::notZero, slow); + } + __ mov (roffset, c_rarg2); __ shrptr(roffset, 2); // offset @@ -164,6 +172,13 @@ __ testb (rcounter, 1); __ jcc (Assembler::notZero, slow); + if (JvmtiExport::can_post_field_access()) { + // Check to see if a field access watch has been set before we + // take the fast path. + __ cmp32(ExternalAddress((address) JvmtiExport::get_field_access_count_addr()), 0); + __ jcc(Assembler::notZero, slow); + } + // Both robj and rtmp are clobbered by try_resolve_jobject_in_native. BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler(); bs->try_resolve_jobject_in_native(masm, /* jni_env */ c_rarg0, robj, rtmp, slow); diff --git a/src/hotspot/share/prims/jni.cpp b/src/hotspot/share/prims/jni.cpp --- a/src/hotspot/share/prims/jni.cpp +++ b/src/hotspot/share/prims/jni.cpp @@ -3776,8 +3776,7 @@ void quicken_jni_functions() { // Replace GetField with fast versions - if (UseFastJNIAccessors && !JvmtiExport::can_post_field_access() - && !VerifyJNIFields && !CountJNICalls && !CheckJNICalls) { + if (UseFastJNIAccessors && !VerifyJNIFields && !CountJNICalls && !CheckJNICalls) { address func; func = JNI_FastGetField::generate_fast_get_boolean_field(); if (func != (address)-1) { diff --git a/test/hotspot/jtreg/runtime/jni/FastGetField/FastGetField.java b/test/hotspot/jtreg/runtime/jni/FastGetField/FastGetField.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/jni/FastGetField/FastGetField.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2019 SAP SE 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 8227680 + * @summary Tests that all FieldAccess notifications for Get*Field + with primitive type are generated. + * @compile FastGetField.java + * @run main/othervm/native -agentlib:FastGetField -XX:+IgnoreUnrecognizedVMOptions -XX:-VerifyJNIFields FastGetField + * @run main/othervm/native -agentlib:FastGetField -XX:+IgnoreUnrecognizedVMOptions -XX:-VerifyJNIFields -XX:+UnlockDiagnosticVMOptions -XX:+ForceUnreachable -XX:+SafepointALot -XX:GuaranteedSafepointInterval=1 FastGetField + */ + +import java.lang.reflect.Field; + + +public class FastGetField { + + private static final String agentLib = "FastGetField"; + + private native boolean initFieldIDs(Class c); + private native boolean initWatchers(Class c); + public native long accessFields(MyItem i); + public static native long getFieldAccessCount(); + + static final int loop_cnt = 10000; + + + class MyItem { + // Names should match JNI types. + boolean Z; + byte B; + short S; + char C; + int I; + long J; + float F; + double D; + + public void change_values() { + Z = true; + B = 1; + C = 1; + S = 1; + I = 1; + J = 1l; + F = 1.0f; + D = 1.0; + } + + public void reset_values() { + Z = false; + B = 0; + C = 0; + S = 0; + I = 0; + J = 0l; + F = 0.0f; + D = 0.0; + } + } + + // Static initialization. + static { + try { + System.loadLibrary(agentLib); + } catch (UnsatisfiedLinkError ex) { + System.err.println("Failed to load " + agentLib + " lib"); + System.err.println("java.library.path: " + System.getProperty("java.library.path")); + throw ex; + } + } + + public void TestFieldAccess() throws Exception { + MyItem i = new MyItem(); + if (!initFieldIDs(MyItem.class)) throw new RuntimeException("FieldID initialization failed!"); + + long duration = System.nanoTime(); + for (int c = 0; c < loop_cnt; ++c) { + if (accessFields(i) != 0l) throw new RuntimeException("Wrong initial result!"); + i.change_values(); + if (accessFields(i) != 8l) throw new RuntimeException("Wrong result after changing!"); + i.reset_values(); + } + duration = System.nanoTime() - duration; + System.out.println(loop_cnt + " iterations took " + duration + "ns."); + + if (getFieldAccessCount() != 0) throw new RuntimeException("Watch not yet active!"); + + // Install watchers. + if (!initWatchers(MyItem.class)) throw new RuntimeException("JVMTI missing!"); + + // Try again with watchers. + if (accessFields(i) != 0l) throw new RuntimeException("Wrong initial result!"); + i.change_values(); + if (accessFields(i) != 8l) throw new RuntimeException("Wrong result after changing!"); + if (getFieldAccessCount() != 16) throw new RuntimeException("Unexpected event count!"); + } + + public static void main(String[] args) throws Exception { + FastGetField inst = new FastGetField(); + inst.TestFieldAccess(); + } +} diff --git a/test/hotspot/jtreg/runtime/jni/FastGetField/libFastGetField.c b/test/hotspot/jtreg/runtime/jni/FastGetField/libFastGetField.c new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/jni/FastGetField/libFastGetField.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2019 SAP SE 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 +#include + +#include "jvmti.h" + +static jvmtiEnv *jvmti = NULL; + +static const char* fields[] = { "Z", "B", "C", "S", "I", "J", "F", "D" }; +#define NUM_FIELDS (sizeof fields / sizeof fields[0]) +static jfieldID fieldIDs[NUM_FIELDS]; +static jlong fieldAccessCount = 0; + + +JNIEXPORT jboolean JNICALL Java_FastGetField_initFieldIDs(JNIEnv *env, jobject this, jclass c) { + for (int i = 0; i < (int)NUM_FIELDS; ++i) { + fieldIDs[i] = (*env)->GetFieldID(env, c, fields[i], fields[i]); + if (fieldIDs[i] == NULL) { + printf("field %d not found\n", i); + return JNI_FALSE; + } + } + return JNI_TRUE; +} + + +JNIEXPORT jboolean JNICALL Java_FastGetField_initWatchers(JNIEnv *env, jobject this, jclass c) { + if (jvmti == NULL) { + printf("jvmti is NULL\n"); + return JNI_FALSE; + } + + for (int i = 0; i < (int)NUM_FIELDS; ++i) { + jvmtiError err = (*jvmti)->SetFieldAccessWatch(jvmti, c, fieldIDs[i]); + if (err != JVMTI_ERROR_NONE) { + printf("SetFieldAccessWatch failed with error %d\n", err); + return JNI_FALSE; + } + } + + return JNI_TRUE; +} + + +JNIEXPORT jlong JNICALL Java_FastGetField_accessFields(JNIEnv *env, jobject this, jobject obj) { + return + (*env)->GetBooleanField(env, obj, fieldIDs[0]) + + (*env)->GetByteField(env, obj, fieldIDs[1]) + + (*env)->GetCharField(env, obj, fieldIDs[2]) + + (*env)->GetShortField(env, obj, fieldIDs[3]) + + (*env)->GetIntField(env, obj, fieldIDs[4]) + + (*env)->GetLongField(env, obj, fieldIDs[5]) + + (jlong)((*env)->GetFloatField(env, obj, fieldIDs[6])) + + (jlong)((*env)->GetDoubleField(env, obj, fieldIDs[7])); +} + + +JNIEXPORT jlong JNICALL Java_FastGetField_getFieldAccessCount(JNIEnv *env, jclass c) { + return fieldAccessCount; +} + + +static void JNICALL onFieldAccess(jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread, + jmethodID method, jlocation location, jclass field_klass, + jobject object, jfieldID field) { + char *fname = NULL, *mname = NULL; + + jvmtiError err = (*jvmti)->GetFieldName(jvmti, field_klass, field, &fname, NULL, NULL); + if (err != JVMTI_ERROR_NONE) { + printf("GetFieldName failed with error %d\n", err); + return; + } + + err = (*jvmti)->GetMethodName(jvmti, method, &mname, NULL, NULL); + if (err != JVMTI_ERROR_NONE) { + printf("GetMethodName failed with error %d\n", err); + return; + } + + printf("%s accessed field %s\n", mname, fname); + + err = (*jvmti)->Deallocate(jvmti, (unsigned char*)fname); + if (err != JVMTI_ERROR_NONE) { + printf("Deallocate failed with error %d\n", err); + return; + } + + err = (*jvmti)->Deallocate(jvmti, (unsigned char*)mname); + if (err != JVMTI_ERROR_NONE) { + printf("Deallocate failed with error %d\n", err); + return; + } + + fieldAccessCount++; +} + + +JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, char* options, void* reserved) { + jvmtiCapabilities capa; + jvmtiEventCallbacks cbs = {0}; + + (*vm)->GetEnv(vm, (void**)&jvmti, JVMTI_VERSION_1_0); + + memset(&capa, 0, sizeof(capa)); + capa.can_generate_field_access_events = 1; + (*jvmti)->AddCapabilities(jvmti, &capa); + + cbs.FieldAccess = &onFieldAccess; + (*jvmti)->SetEventCallbacks(jvmti, &cbs, sizeof(cbs)); + (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_FIELD_ACCESS, NULL); + printf("Loaded agent\n"); + fflush(stdout); + + return 0; +}