1 /*
   2  * Copyright (c) 2015, 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.jvmstat/sun.jvmstat.monitor
  32  * @run testng ClassHierarchyTest
  33  */
  34 
  35 import org.testng.annotations.Test;
  36 import org.testng.Assert;
  37 
  38 import jdk.test.lib.process.OutputAnalyzer;
  39 import jdk.test.lib.dcmd.CommandExecutor;
  40 import jdk.test.lib.dcmd.JMXExecutor;
  41 
  42 import java.io.File;
  43 import java.io.FileInputStream;
  44 import java.io.IOException;
  45 import java.nio.ByteBuffer;
  46 import java.nio.channels.FileChannel;
  47 import java.util.Iterator;
  48 import java.util.regex.Matcher;
  49 import java.util.regex.Pattern;
  50 
  51 public 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 
 186 interface Intf1 {
 187 }
 188 
 189 interface Intf2 extends Intf1 {
 190 }
 191 
 192 class DcmdBaseClass implements Intf2 {
 193 }
 194 
 195 class 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 }