1 /* 2 * Copyright (c) 2014, 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.classloader_stats 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/othervm --add-exports=java.base/jdk.internal.misc=ALL-UNNAMED --add-exports=jdk.internal.jvmstat/sun.jvmstat.monitor=ALL-UNNAMED ClassLoaderStatsTest 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.ByteArrayOutputStream; 43 import java.io.File; 44 import java.io.FileInputStream; 45 import java.io.IOException; 46 import java.lang.invoke.MethodHandles; 47 import java.lang.invoke.MethodHandles.Lookup; 48 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*; 49 import java.nio.ByteBuffer; 50 import java.nio.channels.FileChannel; 51 import java.nio.file.Path; 52 import java.nio.file.Paths; 53 import java.util.Iterator; 54 import java.util.regex.Matcher; 55 import java.util.regex.Pattern; 56 57 import jdk.internal.misc.Unsafe; 58 59 public class ClassLoaderStatsTest { 60 61 // Expected output from VM.classloader_stats: 62 // ClassLoader Parent CLD* Classes ChunkSz BlockSz Type 63 // 0x0000000800bd3830 0x000000080037f468 0x00007f001c2ea170 1 10240 4672 ClassLoaderStatsTest$DummyClassLoader 64 // 2 2048 1088 + hidden classes 65 // 0x0000000000000000 0x0000000000000000 0x00007f00e852d190 1607 4628480 3931216 <boot class loader> 66 // 38 124928 85856 + hidden classes 67 // 0x00000008003b5508 0x0000000000000000 0x00007f001c2d4760 1 6144 4040 jdk.internal.reflect.DelegatingClassLoader 68 // 0x000000080037f468 0x000000080037ee80 0x00007f00e868e3f0 228 1368064 1286672 jdk.internal.loader.ClassLoaders$AppClassLoader 69 // ... 70 71 static Pattern clLine = Pattern.compile("0x\\p{XDigit}*\\s*0x\\p{XDigit}*\\s*0x\\p{XDigit}*\\s*(\\d*)\\s*(\\d*)\\s*(\\d*)\\s*(.*)"); 72 static Pattern hiddenLine = Pattern.compile("\\s*(\\d*)\\s*(\\d*)\\s*(\\d*)\\s*.*"); 73 74 public static DummyClassLoader dummyloader; 75 76 public void run(CommandExecutor executor) throws ClassNotFoundException { 77 78 // create a classloader and load our special classes 79 dummyloader = new DummyClassLoader(); 80 Class<?> c = Class.forName("TestClass", true, dummyloader); 81 if (c.getClassLoader() != dummyloader) { 82 Assert.fail("TestClass defined by wrong classloader: " + c.getClassLoader()); 83 } 84 85 OutputAnalyzer output = executor.execute("VM.classloader_stats"); 86 Iterator<String> lines = output.asLines().iterator(); 87 while (lines.hasNext()) { 88 String line = lines.next(); 89 Matcher m = clLine.matcher(line); 90 if (m.matches()) { 91 // verify that DummyClassLoader has loaded 1 regular class and 2 hidden classes 92 if (m.group(4).equals("ClassLoaderStatsTest$DummyClassLoader")) { 93 System.out.println("DummyClassLoader line: " + line); 94 if (!m.group(1).equals("1")) { 95 Assert.fail("Should have loaded 1 class: " + line); 96 } 97 checkPositiveInt(m.group(2)); 98 checkPositiveInt(m.group(3)); 99 100 String next = lines.next(); 101 System.out.println("DummyClassLoader next: " + next); 102 if (!next.contains("hidden classes")) { 103 Assert.fail("Should have a hidden class"); 104 } 105 Matcher m2 = hiddenLine.matcher(next); 106 m2.matches(); 107 if (!m2.group(1).equals("2")) { 108 // anonymous classes are included in the hidden classes count. 109 Assert.fail("Should have loaded 2 hidden classes, but found : " + m2.group(1)); 110 } 111 checkPositiveInt(m2.group(2)); 112 checkPositiveInt(m2.group(3)); 113 } 114 } 115 } 116 } 117 118 private static void checkPositiveInt(String s) { 119 if (Integer.parseInt(s) <= 0) { 120 Assert.fail("Value should have been > 0: " + s); 121 } 122 } 123 124 public static class DummyClassLoader extends ClassLoader { 125 126 public static final String CLASS_NAME = "TestClass"; 127 128 static ByteBuffer readClassFile(String name) 129 { 130 File f = new File(System.getProperty("test.classes", "."), name); 131 try (FileInputStream fin = new FileInputStream(f); 132 FileChannel fc = fin.getChannel()) 133 { 134 return fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()); 135 } catch (IOException e) { 136 Assert.fail("Can't open file: " + name, e); 137 } 138 139 /* Will not reach here as Assert.fail() throws exception */ 140 return null; 141 } 142 143 protected Class<?> loadClass(String name, boolean resolve) 144 throws ClassNotFoundException 145 { 146 Class<?> c; 147 if (!"TestClass".equals(name)) { 148 c = super.loadClass(name, resolve); 149 } else { 150 // should not delegate to the system class loader 151 c = findClass(name); 152 if (resolve) { 153 resolveClass(c); 154 } 155 } 156 return c; 157 } 158 159 protected Class<?> findClass(String name) 160 throws ClassNotFoundException 161 { 162 if (!"TestClass".equals(name)) { 163 throw new ClassNotFoundException("Unexpected class: " + name); 164 } 165 return defineClass(name, readClassFile(name + ".class"), null); 166 } 167 } /* DummyClassLoader */ 168 169 @Test 170 public void jmx() throws ClassNotFoundException { 171 run(new JMXExecutor()); 172 } 173 } 174 175 class HiddenClass { } 176 177 class TestClass { 178 private static final String HCName = "HiddenClass.class"; 179 private static final String DIR = System.getProperty("test.classes"); 180 static Unsafe unsafe = Unsafe.getUnsafe(); 181 182 static { 183 try { 184 // Create a hidden non-strong class and an anonymous class. 185 byte[] klassBuf = readClassFile(DIR + File.separator + HCName); 186 Class<?> hc = defineHiddenClass(klassBuf); 187 Class ac = unsafe.defineAnonymousClass(TestClass.class, klassBuf, new Object[0]); 188 } catch (Throwable e) { 189 throw new RuntimeException("Unexpected exception in TestClass: " + e.getMessage()); 190 } 191 } 192 193 194 static byte[] readClassFile(String classFileName) throws Exception { 195 File classFile = new File(classFileName); 196 try (FileInputStream in = new FileInputStream(classFile); 197 ByteArrayOutputStream out = new ByteArrayOutputStream()) 198 { 199 int b; 200 while ((b = in.read()) != -1) { 201 out.write(b); 202 } 203 return out.toByteArray(); 204 } 205 } 206 207 static Class<?> defineHiddenClass(byte[] bytes) throws Exception { 208 Lookup lookup = MethodHandles.lookup(); 209 Class<?> hc = lookup.defineHiddenClass(bytes, false, NESTMATE).lookupClass(); 210 return hc; 211 } 212 }