This tutorial takes a simplified client-server application that uses a shared blocking queue as its custom transport solution. The client sends a "String" message by adding it to the queue. The server receives a "String" message by removing it from the queue.
Although this example runs in a single JVM (to keep it simple), it uses two threads to simulate an application running in two JVMs. (If your intention is to correlate threads running in a single JVM, there is a simpler solution that will help you do this. See"Performing Correlation Across Multiple Threads" on page 152for more details).
The sample code is shown below:
public class SimulatedCrossVM {
private static int INTERVAL = 5 * 1000; // 5 seconds
private static BlockingQueue<String> queue = new LinkedBlockingQueue<String>
();
private static class ReceiverSide extends Thread {
public ReceiverSide() { super("Receiver");
}
public void execute(String receivedString) throws InterruptedException { System.out.println("Executing message: " + receivedString);
sleep(2 * INTERVAL);
}
private void receiveAndHandleMessage() throws InterruptedException { String message = null;
catch (Throwable t) { // oops
t.printStackTrace();
} } }
private static class SenderSide extends Thread {
// For simulated TCP connection private String destHost;
private int destPort;
public SenderSide(String host, int port) { super(host + ":" + port);
destHost = host;
destPort = port;
}
public void sendMessage(String origMessage) throws InterruptedException { queue.put(origMessage);
}
private String generateMessage() {
String message = "T" + System.currentTimeMillis();
return message;
}
private void generateAndSendMessage() throws InterruptedException { sleep(2 * INTERVAL);
// generate message
String message = generateMessage();System.out.println("Sender's original m essage: " + message);
// And send it (outbound call) sendMessage(message);
sleep(INTERVAL);
}
public void run() {
public static void main(String[] args) {
SenderSide sender = new SenderSide("fake-host", 12345);
ReceiverSide receiver = new ReceiverSide();
sender.start();
receiver.start();
} }
Executing this code will have the following output:
Sender's original message: T1313785958127 Executing message: T1313785958127
Step 1: Instrument Your Methods
By instrumenting your methods, you let Diagnostics know which methods are important. Since these methods are custom, the out-of-the-box instrumentation points won't do anything. Edit the etc/autodetect.points file by adding the following instrumentation points. See"Maintaining Instrumentation from the Java Profiler UI" on page 169for guidance on defining instrumentation points.
signature = !.*
layer = Sending [SimCrossVM-Receiver]
class = SimulatedCrossVM$ReceiverSide method = receiveAndHandleMessage signature = !.*
layer = Receiving [SimCrossVM-Inbound]
class = SimulatedCrossVM$ReceiverSide method = execute
signature = !.*
layer = Receiving
Result: Running this instrumented test program, you see the following Server Requests:
Here are the call profiles shown for the sender and receiver.
Step 2: Add “Coloring” to the Sender Logic
In this step, we add "coloring" to the messages sent by the client. When the instrumented server receives this colored message, HP Diagnostics will correlate them. This part is trickier, if you're not familiar with the code snippet syntax, it is described in"Defining Points With Code Snippets" on page 120.
First, we mark the method as an outbound point that uses a code snippet (outbound:snippet), and identify the code snippet to execute before invoking the method (before:code:5ea4753f). Since we're going to use the first argument, it's a good idea to provide a more specific signature (!\
(Ljava/lang/String;.*).
[SimCrossVM-Outbound]
class = SimulatedCrossVM$SenderSide method = sendMessage
signature = !\(Ljava/lang/String;.*
layer = Sending
detail = outbound:snippet,before:code:5ea4753f
The corresponding code snippet is shown below. Line 1 creates a string (#target) that includes the hostname and destination port of the server. Line 2 defines a new string (#diagArg) that follows a special syntax (DIAG_ARG:type=<type>&name=<name>&target=<target>). The "type" is the technology type and can be any name you choose; it will be used in the next step. The "name" and
"target" are technology dependent values that will be shown in the UI; they can also be anything you choose. Line 3 defines a third string (#color) which will be used to identify this specific invocation of the method call from any other. Line 4 updates the method's 1st argument with the colored String, which will cause sendMessage to send a modified String. Finally, line 5, places the coloring on the stack for usage by HP Diagnostics.
1. 5ea4753f = #target=#callee.destHost+":"+#callee.destPort; \
2. #diagArg = "DIAG_ARG:type=CB-TCP&name=Senders.sendMessage&target="+#target; \ 3. #color = (null == #arg1 ? null : @[email protected]
(#location, @RemoteCaptureProxy@ENCODED_COLORING, #diagArg.toString())); \
4. #arg1 = @[email protected](#color, #arg1);\
5. #diagArg;
Running the example updates the output as follows. Notice the receiving side did not get the same string message that was sent. This is a result of the code snippet's Line 4. In many cases, the receiving side may not handle this well. It's a good idea to note the receiver's behavior as this can happen "accidentally" if the client and server are not both using the same instrumentation, and in particular, not both instrumented.
Sender's original message: T1313786970403 Executing message: HP_DIAG1_!Dhf/
ABAABKrh3Qf0cy7yaLsAAAAAAA9mYWtlLWhvc3Q6MTIzNDUAYTEzMTM3ODY5N jAzODgmU2ltdWxhdGVDcm9zc1ZNJlNpbXVsYXRlZENyb3NzVk0kU2VuZGVyU2lk ZS52b2lkIGdlbmVyYXRlQW5kU2VuZE1lc3NhZ2UoKSZcMCZcMCZcMCY=:T131378 6970403
At this point, the only change you'll see in the UI is some "Outbound Calls". Notice the values in the columns "Outbound Call" and "Remote Target", these are the values you provided in the code snippet "name" and "target".
Step 3: Remove Coloring from the Receiver Side
The last step is to remove the coloring on the receive side so that the receiver can get the original
"uncolored" message from the sender. First we mark the point as an inbound point with the
technology type used in the code snippet defined in step 2, and assign a code snippet to run before this method is called. Again, we also specify a more specify signature since that argument will be used in the code snippet.
[SimCrossVM-Inbound]
class = SimulatedCrossVM$ReceiverSide method = execute
signature = !\(Ljava/lang/String;.*
detail = before:code:d2c83d3c,inbound:CB-TCP layer = Receiving
The corresponding code snippet is shown below. Line 1 extracts the coloring from the incoming message. Line 2 updates the method's 1st argument, restoring it to the original message sent by the client. Line 3 puts the coloring on the stack (and an empty String) for use by HP Diagnostics.
1. #arg1=@[email protected](#arg1); \ 2. d2c83d3c = #coloring=@[email protected]
(#arg1); \ 3. "";#coloring;
The program's output is now restored to the original:
Sender's original message: T1313789287234 Executing message: T1313789287234
The Server Request view now shows a Cross-VM call profile is available for the Sender's
"generateAndSendMessage". Open this call profile and observe the client and server call profiles are now stitched together! They're not doing much in this sample application, but in a real application, you would be able to see if performance issues occur in the client, server, or both.
This call profile looks a bit strange but is typical for asynchronous applications. The client does not wait for a response, but does continue to do some processing (err sleeping for 5 seconds). During that time the server is processing the request and completes a few seconds afterwards. You will see the time durations for the methods in the tree as shown below. Notice also the diamonds with the number 2 inside, which represent the JVM depth. If your server made yet another outbound call, you could have 3 or more! In those cases, cross VM correlation because especially useful. Imagine trying to find the source of a performance issue across that many JVMs!