Last active
November 9, 2017 19:51
-
-
Save qwwdfsad/8c72f6bbde1d9376bc2391c73a14025f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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