Using Java internal trace

I was trying to understand a class load problem, and needed to use Java internal virtual machine trace to see what was happening.

You specify -Xtrace options at start up. Even with limiting options, I had over 2 millions lines of trace output – and the Java application had not fully started!

There are lots of components you can specify see here. I used

 -Xtrace:maximal={j9jcl,j9shr,j9prt,omrport},output=/u/tmp/java/trace.bin 

where

  • j9jcl is Java class libraries
  • j9shr is shared classes libraries
  • j9prt is VM port libraries… the platform specific (ported) code
  • omrport is the OMR ported code. (OMR is a set of common components, such as threads and garbage collection)

I haven’t used it,but you can say do something when this trace point is triggered, stop (suspend) tracing.

-Xtrace:trigger=tpnid{abc.123,suspend}

In the example trace output

07:49:24.699384689 0x000000002167f200 omrport.657 Event J9 VM NLS message: Failed to find class org/springframework/boot/loader/launch/PropertiesLauncher in shared cache for class-loader id 0.

where the trace point is omrport.657

You can put the definitions in a file -Xtrace:properties=<filename>

I have a trace file – now what?

You can format the trace file on z/OS, but I downloaded the file (in binary) to my Linux machine, because a) I have more disk space, 2) the java formatting is faster, 3) I have better tools to look at the data. You need the save level of Java (or better) to format all the fields. If there is a mismatch, some fields will not be formatted. The traceformat command is described here.

Format the data

/home/colinpaice/Downloads/jdk-21.0.6+7/bin/traceformat -verbose trace.bin trace.txt

It produced

Writing formatted trace output to file trace.txt
Processing 245.45312Mb of binary trace data
Processed 10.0Mb (4%), burst speed: 1.9344624Mb/s, average: 1.9344624Mb/s
Processed 20.0Mb (8%), burst speed: 2.4675941Mb/s, average: 2.1687446Mb/s
Processed 30.0Mb (12%), burst speed: 3.4868424Mb/s, average: 2.4814205Mb/s
...

I wrote the blog post Finding the needle in a field of hay, to help extract the records of interest.

On the traceformat command you can specify

  • -indent: Indents trace messages at each Entry trace point and outdents trace messages at each Exit trace point. The default is not to indent the messages.
  • -threads: Filters the output for the given thread IDs only. thread id is the ID of the thread, which can be specified in decimal or hex (0x) format. Any number of thread IDs can be specified, separated by commas.

Looking at the data

I used the commands grep -n …., tail -n +999, head -n 500, less as described here. But for detailed analysis I piped the output into a file, and used an editor on the output. This meant the lines did not wrap, I could annotate (and save) the trace, and highlight interesting records.

Because some of the lines are very long, I split interesting lines so all of the data was displayed in the window, and I did not need to scroll.

Trying to use a Java jmod on midrange

I was using functions shipped in a .jmod on z/OS, and could not find how to get use them in Eclipse IDE.

The code was

...
import com.ibm.oti.shared.*;
...

Building Java code for z/OS on other operating systems told me how to build a jar file from the .jmod

I had to use the right level of Java on midrange. See Getting the right Java installed on Linux. This level had the openj9.sharedclasses.jmod file.

Even though I built the jar file and updated the Java build class path, it could not find the package, getting message “The package com.ibm.oti.shared is not available”.

I found

  • I had to add the .jmod file to the “module” part of the build class path, and not the classpath itself.
  • I had a jar with the same name in the Eclipse application tree.

I fixed these and it worked.

Getting the right Java installed on Linux

I was programming Java Shared Classes Caching on z/OS, and tried using Eclipse IDE on Linux to edit the source. I had several problems with this.

The import files are in a .jmod file, not the .jar files I was expecting.

Compile on z/OS

To compile the Java source on z/OS I needed to add the .jmod file to the classpath.

cp="-cp /usr/lpp/java/J21.0_64/jmods/openj9.sharedclasses.jmod" 
/usr/lpp/java/J8.0_64/J21.0_64/bin/javac $cp m4.java

With this I could use

...
import com.ibm.oti.shared.*;
...

When I used the source in the Eclipse IDE, it could not find the imports.

Wrong version of Java

I had installed Java openj9-21 on Linux, but it did not have the openj9.sharedclasses.jmod file.

I downloaded the Semeru code from here.

Often Java is installed in /usr/java/…. files. I installed the new version into one of my personal directories.

The file ibm-semeru-open-jdk_x64_linux_21.0.6_7_openj9-0.49.0.tar.gz was downloaded to my Downloads directory.

You can untar it in place – or unzip it to a different location using the -C …. option

tar -xvf ibm-semeru-open-jdk_x64_linux_21.0.6_7_openj9-0.49.0.tar.gz 

This created a directory jdk-21.0.6+7 and created the files below it.

You can update the “sudo update-alternatives…” command by

sudo update-alternatives –list java
sudo update-alternatives –install /usr/bin/javac javac /home/colinpaice/Downloads/jdk-21.0.6+7/bin/javac
sudo update-alternatives –install /usr/bin/java java /home/colinpaice/Downloads/jdk-21.0.6+7/bin/java 1
sudo update-alternatives –install /usr/bin/jar jar /home/colinpaice/Downloads/jdk-21.0.6+7/bin/jar 1

Which defines which symlinks to define for the javac, java and jar commands.

You also need to update your Java_Home environment variable.

There’s more

I had to configure Eclipse to use this new level of Java. Window-> Preferences-> Installed JREs, and use the location of the directory /home/colinpaice/Downloads/jdk-21.0.6+7.