20 Create Event Stream from External Process
The sample StreamExternalEventsWithAttachAPISample.java
creates an event stream from a separate Java process, the sample
SleepOneSecondIntervals.java
.
SleepOneSecondIntervals
repeatedly sleeps for 1 second intervals; as
demonstrated in Create Event Stream in Process, Active, every time Thread.sleep() is called, a
jdk.ThreadSleep
event occurs.
public class SleepOneSecondIntervals {
public static void main(String... args) throws Exception {
long pid = ProcessHandle.current().pid();
System.out.println("Process ID: " + pid);
while(true) {
System.out.println("Sleeping for 1s...");
Thread.sleep(1000);
}
}
}
StreamExternalEventsWithAttachAPISample
uses the Attach API
to obtain the virtual machine in which SleepOneSecondIntervals
is
running. From this virtual machine,
StreamExternalEventsWithAttachAPISample
obtains the ___location of its
Flight Recorder repository though the jdk.jfr.repository
property. It
then creates an EventStream
with this repository through the EventStream::openRepository(Paths) method.
import java.nio.file.Paths;
import java.util.Optional;
import java.util.Properties;
import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;
import jdk.jfr.consumer.EventStream;
public class StreamExternalEventsWithAttachAPISample {
public static void main(String... args) throws Exception {
Optional<VirtualMachineDescriptor> vmd =
VirtualMachine.list().stream()
.filter(v -> v.displayName()
.contains("SleepOneSecondIntervals"))
.findFirst();
if (vmd.isEmpty()) {
throw new RuntimeException("Cannot find VM for SleepOneSecondIntervals");
}
VirtualMachine vm = VirtualMachine.attach(vmd.get());
// Get system properties from attached VM
Properties props = vm.getSystemProperties();
String repository = props.getProperty("jdk.jfr.repository");
System.out.println("jdk.jfr.repository: " + repository);
try (EventStream es = EventStream
.openRepository(Paths.get(repository))) {
System.out.println("Found repository ...");
es.onEvent("jdk.ThreadSleep", System.out::println);
es.start();
}
}
}
Compile SleepOneSecondIntervals.java
and
StreamExternalEventsWithAttachAPISample.java
. Then run
SleepOneSecondIntervals
with this command:
java -XX:StartFlightRecording SleepOneSecondIntervals
In a new command shell, run
StreamExternalEventsWithAttachAPISample
:
java StreamExternalEventsWithAttachAPISample
It prints output similar to the following:
jdk.jfr.repository: C:\Users\<your user name>\AppData\Local\Temp\2019_12_08_23_32_47_5100
Found repository ...
jdk.ThreadSleep {
startTime = 00:15:31.643
duration = 1.04 s
time = 1.00 s
eventThread = "main" (javaThreadId = 1)
stackTrace = [
java.lang.Thread.sleep(long)
SleepOneSecondIntervals.main(String[]) line: 8
]
}
jdk.ThreadSleep {
startTime = 00:15:32.689
duration = 1.05 s
time = 1.00 s
eventThread = "main" (javaThreadId = 1)
stackTrace = [
java.lang.Thread.sleep(long)
SleepOneSecondIntervals.main(String[]) line: 8
]
}
...
The sample StreamExternalEventsWithJcmdSample.java
is
similar to StreamExternalEventsWithAttachAPISample
except it starts
Flight Recorder for SleepOneSecondIntervals
with the Attach API. With
this API, the sample runs the command jcmd <PID> JFR.start
with the PID of
SleepOneSecondIntervals
:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Paths;
import java.util.Properties;
import com.sun.tools.attach.VirtualMachine;
import jdk.jfr.consumer.EventStream;
public class StreamExternalEventsWithJcmdSample {
public static void main(String... args) throws Exception {
if (args[0] == null) {
System.err.println("Requires PID of process as argument");
System.exit(1);
}
String pid = args[0];
Process p = Runtime.getRuntime().exec(
"jcmd " + pid + " JFR.start");
printOutput(p);
// Wait for jcmd to start the recording
Thread.sleep(1000);
VirtualMachine vm = VirtualMachine.attach(pid);
Properties props = vm.getSystemProperties();
String repository = props.getProperty("jdk.jfr.repository");
System.out.println("jdk.jfr.repository: " + repository);
try (EventStream es = EventStream
.openRepository(Paths.get(repository))) {
System.out.println("Found repository ...");
es.onEvent("jdk.ThreadSleep", System.out::println);
es.start();
}
}
private static void printOutput(Process proc) throws IOException {
BufferedReader stdInput = new BufferedReader(
new InputStreamReader(proc.getInputStream()));
BufferedReader stdError = new BufferedReader(
new InputStreamReader(proc.getErrorStream()));
// Read the output from the command
System.out.println(
"Here is the standard output of the command:\n");
String s = null;
while ((s = stdInput.readLine()) != null) {
System.out.println(s);
}
// Read any errors from the attempted command
System.out.println(
"Here is the standard error of the " + "command (if any):\n");
while ((s = stdError.readLine()) != null) {
System.out.println(s);
}
}
}
Compile SleepOneSecondIntervals.java
and
StreamExternalEventsWithJcmdSample.java
. Then run
SleepOneSecondIntervals
with this command:
java -XX:StartFlightRecording SleepOneSecondIntervals
It prints output similar to the following:
Started recording 1. No limit specified, using maxsize=250MB as default.
Use jcmd 5100 JFR.dump name=1 filename=FILEPATH to copy recording data to file.
Process ID: 5100
Sleeping for 1s...
Sleeping for 1s...
Sleeping for 1s...
...
Note the PID for SleepOneSecondIntervals
(in this example,
it's 5100). While this sample is running, in a new command shell, run
StreamExternalEventsWithJcmdSample
with this command.
java StreamExternalEventsWithJcmdSample <PID of SleepOneSecondIntervals>
It prints output similar to StreamExternalEventsWithAttachAPISample
.