Probing Java Methods with SystemTap

Today we’ll be looking at systemtap’s latest native java probing capabilities.
These go beyond systemtap’s existing hotspot-based probe points to actual entry,
exit, and line number specific to the relevant java method.  This allows for pinpoint probing of a java application, without the need to place probes on the underlying JVM itself.

How to install (if running RHEL 7 Beta)

# yum install systemtap systemtap-runtime-java

Basic Usage

How do I use systemtap to probe a java method?

Below we have a simple threaded java program, which waits for our input on the command line.  Given the input ‘int’ or ‘long’, the program will print out a predetermined
variable (that we would like to know the value).

package foo.bar;

import java.lang.*;
import java.io.DataInputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;

class ThreadedExample
{

    public static void printMessage(int message)
    {
    System.out.println("message: " + message);
    }

    public static void printMessage(long message)
    {
    System.out.println("message: " + message);
    }

    public static void main(String[] args)
    {
    try
        {
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        String next = in.readLine();
        while(next != null && next.length() > 0 && !next.contains("end"))
            {
            final String arg = next;
            final int i = 42;
            final long j = 987234864;
            Thread thread = new Thread(arg)
                {
                public void run()
                {
                    if(arg.equals("int"))
                        printMessage(i);
                    else if(arg.equals("long"))
                         printMessage(j);
                    else
                    System.out.println("Neither of the types");
                }
                };
            thread.start();
            try
                {
                thread.join();
                } catch (Exception e){}
            next = in.readLine();
            }
        } catch (Exception e){}
    }
}

compile and run the java program in the typical way;

lberk:]$ javac foo/bar/ThreadedExample.java
lberk:]$ java foo.bar.ThreadedExample
int
message: 42
test
Neither of the types
long
message: 987234864

Good, now to run a systemtap script which will probe both printMessage methods
and display the parameter passed.

Java probe points in systemtap follow the basic syntax of
probe java(“PNAME”).class(“CLASS”).method(“METHOD”) { handler }

where PNAME is either the string of the java program as listed by jps -l or the
specific PID of the java program.  It should be noted that the java program must
be already running at the time of script invocation.  The CLASS parameter is the
fully qualified class, and the METHOD parameter is the method name with parameter
types.

An example of a systemtap script written for ThreadedExample.java is below:

ThreadExample.stp

#!/usr/bin/env stap
probe java("foo.bar.ThreadedExample").class("ThreadedExample").method("printMessage(int)")
{
    printf("Hit printMessage(int): %dn", $arg1)
}
probe java("foo.bar.ThreadedExample").class("ThreadedExample").method("printMessage(long)")
{
    printf("Hit printMessage(long): %dn", $arg1)
}

Running the systemtap script in another terminal (and specifying “int”, “long” respectively);

lberk:]$ stap ThreadedExample.stp

Hit printMessage(int): 42                                          
Hit printMessage(long): 987234864

JBoss-EAP Example

A common method for running java programs is on an application server.  One of the popular choices is JBoss, and Systemtap is able to probe java methods running on JBoss as well!

This example will use the JBoss Enterprise Application Platform, using the helloworld example which can be obtained by running:

git clone git://github.com/jboss-jdf/jboss-as-quickstart.git --branch jdf-2.1.2.Final

In order to probe a standalone server, we need to ensure both the byteman and systemtap packages are visible for the target’s class loaders.  We can do this by either editing the configuration file directly, or by modifying the environment variable on server startup to list org.jboss.byteman and org.systemtap.byteman.helper package in the
JBOSS_MODULES_SYSTEM_PKGS option.  For our example we’ve launched the startup server using the environment variable.

lberk:]$ JBOSS_MODULES_SYSTEM_PKGS="org.jboss.byteman,org.systemtap.byteman.helper" $HOME/jboss-as-user-instance/bin/standalone.sh

We then deploy the helloworld example by running the following command in the
jboss-as-quickstart/helloworld directory:

lberk:]$ mvn clean package jboss-as:deploy

At this point, we have a running hello world example that displays a “Hello World!” at
http://localhost:8080/jboss-as-helloworld/HelloWorld

We can tell when this web page is requested, and how many times by writing a simple
systemtap script that targets the “doGet” method.

stap jboss-example.stp
———————-

#!/usr/bin/env stap
global counter

probe

java("/usr/share/jbossas/jboss-modules.jar").class("org.jboss.as.quickstarts.helloworld.HelloWorldServlet").method("doGet")
{
    counter++
    printf("%s, %dn", ctime(gettimeofday_s()), counter)
}

This will provide output similar to the following for each webpage request:

Mon Jun 17 02:37:05 2013, 1
Mon Jun 17 02:37:06 2013, 2
Mon Jun 17 02:37:07 2013, 3
Mon Jun 17 02:37:08 2013, 4
Mon Jun 17 02:37:08 2013, 5
Mon Jun 17 02:37:09 2013, 6
Mon Jun 17 02:37:09 2013, 7

Join the Red Hat Developer Program (it’s free) and get access to related cheat sheets, books, and product downloads.

Share