Please contact Navid Mehregani (firstname.lastname@example.org) for any comments or questions about this document.
Important: This document is to be used with TPTP 4.2. For a short introductory movie on Probekit, please click here.
1.0 Introduction to Probekit
2.0 Closer Look at Probes
2.1 Method Probes VS. Callsite Probes
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
Appendix A: Fragment Types
Appendix B: Fragment Data Items
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.
- Exactly one Target
- Exactly one Import
- One or more Fragments
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.
Before probe Fragments can be explained, the two different types of probes have to be discussed.
- 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.
- 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.
- Fragment Type
- Data Items
- Java Source Code
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.
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.
Please also see: An important note about dynamic probekit
To use dynamic instrumentation, select the desired launch configuration on the left, then select the 'Monitor' tab as indicated in figure 2.1.
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.
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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- Run the Order class. Notice that it simply prints out the following message:
- Right click on the EntryExit probe > select 'Instrument' > select 'Static instrumentation' as indicated in figure 4.0
- 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.
- 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
- Recompile the Order class by selecting Project > Clean > Select 'Clean projects selected below' > Select 'ProbekitExample' > Click on OK
- Copy the Order class to the ProbekitExample project and run it as indicated in steps 1 and 2 of section 4.1
- Click on the profile button and select Profile as indicated in figure 2.0
- Select the 'Order' launch configuration and click on the monitor tab as indicated in figure 2.1
- Double click on the 'Java Profiling' entry to access the filter page as indicated in figure 5.0
- Create a new filter set by clicking on the first 'Add' button. Call it 'Order Class'
- Remove all the filters for the newly created filter set by selecting them and clicking on the 'Remove' button
- Add the following two filters by using the 'Add' button:
Class Method Name Rule Order * INCLUDE * * EXCLUDE
- Click on 'Finish' to close the filter page
- Place a check mark beside 'Probe Insertion' > click on 'Edit Options' to get to the screen displayed in figure 2.2
- 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
- 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
- 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).
- Download the ProfilingChartProbe.probe, MethodInvocationBarChart.java, and MethodTimePieChart.java files from the resources section.
- Copy and paste all three files downloaded to the 'src' folder of the ProbekitExample project created in step 1 of section 4.0
- 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
- Add the folder containing MethodInvocationBarChart.class and MethodTimePieChart.class to the system CLASSPATH
- Click on the profile button and select 'Profile...' (See figure 2.0)
- Create a new 'Eclipse Application' launch configuration > Click on the 'Monitor' tab (See figure 2.1)
- Check 'Probe Insertion'. Make sure 'Probe Insertion' is selected and click on 'Edit Options' (See figure 2.2)
- Select 'ProfilingChartProbe' and click on Finish
- Double click on 'Java Profiling' to access the filter page (See figure 5.0)
- Add the following filters as described in steps 5-7 of section 4.2
- 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
- Include the location of the classes/JARs in the BOOTCLASSPATH and just use the classes as they are in the probe
- Include the location of the classes/JARs in the CLASSPATH and use reflection to resolve the classes and invoke their methods
An important note about dynamic instrumentation: Dynamic instrumentation uses the boot class loader to load the probe class. This raises a problem when helper classes (such as MethodInvocationBarChart and MethodTimePieChart) need to be used in a probe. There are two ways probe authors can make use of helper classes in probes:
The ProfilingChartProbe.probe uses the second option to resolve the MethodInvocationBarChart and MethodTimePieChart classes. This is an important distinction between dynamic and static Probekit. Please refer to the code in ProfilingChartProbe.probe to see how it resolves its helper classes.
JFreeChart is an open source tool distributed under the terms of the GNU Lesser General Public Licence
|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.|
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.
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>.
The method argument and return type signature, in internal format. Not valid for staticInitializer fragments.
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.
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.|
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.|