1/*
2 * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/*
25 * @test SegmentedCodeCacheDtraceTest
26 * @bug 8015774
27 * @summary testing of dtrace for segmented code cache
28 * @requires os.family=="solaris"
29 * @modules java.base/jdk.internal.misc
30 * @library /test/lib /
31 *
32 * @build sun.hotspot.WhiteBox
33 * @run driver ClassFileInstaller sun.hotspot.WhiteBox
34 *                                sun.hotspot.WhiteBox$WhiteBoxPermission
35 * @run main/othervm/timeout=600 -Xbootclasspath/a:.
36 *     -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
37 *     compiler.codecache.dtrace.SegmentedCodeCacheDtraceTest
38 */
39
40package compiler.codecache.dtrace;
41
42import compiler.testlibrary.CompilerUtils;
43import jdk.test.lib.Asserts;
44import jdk.test.lib.JDKToolFinder;
45import jdk.test.lib.process.OutputAnalyzer;
46import jdk.test.lib.Utils;
47
48import java.io.IOException;
49import java.lang.reflect.Executable;
50import java.nio.file.Files;
51import java.nio.file.Paths;
52import java.util.Arrays;
53import java.util.Collections;
54import java.util.HashMap;
55import java.util.HashSet;
56import java.util.List;
57import java.util.Map;
58import java.util.Random;
59import java.util.Set;
60import java.util.regex.Matcher;
61import java.util.regex.Pattern;
62import java.util.stream.Collectors;
63
64public class SegmentedCodeCacheDtraceTest {
65
66    private static final String WORKER_CLASS_NAME
67            = SegmentedCodeCacheDtraceTestWorker.class.getName();
68    private static final String JAVA_OPTS = " -XX:+DTraceMethodProbes "
69            + "-Xbootclasspath/a:" + System.getProperty("test.classes") + " "
70            + "-XX:+UnlockDiagnosticVMOptions "
71            + "-XX:+WhiteBoxAPI -XX:+SegmentedCodeCache "
72            + "-XX:CompileCommand=compileonly,"
73            + WORKER_CLASS_NAME + "::* "
74            + " -classpath " + System.getProperty("test.class.path") + " "
75            + String.join(" ", Utils.getTestJavaOpts());
76    private static final String DTRACE_SCRIPT
77            = "SegmentedCodeCacheDtraceTestScript.d";
78    private static final List<Executable> MLIST =
79            SegmentedCodeCacheDtraceTestWorker.TESTED_METHODS_LIST;
80    private static final int WORKER_METHODS_COUNT = MLIST.size();
81
82    private void runTest(TestCombination tc) {
83        String params = MLIST.stream()
84                .map(Executable::getName)
85                .map(x -> tc.data.get(x).compileLevel + " " + tc.data.get(x).isInlined)
86                .collect(Collectors.joining(" "));
87        DtraceRunner runner = new DtraceRunner();
88        runner.runDtrace(JDKToolFinder.getTestJDKTool("java"), JAVA_OPTS,
89                WORKER_CLASS_NAME, params, Paths.get(System.getProperty("test.src"),
90                        DTRACE_SCRIPT).toString(),
91                DtraceRunner.PERMIT_DESTRUCTIVE_ACTIONS_DTRACE_OPTION,
92                new SegmentedCodeCacheDtraceResultsAnalyzer());
93    }
94
95    private static TestCombination generateUniqueCombination(
96            int[] availableLevels, Set<TestCombination> combinations) {
97        int len = availableLevels.length;
98        /* first, check if we're out of combinations. */
99        int maxCombinationsCount
100                = (1 << WORKER_METHODS_COUNT)
101                * (int) Math.pow(len, WORKER_METHODS_COUNT);
102        if (combinations.size() == maxCombinationsCount) {
103            return null;
104        }
105        Random r = Utils.getRandomInstance();
106        while (combinations.size() < maxCombinationsCount) {
107            int levels[] = new int[WORKER_METHODS_COUNT];
108            boolean inlines[] = new boolean[WORKER_METHODS_COUNT];
109            for (int i = 0; i < WORKER_METHODS_COUNT; i++) {
110                levels[i] = availableLevels[r.nextInt(len)];
111                inlines[i] = r.nextBoolean();
112            }
113            TestCombination tc = new TestCombination(levels, inlines);
114            if (combinations.add(tc)) {
115                return tc;
116            }
117        }
118        return null;
119    }
120
121    public static void main(String args[]) {
122        int iterations
123                = Integer.getInteger("jdk.test.lib.iterations", 1);
124        if (!DtraceRunner.dtraceAvailable()) {
125            System.out.println("INFO: There is no dtrace avaiable. Skipping.");
126            return;
127        }
128        int[] availableLevels = CompilerUtils.getAvailableCompilationLevels();
129        // adding one more entry(zero) for interpeter
130        availableLevels
131                = Arrays.copyOf(availableLevels, availableLevels.length + 1);
132        Set<TestCombination> combinations = new HashSet<>();
133        for (int i = 0; i < iterations; i++) {
134            TestCombination tc
135                    = generateUniqueCombination(availableLevels, combinations);
136            if (tc == null) {
137                System.out.println("INFO: no more combinations available");
138                return;
139            } else {
140                System.out.println("INFO: Running testcase for: " + tc);
141                new SegmentedCodeCacheDtraceTest().runTest(tc);
142            }
143        }
144    }
145
146    private static class MethodData {
147
148        public final int compileLevel;
149        public final boolean isInlined;
150        public final String name;
151
152        public MethodData(String name, int compileLevel, boolean isInlined) {
153            this.name = name;
154            this.compileLevel = compileLevel;
155            this.isInlined = isInlined;
156        }
157
158        @Override
159        public boolean equals(Object o) {
160            if (o == null || !(o instanceof MethodData)) {
161                return false;
162            }
163            MethodData md = (MethodData) o;
164            return md.compileLevel == compileLevel
165                    && md.isInlined == isInlined
166                    && md.name.equals(name);
167        }
168
169        @Override
170        public int hashCode() {
171            return 100 * name.hashCode() + 10 * compileLevel + (isInlined ? 1 : 0);
172        }
173
174        @Override
175        public String toString() {
176            return name + " " + compileLevel + " " + isInlined;
177        }
178    }
179
180    private static class TestCombination {
181
182        private final Map<String, MethodData> data;
183
184        public TestCombination(int compLevels[], boolean inlines[]) {
185            Map<String, MethodData> d = new HashMap<>();
186            for (int i = 0; i < MLIST.size(); i++) {
187                d.put(MLIST.get(i).getName(), new MethodData(MLIST.get(i).getName(),
188                        compLevels[i], inlines[i]));
189            }
190            data = Collections.unmodifiableMap(d);
191        }
192
193        @Override
194        public boolean equals(Object o) {
195            if (o == null || !(o instanceof TestCombination)) {
196                return false;
197            }
198            TestCombination second = (TestCombination) o;
199            return second.data.equals(data);
200        }
201
202        @Override
203        public int hashCode() {
204            int sum = 0;
205            for (MethodData md : data.values()) {
206                sum += md.hashCode();
207            }
208            return sum;
209        }
210
211        private String getMethodDescString(MethodData md) {
212            return (md == null)
213                    ? null
214                    : String.format("Method %s compilation level %d and %s",
215                            md.name, md.compileLevel,
216                            md.isInlined ? "inlined" : "not inlined");
217        }
218
219        @Override
220        public String toString() {
221            return data.values().stream().map(m -> getMethodDescString(m))
222                    .collect(Collectors.joining(Utils.NEW_LINE,
223                                    "Combination: ", ""));
224        }
225    }
226
227    private class SegmentedCodeCacheDtraceResultsAnalyzer
228            implements DtraceResultsAnalyzer {
229
230        private static final int EXPECTED_MATCH_COUNT = 2;
231
232        private final Pattern checkPattern;
233
234        public SegmentedCodeCacheDtraceResultsAnalyzer() {
235            String workerClassRegExp = "\\s*" + WORKER_CLASS_NAME + "\\.";
236            String delimeter = "\\(\\)V\\*?" + workerClassRegExp;
237            String suffix = "test\\(\\)V\\*?" + workerClassRegExp
238                    + "main\\(\\[Ljava\\/lang\\/String;\\)V";
239            StringBuilder sb = new StringBuilder(workerClassRegExp);
240            // method order is important, so, going from list tail to head,
241            // accoring to call order representation in stacktrace
242            for (int i = MLIST.size() - 1; i > -1; i--) {
243                sb.append(MLIST.get(i).getName()).append(delimeter);
244            }
245            sb.append(suffix);
246            checkPattern = Pattern.compile(sb.toString());
247            /* such pattern match should pass on a stacktrace like
248             CPU     ID                    FUNCTION:NAME
249             0  53573 __1cNSharedRuntimeTdtrace_method_entry6FpnKJavaThread_pnGMethod__i_:method-entry ustack:
250
251             libjvm.so`__1cNSharedRuntimeTdtrace_method_entry6FpnKJavaThread_pnGMethod__i_+0x39c
252             SegmentedCodeCacheDtraceTestWorker.baz()V*
253             SegmentedCodeCacheDtraceTestWorker.bar()V
254             SegmentedCodeCacheDtraceTestWorker.foo()V*
255             SegmentedCodeCacheDtraceTestWorker.test()V
256             SegmentedCodeCacheDtraceTestWorker.main([Ljava/lang/String;)V
257             0xffffffff6b0004b8
258             libjvm.so`__1cJJavaCallsLcall_helper6FpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGThread__v_+0x94c
259             libjvm.so`__1cRjni_invoke_static6FpnHJNIEnv__pnJJavaValue_pnI_jobject_nLJNICallType_pnK_jmethodID_pnSJNI_ArgumentPusher_pnGThread__v_+0xa64
260             libjvm.so`jni_CallStaticVoidMethod+0x508
261             libjli.so`JavaMain+0x584
262             libc.so.1`_lwp_start
263             jstack:
264
265             libjvm.so`__1cNSharedRuntimeTdtrace_method_entry6FpnKJavaThread_pnGMethod__i_+0x39c
266             SegmentedCodeCacheDtraceTestWorker.baz()V*
267             SegmentedCodeCacheDtraceTestWorker.bar()V
268             SegmentedCodeCacheDtraceTestWorker.foo()V*
269             SegmentedCodeCacheDtraceTestWorker.test()V
270             SegmentedCodeCacheDtraceTestWorker.main([Ljava/lang/String;)V
271             0xffffffff6b0004b8
272             libjvm.so`__1cJJavaCallsLcall_helper6FpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGThread__v_+0x94c
273             libjvm.so`__1cRjni_invoke_static6FpnHJNIEnv__pnJJavaValue_pnI_jobject_nLJNICallType_pnK_jmethodID_pnSJNI_ArgumentPusher_pnGThread__v_+0xa64
274             libjvm.so`jni_CallStaticVoidMethod+0x508
275             libjli.so`JavaMain+0x584
276             libc.so.1`_lwp_start
277             */
278        }
279
280        protected List<String> loadLog(String dtraceOutFile) throws IOException {
281            return Files.readAllLines(Paths.get(dtraceOutFile));
282        }
283
284        @Override
285        public void analyze(OutputAnalyzer oa, String dtraceOutFilePath) {
286            oa.shouldHaveExitValue(0);
287            List<String> dOut;
288            try {
289                dOut = loadLog(dtraceOutFilePath);
290            } catch (IOException e) {
291                throw new Error("Can't load log", e);
292            }
293            StringBuilder allDtraceOutput = new StringBuilder();
294            for (String entry : dOut) {
295                allDtraceOutput.append(entry);
296            }
297            int matchCount = getMatchCount(allDtraceOutput.toString());
298            Asserts.assertEQ(matchCount, EXPECTED_MATCH_COUNT,
299                    "Unexpected output match amount. expected: "
300                    + EXPECTED_MATCH_COUNT + " but found " + matchCount);
301        }
302
303        protected int getMatchCount(String source) {
304            Matcher m = checkPattern.matcher(source);
305            int matchCount = 0;
306            while (m.find()) {
307                matchCount++;
308            }
309            return matchCount;
310        }
311    }
312}
313