Creating Custom Profilers with Probekit
Creating Custom Profilers with Probekit

Please contact Navid Mehregani (nmehrega@ca.ibm.com) for any comments or questions about this document.
Important: This document is to be used with TPTP 4.3. For a short introductory movie on Probekit, please click here.


Table of Content

1.0 Introduction to Probekit
2.0 Closer Look at Probes
2.1 Method Probes VS. Callsite Probes
2.2 Fragments
3.0 Static VS. Dynamic Instrumentation
3.1 Profile launch Configuration
3.2 Two-Tier Filtering
4.0 Example 1: Creating the EntryExit Probe
4.1 Example 1: Static Instrumentation with EntryExit Probe
4.2 Example 1: Dynamic Instrumentation with EntryExit Probe
5.0 Example 2: Graphing Method Calls with JFreeChart
6.0 Resources
7.0 Legal

Appendix A: Fragment Types
Appendix B: Fragment Data Items


1.0 Introduction
Probekit allows developers to write fragments of JavaTM code that can be invoked at the specified points in the Java class. This allows the collection of runtime data about the application, which can be used for profiling, debugging, or reverse engineering. One common use of Probekit is to create lightweight profilers that collect only the data developers are interested in. Another usage is to trace the method invocations based on certain actions for the purpose of reverse engineering.

Probekit offers various injection points that developers can use for their probe. Example of injection points offered in Probekit include: Method entry, method exit, catch/finally blocks, class loading, etc…

This document presents the basic concepts behind Probekit. The given examples help to introduce readers to the power and flexibility offered by Probekit.



2.0 Closer Look at Probes
Probes are created in a Probekit source file, which has a .probe extension. Probekit source files can be edited using the Probekit editor provided in TPTP. These files can contain one or more probes. Each probe is composed of the following:
  1. Exactly one Target
  2. Exactly one Import
  3. One or more Fragments
The Targets entry is used to specify the filtering criteria for the probe (i.e. what classes/methods the probe should target). This is done by specifying the names of the package, class, and method that should be targeted. The * (asterisk) character can be used to make multiple selections. Please see section 3.2 for more information about Probekit filtering. If the Targets page is left empty, the Probe will target all methods of the classes that are instrumented. Figure 1.0 is a snapshot of the Targets page.


Figure 1.0 - Targets page of a probe, targetting just the 'doPost' method of all classes

The Imports page is used to specify the Java packages and classes that are referenced by the probe. Import directives are optional and they use the same syntax as the Java import statement. When adding import directives, the import keyword and the final semicolon (;) should not be included. Figure 1.1 is a snapshot of the imports page that shows java.io.* being added as an import directive.



Figure 1.1 - Imports page of a probe with java.io.* added as an import directive

Before probe Fragments can be explained, the two different types of probes have to be discussed.



2.1 Method Probes VS. Callsite Probes
There are two different kinds of probes: method probes and callsite probes.
  1. Method probes are inserted into the body of a target method. For method probes, the byte-code instrumentation (BCI) engine instruments the class or jar files that contain the target method.
  2. Callsite probes are inserted into the body of any method that calls the target method. In other words, they are inserted at the call site, into the calling method, and not the called method. For callsite probes, the BCI engine instruments the class or jar files containing the methods that call the target methods.
    Callsite probes are often used when monitoring calls from a project to system libraries. Callsite probes are also useful when it is difficult or impossible to instrument the class files containing the methods that shoubld be targeted.

Whether a probe is a method probe or a callsite probe depends on the probe fragment type.
A Probekit source file (probe file) can contain both method probes and callsite probes. An individual probe, however, cannot contain a mixture of method and callsite probe fragments; all probe fragments in a probe must be of the same category.


2.2 Fragments
A probe's fragment defines the probe's logic. Every probe must contain at least one fragment. Fragments are composed of three things:
  1. Fragment Type
  2. Data Items
  3. Java Source Code
The fragment type indicates where the fragment code will get injected into the application. In other words, the fragment type indicates when the fragment will run in the targeted methods.

For example, an entry fragment will run upon method entry. Similarly an exit fragment runs upon normal method exit. A catch fragment runs at the beginning of a catch clause in the method, or at the beginning of a finally clause that runs as the result of an exception. As indicated in section 2.1 fragment types of callsite probes cannot be mixed with fragment types of method probes. Also note that a probe can contain more than one fragment, but cannot contain more than one fragment of any given type (e.g. A given probe can contain only one entry fragment).

All probes contain a special fragment called the Fragment at Class Scope. Any variables declared in this fragment will be visible to all other fragments. This fragment can also be used for any initialization work required by the probe when it's first loaded. A static initializer block can be used to complete any initialization work required by the probe. It's important to keep in mind that the code specified in this fragment is executed only once when the probe is loaded. To access the fragment at class scope, select the 'Probe' entry in the Probekit editor.

For a complete listing of fragment types please refer to Appendix A.

A fragment's data items are the names and types of data items that the fragment's source code can reference. The specification of data items is optional. A fragment can contain more than one data item, but each type of item can appear only once. A data item is made up of a data type and an arbitrary name.

The className and methodName data items can, for example, be used to retrieve the name of the class and method that's instrumented. These data items can then be used in the fragment's code by referencing their names. For a complete listing of fragment data items please refer to Appendix B.

The Java code section of a fragment can be used to specify the Java code that should be invoked by the fragment. This is clarified in the examples given in sections 4.0 and 5.0.



3.0 Static VS. Dynamic Instrumentation
Once a probe has been completed, it can be used to instrument Java applications. Probekit offers two different types of instrumentation: static and dynamic.

With static instrumentation, probes are injected prior to execution. Developers have to select all the class or JAR files they want to instrument prior to running the application. When files are statically instrumented, they are modified on disk to inject the probes into the classes. Section 4.1 gives step-by-step instructions on how classes can be statically instrumented. The only way of specifying filtering criteria when using static instrumentation is through the Targets page of a probe. Once Java classes are statically instrumented, they can be executed normally to have the probes collect the necessary runtime data. There is a manual clean-up step involved with static instrumentation to get the application back to its original state.

Dynamic instrumentation, on the other hand, instruments classes dynamically in-memory without modifying any files on disk. Dynamic instrumentation uses the JVMPI agent included in TPTP to get event notifications when classes are being loaded. It then replaces the in-memory representation of these classes with the instrumented version. Since dynamic instrumentation makes use of the JVMPI agent, the Java application that's to be instrumented has to be executed in profiling mode. Section 4.2 gives step-by-step instruction on how Java applications can be dynamically instrumented.

Dynamic instrumentation offers a lot of advantages over static instrumentation. With static instrumentation, the application has to be instrumented every time there is a modification either in the probe(s) or the application itself. Dynamic instrumentation, however, doesn't suffer from this problem since it instruments the classes in-memory. Another advantage of dynamic instrumentation is the fact that it doesn't modify any files on disk, thus it doesn't modify the normal behavior of the application, which means that no clean-up work is required to get the application back to its original state.

As stated above, with dynamic instrumentation, the application has to be executed in profiling mode. This is done by using the profile launch configuration provided in TPTP. Other than the Targets page, the user has the option of also providing filtering criteria through the profile launch configuration, which allows filtering at the JVMPI level. Thus there are two different ways of specifying filtering criteria with dynamic instrumentation. This idea is known as two-tier filtering and it's explained in section 3.2.



3.1 Profile launch Configuration
Once TPTP is properly installed, the profile launch configuration can be accessed by clicking on the profile button and selecting 'Profile'. The profile button is indicated in figure 2.0.


Figure 2.0 - Profile button

To use dynamic instrumentation, select the desired launch configuration on the left, then select the 'Monitor' tab as indicated in figure 2.1.


Figure 2.1 - Profile launch configuration

The 'Probe Insertion' analysis type is used to indicate that the application should be dynamically instrumented. To select the specific probe that will be used for instrumentation, select 'Probe Insertion' and click on 'Edit Options'. Figure 2.2 shows the options page for Probekit, which allows for the selection of probes that are available in the workspace. To modify the JVMPI filters, double click on the 'Java Profiling' entry in the 'Monitor' tab. By default, all internal classes in Java and Eclipse are filtered out.


Figure 2.2 - Probekit options page


3.2 Two-Tier Filtering
As mentioned in section 3.0, dynamic Probekit uses two-tier filtering which allows the user to specify filters in the Targets page of the probe as well as the filters page in the profile launch configuration. Both sets of filters are used to determine which classes/methods should be instrumented. The filtering criteria specified in the profile launch configuration are at the JVMPI level and the filtering criteria specified in the Targets page are at the Probe level.

When using dynamic instrumentation, users must be careful to check both the Targets page of the probe as well as the filter page in the profile launch configuration to ensure that the desired filtering criteria is achieved.

An example where two-tier filtering would be helpful is when a probe is created that should only be targeted to the doPost and doGet methods of Servlets. The probe author can modify the Targets page of the probe to target it to only doPost and doGet methods. The probe user can then use the filtering criteria in the profile launch configuration to target the probe only to a selected set of Servlets. Although two-tier filtering can be quite helpful, in most cases, using just the filtering criteria in the profile launch configuration will suffice.



4.0 Example 1: Creating the EntryExit Probe
In this example a probe is created to print out a message upon each method entry and exit. Another message is printed if an exception occurs, indicating the exception message. This section will illustrate how the probe can be created. The next two sections will illustrate how to statically and dynamically instrument a class with the probe.

  1. Create a new Java project: Switch to the Java perspective > right click in the package explorer view > new > project > select Java Project > next > call it ProbekitExample > select 'create separate source and output folders as indicated in figure 3.0 > Finish.



  2. Figure 3.0 - New Java Project Wizard

  3. Create a new Probekit source file: Right click on the source folder of the newly created project > new > other > expand Profiling and Logging > Select Probekit Source File > Type in 'EntryExit.probe' for the filename > Finish. The newly created probe should now be open in the Probekit editor. It should have a single entry fragment.
  4. Creating all the fragments: Create two other fragments by right clicking on the 'Probe' entry and selecting new > Fragment. Select one of the newly created fragments and change its type to 'exit'. Change the type of other fragment to 'catch'. There should now be three fragments of type entry, exit, and catch as indicated in figure 3.1.



  5. Figure 3.1 - Newly created probe

  6. Fragment at Class Scope: Select the 'Probe' entry to access the Fragment at Class Scope > type in the following in the fragment:

    /* The following static field will be used by multiple fragments */
    public static String spaces = "";

    This variable will be used to properly indent the outputs of the method entry and method exit fragments.
  7. Entry Fragment: Select the entry fragment > Add the following two data items by clicking on the 'Add' button:

    Data Type
    Name
         className           myClassName     
         methodName           myMethodName     

    Add the following code to the Java code section of this fragment:
    /* Print out method entry */
    System.out.println(spaces + "Entered: " + myClassName + "." + myMethodName);
    spaces = spaces + "    ";

    This code will simply print out the class and method name upon each method entry. It then fixes the indentation by adjusting the 'spaces' variable.
  8. Exit Fragment: Select the exit fragment > add the same data items as step 5 to this fragment > add the following to the Java code section of this fragment:
    /* Print out method exit */
    spaces = spaces.substring(4);
    System.out.println(spaces + "Exited: " + myClassName + "." + myMethodName);

    The above code first fixes the indentation and then prints out the name of the class and method that has just exited.
  9. Catch Fragment: Select the catch fragment > add the same data items that were added in step 5 > also add the following data item:

    Data Type
    Name
         exceptionObject           myExceptionObject     

    Add the following to the Java code section:
    System.out.println(spaces + "Exception: " + myClassName + "." + myMethodName + " ! " + myExceptionObject.getMessage());

    This will simply print out the name of the class and method where the exception occurred. It also prints out the exception message.
4.1 Example 1: Static Instrumentation with EntryExit Probe
The following are instructions for statically instrumenting a class with the EntryExit probe. This section will statically instrument the Order.java class posted in the resources section of this document.
  1. First copy the Order.java class in the 'src' folder of 'ProbekitExample'. This is a very simple class that's supposed to simulate the process flow of placing an order. It simply calls a few methods and one of the methods cause an exception. When executed, it simply prints out when it has started and when it's finished.
  2. Run the Order class. Notice that it simply prints out the following message:
    Start
    Finish
  3. Right click on the EntryExit probe > select 'Instrument' > select 'Static instrumentation' as indicated in figure 4.0



  4. Figure 4.0 - Static Instrumentation

  5. Probekit now displays a list of all .class and .jar files in the workspace as indicated in figure 4.1. Select Order.class from the given list of files and click on OK.



  6. Figure 4.1 - Classes/JARs available for instrumentation

  7. The Order class has now been statically instrumented with the EntryExit probe. Run the Order class again. Notice that this time all the output statements from the probe is also displayed:
    	Entered: Order.main
    	Start
    		Entered: Order.<init>
    		Exited: Order.<init>
    		Entered: Order.placeOrder
    			Entered: Order.getSupplierList
    			Exited: Order.getSupplierList
    			Entered: Order.connectWithSupplier
    			Exited: Order.connectWithSupplier
    			Entered: Order.sendOrder
    				Entered: Order.constructOrderForm
    					Entered: Order.checkProductAvailability
    					Exited: Order.checkProductAvailability
    				Exited: Order.constructOrderForm
    				Exception: Order.sendOrder ! Too many items requested. 
    			Exited: Order.sendOrder
    		Exited: Order.placeOrder
    	Finish
    	Exited: Order.main
    		
  8. Recompile the Order class by selecting Project > Clean > Select 'Clean projects selected below' > Select 'ProbekitExample' > Click on OK


4.2 Example 1: Dynamic Instrumentation with EntryExit Probe
This section will explain how to dynamically instrument the Order class with the EntryExit probe.
  1. Copy the Order class to the ProbekitExample project and run it as indicated in steps 1 and 2 of section 4.1
  2. Click on the profile button and select Profile as indicated in figure 2.0
  3. Select the 'Order' launch configuration and click on the monitor tab as indicated in figure 2.1
  4. Double click on the 'Java Profiling' entry to access the filter page as indicated in figure 5.0



  5. Figure 5.0 - Filters Page

  6. Create a new filter set by clicking on the first 'Add' button. Call it 'Order Class'
  7. Remove all the filters for the newly created filter set by selecting them and clicking on the 'Remove' button
  8. Add the following two filters by using the 'Add' button:

         Class     
         Method Name     
         Rule     
         Order     
         *     
         INCLUDE     
         *     
         *     
         EXCLUDE     

  9. Click on 'Finish' to close the filter page
  10. Place a check mark beside 'Probe Insertion' > click on 'Edit Options' to get to the screen displayed in figure 2.2
  11. Select 'EntryExit' from the list of probes displayed > click on 'Finish' > click on 'Profile'. The Order class should execute and display all the probe output
  12. Notice when the Order class is executed normally, it displays its usual Start/Finish output, but when it's executed in profiling mode, it displays all the probe output
5.0 Example 2: Graphing Method Calls with JFreeChart
This example is presented to illustrate the power and flexibility of Probekit. The probe created in this example uses JFreeChart to generate a 3D bar chart for graphing the methods that have been invoked more than 2500 times. It also uses a 3D pie chart to graph the methods that take more than 3 seconds to execute. This particular example targets the probe on the eclipse.ui classes, but it can be used to target any desired classes. The probe demonstrated in this example uses the MethodInvocationBarChart and MethodTimePieChart classes to populate the bar and pie charts. The reader should review the content of the probe to grasp a better understanding of this example. All the files used in this example are available in the resources section of this document. Follow the instructions below for setting up and running the probe:
  1. Download JFreeChart from http://www.jfree.org/jfreechart/ and include the following JARs in CLASSPATH: jfreechart-nnn.jar and jcommon-nnn.jar (where nnn is the version).
  2. Download the ProfilingChartProbe.probe, MethodInvocationBarChart.java, and MethodTimePieChart.java files from the resources section.
  3. Copy and paste all three files downloaded to the 'src' folder of the ProbekitExample project created in step 1 of section 4.0
  4. The jfreechart-nnn.jar and jcommon-nnn.jar JAR files need to be included in the build path of the project to resolve the compile errors in MethodInvocationBarChart and MethodTimePieChart
  5. Add the folder containing MethodInvocationBarChart.class and MethodTimePieChart.class to the system CLASSPATH
  6. Click on the profile button and select 'Profile...' (See figure 2.0)
  7. Create a new 'Eclipse Application' launch configuration > Click on the 'Monitor' tab (See figure 2.1)
  8. Check 'Probe Insertion'. Make sure 'Probe Insertion' is selected and click on 'Edit Options' (See figure 2.2)
  9. Select 'ProfilingChartProbe' and click on Finish
  10. Double click on 'Java Profiling' to access the filter page (See figure 5.0)
  11. Add the following filters as described in steps 5-7 of section 4.2

  12.      Class     
         Method Name     
         Rule     
         org.eclipse.ui*     
         *     
         INCLUDE     
         *     
         *     
         EXCLUDE     

  13. Click on Finish > click on 'Profile'. A runtime workbench should be launched along with two charts as indicated in figure 6.0 and figure 6.1



  14. Figure 6.0 - Bar chart indicating methods that have been invoked more than 2500 times




    Figure 6.1 - Pie chart indicating methods that have taken more than 3 seconds to execute



6.0 Resources
The following lists the files used in the examples covered in this document. It also lists other resources the reader might find helpful:



7.0 Legal
Java and all Java-based trademarks are trademarks of Sun Microsystems, Inc. in the United States, other countries, or both.

JFreeChart is an open source tool distributed under the terms of the GNU Lesser General Public Licence


Appendix A: Fragment Types
The following table lists and describes the fragment types, and indicates whether the type can be used for a method or callsite probe. (For the distinction between method probes and callsite probes, see section 2.1)

Fragment type
Method or Callsite
Description
entry Method entry fragments run upon method entry.
exit Method exit fragments run upon method exit: a normal exit, when the method throws an exception, or when a thrown exception propagates out of the method.
catch Method catch fragments run at the beginning of a catch clause in the method, or at the beginning of a finally clause that runs as the result of an exception.
staticInitializer Method staticInitializer fragments run inside the class initializer of every probed class. If the class does not already have a static initializer, one will be created.
executableUnit Method executableUnit fragments run before every executable unit of code in methods that match the probe's target and filter specification, and for which source code is available. If the method does not have source line information, it will appear to have a single executable unit, numbered zero.
beforeCall Callsite beforeCall fragments run in the calling method immediately before the target method is called. Not valid for method probes.
afterCall Callsite afterCall fragments run in the calling method immediately after the target method exits: either a normal exit, or when the target method throws an exception. Not valid for method probes.


Appendix B: Fragment Data Items
The following table lists and describes the data types available through the Probekit editor:

Data Type
Type
Description
className String

For method probes, the class name of the probed method, including the package name, in internal format; for callsite probes, the class name of the called method.

Example: org/eclipse/tptp/SomeClass

methodName String

For method probes, the method name of the probed method, in internal format; for callsite probes, the method name of the called method.

Constructors have the method name <init>, and static class initializers have the method name <clinit>.

methodSig String

The method argument and return type signature, in internal format. Not valid for staticInitializer fragments.

Example: (Ljava/lang/String;)I

thisObject Object

The this object (for instance methods) that was passed to the probed method. Not valid for staticInitializer fragments.

thisObject is null for static methods, for entry fragments that are applied to constructors, and for exit fragments applied to constructors when the constructor throws an exception.

args Object[]

An array of Object references representing the arguments to the probed method. There is one element in this array for each argument to the method (not counting the this argument). Arguments that are primitive types are boxed into temporary objects of the appropriate reference type, for example: Integer for int. If the method takes no arguments, the size of the Object[] array is zero.

Note that constructors for non-static inner classes have one hidden argument per "inner" level, so the argument array will contain more elements than appear in the source code. Not valid for staticInitializer fragments.

returnedObject Object A reference to the object being returned. This type is available only in exit and afterCall fragments. If the return type of the probed method is a primitive type, the returned value is bound into a temporary object of the appropriate reference type. If the method is void (does not return a value) or it exits by exception, returnedObject is null.
exceptionObject Throwable A reference to the exception object being thrown. This type is available only to catch and exit fragments. If the method exits normally, exceptionObject will be null.
isFinally boolean A flag indicating whether the fragment was called from a finally clause (true), or from a catch clause (false). Valid only in catch fragments.
staticField (varies) The object referred to by the static field. Its type is the same type that was declared in the staticField object. This is valid only if the probe declares a staticField. Not valid for callsite probes.
classSourceFile String The source file name information available from the debug attributes of the class file. If there is no source information, classSourceFile is null. For Java the value is typically just the file name, without path information. Not valid for callsite probes.
methodNames String

An encoded list of method names and signatures. The order of the methods in this list is the same as the order of the methods in methodLineTables, and is the same as the ordering reflected by the methodNumber data item. The list does not include the names of any methods that were inserted into the class by Probekit.

The methodNames string consists of one or more method names and signatures, separated by a plus ("+") sign. The method signatures are in Java internal format. For example, a class with two methods, a default constructor, and a method run that takes a String and returns an int, has this methodNames string: <init>()V+run(Ljava/lang/String;)I

Not valid for callsite probes.

methodLineTables String An encoded list of line numbers that correspond with every executable unit of code in the class. The list does not include executable units that were inserted into the class by Probekit. For an explanation of the encoding, see the Probekit help content included in TPTP. Not valid for callsite probes.
methodNumber Integer The index number in the methodNames table for the method into which the probe fragment was inserted. Not valid for callsite probes or staticInitializer fragments.
executableUnitNumber Integer The number of the executable unit that the probe fragment was inserted into. This data type is valid only for executableUnit and catch probe fragments. If the method does not have source line information, it appears to have a single executable unit, numbered zero.