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

Java Instrumentation Agent's Boot-Class-Path is not visible

    Details

      Description

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

      ADDITIONAL OS VERSION INFORMATION :
      Windows 10

      EXTRA RELEVANT SYSTEM CONFIGURATION :
      I am using Instrumentation agent which starts after the VM has been started. I have a main method defined as

      public static void agentmain(String args, Instrumentation inst)

      A DESCRIPTION OF THE PROBLEM :
      I believe this may be a bug in Instrumentation package where it cannot see the Classes within the jars specified in the agent jar MANIFEST.mf. My target application is a Tomcat-based web application which gets deployed standalone (plain directory, no war).

      My agent main method is the following:

      public static void agentmain(String args, Instrumentation ins) {

              Class.forName("a.package.outside.this.jar");

      }

      The agent fails with NoClassDefFoundError. The class is originally loaded by the JVM before the agent was invoked. But as soon as the agent is invoked, all the loaded class references are lost inside the agent. According to the documentation, If I have specified jars in Boot-Class-Path attribute in my agent MANIFEST.MF - it should locate the jars and load classes as necessary.

      I tried to start the application with -verbose:class argument. I can see that my jars are in the following state when the agent is first invoked:

      [Opened file:/C:/somelocation/one.jar]
      [Opened file:/C:/somelocation/two.jar]
      [Opened file:/C:/somelocation/three.jar]

      But I never manged to load any classes from the jar. I believe there is a fundamental gap in documentation or this a bug. Ideally, the instrumentation API should use URLClassLoader to load all resources which it is not doing currently.


      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      1. Create a simply instrumentation agent using a class file.
      2. Only specify public static void agentmain(String args, Instrumentation inst) method.
      3. From the method, try to call Class.forName() and provide a package name of a class that has already been loaded by the VM. This class has to be user-defined class i.e. nothing from JDK native package.
      4. Create a MANIFEST.MF for this agent before packaging into a jar. Please follow java instrumentation API documentation to create the MANIFEST.
      5. With the MANIFEST.MF above, please add jar paths to the Boot-Class-Path location which will be searched by the System class loader when the agent is attached to the VM.
      6. Start your application with -verbose:class argument
      7. Inject the agent into your application.
      8. A NoClassDefFoundError will be thrown as soon as "Class.forName()" is invoked as mentioned in 3.


      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      If a Class is loaded by the target VM, attaching an Instrumentation agent will not make any difference.

      For example,

      1. A user-defined class "user.defined.package.Class1" has been loaded by the target JVM before the agent is loaded.

      2. Now the agent is attached to the VM.

      3. Agent loads with all the necessary classes.

      4. I call Class.forName("user.defined.package.Class1") from agent's agentmain method.

      5. I should get the class.
      ACTUAL -
      1. NoClassDefFoundError - if the jar is in Boot-Class-Path
      2. ClassNotFoundException - if the jar is not in Boot-Class-Path

      ERROR MESSAGES/STACK TRACES THAT OCCUR :
      1. NoClassDefFoundError - if the jar is in Boot-Class-Path
      2. ClassNotFoundException - if the jar is not in Boot-Class-Path

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      package my.agent;

      public class ByteAgent {


      public static void agentmain (String args, Instrumentation inst) {

               //Put a debug pointer here to stop and see the agent behaviour
                 System.out.println("I am here");
                Class.forName("my.dummy.source.MyClass");

      }


      }

      --------------------

      package my.dummy.source


      public class MyClass {

      private String myString = "";

      public MyClass(String str) {

      this.myString = str;
      }

      public MyClass() {
      this.str = "DUMMY";
      }

      public String getMyString() {
      return this.myString;
      }

      }

      -------

      package my.other.pkg

      import my.dummy.source.MyClass

      public class AppRunner {

      public static void main (String[] args) {


      MyClass myClass = new MyClass();

      // Please add code here to invoke agent jar
      // Put a debug pointer on Class

      }



      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      I have to load all the classes in in memory using getAllLoadedClasses() which is quite expensive for heap memory usage and garbage collection. A

        Attachments

          Activity

            People

            • Assignee:
              fmatte Fairoz Matte
              Reporter:
              webbuggrp Webbug Group
            • Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved: