1/*
2 * Copyright (c) 2015, 2017, 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
26 * @summary Test of diagnostic command VM.class_hierarchy
27 * @library /test/lib
28 * @modules java.base/jdk.internal.misc
29 *          java.compiler
30 *          java.management
31 *          jdk.internal.jvmstat/sun.jvmstat.monitor
32 * @run testng ClassHierarchyTest
33 */
34
35import org.testng.annotations.Test;
36import org.testng.Assert;
37
38import jdk.test.lib.process.OutputAnalyzer;
39import jdk.test.lib.dcmd.CommandExecutor;
40import jdk.test.lib.dcmd.JMXExecutor;
41
42import java.io.File;
43import java.io.FileInputStream;
44import java.io.IOException;
45import java.nio.ByteBuffer;
46import java.nio.channels.FileChannel;
47import java.util.Iterator;
48import java.util.regex.Matcher;
49import java.util.regex.Pattern;
50
51public class ClassHierarchyTest {
52
53    // $> jcmd DcmdTestClass VM.class_hierarchy  DcmdTestClass | grep DcmdTestClass\$\$Lambda
54    // |--DcmdTestClass$$Lambda$1/4081552/0xa529fbb0
55
56    // > VM.class_hierarchy DcmdBaseClass
57    // java.lang.Object/null
58    // |--DcmdBaseClass/0xa4abcd48
59
60    // > VM.class_hierarchy DcmdBaseClass -s
61    // java.lang.Object/null
62    // |--DcmdBaseClass/0xa4abcd48
63    // |  |--DcmdTestClass/0xa4abcd48
64
65    // > VM.class_hierarchy DcmdBaseClass -i -s
66    // java.lang.Object/null
67    // |--DcmdBaseClass/0xa4abcd48
68    // |  implements Intf2/0xa4abcd48 (declared intf)
69    // |  implements Intf1/0xa4abcd48 (inherited intf)
70    // |  |--DcmdTestClass/0xa4abcd48
71    // |  |  implements Intf1/0xa4abcd48 (inherited intf)
72    // |  |  implements Intf2/0xa4abcd48 (inherited intf)
73
74    static Pattern expected_lambda_line =
75        Pattern.compile("\\|--DcmdTestClass\\$\\$Lambda.*");
76
77    static Pattern expected_lines[] = {
78        Pattern.compile("java.lang.Object/null"),
79        Pattern.compile("\\|--DcmdBaseClass/0x(\\p{XDigit}*)"),
80        Pattern.compile("\\|  implements Intf2/0x(\\p{XDigit}*) \\(declared intf\\)"),
81        Pattern.compile("\\|  implements Intf1/0x(\\p{XDigit}*) \\(inherited intf\\)"),
82        Pattern.compile("\\|  \\|--DcmdTestClass/0x(\\p{XDigit}*)"),
83        Pattern.compile("\\|  \\|  implements Intf1/0x(\\p{XDigit}*) \\(inherited intf\\)"),
84        Pattern.compile("\\|  \\|  implements Intf2/0x(\\p{XDigit}*) \\(inherited intf\\)")
85    };
86
87    public void run(CommandExecutor executor) throws ClassNotFoundException {
88        OutputAnalyzer output;
89        Iterator<String> lines;
90        int i;
91
92        // Load our test class whose hierarchy we will print.
93        Class<?> c = Class.forName("DcmdTestClass");
94
95        // Verify the presence of the lamba anonymous class
96        output = executor.execute("VM.class_hierarchy");
97        lines = output.asLines().iterator();
98        Boolean foundMatch = false;
99        while (lines.hasNext()) {
100            String line = lines.next();
101            Matcher m = expected_lambda_line.matcher(line);
102            if (m.matches()) {
103                foundMatch = true;
104                break;
105            }
106        }
107        if (!foundMatch) {
108            Assert.fail("Failed to find lamda class");
109        }
110
111        // Verify the output for the simple hierachry of just DcmdBaseClass.
112        output = executor.execute("VM.class_hierarchy DcmdBaseClass");
113        lines = output.asLines().iterator();
114        i = 0;
115        while (lines.hasNext()) {
116            String line = lines.next();
117            Matcher m = expected_lines[i].matcher(line);
118            i++;
119            if (!m.matches()) {
120                Assert.fail("Failed to match line #" + i + ": " + line);
121            }
122            // Should only be two lines of output in this form.
123            if (i == 2) break;
124        }
125        if (lines.hasNext()) {
126            String line = lines.next();
127            Assert.fail("Unexpected dcmd output: " + line);
128        }
129
130        // Verify the output for the full hierarchy of DcmdBaseClass, but without interfaces.
131        output = executor.execute("VM.class_hierarchy DcmdBaseClass -s");
132        lines = output.asLines().iterator();
133        i = 0;
134        while (lines.hasNext()) {
135            String line = lines.next();
136            Matcher m = expected_lines[i].matcher(line);
137            i++;
138            if (!m.matches()) {
139                Assert.fail("Failed to match line #" + i + ": " + line);
140            }
141            // "implements" lines should not be in this output.
142            if (i == 2 || i == 4) i += 2;
143        }
144        if (lines.hasNext()) {
145            String line = lines.next();
146            Assert.fail("Unexpected dcmd output: " + line);
147        }
148
149        // Verify the output for the full hierarchy of DcmdBaseClass, including interfaces.
150        output = executor.execute("VM.class_hierarchy DcmdBaseClass -i -s");
151        lines = output.asLines().iterator();
152        i = 0;
153        String classLoaderAddr = null;
154        while (lines.hasNext()) {
155            String line = lines.next();
156            Matcher m = expected_lines[i].matcher(line);
157            i++;
158            if (!m.matches()) {
159                Assert.fail("Failed to match line #" + i + ": " + line);
160            }
161            if (i == 2) {
162                // Fetch the ClassLoader address, which should be the same in
163                // subsequent lines.
164                classLoaderAddr = m.group(1);
165                System.out.println(classLoaderAddr);
166            } else if (i > 2) {
167                if (!classLoaderAddr.equals(m.group(1))) {
168                    Assert.fail("Classloader address didn't match on line #"
169                                        + i + ": " + line);
170                }
171            }
172            if (i == expected_lines.length) break;
173        }
174        if (lines.hasNext()) {
175            String line = lines.next();
176            Assert.fail("Unexpected dcmd output: " + line);
177        }
178    }
179
180    @Test
181    public void jmx() throws ClassNotFoundException {
182        run(new JMXExecutor());
183    }
184}
185
186interface Intf1 {
187}
188
189interface Intf2 extends Intf1 {
190}
191
192class DcmdBaseClass implements Intf2 {
193}
194
195class DcmdTestClass extends DcmdBaseClass {
196    static {
197        // Force creation of anonymous class (for the lambdaform).
198        Runnable r = () -> System.out.println("Hello");
199        r.run();
200    }
201}
202