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