1 /* 2 * Copyright (c) 2017, 2024, 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 * @bug 8192985 27 * @summary Test the clhsdb 'inspect' command 28 * @requires vm.hasSA 29 * @library /test/lib 30 * @run main/othervm/timeout=480 ClhsdbInspect 31 */ 32 33 import java.util.HashMap; 34 import java.util.List; 35 import java.util.Map; 36 import java.util.ArrayList; 37 import jdk.test.lib.apps.LingeredApp; 38 import jtreg.SkippedException; 39 40 public class ClhsdbInspect { 41 42 public static void main(String[] args) throws Exception { 43 System.out.println("Starting the ClhsdbInspect test"); 44 45 LingeredAppWithLock theApp = null; 46 try { 47 ClhsdbLauncher test = new ClhsdbLauncher(); 48 49 theApp = new LingeredAppWithLock(); 50 LingeredApp.startApp(theApp); 51 System.out.println("Started LingeredApp with pid " + theApp.getPid()); 52 53 // Run the 'jstack -v' command to get the address of a Method*, 54 // the oop address of a java.lang.ref.ReferenceQueue$Lock 55 // and the oop address of a java.lang.Class object 56 List<String> cmds = List.of("jstack -v"); 57 58 String jstackOutput = test.run(theApp.getPid(), cmds, null, null); 59 60 // the key is a literal, searched in the jstack output; the value is a regex searched in the clhsdb output 61 Map<String, String> tokensMap = new HashMap<>(); 62 tokensMap.put("(a java.lang.Class for LingeredAppWithLock)", 63 "instance of Oop for java/lang/Class"); 64 tokensMap.put("Method*=", "Type is Method"); 65 tokensMap.put("(a java.lang.ref.ReferenceQueue$Lock)", 66 "instance of Oop for java/lang/ref/ReferenceQueue\\$Lock"); 67 68 String[] lines = jstackOutput.split("\\R"); 69 70 for (String key: tokensMap.keySet()) { 71 cmds = new ArrayList<String>(); 72 Map<String, List<String>> expStrMap = new HashMap<>(); 73 74 String addressString = null; 75 for (String line : lines) { 76 if (line.contains(key)) { 77 // Escape the token "Method*=" because the split method uses 78 // a regex, not just a straight String. 79 String escapedKey = key.replace("*","\\*"); 80 String[] words = line.split(escapedKey+"|[ ]"); 81 for (String word : words) { 82 word = word.replace("<","").replace(">",""); 83 if (word.startsWith("0x")) { 84 addressString = word; 85 break; 86 } 87 } 88 if (addressString != null) 89 break; 90 } 91 } 92 93 if (addressString == null) throw new NullPointerException("Token '" + key + "' not found in jstack output"); 94 String cmd = "inspect " + addressString; 95 cmds.add(cmd); 96 expStrMap.put(cmd, List.of(tokensMap.get(key))); 97 test.run(theApp.getPid(), cmds, expStrMap, null); 98 } 99 100 // This part is testing JDK-8261269. When inspecting a java object, we want to make 101 // sure the address is not printed twice and that "Oop for ..." is not printed twice. 102 // 103 // The goal of this test is to dump the Class instance for java.lang.System. It contains 104 // some Oop statics, and that's where the redundant "Oop for..." was noticed. The script 105 // looks something like this: 106 // 107 // hsdb> class java.lang.System 108 // java/lang/System @0x000000080000f388 109 // 110 // hsdb> inspect 0x000000080000f388 111 // Type is InstanceKlass (size of 480) 112 // ... 113 // OopHandle Klass::_java_mirror: OopHandle @ 0x000000080000f400 114 // ... 115 // 116 // hsdb> examine 0x000000080000f400 117 // 0x000000080000f400: 0x00007fd8b812e5e8 118 // 119 // hsdb> examine 0x00007fd8b812e5e8 120 // 0x00007fd8b812e5e8: 0x00000007fef00770 121 // 122 // hsdb> inspect 0x00000007fef00770 123 // instance of Oop for java/lang/Class @ 0x00000007fef00770 @ 0x00000007fef00770 (size = 160) 124 // in: Oop for java/io/BufferedInputStream @ 0x0000000082005b08 Oop for java/io/BufferedInputStream @ 0x0000000082005b08 125 // out: Oop for java/io/PrintStream @ 0x0000000082007b60 Oop for java/io/PrintStream @ 0x0000000082007b60 126 // err: Oop for java/io/PrintStream @ 0x000000008200e0c8 Oop for java/io/PrintStream @ 0x000000008200e0c8 127 128 String cmd; 129 Map<String, List<String>> expStrMap; 130 Map<String, List<String>> unexpStrMap; 131 132 // Start with the "class java.lang.System" 133 cmd = "class java.lang.System"; 134 cmds = List.of(cmd); 135 expStrMap = new HashMap<>(); 136 expStrMap.put(cmd, List.of("java.lang.System @0x")); 137 String classCmdOutput = test.run(theApp.getPid(), cmds, expStrMap, null); 138 139 // "inspect" the address produced by the "class java.lang.System". This is the InstanceKlass. 140 String classAddress = classCmdOutput.substring(classCmdOutput.indexOf("@0x")+1); 141 lines = classAddress.split("\\R"); 142 classAddress = lines[0]; 143 cmd = "inspect " + classAddress; 144 cmds = List.of(cmd); 145 expStrMap = new HashMap<>(); 146 expStrMap.put(cmd, List.of("Type is InstanceKlass", "Klass::_java_mirror: OopHandle @")); 147 String inspectCmdOutput = test.run(theApp.getPid(), cmds, expStrMap, null); 148 149 // Get the Klass::_java_mirror value from the InstanceKlass 150 String mirrorPattern = "Klass::_java_mirror: OopHandle @ "; 151 String mirrorAddress = inspectCmdOutput.substring( 152 inspectCmdOutput.indexOf(mirrorPattern) + mirrorPattern.length()); 153 lines = mirrorAddress.split("\\R"); 154 mirrorAddress = lines[0]; 155 156 // Use "examine" to do an indirection of the _java_mirror. 157 cmd = "examine " + mirrorAddress; 158 cmds = List.of(cmd); 159 expStrMap = new HashMap<>(); 160 expStrMap.put(cmd, List.of(mirrorAddress + ": 0x")); 161 String examineCmdOutput = test.run(theApp.getPid(), cmds, expStrMap, null); 162 String examineResult = examineCmdOutput.substring(examineCmdOutput.indexOf(": 0x")+2); 163 lines = examineResult.split("\\R"); 164 examineResult = lines[0].trim(); // examine leaves a trailing space 165 166 // Do another indirection using "examine" to get to the address of the Class instance. 167 cmd = "examine " + examineResult; 168 cmds = List.of(cmd); 169 expStrMap = new HashMap<>(); 170 expStrMap.put(cmd, List.of(examineResult + ": 0x")); 171 examineCmdOutput = test.run(theApp.getPid(), cmds, expStrMap, null); 172 examineResult = examineCmdOutput.substring(examineCmdOutput.indexOf(": 0x")+2); 173 lines = examineResult.split("\\R"); 174 examineResult = lines[0].trim(); // examine leaves a trailing space 175 176 // inspect the Class instance 177 String instanceOfString = "instance of Oop for java/lang/Class @ "; 178 String staticFieldString = "Oop for java/io/BufferedInputStream @"; 179 cmd = "inspect " + examineResult; 180 cmds = List.of(cmd); 181 expStrMap = new HashMap<>(); 182 expStrMap.put(cmd, List.of(instanceOfString + examineResult, 183 "in: " + staticFieldString)); 184 unexpStrMap = new HashMap<>(); 185 // Make sure we don't see the address of the class intance twice, and make sure 186 // we don't see "Oop for ..." twice for the "in" static field. 187 unexpStrMap.put(cmd, List.of( 188 instanceOfString + examineResult + " @ " + examineResult, 189 "in: " + staticFieldString + " .* " + staticFieldString)); 190 inspectCmdOutput = test.run(theApp.getPid(), cmds, expStrMap, unexpStrMap); 191 } catch (SkippedException e) { 192 throw e; 193 } catch (Exception ex) { 194 throw new RuntimeException("Test ERROR " + ex, ex); 195 } finally { 196 LingeredApp.stopApp(theApp); 197 } 198 System.out.println("Test PASSED"); 199 } 200 }