package com.jsoniter.demo; import java.io.File; import java.lang.management.ManagementFactory; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import org.openjdk.jmh.infra.BenchmarkParams; import org.openjdk.jmh.profile.ExternalProfiler; import org.openjdk.jmh.results.AggregationPolicy; import org.openjdk.jmh.results.Aggregator; import org.openjdk.jmh.results.BenchmarkResult; import org.openjdk.jmh.results.Result; import org.openjdk.jmh.results.ResultRole; /** * * @author zoly */ public final class JmhFlightRecorderProfiler implements ExternalProfiler { private static final String DUMP_FOLDER = System.getProperty("jmh.stack.profiles", "/tmp"); private static final String DEFAULT_OPTIONS = System.getProperty("jmh.fr.options", "defaultrecording=true,settings=profile"); @Override public Collection<String> addJVMInvokeOptions(final BenchmarkParams params) { return Collections.emptyList(); } private volatile String dumpFile; private static volatile String benchmarkName; public static String benchmarkName() { return benchmarkName; } /** * See: * http://docs.oracle.com/cd/E15289_01/doc.40/e15070/usingjfr.htm * and * http://docs.oracle.com/cd/E15289_01/doc.40/e15070/config_rec_data.htm * @param params * @return */ @Override public Collection<String> addJVMOptions(final BenchmarkParams params) { final String id = params.id(); benchmarkName = id; dumpFile = DUMP_FOLDER + '/' + id + ".jfr"; String flightRecorderOptions = DEFAULT_OPTIONS + ",dumponexit=true,dumponexitpath=" + dumpFile; return Arrays.asList( "-XX:+FlightRecorder", "-XX:FlightRecorderOptions=" + flightRecorderOptions); } @Override public void beforeTrial(final BenchmarkParams benchmarkParams) { final List<String> inputArguments = ManagementFactory.getRuntimeMXBean().getInputArguments(); // if (new Version(org.spf4j.base.Runtime.JAVA_VERSION).compareTo(new Version("1.8.0_40")) <= 0 // && !inputArguments.contains("-XX:+UnlockCommercialFeatures")) { // throw new RuntimeException("-XX:+UnlockCommercialFeatures must pre present in the JVM options," // + " current options are: " + inputArguments); // } } @Override public boolean allowPrintOut() { return true; } @Override public boolean allowPrintErr() { return false; } @Override public String getDescription() { return "Java Flight Recording profiler runs for every benchmark."; } @Override public Collection<? extends Result> afterTrial(final BenchmarkResult bp, final long l, final File file, final File file1) { NoResult r = new NoResult("Profile saved to " + dumpFile + ", results: " + bp + ", stdOutFile = " + file + ", stdErrFile = " + file1); return Collections.singleton(r); } private static final class NoResult extends Result<NoResult> { private static final long serialVersionUID = 1L; private final String output; NoResult(final String output) { super(ResultRole.SECONDARY, "JFR", of(Double.NaN), "N/A", AggregationPolicy.SUM); this.output = output; } @Override protected Aggregator<NoResult> getThreadAggregator() { return new NoResultAggregator(); } @Override protected Aggregator<NoResult> getIterationAggregator() { return new NoResultAggregator(); } private static class NoResultAggregator implements Aggregator<NoResult> { @Override public NoResult aggregate(final Collection<NoResult> results) { StringBuilder agg = new StringBuilder(); for (NoResult r : results) { agg.append(r.output); } return new NoResult(agg.toString()); } } } @Override public String toString() { return "JmhFlightRecorderProfiler{" + "dumpFile=" + dumpFile + '}'; } }