Skip to content

Instantly share code, notes, and snippets.

@qwwdfsad

qwwdfsad/jmh_dtrace.patch

Last active Nov 9, 2017
Embed
What would you like to do?
Index: jmh/jmh-core/src/main/java/org/openjdk/jmh/util/Utils.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- jmh/jmh-core/src/main/java/org/openjdk/jmh/util/Utils.java (revision 1434:1ddf31f810a3100b9433c3fedf24615e85b1d1a7)
+++ jmh/jmh-core/src/main/java/org/openjdk/jmh/util/Utils.java (revision 1434+:1ddf31f810a3+)
@@ -446,6 +446,31 @@
return messages;
}
+ public static Process runAsync(String... cmd) {
+ try {
+ return new ProcessBuilder(cmd).start();
+ } catch (IOException ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+
+ public static Collection<String> destroy(Process process) {
+ Collection<String> messages = new ArrayList<>();
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ process.destroy();
+ int exitCode = process.waitFor();
+ if (exitCode == 0) {
+ return Collections.emptyList();
+ }
+
+ messages.add(baos.toString());
+ return messages;
+ } catch (InterruptedException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
public static Collection<String> runWith(List<String> cmd) {
Collection<String> messages = new ArrayList<>();
try {
Index: jmh/jmh-core/src/main/java/org/openjdk/jmh/profile/ProfilerFactory.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- jmh/jmh-core/src/main/java/org/openjdk/jmh/profile/ProfilerFactory.java (revision 1434:1ddf31f810a3100b9433c3fedf24615e85b1d1a7)
+++ jmh/jmh-core/src/main/java/org/openjdk/jmh/profile/ProfilerFactory.java (revision 1434+:1ddf31f810a3+)
@@ -27,7 +27,6 @@
import org.openjdk.jmh.runner.options.ProfilerConfig;
import java.io.PrintStream;
-import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
@@ -178,6 +177,7 @@
BUILT_IN.put("perfnorm", LinuxPerfNormProfiler.class);
BUILT_IN.put("perfasm", LinuxPerfAsmProfiler.class);
BUILT_IN.put("xperfasm", WinPerfAsmProfiler.class);
+ BUILT_IN.put("dtraceasm", DTraceAsmProfiler.class);
BUILT_IN.put("pauses", PausesProfiler.class);
BUILT_IN.put("safepoints", SafepointsProfiler.class);
}
Index: jmh/jmh-core/src/main/java/org/openjdk/jmh/profile/DTraceAsmProfiler.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- jmh/jmh-core/src/main/java/org/openjdk/jmh/profile/DTraceAsmProfiler.java (revision 1434+:1ddf31f810a3+)
+++ jmh/jmh-core/src/main/java/org/openjdk/jmh/profile/DTraceAsmProfiler.java (revision 1434+:1ddf31f810a3+)
@@ -0,0 +1,194 @@
+package org.openjdk.jmh.profile;
+
+import joptsimple.OptionException;
+import joptsimple.OptionParser;
+import joptsimple.OptionSpec;
+import org.openjdk.jmh.infra.BenchmarkParams;
+import org.openjdk.jmh.results.BenchmarkResult;
+import org.openjdk.jmh.results.Result;
+import org.openjdk.jmh.util.*;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Mac OS X perfasm profiler based on DTrace "profile" provider which samples program counter by timer interrupt.
+ * Due to DTrace limitations on Mac OS X target JVM cannot be run directly under DTrace control, so DTrace is run separately,
+ * all processes are sampled and irrelevant samples are filtered out in {@link #readEvents(double, double)} stage.
+ * Super user privileges are required in order to run DTrace.
+ * <p>
+ * If you see a lot of "[unknown]" regions in profile then you are probably hitting kernel code, kernel sampling is not yet supported.
+ *
+ * @author Tolstopyatov Vsevolod
+ * @since 18/10/2017
+ */
+public class DTraceAsmProfiler extends AbstractPerfAsmProfiler {
+
+ private final long sampleFrequency;
+ private volatile String pid;
+ private volatile Process dtraceProcess;
+ private OptionSpec<Long> optFrequency;
+
+ public DTraceAsmProfiler(String initLine) throws ProfilerException {
+ super(initLine, "sampled_pc");
+
+ // Check DTrace availability
+ Collection<String> messages = Utils.tryWith("sudo", "dtrace", "-V");
+ if (!messages.isEmpty()) {
+ throw new ProfilerException(messages.toString());
+ }
+
+ try {
+ sampleFrequency = set.valueOf(optFrequency);
+ } catch (OptionException e) {
+ throw new ProfilerException(e.getMessage());
+ }
+ }
+
+ @Override
+ public void beforeTrial(BenchmarkParams params) {
+ super.beforeTrial(params);
+ }
+
+ @Override
+ public Collection<? extends Result> afterTrial(BenchmarkResult br, long pid, File stdOut, File stdErr) {
+ if (pid == 0) {
+ throw new IllegalStateException("DTrace needs the forked VM PID, but it is not initialized");
+ }
+
+ Collection<String> messages = Utils.destroy(dtraceProcess);
+ if (!messages.isEmpty()) {
+ throw new IllegalStateException(messages.toString());
+ }
+
+ this.pid = String.valueOf(pid);
+ return super.afterTrial(br, pid, stdOut, stdErr);
+ }
+
+ @Override
+ public Collection<String> addJVMInvokeOptions(BenchmarkParams params) {
+ dtraceProcess = Utils.runAsync("sudo", "dtrace", "-n", "profile-" + sampleFrequency +
+ " /arg1/ { printf(\"%d 0x%lx %d\", pid, arg1, timestamp); ufunc(arg1)}", "-o",
+ perfBinData.getAbsolutePath());
+ return Collections.emptyList();
+ }
+
+ @Override
+ public String getDescription() {
+ return "DTrace profile provider + PrintAssembly Profiler";
+ }
+
+ @Override
+ protected void addMyOptions(OptionParser parser) {
+ optFrequency = parser.accepts("frequency",
+ "Sampling frequency. This is synonymous to profile-#")
+ .withRequiredArg().ofType(Long.class).describedAs("freq").defaultsTo(1001L);
+ }
+
+ @Override
+ protected void parseEvents() {
+ // Do nothing because DTrace writes text output anyway
+ }
+
+ @Override
+ protected PerfEvents readEvents(double skipMs, double lenMs) {
+ long start = (long) skipMs;
+ long end = (long) (skipMs + lenMs);
+
+ try (FileReader fr = new FileReader(perfBinData.file());
+ BufferedReader reader = new BufferedReader(fr)) {
+
+ Deduplicator<MethodDesc> dedup = new Deduplicator<>();
+ Multimap<MethodDesc, Long> methods = new HashMultimap<>();
+ Multiset<Long> events = new TreeMultiset<>();
+
+ long dtraceTimestampBase = 0L;
+ String line;
+ while ((line = reader.readLine()) != null) {
+
+ // Filter out DTrace misc
+ if (!line.contains(":profile")) {
+ continue;
+ }
+
+ line = line.trim();
+ line = line.substring(line.indexOf(":profile"));
+ String[] splits = line.split(" ");
+ String sampledPid = splits[1];
+
+ if (!sampledPid.equals(pid)) {
+ continue;
+ }
+
+ // Sometimes DTrace ufunc fails and gives no information about symbols
+ if (splits.length < 4) {
+ continue;
+ }
+
+ long timestamp = Long.valueOf(splits[3]);
+ if (dtraceTimestampBase == 0) {
+ // Use first event timestamp as base for time comparison
+ dtraceTimestampBase = timestamp;
+ continue;
+ }
+
+ long elapsed = timestamp - dtraceTimestampBase;
+ long elapsedMs = TimeUnit.NANOSECONDS.toMillis(elapsed);
+
+ if (elapsedMs < start || elapsedMs > end) {
+ continue;
+ }
+
+ long address = Long.decode(splits[2]);
+ events.add(address);
+
+ String methodLine = splits[4];
+ // JIT-compiled code has address instead of symbol information
+ if (methodLine.startsWith("0x")) {
+ continue;
+ }
+
+ String symbol = "[unknown]";
+ String[] methodSplit = methodLine.split("`");
+ String library = methodSplit[0];
+ if ("".equals(library)) {
+ library = "[unknown]";
+ }
+
+ if (methodSplit.length == 2) {
+ symbol = methodSplit[1];
+ }
+
+ methods.put(dedup.dedup(MethodDesc.nativeMethod(symbol, library)), address);
+ }
+
+ IntervalMap<MethodDesc> methodMap = new IntervalMap<>();
+ for (MethodDesc md : methods.keys()) {
+ Collection<Long> longs = methods.get(md);
+ methodMap.add(md, Utils.min(longs), Utils.max(longs));
+ }
+
+ Map<String, Multiset<Long>> allEvents = new TreeMap<>();
+ assert this.events.size() == 1;
+ allEvents.put(this.events.get(0), events);
+ return new PerfEvents(this.events, allEvents, methodMap);
+
+ } catch (IOException e) {
+ return new PerfEvents(events);
+ }
+
+ }
+
+ @Override
+ protected String perfBinaryExtension() {
+ // DTrace produces human-readable txt
+ return ".txt";
+ }
+}
Index: jmh/jmh-core/src/main/java/org/openjdk/jmh/profile/AbstractPerfAsmProfiler.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- jmh/jmh-core/src/main/java/org/openjdk/jmh/profile/AbstractPerfAsmProfiler.java (revision 1434:1ddf31f810a3100b9433c3fedf24615e85b1d1a7)
+++ jmh/jmh-core/src/main/java/org/openjdk/jmh/profile/AbstractPerfAsmProfiler.java (revision 1434+:1ddf31f810a3+)
@@ -270,7 +270,7 @@
@Override
public Collection<? extends Result> afterTrial(BenchmarkResult br, long pid, File stdOut, File stdErr) {
- PerfResult result = processAssembly(br, stdOut, stdErr);
+ PerfResult result = processAssembly(br);
// we know these are not needed anymore, proactively delete
hsLog.delete();
@@ -311,7 +311,7 @@
*/
protected abstract String perfBinaryExtension();
- private PerfResult processAssembly(BenchmarkResult br, File stdOut, File stdErr) {
+ private PerfResult processAssembly(BenchmarkResult br) {
/**
* 1. Parse binary events.
*/
@@ -647,11 +647,11 @@
}
}
- void printDottedLine(PrintWriter pw) {
+ private void printDottedLine(PrintWriter pw) {
printDottedLine(pw, null);
}
- void printDottedLine(PrintWriter pw, String header) {
+ private void printDottedLine(PrintWriter pw, String header) {
final int HEADER_WIDTH = 100;
pw.print("....");
@@ -668,7 +668,7 @@
pw.println();
}
- List<Region> makeRegions(Assembly asms, PerfEvents events) {
+ private List<Region> makeRegions(Assembly asms, PerfEvents events) {
List<Region> regions = new ArrayList<>();
SortedSet<Long> allAddrs = events.getAllAddresses();
@@ -728,7 +728,7 @@
return intervals;
}
- Collection<Collection<String>> splitAssembly(File stdOut) {
+ private Collection<Collection<String>> splitAssembly(File stdOut) {
try (FileReader in = new FileReader(stdOut);
BufferedReader br = new BufferedReader(in)) {
Multimap<Long, String> writerToLines = new HashMultimap<>();
@@ -764,7 +764,7 @@
}
}
- Assembly readAssembly(File stdOut) {
+ private Assembly readAssembly(File stdOut) {
List<ASMLine> lines = new ArrayList<>();
SortedMap<Long, Integer> addressMap = new TreeMap<>();
@@ -919,11 +919,11 @@
static class PerfResultAggregator implements Aggregator<PerfResult> {
@Override
public PerfResult aggregate(Collection<PerfResult> results) {
- String output = "";
+ StringBuilder output = new StringBuilder();
for (PerfResult r : results) {
- output += r.output;
+ output.append(r.output);
}
- return new PerfResult(output);
+ return new PerfResult(output.toString());
}
}
Index: jmh/jmh-samples/src/main/java/org/openjdk/jmh/samples/JMHSample_35_Profilers.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- jmh/jmh-samples/src/main/java/org/openjdk/jmh/samples/JMHSample_35_Profilers.java (revision 1434:1ddf31f810a3100b9433c3fedf24615e85b1d1a7)
+++ jmh/jmh-samples/src/main/java/org/openjdk/jmh/samples/JMHSample_35_Profilers.java (revision 1434+:1ddf31f810a3+)
@@ -33,6 +33,7 @@
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.profile.ClassloaderProfiler;
+import org.openjdk.jmh.profile.DTraceAsmProfiler;
import org.openjdk.jmh.profile.LinuxPerfProfiler;
import org.openjdk.jmh.profile.StackProfiler;
import org.openjdk.jmh.runner.Runner;
@@ -352,7 +353,7 @@
* $ java -jar target/benchmarks.jar JMHSample_35.*Atomic -prof perfnorm -f 3 (Linux)
* $ java -jar target/benchmarks.jar JMHSample_35.*Atomic -prof perfasm -f 1 (Linux)
* $ java -jar target/benchmarks.jar JMHSample_35.*Atomic -prof xperfasm -f 1 (Windows)
- *
+ * $ java -jar target/benchmarks.jar JMHSample_35.*Atomic -prof dtraceasm -f 1 (Mac OS X)
* b) Via the Java API:
* (see the JMH homepage for possible caveats when running from IDE:
* http://openjdk.java.net/projects/code-tools/jmh/)
@@ -365,6 +366,7 @@
// .addProfiler(LinuxPerfNormProfiler.class)
// .addProfiler(LinuxPerfAsmProfiler.class)
// .addProfiler(WinPerfAsmProfiler.class)
+// .addProfiler(DTraceAsmProfiler.class)
.build();
new Runner(opt).run();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment