8247514: Improve clhsdb 'findpc' ability to determine what an address points to by improving PointerFinder and PointerLocation classes

Reviewed-by: ysuenaga, kevinw
This commit is contained in:
Chris Plummer 2021-02-12 22:01:43 +00:00
parent dc46aa85c6
commit e29c560a11
5 changed files with 284 additions and 54 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -267,7 +267,8 @@ public class Method extends Metadata {
}
public void printValueOn(PrintStream tty) {
tty.print("Method " + getName().asString() + getSignature().asString() + "@" + getAddress());
tty.print("Method " + getMethodHolder().getName().asString() + "." +
getName().asString() + getSignature().asString() + "@" + getAddress());
}
public void iterateFields(MetadataVisitor visitor) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -467,7 +467,7 @@ public class JavaThread extends Thread {
return fr;
}
private Address lastSPDbg() {
public Address lastSPDbg() {
return access.getLastSP(addr);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -26,10 +26,13 @@ package sun.jvm.hotspot.utilities;
import sun.jvm.hotspot.code.*;
import sun.jvm.hotspot.debugger.*;
import sun.jvm.hotspot.debugger.cdbg.*;
import sun.jvm.hotspot.gc.shared.*;
import sun.jvm.hotspot.interpreter.*;
import sun.jvm.hotspot.runtime.*;
import sun.jvm.hotspot.memory.*;
import sun.jvm.hotspot.oops.Metadata;
import sun.jvm.hotspot.runtime.*;
import sun.jvm.hotspot.types.WrongTypeException;
/** This class, only intended for use in the debugging system,
provides the functionality of find() in the VM. */
@ -37,7 +40,60 @@ import sun.jvm.hotspot.memory.*;
public class PointerFinder {
public static PointerLocation find(Address a) {
PointerLocation loc = new PointerLocation(a);
Threads threads = VM.getVM().getThreads();
// Check if address is a pointer to a Metadata object.
try {
loc.metadata = Metadata.instantiateWrapperFor(a);
return loc;
} catch (Exception e) {
// Just ignore. This just means we aren't dealing with a Metadata pointer.
}
// Check if address is some other C++ type that we can deduce
loc.ctype = VM.getVM().getTypeDataBase().guessTypeForAddress(a);
if (loc.ctype == null && VM.getVM().isSharingEnabled()) {
// Check if the value falls in the _md_region
try {
Address loc1 = a.getAddressAt(0);
FileMapInfo cdsFileMapInfo = VM.getVM().getFileMapInfo();
if (cdsFileMapInfo.inCopiedVtableSpace(loc1)) {
loc.ctype = cdsFileMapInfo.getTypeForVptrAddress(loc1);
}
} catch (AddressException | WrongTypeException e) {
// This can happen if "a" or "loc1" is a bad address. Just ignore.
}
}
if (loc.ctype != null) {
return loc;
}
// Check if address is in the stack of a JavaThread
for (int i = 0; i < threads.getNumberOfThreads(); i++) {
JavaThread t = threads.getJavaThreadAt(i);
Address stackBase = t.getStackBase();
if (stackBase != null) {
Long stackSize = t.getStackSize();
Address stackEnd = stackBase.addOffsetTo(-stackSize);
if (a.lessThanOrEqual(stackBase) && a.greaterThan(stackEnd)) {
loc.stackThread = t;
return loc;
}
}
}
// Check if address is a native (C++) symbol
JVMDebugger dbg = VM.getVM().getDebugger();
CDebugger cdbg = dbg.getCDebugger();
if (cdbg != null) {
loc.loadObject = cdbg.loadObjectContainingPC(a);
if (loc.loadObject != null) {
loc.nativeSymbol = loc.loadObject.closestSymbolToPC(a);
return loc;
}
}
// Check if address is in the java heap.
CollectedHeap heap = VM.getVM().getUniverse().heap();
if (heap instanceof GenCollectedHeap) {
GenCollectedHeap genheap = (GenCollectedHeap) heap;
@ -50,13 +106,12 @@ public class PointerFinder {
}
}
if (Assert.ASSERTS_ENABLED) {
if (Assert.ASSERTS_ENABLED) {
Assert.that(loc.gen != null, "Should have found this in a generation");
}
if (VM.getVM().getUseTLAB()) {
// Try to find thread containing it
Threads threads = VM.getVM().getThreads();
for (int i = 0; i < threads.getNumberOfThreads(); i++) {
JavaThread t = threads.getJavaThreadAt(i);
ThreadLocalAllocBuffer tlab = t.tlab();
@ -78,6 +133,7 @@ public class PointerFinder {
}
}
// Check if address is in the interpreter
Interpreter interp = VM.getVM().getInterpreter();
if (interp.contains(a)) {
loc.inInterpreter = true;
@ -85,6 +141,7 @@ public class PointerFinder {
return loc;
}
// Check if address is in the code cache
if (!VM.getVM().isCore()) {
CodeCache c = VM.getVM().getCodeCache();
if (c.contains(a)) {
@ -127,7 +184,6 @@ public class PointerFinder {
return loc;
}
// Look in thread-local handles
Threads threads = VM.getVM().getThreads();
for (int i = 0; i < threads.getNumberOfThreads(); i++) {
JavaThread t = threads.getJavaThreadAt(i);
JNIHandleBlock handleBlock = t.activeHandles();

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -27,10 +27,13 @@ package sun.jvm.hotspot.utilities;
import java.io.*;
import sun.jvm.hotspot.code.*;
import sun.jvm.hotspot.debugger.*;
import sun.jvm.hotspot.debugger.cdbg.*;
import sun.jvm.hotspot.gc.shared.*;
import sun.jvm.hotspot.interpreter.*;
import sun.jvm.hotspot.runtime.*;
import sun.jvm.hotspot.memory.*;
import sun.jvm.hotspot.oops.Metadata;
import sun.jvm.hotspot.runtime.*;
import sun.jvm.hotspot.types.Type;
/** This class attempts to describe possible locations of pointers in
the VM. */
@ -45,6 +48,13 @@ public class PointerLocation {
Address addr;
Metadata metadata;
Type ctype;
JavaThread stackThread;
LoadObject loadObject;
ClosestSymbol nativeSymbol;
CollectedHeap heap;
Generation gen;
@ -80,6 +90,22 @@ public class PointerLocation {
this.addr = addr;
}
public boolean isMetadata() {
return metadata != null;
}
public boolean isCtype() {
return ctype != null;
}
public boolean isInJavaStack() {
return stackThread != null;
}
public boolean isNativeSymbol() {
return loadObject != null;
}
public boolean isInHeap() {
return (heap != null || (gen != null));
}
@ -175,8 +201,9 @@ public class PointerLocation {
}
public boolean isUnknown() {
return (!(isInHeap() || isInInterpreter() || isInCodeCache() ||
isInStrongGlobalJNIHandles() || isInWeakGlobalJNIHandles() || isInLocalJNIHandleBlock()));
return (!(isMetadata() || isCtype() || isInJavaStack() || isNativeSymbol() || isInHeap() ||
isInInterpreter() || isInCodeCache() || isInStrongGlobalJNIHandles() ||
isInWeakGlobalJNIHandles() || isInLocalJNIHandleBlock()));
}
public String toString() {
@ -186,24 +213,66 @@ public class PointerLocation {
}
public void print() {
printOn(System.out);
printOn(System.out, true, true);
}
public void print(boolean printAddress, boolean verbose) {
printOn(System.out, printAddress, verbose);
}
public void printOn(PrintStream tty) {
tty.print("Address ");
if (addr == null) {
tty.print("0x0");
} else {
tty.print(addr.toString());
printOn(tty, true, true);
}
public void printOn(PrintStream tty, boolean printAddress, boolean verbose) {
if (printAddress) {
tty.print("Address ");
if (addr == null) {
tty.print("0x0");
} else {
tty.print(addr.toString());
}
tty.print(": ");
}
tty.print(": ");
if (isInHeap()) {
if (isMetadata()) {
metadata.printValueOn(tty); // does not include "\n"
tty.println();
} else if (isCtype()) {
tty.println("Is of type " + ctype.getName());
} else if (isInJavaStack()) {
if (verbose) {
tty.format("In java stack [%s,%s,%s] for thread %s:\n ",
stackThread.getStackBase(), stackThread.lastSPDbg(),
stackThread.getStackBase().addOffsetTo(-stackThread.getStackSize()),
stackThread);
stackThread.printThreadInfoOn(tty); // includes "\n"
} else {
tty.format("In java stack for thread \"%s\" %s\n", stackThread.getThreadName(), stackThread);
}
} else if (isNativeSymbol()) {
CDebugger cdbg = VM.getVM().getDebugger().getCDebugger();
long diff;
if (nativeSymbol != null) {
String name = nativeSymbol.getName();
if (cdbg.canDemangle()) {
name = cdbg.demangle(name);
}
tty.print(name);
diff = nativeSymbol.getOffset();
} else {
tty.print(loadObject.getName());
diff = addr.minus(loadObject.getBase());
}
if (diff != 0L) {
tty.print(" + 0x" + Long.toHexString(diff));
}
tty.println();
} else if (isInHeap()) {
if (isInTLAB()) {
tty.print("In thread-local allocation buffer for thread \"" +
getTLABThread().getThreadName() + "\" (");
getTLABThread().printThreadIDOn(tty);
tty.print("In thread-local allocation buffer for thread (");
getTLABThread().printThreadInfoOn(tty);
tty.print(") ");
getTLAB().printOn(tty);
getTLAB().printOn(tty); // includes "\n"
} else {
if (isInNewGen()) {
tty.print("In new generation ");
@ -213,13 +282,16 @@ public class PointerLocation {
tty.print("In unknown section of Java heap");
}
if (getGeneration() != null) {
getGeneration().printOn(tty);
getGeneration().printOn(tty); // does not include "\n"
}
tty.println();
}
} else if (isInInterpreter()) {
tty.println("In interpreter codelet \"" + interpreterCodelet.getDescription() + "\"");
interpreterCodelet.printOn(tty);
tty.print("In interpreter codelet: ");
interpreterCodelet.printOn(tty); // includes "\n"
} else if (isInCodeCache()) {
// TODO: print the type of CodeBlob. See "look for known code blobs" comment
// in PStack.java for example code.
CodeBlob b = getCodeBlob();
tty.print("In ");
if (isInBlobCode()) {
@ -235,28 +307,32 @@ public class PointerLocation {
tty.print("unknown location");
}
tty.print(" in ");
b.printOn(tty);
if (verbose) {
b.printOn(tty); // includes "\n"
} else {
tty.println(b.toString());
}
// FIXME: add more detail
} else if (isInStrongGlobalJNIHandles()) {
tty.print("In JNI strong global");
tty.println("In JNI strong global");
} else if (isInWeakGlobalJNIHandles()) {
tty.print("In JNI weak global");
tty.println("In JNI weak global");
} else if (isInLocalJNIHandleBlock()) {
tty.print("In thread-local");
tty.print(" JNI handle block (" + handleBlock.top() + " handle slots present)");
if (handleThread.isJavaThread()) {
tty.print(" for JavaThread ");
((JavaThread) handleThread).printThreadIDOn(tty);
((JavaThread) handleThread).printThreadIDOn(tty); // includes "\n"
} else {
tty.print(" for a non-Java Thread");
tty.println(" for a non-Java Thread");
}
} else {
// This must be last
if (Assert.ASSERTS_ENABLED) {
Assert.that(isUnknown(), "Should have unknown location");
}
tty.print("In unknown location");
tty.println("In unknown location");
}
}
}

View File

@ -27,6 +27,7 @@ import java.util.Map;
import java.util.ArrayList;
import jdk.test.lib.apps.LingeredApp;
import jdk.test.lib.Platform;
import jdk.test.lib.util.CoreUtils;
import jtreg.SkippedException;
@ -75,12 +76,18 @@ import jtreg.SkippedException;
*/
public class ClhsdbFindPC {
static LingeredApp theApp = null;
static String coreFileName = null;
static ClhsdbLauncher test = null;
private static void testFindPC(boolean withXcomp, boolean withCore) throws Exception {
LingeredApp theApp = null;
String coreFileName = null;
try {
ClhsdbLauncher test = new ClhsdbLauncher();
String segvAddress = null;
List<String> cmds = null;
String cmdStr = null;
Map<String, List<String>> expStrMap = null;
test = new ClhsdbLauncher();
theApp = new LingeredAppWithTrivialMain();
theApp.setForceCrash(withCore);
@ -97,24 +104,38 @@ public class ClhsdbFindPC {
}
System.out.println("with pid " + theApp.getPid());
// Get the core file name if we are debugging a core instead of live process
if (withCore) {
coreFileName = CoreUtils.getCoreFileLocation(theApp.getOutput().getStdout(), theApp.getPid());
String crashOutput = theApp.getOutput().getStdout();
// Get the core file name if we are debugging a core instead of live process
coreFileName = CoreUtils.getCoreFileLocation(crashOutput, theApp.getPid());
// Get the SEGV Address from the following line:
// # SIGSEGV (0xb) at pc=0x00007f20a897f7f4, pid=8561, tid=8562
String[] parts = crashOutput.split(" pc=");
String[] tokens = parts[1].split(",");
segvAddress = tokens[0];
// Test the 'findpc' command passing in the SEGV address
cmds = new ArrayList<String>();
cmdStr = "findpc " + segvAddress;
cmds.add(cmdStr);
expStrMap = new HashMap<>();
if (Platform.isOSX()) {
// OSX will only find addresses in JVM libraries, not user or system libraries
expStrMap.put(cmdStr, List.of("In unknown location"));
} else { // symbol lookups not supported with OSX live process
expStrMap.put(cmdStr, List.of("Java_jdk_test_lib_apps_LingeredApp_crash"));
}
runTest(withCore, cmds, expStrMap);
}
// Run 'jstack -v' command to get the findpc address
List<String> cmds = List.of("jstack -v");
String output;
if (withCore) {
output = test.runOnCore(coreFileName, cmds, null, null);
} else {
output = test.run(theApp.getPid(), cmds, null, null);
}
// Run 'jstack -v' command to get the pc and other useful values
cmds = List.of("jstack -v");
String jStackOutput = runTest(withCore, cmds, null);
// Extract pc address from the following line:
// - LingeredAppWithTrivialMain.main(java.lang.String[]) @bci=1, line=33, pc=0x00007ff18ff519f0, ...
String pcAddress = null;
String[] parts = output.split("LingeredAppWithTrivialMain.main");
String[] parts = jStackOutput.split("LingeredAppWithTrivialMain.main");
String[] tokens = parts[1].split(" ");
for (String token : tokens) {
if (token.contains("pc")) {
@ -128,11 +149,11 @@ public class ClhsdbFindPC {
throw new RuntimeException("Cannot find LingeredAppWithTrivialMain.main pc in output");
}
// Test the 'findpc' command passing in the pc obtained from above
// Test the 'findpc' command passing in the pc obtained from jstack above
cmds = new ArrayList<String>();
String cmdStr = "findpc " + pcAddress;
cmdStr = "findpc " + pcAddress;
cmds.add(cmdStr);
Map<String, List<String>> expStrMap = new HashMap<>();
expStrMap = new HashMap<>();
if (withXcomp) {
expStrMap.put(cmdStr, List.of(
"In code in NMethod for LingeredAppWithTrivialMain.main",
@ -143,12 +164,78 @@ public class ClhsdbFindPC {
expStrMap.put(cmdStr, List.of(
"In interpreter codelet"));
}
runTest(withCore, cmds, expStrMap);
if (withCore) {
test.runOnCore(coreFileName, cmds, expStrMap, null);
// Run findpc on a Method*. We can find one in the jstack output. For example:
// - LingeredAppWithTrivialMain.main(java.lang.String[]) @bci=1, line=33, pc=..., Method*=0x0000008041000208 ...
// This is testing the PointerFinder support for C++ MetaData types.
parts = jStackOutput.split("LingeredAppWithTrivialMain.main");
parts = parts[1].split("Method\\*=");
parts = parts[1].split(" ");
String methodAddr = parts[0];
cmdStr = "findpc " + methodAddr;
cmds = List.of(cmdStr);
expStrMap = new HashMap<>();
expStrMap.put(cmdStr, List.of("Method ",
"LingeredAppWithTrivialMain.main",
methodAddr));
runTest(withCore, cmds, expStrMap);
// Run findpc on a JavaThread*. We can find one in the jstack output.
// The tid for a thread is it's JavaThread*. For example:
// "main" #1 prio=5 tid=0x00000080263398f0 nid=0x277e0 ...
// This is testing the PointerFinder support for all C++ types other than MetaData types.
parts = jStackOutput.split("tid=");
parts = parts[1].split(" ");
String tid = parts[0]; // address of the JavaThread
cmdStr = "findpc " + tid;
cmds = List.of(cmdStr);
expStrMap = new HashMap<>();
expStrMap.put(cmdStr, List.of("Is of type JavaThread"));
runTest(withCore, cmds, expStrMap);
// Run findpc on a java stack address. We can find one in the jstack output.
// "main" #1 prio=5 tid=... nid=0x277e0 waiting on condition [0x0000008025aef000]
// The stack address is the last word between the brackets.
// This is testing the PointerFinder support for thread stack addresses.
parts = jStackOutput.split("tid=");
parts = parts[1].split(" \\[");
parts = parts[1].split("\\]");
String stackAddress = parts[0]; // address of the thread's stack
cmdStr = "findpc " + stackAddress;
cmds = List.of(cmdStr);
expStrMap = new HashMap<>();
expStrMap.put(cmdStr, List.of("In java stack"));
runTest(withCore, cmds, expStrMap);
// Run 'examine <addr>' using a thread's tid as the address. The
// examine output will be the of the form:
// <tid>: <value>
// Where <value> is the word stored at <tid>. <value> also happens to
// be the vtable address. We then run findpc on this vtable address.
// This tests PointerFinder support for native C++ symbols.
cmds = List.of("examine " + tid);
String examineOutput = runTest(withCore, cmds, null);
// Extract <value>.
parts = examineOutput.split(tid + ": ");
String value = parts[1];
// Use findpc on <value>. The output should look something like:
// Address 0x00007fed86f610b8: vtable for JavaThread + 0x10
cmdStr = "findpc " + value;
cmds = List.of(cmdStr);
expStrMap = new HashMap<>();
if (Platform.isWindows()) {
expStrMap.put(cmdStr, List.of("jvm.+JavaThread"));
} else if (Platform.isOSX()) {
if (withCore) {
expStrMap.put(cmdStr, List.of("__ZTV10JavaThread"));
} else { // symbol lookups not supported with OSX live process
expStrMap.put(cmdStr, List.of("In unknown location"));
}
} else {
test.run(theApp.getPid(), cmds, expStrMap, null);
expStrMap.put(cmdStr, List.of("vtable for JavaThread"));
}
runTest(withCore, cmds, expStrMap);
} catch (SkippedException se) {
throw se;
} catch (Exception ex) {
@ -160,6 +247,16 @@ public class ClhsdbFindPC {
}
}
private static String runTest(boolean withCore, List<String> cmds, Map<String, List<String>> expStrMap)
throws Exception
{
if (withCore) {
return test.runOnCore(coreFileName, cmds, expStrMap, null);
} else {
return test.run(theApp.getPid(), cmds, expStrMap, null);
}
}
public static void main(String[] args) throws Exception {
boolean withXcomp = Boolean.parseBoolean(args[0]);
boolean withCore = Boolean.parseBoolean(args[1]);