author | dpochepk |
Thu, 25 Dec 2014 15:57:38 +0300 | |
changeset 28392 | f64a0abbff20 |
parent 28391 | 899a849e55f8 |
child 28393 | 18701d7781e7 |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/compiler/codecache/dtrace/SegmentedCodeCacheDtraceTest.java Thu Dec 25 15:57:38 2014 +0300 @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import com.oracle.java.testlibrary.Asserts; +import com.oracle.java.testlibrary.JDKToolFinder; +import com.oracle.java.testlibrary.OutputAnalyzer; +import com.oracle.java.testlibrary.Utils; +import com.oracle.java.testlibrary.dtrace.DtraceResultsAnalyzer; +import com.oracle.java.testlibrary.dtrace.DtraceRunner; +import java.io.IOException; +import java.lang.reflect.Executable; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/* + * @test SegmentedCodeCacheDtraceTest + * @bug 8015774 + * @requires os.family=="solaris" + * @library /testlibrary /compiler/testlibrary /../../test/lib + * @build SegmentedCodeCacheDtraceTestWorker + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main/othervm/timeout=600 -Xbootclasspath/a:. -XX:+TieredCompilation + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * SegmentedCodeCacheDtraceTest + * @summary testing of dtrace for segmented code cache + */ +public class SegmentedCodeCacheDtraceTest { + + private static final String WORKER_CLASS_NAME + = SegmentedCodeCacheDtraceTestWorker.class.getName(); + private static final String JAVA_OPTS = " -XX:+DTraceMethodProbes " + + "-Xbootclasspath/a:" + System.getProperty("test.classes") + " " + + "-XX:+UnlockDiagnosticVMOptions " + + "-XX:+WhiteBoxAPI -XX:+SegmentedCodeCache " + + "-XX:CompileCommand=compileonly," + + WORKER_CLASS_NAME + "::* " + + " -classpath " + System.getProperty("test.class.path") + " " + + String.join(" ", Utils.getTestJavaOpts()); + private static final String DTRACE_SCRIPT + = "SegmentedCodeCacheDtraceTestScript.d"; + private static final List<Executable> MLIST = + SegmentedCodeCacheDtraceTestWorker.TESTED_METHODS_LIST; + private static final int WORKER_METHODS_COUNT = MLIST.size(); + + private void runTest(TestCombination tc) { + String params = MLIST.stream() + .map(Executable::getName) + .map(x -> tc.data.get(x).compileLevel + " " + tc.data.get(x).isInlined) + .collect(Collectors.joining(" ")); + DtraceRunner runner = new DtraceRunner(); + runner.runDtrace(JDKToolFinder.getTestJDKTool("java"), JAVA_OPTS, + WORKER_CLASS_NAME, params, Paths.get(System.getProperty("test.src"), + DTRACE_SCRIPT).toString(), + DtraceRunner.PERMIT_DESTRUCTIVE_ACTIONS_DTRACE_OPTION, + new SegmentedCodeCacheDtraceResultsAnalyzer()); + } + + private static TestCombination generateUniqueCombination( + int[] availableLevels, Set<TestCombination> combinations) { + int len = availableLevels.length; + /* first, check if we're out of combinations. */ + int maxCombinationsCount + = (1 << WORKER_METHODS_COUNT) + * (int) Math.pow(len, WORKER_METHODS_COUNT); + if (combinations.size() == maxCombinationsCount) { + return null; + } + Random r = Utils.getRandomInstance(); + while (combinations.size() < maxCombinationsCount) { + int levels[] = new int[WORKER_METHODS_COUNT]; + boolean inlines[] = new boolean[WORKER_METHODS_COUNT]; + for (int i = 0; i < WORKER_METHODS_COUNT; i++) { + levels[i] = availableLevels[r.nextInt(len)]; + inlines[i] = r.nextBoolean(); + } + TestCombination tc = new TestCombination(levels, inlines); + if (combinations.add(tc)) { + return tc; + } + } + return null; + } + + public static void main(String args[]) { + int iterations + = Integer.getInteger("com.oracle.java.testlibrary.iterations", 1); + if (!DtraceRunner.dtraceAvailable()) { + System.out.println("INFO: There is no dtrace avaiable. Skipping."); + return; + } + int[] availableLevels = CompilerUtils.getAvailableCompilationLevels(); + // adding one more entry(zero) for interpeter + availableLevels + = Arrays.copyOf(availableLevels, availableLevels.length + 1); + Set<TestCombination> combinations = new HashSet<>(); + for (int i = 0; i < iterations; i++) { + TestCombination tc + = generateUniqueCombination(availableLevels, combinations); + if (tc == null) { + System.out.println("INFO: no more combinations available"); + return; + } else { + System.out.println("INFO: Running testcase for: " + tc); + new SegmentedCodeCacheDtraceTest().runTest(tc); + } + } + } + + private static class MethodData { + + public final int compileLevel; + public final boolean isInlined; + public final String name; + + public MethodData(String name, int compileLevel, boolean isInlined) { + this.name = name; + this.compileLevel = compileLevel; + this.isInlined = isInlined; + } + + @Override + public boolean equals(Object o) { + if (o == null || !(o instanceof MethodData)) { + return false; + } + MethodData md = (MethodData) o; + return md.compileLevel == compileLevel + && md.isInlined == isInlined + && md.name.equals(name); + } + + @Override + public int hashCode() { + return 100 * name.hashCode() + 10 * compileLevel + (isInlined ? 1 : 0); + } + + @Override + public String toString() { + return name + " " + compileLevel + " " + isInlined; + } + } + + private static class TestCombination { + + private final Map<String, MethodData> data; + + public TestCombination(int compLevels[], boolean inlines[]) { + Map<String, MethodData> d = new HashMap<>(); + for (int i = 0; i < MLIST.size(); i++) { + d.put(MLIST.get(i).getName(), new MethodData(MLIST.get(i).getName(), + compLevels[i], inlines[i])); + } + data = Collections.unmodifiableMap(d); + } + + @Override + public boolean equals(Object o) { + if (o == null || !(o instanceof TestCombination)) { + return false; + } + TestCombination second = (TestCombination) o; + return second.data.equals(data); + } + + @Override + public int hashCode() { + int sum = 0; + for (MethodData md : data.values()) { + sum += md.hashCode(); + } + return sum; + } + + private String getMethodDescString(MethodData md) { + return (md == null) + ? null + : String.format("Method %s compilation level %d and %s", + md.name, md.compileLevel, + md.isInlined ? "inlined" : "not inlined"); + } + + @Override + public String toString() { + return data.values().stream().map(m -> getMethodDescString(m)) + .collect(Collectors.joining(Utils.NEW_LINE, + "Combination: ", "")); + } + } + + private class SegmentedCodeCacheDtraceResultsAnalyzer + implements DtraceResultsAnalyzer { + + private static final int EXPECTED_MATCH_COUNT = 2; + + private final Pattern checkPattern; + + public SegmentedCodeCacheDtraceResultsAnalyzer() { + String workerClassRegExp = "\\s*" + WORKER_CLASS_NAME + "\\."; + String delimeter = "\\(\\)V\\*?" + workerClassRegExp; + String suffix = "test\\(\\)V\\*?" + workerClassRegExp + + "main\\(\\[Ljava\\/lang\\/String;\\)V"; + StringBuilder sb = new StringBuilder(workerClassRegExp); + // method order is important, so, going from list tail to head, + // accoring to call order representation in stacktrace + for (int i = MLIST.size() - 1; i > -1; i--) { + sb.append(MLIST.get(i).getName()).append(delimeter); + } + sb.append(suffix); + checkPattern = Pattern.compile(sb.toString()); + /* such pattern match should pass on a stacktrace like + CPU ID FUNCTION:NAME + 0 53573 __1cNSharedRuntimeTdtrace_method_entry6FpnKJavaThread_pnGMethod__i_:method-entry ustack: + + libjvm.so`__1cNSharedRuntimeTdtrace_method_entry6FpnKJavaThread_pnGMethod__i_+0x39c + SegmentedCodeCacheDtraceTestWorker.baz()V* + SegmentedCodeCacheDtraceTestWorker.bar()V + SegmentedCodeCacheDtraceTestWorker.foo()V* + SegmentedCodeCacheDtraceTestWorker.test()V + SegmentedCodeCacheDtraceTestWorker.main([Ljava/lang/String;)V + 0xffffffff6b0004b8 + libjvm.so`__1cJJavaCallsLcall_helper6FpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGThread__v_+0x94c + libjvm.so`__1cRjni_invoke_static6FpnHJNIEnv__pnJJavaValue_pnI_jobject_nLJNICallType_pnK_jmethodID_pnSJNI_ArgumentPusher_pnGThread__v_+0xa64 + libjvm.so`jni_CallStaticVoidMethod+0x508 + libjli.so`JavaMain+0x584 + libc.so.1`_lwp_start + jstack: + + libjvm.so`__1cNSharedRuntimeTdtrace_method_entry6FpnKJavaThread_pnGMethod__i_+0x39c + SegmentedCodeCacheDtraceTestWorker.baz()V* + SegmentedCodeCacheDtraceTestWorker.bar()V + SegmentedCodeCacheDtraceTestWorker.foo()V* + SegmentedCodeCacheDtraceTestWorker.test()V + SegmentedCodeCacheDtraceTestWorker.main([Ljava/lang/String;)V + 0xffffffff6b0004b8 + libjvm.so`__1cJJavaCallsLcall_helper6FpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGThread__v_+0x94c + libjvm.so`__1cRjni_invoke_static6FpnHJNIEnv__pnJJavaValue_pnI_jobject_nLJNICallType_pnK_jmethodID_pnSJNI_ArgumentPusher_pnGThread__v_+0xa64 + libjvm.so`jni_CallStaticVoidMethod+0x508 + libjli.so`JavaMain+0x584 + libc.so.1`_lwp_start + */ + } + + protected List<String> loadLog(String dtraceOutFile) throws IOException { + return Files.readAllLines(Paths.get(dtraceOutFile)); + } + + @Override + public void analyze(OutputAnalyzer oa, String dtraceOutFilePath) { + oa.shouldHaveExitValue(0); + List<String> dOut; + try { + dOut = loadLog(dtraceOutFilePath); + } catch (IOException e) { + throw new Error("Can't load log", e); + } + StringBuilder allDtraceOutput = new StringBuilder(); + for (String entry : dOut) { + allDtraceOutput.append(entry); + } + int matchCount = getMatchCount(allDtraceOutput.toString()); + Asserts.assertEQ(matchCount, EXPECTED_MATCH_COUNT, + "Unexpected output match amount. expected: " + + EXPECTED_MATCH_COUNT + " but found " + matchCount); + } + + protected int getMatchCount(String source) { + Matcher m = checkPattern.matcher(source); + int matchCount = 0; + while (m.find()) { + matchCount++; + } + return matchCount; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/compiler/codecache/dtrace/SegmentedCodeCacheDtraceTestScript.d Thu Dec 25 15:57:38 2014 +0300 @@ -0,0 +1,33 @@ +#!/usr/sbin/dtrace -s + +/* + Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + + This code is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License version 2 only, as + published by the Free Software Foundation. + + This code is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + version 2 for more details (a copy is included in the LICENSE file that + accompanied this code). + + You should have received a copy of the GNU General Public License version + 2 along with this work; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + + Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + or visit www.oracle.com if you need additional information or have any + questions. +*/ + +hotspot$target:::method-entry +/ copyinstr(arg3, arg4) == "baz" / +{ + printf("ustack:\n"); + ustack(50, 500); + printf("jstack:\n"); + jstack(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/compiler/codecache/dtrace/SegmentedCodeCacheDtraceTestWorker.java Thu Dec 25 15:57:38 2014 +0300 @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import com.oracle.java.testlibrary.Utils; +import java.lang.reflect.Executable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import sun.hotspot.WhiteBox; + +public class SegmentedCodeCacheDtraceTestWorker { + + private static final String METHOD1_NAME = "foo"; + private static final String METHOD2_NAME = "bar"; + private static final String METHOD3_NAME = "baz"; + public static final List<Executable> TESTED_METHODS_LIST; + private final WhiteBox wb; + private final int compLevels[]; + + static { + List<Executable> methods = new ArrayList<>(); + try { + // method order is important. Need to place methods in call order, + // to be able to verify results later + methods.add(SegmentedCodeCacheDtraceTestWorker.class.getMethod(METHOD1_NAME)); + methods.add(SegmentedCodeCacheDtraceTestWorker.class.getMethod(METHOD2_NAME)); + methods.add(SegmentedCodeCacheDtraceTestWorker.class.getMethod(METHOD3_NAME)); + } catch (NoSuchMethodException e) { + throw new Error("TESTBUG: no expected method found", e); + } + TESTED_METHODS_LIST = Collections.unmodifiableList(methods); + } + + protected static final boolean BACKGROUND_COMPILATION + = WhiteBox.getWhiteBox().getBooleanVMFlag("BackgroundCompilation"); + + public static void main(String[] args) { + if (args.length != 2 * TESTED_METHODS_LIST.size()) { + throw new Error("Usage: java <thisClass> <fooCompLevel> <fooInlined>" + + "<barCompLevel> <barInlined> " + + "<bazCompLevel> <bazInlined>"); + } else { + int compLevels[] = new int[TESTED_METHODS_LIST.size()]; + boolean inlines[] = new boolean[TESTED_METHODS_LIST.size()]; + for (int i = 0; i < TESTED_METHODS_LIST.size(); i++) { + compLevels[i] = Integer.parseInt(args[2 * i]); + inlines[i] = Boolean.parseBoolean(args[2 * i + 1]); + } + new SegmentedCodeCacheDtraceTestWorker(compLevels, inlines).test(); + } + } + + public SegmentedCodeCacheDtraceTestWorker(int compLevels[], boolean inlines[]) { + wb = WhiteBox.getWhiteBox(); + this.compLevels = Arrays.copyOf(compLevels, compLevels.length); + for (int i = 0; i < compLevels.length; i++) { + if (inlines[i]) { + wb.testSetForceInlineMethod(TESTED_METHODS_LIST.get(i), true); + } else { + wb.testSetDontInlineMethod(TESTED_METHODS_LIST.get(i), true); + } + } + } + + private void waitForCompilation(Executable executable, int compLevel) { + if (compLevel > 0) { + Utils.waitForCondition(() -> wb.isMethodCompiled(executable)); + } + } + + protected void test() { + for (int i = 0; i < TESTED_METHODS_LIST.size(); i++) { + Executable method = TESTED_METHODS_LIST.get(i); + int compLevel = compLevels[i]; + wb.enqueueMethodForCompilation(method, compLevel); + waitForCompilation(method, compLevel); + } + foo(); + } + + public static void foo() { + bar(); + } + + public static void bar() { + baz(); + } + + public static void baz() { + System.out.println("Reached baz method"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/compiler/testlibrary/CompilerUtils.java Thu Dec 25 15:57:38 2014 +0300 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import com.oracle.java.testlibrary.Asserts; +import com.oracle.java.testlibrary.Platform; +import java.util.stream.IntStream; +import sun.hotspot.WhiteBox; + +public class CompilerUtils { + + private CompilerUtils() { + // to prevent from instantiation + } + + /** + * Returns available compilation levels + * + * @return int array with compilation levels + */ + public static int[] getAvailableCompilationLevels() { + if (!WhiteBox.getWhiteBox().getBooleanVMFlag("UseCompiler")) { + return new int[0]; + } + if (WhiteBox.getWhiteBox().getBooleanVMFlag("TieredCompilation")) { + Long flagValue = WhiteBox.getWhiteBox() + .getIntxVMFlag("TieredStopAtLevel"); + int maxLevel = flagValue.intValue(); + Asserts.assertEQ(new Long(maxLevel), flagValue, + "TieredStopAtLevel has value out of int capacity"); + return IntStream.rangeClosed(1, maxLevel).toArray(); + } else { + if (Platform.isServer()) { + return new int[]{4}; + } + if (Platform.isClient() || Platform.isMinimal()) { + return new int[]{1}; + } + } + return new int[0]; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/testlibrary/com/oracle/java/testlibrary/dtrace/DtraceResultsAnalyzer.java Thu Dec 25 15:57:38 2014 +0300 @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.java.testlibrary.dtrace; + +import com.oracle.java.testlibrary.OutputAnalyzer; + +public interface DtraceResultsAnalyzer { + public void analyze(OutputAnalyzer oa, String logFilePath); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/testlibrary/com/oracle/java/testlibrary/dtrace/DtraceRunner.java Thu Dec 25 15:57:38 2014 +0300 @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.java.testlibrary.dtrace; + +import com.oracle.java.testlibrary.Asserts; +import com.oracle.java.testlibrary.OutputAnalyzer; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class DtraceRunner { + + private static final String DTRACE_DEFAULT_PATH = "/usr/sbin/dtrace"; + private static final String DTRACE_PATH_PROPERTY + = "com.oracle.test.dtrace.path"; + private static final String OUTPUT_FILE_DTRACE_OPTION = "o"; + private static final String RUN_COMMAND_DTRACE_OPTION = "c"; + private static final String RUN_SCRIPT_DTRACE_OPTION = "s"; + private static final String ALLOW_ZERO_PROBE_DESCRIPTION_DTRACE_OPTION = "Z"; + private static final String DTRACE_OPTION_PREFIX = "-"; + public static final String PERMIT_DESTRUCTIVE_ACTIONS_DTRACE_OPTION = "w"; + public static final String DTRACE_OUT_LOG = "dtrace.out"; + + private final String dtraceExecutable; + + public DtraceRunner() { + dtraceExecutable = getDtracePath(); + } + + private List<String> getLaunchCmd(String java, String javaOpts, + String execClass, String testArgs, String dtraceScript, + String dtraceAddOpts) { + Asserts.assertTrue(!java.matches("\\s"), "Current dtrace implementation" + + " can't handle whitespaces in application path"); + List<String> result = new ArrayList<>(); + result.add(dtraceExecutable); + result.add(DTRACE_OPTION_PREFIX + System.getProperty("sun.arch.data.model")); + result.add(DTRACE_OPTION_PREFIX + + ALLOW_ZERO_PROBE_DESCRIPTION_DTRACE_OPTION + + ((dtraceAddOpts == null) ? "" : dtraceAddOpts) + + RUN_SCRIPT_DTRACE_OPTION); // run_script should be last one + result.add(dtraceScript); + result.add(DTRACE_OPTION_PREFIX + OUTPUT_FILE_DTRACE_OPTION); + result.add(DTRACE_OUT_LOG); + result.add(DTRACE_OPTION_PREFIX + RUN_COMMAND_DTRACE_OPTION); + result.add(java + " " + javaOpts + " " + execClass + " " + testArgs); + return result; + } + + private void backupLogFile(File file) { + if (file.exists()) { + file.renameTo(new File(file.getPath() + ".bak")); + } + } + + public void runDtrace(String java, String javaOpts, String execClass, + String testArgs, String dtraceScript, String dtraceAddOpts, + DtraceResultsAnalyzer analyzer) { + backupLogFile(new File(DTRACE_OUT_LOG)); + ProcessBuilder pbuilder = new ProcessBuilder( + getLaunchCmd(java, javaOpts, execClass, testArgs, + dtraceScript, dtraceAddOpts)); + OutputAnalyzer oa; + try { + oa = new OutputAnalyzer(pbuilder.start()); + } catch (IOException e) { + throw new Error("TESTBUG: Can't start process", e); + } + analyzer.analyze(oa, DTRACE_OUT_LOG); + } + + public static boolean dtraceAvailable() { + String path = getDtracePath(); + if (path == null) { + return false; + } + // now we'll launch dtrace to trace itself just to be sure it works + // and have all additional previleges set + ProcessBuilder pbuilder = new ProcessBuilder(path, path); + try { + OutputAnalyzer oa = new OutputAnalyzer(pbuilder.start()); + if (oa.getExitValue() != 0) { + return false; + } + } catch (IOException e) { + throw new Error("Couldn't launch dtrace", e); + } + return true; + } + + private static String getDtracePath() { + String propPath = System.getProperty(DTRACE_PATH_PROPERTY); + if (propPath != null && new File(propPath).exists()) { + return propPath; + } else if (new File(DTRACE_DEFAULT_PATH).exists()) { + return DTRACE_DEFAULT_PATH; + } + return null; + } +}