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
.