Before you begin, you must complete Exercise 1.3: Deploying your probe.
Probekit can be used to solve many real-world debugging and runtime-analysis problems. You've now learned the basics of writing and deploying probes, so this and the following lesson will not walk you through the activities, but instead provide suggestions for probes that you might write to investigate your own application.
Assume that some method in a servlet occasionally throws an exception. The log message that comes out is not very helpful; it doesn't clearly indicate what the problem is. To get more information, you can write a probe that executes whenever the method throws an exception, and the Java code in the probe's fragment can log the exception message and also the arguments to the method. This will help you see why the exception is being thrown.
When you write and deploy this probe, note the following points:
System.out.println("[Exception probe triggered!]"); for (int i = 0; i < args.length; i++) { System.out.println("Argument " + i + "=" + args[i]); }
Here is a fairly intricate probe example. Let's say there is a method in your program that usually executes quickly but occasionally takes a long time (longer than 100ms, for example).
You can write a probe that takes note of the entry and exit times whenever the method executes. When the execution time is short, the probe does nothing. When the execution time is long, the probe reports the arguments to the method and other important information about program state at the time. This allows you to determine the circumstances that lead to the longer execution times.
When you write the probe, note the following points:
You can use the "fragment at class scope" feature of Probekit to write a Java code fragment that will be compiled into the generated class outside of any method. In this case the fragment is used to define a static field called entryTime that will be used by both the entry and exit fragments.
To add this element to a probe, click the Probe element in the Probekit editor; the editor displays a field for you to enter the Java code for the fragment at class scope:
static long entryTime; static final long thresholdDuration = 100;
entryTime = System.currentTimeMillis();
long now = System.currentTimeMillis(); if ((now - entryTime) > thresholdDuration) { System.out.println( "[Spent a long time in " + className + "." + methodName + ": this=" + thisObject + "]"); }
At method entry, this probe records the current time. At method exit, the probe compares the current time and the recorded entry time, and if more than a certain amount of time has elapsed (the amount you specified in "thresholdDuration"), it prints a report using System.out.
The probe that we just discussed doesn't work for methods that are recursive (directly or indirectly), and it doesn't work for methods that might be running in multiple threads at the same time. To solve the recursive problem, you could record the entry times on a Stack; to solve the multi-threaded problem, you could store the stack in a ThreadLocal variable.
Here is what you need to create a recursion-safe, thread-safe version of the probe:
static ThreadLocal tl = new ThreadLocal() { public Object initialValue() { return new Stack(); } }; static long thresholdDuration = 100; // Report if time is over 100ms
long now = System.currentTimeMillis(); Stack stk = (Stack)tl.get(); stk.push(new Long(now));
long now = System.currentTimeMillis(); Stack stk = (Stack)tl.get(); long entryTime = ((Long)stk.pop()).longValue(); if (now - entryTime > thresholdDuration) { System.out.println( "[Spent a long time in " + cName + "." + mName + ": this=" + thisObj + "]"); }
Continue the tutorial with Exercise 1.5: Using a runtime library for more complex probe logic.