Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8193369

post_field_access does not work for some functions, possibly related to fast_getfield

    XMLWordPrintable

Details

    • b07
    • x86_64
    • linux_ubuntu

    Description

      FULL PRODUCT VERSION :
      java version "1.8.0_151"
      Java(TM) SE Runtime Environment (build 1.8.0_151-b12)
      Java HotSpot(TM) 64-Bit Server VM (build 25.151-b12, mixed mode)


      FULL OS VERSION :
      Relevant at least to x86_64, both linux and windows.


      A DESCRIPTION OF THE PROBLEM :
      "fast_getfield" commands do not trigger JVMTI access events, only regular getfield.

      Also see note on fast_getfield on "JDK-4300409 SetFieldAccessWatch not implemented" regarding faulty implementation on fast_getfield.

      See steps to reproduce for more information.


      THE PROBLEM WAS REPRODUCIBLE WITH -Xint FLAG: Yes

      THE PROBLEM WAS REPRODUCIBLE WITH -server FLAG: Yes

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Using JVMTI, do SetFieldAccessWatch recusrively on an object that holds an ArrayList.

      Performing "itemList.add(index, new_item)" will show all field access events.
      Performing "itemList.add(new_item)" will NOT show all field access events.

      The reason is that in the first case the template interpreter treats the bytecode as "getfield" and in the second case the interpreter runs it as "fast_getfield".

      Whatever is a "fast_getfield" - no events are sent on it.

      Attached all the code required to reproduce.


      EXPECTED VERSUS ACTUAL BEHAVIOR :
      Expected to recieve events on getfield commands in ArrayList.add (public boolean add(E);)
      on codes #2, #11, #16 and modification on #22.

      Actual behavior: only get events on #16 and modification on #22.

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      #################### java code (jvmti agent code follows ###################
      public class Main {

          public static void main(String[] args) throws IOException, InterruptedException, NoSuchFieldException {
              inspectField(MyList.class, MyList.class.getDeclaredField("items"));

              MyList root = new MyList();
              MyItem item = new MyItem();

              System.out.println("############### add(index, item), get all getfield events");
              root.items.add(0, item);
              System.out.println("############### add(item), missing getfield events");
              root.items.add(item);
              System.out.println("############### add(index, item), get all getfield events");
              root.items.add(1, item);
              
          }

          private static native void inspectField(Class klass, Field field);
      }

      public class MyList {
          public List<MyItem> items = new ArrayList<>();

      }

      public class MyItem {

      }



      ##################### C++ code for JVMTI AGENT ########################
      #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);
      }

      #############################################################
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      No workaround found.

      Attachments

        1. MyAgent.cpp
          5 kB
        2. MyAgent.cpp
          5 kB
        3. com.vfunction.zip
          1 kB
        4. com.vfunction.zip
          1 kB
        5. com_vfunction_Main.h
          0.5 kB
        6. com_vfunction_Main.h
          0.5 kB
        7. bytecode.txt
          1 kB
        8. bytecode.txt
          1 kB

        Issue Links

          Activity

            People

              amenkov Alex Menkov
              webbuggrp Webbug Group
              Votes:
              0 Vote for this issue
              Watchers:
              6 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved: