Replacing JDK classes for debugging

Sometimes one needs to get diagnostic information from code buried in the JDK in a situation where the code can’t easily be run in a debugger, e.g., when a non-technical user has a reproducible problem that no one else can reproduce. In this situation, it can be useful to replace a JDK class with a class of our own. The technique is not difficult once you know how, but examples of doing it are hard to find. Here is a minimal example:

Suppose we would like to print something whenever java.util.Arrays.sort(Object[]) is called. To do that, we get the source code for java.util.Arrays, put it in a directory named java/util and edit the relevant method:

    public static void sort(Object[] a) {
        // Added by me
        System.out.println("HEY HEY HEY");

        // Preexisting code
        ...
    }

We compile our modified Arrays.java:

javac --patch-module java.base=. java/util/Arrays.java

The --patch-module option tells javac that we want classes from . to be merged into the java.base module, which is where Arrays lives.

Here is our test program, Test.java, from which we will call Arrays.sort():

import java.util.Arrays;

class Test {
  public static void main(String[] args) {
    Arrays.sort(new Object[0]);
  }
}

Compile the test program as usual:

javac Test.java

Now we run the test program, first using unmodified JDK classes:

$ java Test
$

There is no output, as expected.

Next, we run the test program using the modified Arrays class:

$ java --patch-module java.base=. Test
HEY HEY HEY
$

We can see from the output that our modified method was run.

1 Like