JVMTI not receiving field access events with fast_getfield
Amir Rapson
amir at vfunction.com
Tue Dec 12 13:03:17 UTC 2017
Hi,
I know the code has some relevant code, but it doesn't seem to work. You
will see some differences: the CALL_VM to the post function has only 2
arguments and not 3, I'm not sure it matters.
The platform is x86_64, Java version is 1.8.0-152. I can try other versions
if you wish.
Please find the attached MyAgent.cpp MyAgent.h and com_vfunction_Main.h to
compile the agent.
Please also find the attached com.vfunction.zip that holds the simple java
util to reproduce the problem.
You'll see the problem on ArrayList.add(item) where you'll get events only
on codes #16 and #22 and not on #2 and #11 (see attached bytecode.txt of
said function).
Please let me know once you managed to reproduce it. I can also send you a
patch file to disable the fast_getfield (and fast putfield) to show that the
problem disappears.
I'm currently looking at the actual assembly of the add function, both with
the modified hotspot and without and will try to assist will offering a fix
for this issue.
One last thing. I saw a very old issue (resolved by Daniel Daugherty)
stating that fast_getfield was not completely addressed with the resolution
on this issue. The bug ID is
https://bugs.openjdk.java.net/browse/JDK-4300409
I'll be happy to assist further.
Best regards,
Amir
-----Original Message-----
From: Vladimir Ivanov [mailto:vladimir.x.ivanov at oracle.com]
Sent: Tuesday, December 12, 2017 2:46 PM
To: Amir Rapson <amir at vfunction.com>
Cc: Coleen.Phillimore at oracle.com; daniel.daugherty at oracle.com
Subject: Re: JVMTI not receiving field access events with fast_getfield
Thanks for the report, Amir!
Unfortunately, I can't access the bug you filed.
Can you, please, share the test case and instructions how to reproduce the
problem?
Also, what Java version & platform do you observe the bug on?
I briefly looked into template interpreter code on x86 and it does have
relevant code to post JVMTI events in fast accessors.
Best regards,
Vladimir Ivanov
On 12/12/17 11:12 AM, Amir Rapson wrote:
> Hi Daniel, Coleen and Vladimir,
>
> I tracked your email addresses from some mailing lists and hopefully
> one of you is a relevant person for my problem.
>
> I also filed a bug request (ID 9051849) but since I don’t have an
> option to add information I decided to email you.
>
> The problem I’m seeing is that JMTI does not receive field access
> events (and probably field modification events) when /fast_getfield/
> is used instead of /get_field/.
>
> I verified this by disabling all the
> /patch_bytecode(Bytecodes::_fast_?getfield, bc, rbx)/ in
> /TemplateTable::getfield_or_static()/ and made sure that JVMTI
> receives all the correct events.
>
> I have the agent code and a short java program that can reproduce the
> problem and would be happy to assist in fixing and or debugging this
> issue.
>
> I would appreciate any feedback.
>
> Best regards,
>
> Amir
>
> Amir Rapson | Founder & VP Eng @ *vFunction* | 7 HaPelech St.
> Tel-Aviv | +972-522650968
>
-------------- next part --------------
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_vfunction_Main */
#ifndef _Included_com_vfunction_Main
#define _Included_com_vfunction_Main
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_vfunction_Main
* Method: inspectField
* Signature: (Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Z)V
*/
JNIEXPORT void JNICALL Java_com_vfunction_Main_inspectField
(JNIEnv *, jclass, jclass, jobject);
#ifdef __cplusplus
}
#endif
#endif
-------------- next part --------------
#include "string.h"
#include "stdlib.h"
#include "jvmti.h"
#include "com_vfunction_Main.h"
static jvmtiEnv *jvmti;
static void printFieldAccessInfo(jmethodID method,
jfieldID field,
jclass field_klass,
bool modified,
jlocation location);
static void tagAndWatch(JNIEnv * jni_env, const jobject newObject);
static void JNICALL onFieldAccess(jvmtiEnv *jvmti_env,
JNIEnv* jni_env,
jthread thread,
jmethodID method,
jlocation location,
jclass field_klass,
jobject parentObject,
jfieldID field)
{
printFieldAccessInfo(method, field, field_klass, false, location);
}
static void JNICALL onFieldModified(jvmtiEnv *jvmti_env,
JNIEnv* jni_env,
jthread thread,
jmethodID method,
jlocation location,
jclass field_klass,
jobject parentObject,
jfieldID field,
char signature_type,
jvalue new_value)
{
printFieldAccessInfo(method, field, field_klass, true, location);
if (signature_type == 'L')
{
jobject newObject = new_value.l;
tagAndWatch(jni_env, newObject);
}
}
void tagAndWatch(JNIEnv * jni_env, const jobject obj)
{
if (obj == NULL)
{
return;
}
jclass klass = jni_env->GetObjectClass(obj);
do
{
jfieldID* klassFields;
jint fieldCount;
jvmtiError err = jvmti->GetClassFields(klass, &fieldCount, &klassFields);
if (err != JVMTI_ERROR_NONE)
{
printf("Failed to get class fields\n");
}
for (int i = 0; i < fieldCount; ++i)
{
err = jvmti->SetFieldModificationWatch(klass, klassFields[i]);
if (err != JVMTI_ERROR_NONE && err != JVMTI_ERROR_DUPLICATE)
{
printf("%s Failed to set field modification %d\n", __FUNCTION__, err);
}
err = jvmti->SetFieldAccessWatch(klass, klassFields[i]);
if (err != JVMTI_ERROR_NONE && err != JVMTI_ERROR_DUPLICATE)
{
printf("%s Failed to set field access %d\n", __FUNCTION__, err);
}
char *sig = NULL;
err = jvmti->GetFieldName(klass, klassFields[i], NULL, &sig, NULL);
if (sig)
{
if (sig[0] == 'L')
{
jobject fieldVal = jni_env->GetObjectField(obj, klassFields[i]);
tagAndWatch(jni_env, fieldVal);
}
jvmti->Deallocate((unsigned char*)sig);
}
}
err = jvmti->Deallocate((unsigned char*)klassFields);
if (err != JVMTI_ERROR_NONE)
{
printf("%s Failed to deallocate fields %d\n", __FUNCTION__, err);
}
klass = jni_env->GetSuperclass(klass);
} while (klass != NULL);
}
JNIEXPORT void JNICALL Java_com_vfunction_Main_inspectField(JNIEnv * env,
jclass caller,
jclass klass,
jobject field)
{
jfieldID fieldId = env->FromReflectedField(field);
jvmtiError err = jvmti->SetFieldModificationWatch(klass, fieldId);
if (err != JVMTI_ERROR_NONE)
{
printf("%s Failed to set field modification %d\n", __FUNCTION__, err);
}
err = jvmti->SetFieldAccessWatch(klass, fieldId);
if (err != JVMTI_ERROR_NONE)
{
printf("%s Failed to set field access %d\n", __FUNCTION__, err);
}
}
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved)
{
jvmtiEventCallbacks callbacks = {};
jvmtiError error;
jint result = jvm->GetEnv((void **)&jvmti, JVMTI_VERSION_1_1);
if (result != JNI_OK)
{
printf("ERROR: Unable to access JVMTI!\n");
return JNI_ERR;
}
else
{
printf("Agent succesfully loaded\n");
}
jvmtiCapabilities capa = {};
capa.can_generate_field_modification_events = 1;
capa.can_generate_field_access_events = 1;
capa.can_tag_objects = 1;
error = jvmti->AddCapabilities(&capa);
if (error != JVMTI_ERROR_NONE)
{
printf("Failed to set capabilities: %d\n", error);
}
callbacks.FieldModification = &onFieldModified;
callbacks.FieldAccess = &onFieldAccess;
error = jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));
if (error != JVMTI_ERROR_NONE)
{
printf("Failed to set event callbacks: %d\n", error);
}
error = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_FIELD_ACCESS, NULL);
if (error != JVMTI_ERROR_NONE)
{
printf("Failed to set event notifications: %d\n", error);
}
error = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_FIELD_MODIFICATION, NULL);
if (error != JVMTI_ERROR_NONE)
{
printf("Failed to set event notifications: %d\n", error);
}
return JNI_OK;
}
static void printFieldAccessInfo(jmethodID method,
jfieldID field,
jclass field_klass,
bool modified,
jlocation location)
{
char *name;
jvmtiError err = jvmti->GetFieldName(field_klass, field, &name, NULL, NULL);
if (err != JVMTI_ERROR_NONE)
{
printf("%s: Failed to get field name", __FUNCTION__);
return;
}
char *fname;
err = jvmti->GetMethodName(method, &fname, NULL, NULL);
if (err != JVMTI_ERROR_NONE)
{
printf("%s: Failed to get method name", __FUNCTION__);
return;
}
jclass methodClass;
err = jvmti->GetMethodDeclaringClass(method, &methodClass);
if (err != JVMTI_ERROR_NONE)
{
printf("%s: Failed to get method class", __FUNCTION__);
return;
}
char *csig;
err = jvmti->GetClassSignature(methodClass, &csig, NULL);
if (err != JVMTI_ERROR_NONE)
{
printf("%s: Failed to get class signature", __FUNCTION__);
return;
}
printf("\"%s%s\" %s field \"%s\", location: %d\n",
csig, fname, modified ? "modified" : "accessed", name, (int)location);
jvmti->Deallocate((unsigned char*)csig);
jvmti->Deallocate((unsigned char*)fname);
jvmti->Deallocate((unsigned char*)name);
}
-------------- next part --------------
A non-text attachment was scrubbed...
Name: com.vfunction.zip
Type: application/x-zip-compressed
Size: 1282 bytes
Desc: not available
URL: <http://mail.openjdk.java.net/pipermail/serviceability-dev/attachments/20171212/8f633403/com.vfunction-0001.zip>
-------------- next part --------------
public boolean add(E);
descriptor: (Ljava/lang/Object;)Z
flags: ACC_PUBLIC
Code:
stack=5, locals=2, args_size=2
0: aload_0
1: aload_0
2: getfield #2 // Field size:I
5: iconst_1
6: iadd
7: invokespecial #41 // Method ensureCapacityInternal:(I)V
10: aload_0
11: getfield #5 // Field elementData:[Ljava/lang/Object;
14: aload_0
15: dup
16: getfield #2 // Field size:I
19: dup_x1
20: iconst_1
21: iadd
22: putfield #2 // Field size:I
25: aload_1
26: aastore
27: iconst_1
28: ireturn
LineNumberTable:
line 462: 0
line 463: 10
line 464: 27
LocalVariableTable:
Start Length Slot Name Signature
0 29 0 this Ljava/util/ArrayList;
0 29 1 e Ljava/lang/Object;
LocalVariableTypeTable:
Start Length Slot Name Signature
0 29 0 this Ljava/util/ArrayList<TE;>;
0 29 1 e TE;
Signature: #176 // (TE;)Z
More information about the serviceability-dev
mailing list