From 304bd08a697d9fe2eb91735d7a936b8d3c05365d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaros=C5=82aw=20Pa=C5=82ka?= Date: Wed, 10 Jan 2024 15:38:46 +0100 Subject: [PATCH] addressed comments to handle custom JFR settings --- .../javaagent/AsyncProfilerDelegate.java | 8 +++-- .../javaagent/JFRProfilerDelegate.java | 32 ++++++++++++++++--- .../main/resources/{ => jfr}/pyroscope.jfc | 0 demo/build.gradle | 1 + demo/src/main/java/App.java | 25 +++++++++------ 5 files changed, 48 insertions(+), 18 deletions(-) rename agent/src/main/resources/{ => jfr}/pyroscope.jfc (100%) diff --git a/agent/src/main/java/io/pyroscope/javaagent/AsyncProfilerDelegate.java b/agent/src/main/java/io/pyroscope/javaagent/AsyncProfilerDelegate.java index 438659e..6d2a98d 100644 --- a/agent/src/main/java/io/pyroscope/javaagent/AsyncProfilerDelegate.java +++ b/agent/src/main/java/io/pyroscope/javaagent/AsyncProfilerDelegate.java @@ -31,6 +31,7 @@ public AsyncProfilerDelegate(Config config) { setConfig(config); } + @Override public void setConfig(final Config config) { this.config = config; this.alloc = config.profilingAlloc; @@ -55,6 +56,7 @@ public void setConfig(final Config config) { /** * Start async-profiler */ + @Override public synchronized void start() { if (format == Format.JFR) { try { @@ -70,22 +72,22 @@ public synchronized void start() { /** * Stop async-profiler */ + @Override public synchronized void stop() { instance.stop(); } /** - * * @param started - time when profiling has been started - * @param ended - time when profiling has ended + * @param ended - time when profiling has ended * @return Profiling data and dynamic labels as {@link Snapshot} */ + @Override public synchronized Snapshot dumpProfile(Instant started, Instant ended) { return dumpImpl(started, ended); } - private String createJFRCommand() { StringBuilder sb = new StringBuilder(); sb.append("start,event=").append(eventType.id); diff --git a/agent/src/main/java/io/pyroscope/javaagent/JFRProfilerDelegate.java b/agent/src/main/java/io/pyroscope/javaagent/JFRProfilerDelegate.java index aefc735..7a4e4f7 100644 --- a/agent/src/main/java/io/pyroscope/javaagent/JFRProfilerDelegate.java +++ b/agent/src/main/java/io/pyroscope/javaagent/JFRProfilerDelegate.java @@ -6,26 +6,33 @@ import java.io.File; import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.nio.file.*; import java.time.Instant; import java.util.ArrayList; import java.util.List; +import static java.lang.String.format; + public final class JFRProfilerDelegate implements ProfilerDelegate { private static final String RECORDING_NAME = "pyroscope"; + private static final String JFR_SETTINGS_RESOURCE = "/jfr/pyroscope.jfc"; private Config config; private File tempJFRFile; private Path jcmdBin; + private Path jfrSettingsPath; public JFRProfilerDelegate(Config config) { setConfig(config); } + @Override public void setConfig(final Config config) { this.config = config; jcmdBin = findJcmdBin(); + jfrSettingsPath = findJfrSettingsPath(); + try { // flight recorder is built on top of a file descriptor, so we need a file. tempJFRFile = File.createTempFile("pyroscope", ".jfr"); @@ -38,6 +45,7 @@ public void setConfig(final Config config) { /** * Start JFR profiler */ + @Override public synchronized void start() { try { List commands = new ArrayList<>(); @@ -46,9 +54,9 @@ public synchronized void start() { commands.add("JFR.start"); commands.add("name=" + RECORDING_NAME); commands.add("filename=" + tempJFRFile.getAbsolutePath()); - commands.add("settings=pyroscope"); + commands.add("settings=" + jfrSettingsPath); ProcessBuilder processBuilder = new ProcessBuilder(commands); - Process process = processBuilder.start(); + Process process = processBuilder.inheritIO().start(); int exitCode = process.waitFor(); if (exitCode != 0) { throw new RuntimeException("Invalid exit code: " + exitCode); @@ -63,6 +71,7 @@ public synchronized void start() { /** * Stop JFR profiler */ + @Override public synchronized void stop() { try { List commands = new ArrayList<>(); @@ -88,6 +97,7 @@ public synchronized void stop() { * @param ended - time when profiling has ended * @return Profiling data and dynamic labels as {@link Snapshot} */ + @Override public synchronized Snapshot dumpProfile(Instant started, Instant ended) { return dumpImpl(started, ended); } @@ -123,4 +133,16 @@ private static Path findJcmdBin() { } return jcmdBin; } + + private static Path findJfrSettingsPath() { + try (InputStream inputStream = JFRProfilerDelegate.class.getResourceAsStream(JFR_SETTINGS_RESOURCE)) { + Path jfrSettingsPath = Files.createTempFile("pyroscope", ".jfc"); + Files.copy(inputStream, jfrSettingsPath, StandardCopyOption.REPLACE_EXISTING); + return jfrSettingsPath; + } catch (IOException e) { + throw new UncheckedIOException(format("unable to load %s from classpath", JFR_SETTINGS_RESOURCE), e); + } + } + + } diff --git a/agent/src/main/resources/pyroscope.jfc b/agent/src/main/resources/jfr/pyroscope.jfc similarity index 100% rename from agent/src/main/resources/pyroscope.jfc rename to agent/src/main/resources/jfr/pyroscope.jfc diff --git a/demo/build.gradle b/demo/build.gradle index 7d88c38..75498ba 100644 --- a/demo/build.gradle +++ b/demo/build.gradle @@ -10,4 +10,5 @@ repositories { } dependencies { implementation(project(":agent")) + implementation("commons-lang:commons-lang:2.6") } diff --git a/demo/src/main/java/App.java b/demo/src/main/java/App.java index cf9480e..203ad2b 100644 --- a/demo/src/main/java/App.java +++ b/demo/src/main/java/App.java @@ -4,9 +4,11 @@ import io.pyroscope.javaagent.api.Exporter; import io.pyroscope.javaagent.api.Logger; import io.pyroscope.javaagent.config.Config; +import io.pyroscope.javaagent.config.ProfilerType; import io.pyroscope.javaagent.impl.DefaultConfigurationProvider; import io.pyroscope.labels.Pyroscope; import io.pyroscope.labels.LabelsSet; +import org.apache.commons.lang.SystemUtils; import java.util.HashMap; import java.util.Map; @@ -17,17 +19,20 @@ public class App { public static final int N_THREADS = 8; public static void main(String[] args) { + Config.Builder configBuilder = new Config.Builder() + .setApplicationName("demo.app{qweqwe=asdasd}") + .setServerAddress("http://localhost:4040") + .setFormat(Format.JFR) + .setLogLevel(Logger.Level.DEBUG) + .setLabels(mapOf("user", "tolyan")); + if (SystemUtils.IS_OS_WINDOWS) { + configBuilder.setProfilerType(ProfilerType.JFR); + } else { + configBuilder.setProfilerType(ProfilerType.ASYNC); + } + Config config = configBuilder.build(); PyroscopeAgent.start( - new PyroscopeAgent.Options.Builder( - new Config.Builder() - .setApplicationName("demo.app{qweqwe=asdasd}") - .setServerAddress("http://localhost:4040") - .setFormat(Format.JFR) - .setLogLevel(Logger.Level.DEBUG) - .setLabels(mapOf("user", "tolyan")) - .build()) -// .setExporter(new MyStdoutExporter()) - .build() + new PyroscopeAgent.Options.Builder(config).build() ); Pyroscope.setStaticLabels(mapOf("region", "us-east-1"));