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