Skip to content

Commit

Permalink
Update async-profiler to 2.8, change alloc,lock params for jfr (#38)
Browse files Browse the repository at this point in the history
* Update async-profiler to 2.8, change alloc,lock params for jfr

* fix copypaste mistake

* rm debug logging

* Temporarly switch to a pyroscope fork of async-profiler to include a fix of a crash
  • Loading branch information
korniltsev authored May 27, 2022
1 parent 9d7a6fc commit df947b4
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 50 deletions.
6 changes: 4 additions & 2 deletions agent/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ repositories {
mavenCentral()
}

def asyncProfilerVersion = "2.7"
def asyncProfilerVersion = "2.8.0.1"
dependencies {
implementation files("$buildDir/async-profiler/async-profiler.jar")
implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.17.1'
Expand Down Expand Up @@ -82,8 +82,10 @@ task asyncProfilerLib {

doLast {
suffixes.forEach { suffix, ext ->
// def repo = "https://github.com/jvm-profiling-tools/async-profiler"
def repo = "https://github.com/pyroscope-io/async-profiler"
download {
src "https://github.com/jvm-profiling-tools/async-profiler/releases/download/v${asyncProfilerVersion}/async-profiler-${asyncProfilerVersion}-${suffix}.${ext}"
src "$repo/releases/download/v${asyncProfilerVersion}/async-profiler-${asyncProfilerVersion}-${suffix}.${ext}"
dest new File(asyncProfilerDir, "async-profiler-${asyncProfilerVersion}-${suffix}.${ext}")
overwrite true
}
Expand Down
101 changes: 56 additions & 45 deletions agent/src/main/java/io/pyroscope/javaagent/Profiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class Profiler {
private final Format format;

private static String libraryPath;

static {
try {
deployLibrary();
Expand All @@ -45,7 +46,7 @@ private static void deployLibrary() throws IOException {
targetDir.mkdirs();

try (final InputStream is = Objects.requireNonNull(
Profiler.class.getResourceAsStream("/" + fileName))) {
Profiler.class.getResourceAsStream("/" + fileName))) {
final Path target = targetDir.toPath().resolve(targetLibraryFileName(fileName)).toAbsolutePath();
Files.copy(is, target, StandardCopyOption.REPLACE_EXISTING);
libraryPath = target.toString();
Expand Down Expand Up @@ -94,7 +95,7 @@ private static String libraryFileName() {
* <p>Adds the checksum to the library file name.</p>
*
* <p>E.g. {@code libasyncProfiler-linux-x64.so} ->
* {@code libasyncProfiler-linux-x64-7b43b7cc6c864dd729cc7dcdb6e3db8f5ee5b4a4.so}</p>
* {@code libasyncProfiler-linux-x64-7b43b7cc6c864dd729cc7dcdb6e3db8f5ee5b4a4.so}</p>
*/
private static String targetLibraryFileName(final String libraryFileName) throws IOException {
if (!libraryFileName.endsWith(".so")) {
Expand All @@ -104,22 +105,18 @@ private static String targetLibraryFileName(final String libraryFileName) throws
final String checksumFileName = libraryFileName + ".sha1";
String checksum;
try (final InputStream is = Objects.requireNonNull(
Profiler.class.getResourceAsStream("/" + checksumFileName))) {
Profiler.class.getResourceAsStream("/" + checksumFileName))) {
checksum = InputStreamUtils.readToString(is);
}

return libraryFileName.substring(0, libraryFileName.length() - 3) + "-" + checksum + ".so";
}

public static void init() {
// Do nothing, rely in the static initialization in the static block.
}

private final AsyncProfiler instance = AsyncProfiler.getInstance(libraryPath);

// TODO this is actually start of snapshot, not profiling as a whole
private Instant profilingStarted = null;
private File tempFile;
private final File tempJFRFile;

Profiler(final Logger logger, final EventType eventType, final String alloc, final String lock, final Duration interval, final Format format) {
this.logger = logger;
Expand All @@ -128,66 +125,80 @@ public static void init() {
this.eventType = eventType;
this.interval = interval;
this.format = format;

if (format == Format.JFR) {
try {
// flight recorder is built on top of a file descriptor, so we need a file.
tempJFRFile = File.createTempFile("pyroscope", ".jfr");
tempJFRFile.deleteOnExit();
} catch (IOException e) {
throw new IllegalStateException(e);
}
} else {
tempJFRFile = null;
}
}

// TODO new method for starting new snapshot/batch
final synchronized void start() {
if (format == Format.JFR) {
startJFR();
try {
instance.execute(createJFRCommand());
} catch (IOException e) {
throw new IllegalStateException(e);
}
} else {
instance.start(eventType.id, interval.toNanos());
}
profilingStarted = Instant.now();
logger.info("Profiling started");
}

private String createJFRCommand() {
StringBuilder sb = new StringBuilder();
sb.append("start,event=").append(eventType.id);
if (alloc != null && !alloc.isEmpty()) {
sb.append(",alloc=").append(alloc);
}
if (lock != null && !lock.isEmpty()) {
sb.append(",lock=").append(lock);
}
sb.append(",interval=").append(interval.toNanos())
.append(",file=").append(tempJFRFile.toString());
return sb.toString();
}

final synchronized Snapshot dump() {
if (profilingStarted == null) {
throw new IllegalStateException("Profiling is not started");
}

final Snapshot result = new Snapshot(
eventType,
profilingStarted,
Instant.now(),
format == Format.JFR ? dumpJFR() : instance.dumpCollapsed(Counter.SAMPLES).getBytes(StandardCharsets.UTF_8)
);
instance.stop();

// TODO use `this.start()` or analogue
profilingStarted = Instant.now();
if (format == Format.JFR) {
restartJFR();
} else {
instance.stop();
instance.start(eventType.id, interval.toNanos());
}
return result;
}
Snapshot result = dumpImpl();

final private void startJFR() {
try {
// flight recorder is built on top of a file descriptor, so we need a file.
tempFile = File.createTempFile("pyroscope", ".jfr");
tempFile.deleteOnExit();
instance.execute(String.format("start,event=%s,alloc=%s,lock=%s,interval=%s,file=%s", eventType.id, alloc, lock, interval.toNanos(), tempFile.toString()));
} catch (IOException e) {
throw new IllegalStateException(e);
}
start();

return result;
}

final private void restartJFR() {
try {
instance.execute(String.format("start,event=%s,alloc=%s,lock=%s,interval=%s,file=%s", eventType.id, alloc, lock, interval.toNanos(), tempFile.toString()));
} catch (IOException e) {
throw new IllegalStateException(e);
private Snapshot dumpImpl() {
final byte[] data;
if (format == Format.JFR) {
data = dumpJFR();
} else {
data = instance.dumpCollapsed(Counter.SAMPLES).getBytes(StandardCharsets.UTF_8);
}
return new Snapshot(
eventType,
profilingStarted,
Instant.now(),
data
);
}

final private byte[] dumpJFR() {
private byte[] dumpJFR() {
try {
instance.stop();
byte[] bytes = new byte[(int) tempFile.length()];
try (DataInputStream ds = new DataInputStream(new FileInputStream(tempFile))) {
byte[] bytes = new byte[(int) tempJFRFile.length()];
try (DataInputStream ds = new DataInputStream(new FileInputStream(tempJFRFile))) {
ds.readFully(bytes);
}
return bytes;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ public static void premain(final String agentArgs,
final Config config;
final Logger logger;
try {
Profiler.init();
config = Config.build();

logger = new SimpleLogger(
Expand Down Expand Up @@ -56,6 +55,7 @@ public Thread newThread(Runnable r) {
}
});
profiler.start();
logger.info("Profiling started");

final Runnable dumpProfile = () -> {
try {
Expand Down
4 changes: 2 additions & 2 deletions agent/src/main/java/io/pyroscope/javaagent/config/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ public final class Config {
private static final String DEFAULT_SPY_NAME = "javaspy";
private static final Duration DEFAULT_PROFILING_INTERVAL = Duration.ofMillis(10);
private static final EventType DEFAULT_PROFILER_EVENT = EventType.ITIMER;
private static final String DEFAULT_PROFILER_ALLOC = "0";
private static final String DEFAULT_PROFILER_LOCK = "0";
private static final String DEFAULT_PROFILER_ALLOC = "";
private static final String DEFAULT_PROFILER_LOCK = "";
private static final Duration DEFAULT_UPLOAD_INTERVAL = Duration.ofSeconds(10);
private static final String DEFAULT_SERVER_ADDRESS = "http://localhost:4040";
private static final Format DEFAULT_FORMAT = Format.COLLAPSED;
Expand Down

0 comments on commit df947b4

Please sign in to comment.