diff -r 4ebc2e2fb97c -r 71c04702a3d5 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java Tue Sep 12 19:03:39 2017 +0200 @@ -0,0 +1,510 @@ +/* + * Copyright (c) 2000, 2017, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.io.*; +import java.util.*; +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.oops.*; +import sun.jvm.hotspot.types.*; +import sun.jvm.hotspot.utilities.*; + +/** This is an abstract class because there are certain OS- and + CPU-specific operations (like the setting and getting of the last + Java frame pointer) which need to be factored out. These + operations are implemented by, for example, + SolarisSPARCJavaThread, and the concrete subclasses are + instantiated by the JavaThreadFactory in the Threads class. */ + +public class JavaThread extends Thread { + private static final boolean DEBUG = System.getProperty("sun.jvm.hotspot.runtime.JavaThread.DEBUG") != null; + + private static AddressField nextField; + private static sun.jvm.hotspot.types.OopField threadObjField; + private static AddressField anchorField; + private static AddressField lastJavaSPField; + private static AddressField lastJavaPCField; + private static CIntegerField threadStateField; + private static AddressField osThreadField; + private static AddressField stackBaseField; + private static CIntegerField stackSizeField; + private static CIntegerField terminatedField; + + private static JavaThreadPDAccess access; + + // JavaThreadStates read from underlying process + private static int UNINITIALIZED; + private static int NEW; + private static int NEW_TRANS; + private static int IN_NATIVE; + private static int IN_NATIVE_TRANS; + private static int IN_VM; + private static int IN_VM_TRANS; + private static int IN_JAVA; + private static int IN_JAVA_TRANS; + private static int BLOCKED; + private static int BLOCKED_TRANS; + + private static int NOT_TERMINATED; + private static int EXITING; + + private static final String ADDRESS_FORMAT = VM.getVM().isLP64() ? "0x%016x" : "0x%08x"; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + private static synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("JavaThread"); + Type anchorType = db.lookupType("JavaFrameAnchor"); + + nextField = type.getAddressField("_next"); + threadObjField = type.getOopField("_threadObj"); + anchorField = type.getAddressField("_anchor"); + lastJavaSPField = anchorType.getAddressField("_last_Java_sp"); + lastJavaPCField = anchorType.getAddressField("_last_Java_pc"); + threadStateField = type.getCIntegerField("_thread_state"); + osThreadField = type.getAddressField("_osthread"); + stackBaseField = type.getAddressField("_stack_base"); + stackSizeField = type.getCIntegerField("_stack_size"); + terminatedField = type.getCIntegerField("_terminated"); + + UNINITIALIZED = db.lookupIntConstant("_thread_uninitialized").intValue(); + NEW = db.lookupIntConstant("_thread_new").intValue(); + NEW_TRANS = db.lookupIntConstant("_thread_new_trans").intValue(); + IN_NATIVE = db.lookupIntConstant("_thread_in_native").intValue(); + IN_NATIVE_TRANS = db.lookupIntConstant("_thread_in_native_trans").intValue(); + IN_VM = db.lookupIntConstant("_thread_in_vm").intValue(); + IN_VM_TRANS = db.lookupIntConstant("_thread_in_vm_trans").intValue(); + IN_JAVA = db.lookupIntConstant("_thread_in_Java").intValue(); + IN_JAVA_TRANS = db.lookupIntConstant("_thread_in_Java_trans").intValue(); + BLOCKED = db.lookupIntConstant("_thread_blocked").intValue(); + BLOCKED_TRANS = db.lookupIntConstant("_thread_blocked_trans").intValue(); + + NOT_TERMINATED = db.lookupIntConstant("JavaThread::_not_terminated").intValue(); + EXITING = db.lookupIntConstant("JavaThread::_thread_exiting").intValue(); + + } + + public JavaThread(Address addr) { + super(addr); + } + + void setThreadPDAccess(JavaThreadPDAccess access) { + this.access = access; + } + + public JavaThread next() { + Address threadAddr = nextField.getValue(addr); + if (threadAddr == null) { + return null; + } + + return VM.getVM().getThreads().createJavaThreadWrapper(threadAddr); + } + + /** NOTE: for convenience, this differs in definition from the underlying VM. + Only "pure" JavaThreads return true; CompilerThreads, the CodeCacheSweeperThread, + JVMDIDebuggerThreads return false. + FIXME: + consider encapsulating platform-specific functionality in an + object instead of using inheritance (which is the primary reason + we can't traverse CompilerThreads, etc; didn't want to have, for + example, "SolarisSPARCCompilerThread".) */ + public boolean isJavaThread() { return true; } + + public boolean isExiting () { + return (getTerminated() == EXITING) || isTerminated(); + } + + public boolean isTerminated() { + return (getTerminated() != NOT_TERMINATED) && (getTerminated() != EXITING); + } + + public static AddressField getAnchorField() { return anchorField; } + + /** Get the last Java stack pointer */ + public Address getLastJavaSP() { + Address sp = lastJavaSPField.getValue(addr.addOffsetTo(anchorField.getOffset())); + return sp; + } + + public Address getLastJavaPC() { + Address pc = lastJavaPCField.getValue(addr.addOffsetTo(anchorField.getOffset())); + return pc; + } + + /** Abstract accessor to last Java frame pointer, implemented by + OS/CPU-specific JavaThread implementation. May return null if + there is no frame pointer or if it is not necessary on this + platform. */ + public Address getLastJavaFP(){ + return access.getLastJavaFP(addr); + } + + /** Abstract accessor to last Java pc, implemented by + OS/CPU-specific JavaThread implementation. May return null if + there is no frame pointer or if it is not necessary on this + platform. */ + + /* + public Address getLastJavaPC(){ + return access.getLastJavaPC(addr); + } + */ + + // FIXME: not yet implementable + // public abstract void setLastJavaFP(Address fp); + + /** A stack pointer older than any java frame stack pointer. Only + needed on some platforms; for example, see + thread_solaris_sparc.hpp. */ + public Address getBaseOfStackPointer(){ + return access.getBaseOfStackPointer(addr); + } + // FIXME: not yet implementable + // public abstract void setBaseOfStackPointer(Address fp); + + /** Tells whether the last Java frame is set */ + public boolean hasLastJavaFrame() { + return (getLastJavaSP() != null); + } + + /** Accessing frames */ + public Frame getLastFrame() { + // FIXME: would need to implement runtime routine + // "cacheStatePD(boolean)" for reflective system to be able to + // flush register windows on SPARC + return cookLastFrame(getLastFramePD()); + } + + /** Internal routine implemented by platform-dependent subclasses */ + protected Frame getLastFramePD(){ + return access.getLastFramePD(this, addr); + } + + /** Accessing frames. Returns the last Java VFrame or null if none + was present. (NOTE that this is mostly unusable in a debugging + system; see getLastJavaVFrameDbg, below, which provides very + different functionality.) */ + public JavaVFrame getLastJavaVFrame(RegisterMap regMap) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(regMap != null, "a map must be given"); + } + Frame f = getLastFrame(); + if (f == null) { + return null; + } + for (VFrame vf = VFrame.newVFrame(f, regMap, this); vf != null; vf = vf.sender()) { + if (vf.isJavaFrame()) { + return (JavaVFrame) vf; + } + } + return null; + } + + /** This should only be used by a debugger. Uses the current frame + guess to attempt to get the topmost JavaVFrame. + (getLastJavaVFrame, as a port of the VM's routine, assumes the + VM is at a safepoint.) */ + public JavaVFrame getLastJavaVFrameDbg() { + RegisterMap regMap = newRegisterMap(true); + sun.jvm.hotspot.runtime.Frame f = getCurrentFrameGuess(); + if (f == null) return null; + boolean imprecise = true; + if (f.isInterpretedFrame() && !f.isInterpretedFrameValid()) { + if (DEBUG) { + System.out.println("Correcting for invalid interpreter frame"); + } + f = f.sender(regMap); + imprecise = false; + } + VFrame vf = VFrame.newVFrame(f, regMap, this, true, imprecise); + if (vf == null) { + if (DEBUG) { + System.out.println(" (Unable to create vframe for topmost frame guess)"); + } + return null; + } + return vf.isJavaFrame() ? (JavaVFrame)vf : vf.javaSender(); + } + + /** In this system, a JavaThread is the top-level factory for a + RegisterMap, since the JavaThread implementation is already + platform-specific and RegisterMap is also necessarily + platform-specific. The updateMap argument indicates whether the + register map needs to be updated, for example during stack + traversal -- see frame.hpp. */ + public RegisterMap newRegisterMap(boolean updateMap){ + return access.newRegisterMap(this, updateMap); + } + + /** This is only designed to be used by the debugging system. + Returns a "best guess" of the topmost frame on the stack. This + guess should be as "raw" as possible. For example, if the + topmost frame is an interpreter frame (the return PC is in the + interpreter) but is not a valid frame (i.e., the BCI has not yet + been set up) this should still return the topmost frame and not + the sender. Validity checks are done at higher levels. */ + public Frame getCurrentFrameGuess(){ + return access.getCurrentFrameGuess(this, addr); + } + + /** Also only intended for use by the debugging system. Provides the + same effect of OSThread::print(); that is, prints a value which + allows the user to intuitively understand which native OS thread + maps to this Java thread. Does not print a newline or leading or + trailing spaces. */ + public void printThreadIDOn(PrintStream tty) { + access.printThreadIDOn(addr,tty); + } + + public void printThreadID() { + printThreadIDOn(System.out); + } + + public ThreadProxy getThreadProxy() { + return access.getThreadProxy(addr); + } + + // + // Safepoint support + // + + public JavaThreadState getThreadState() { + int val = (int) threadStateField.getValue(addr); + if (val == UNINITIALIZED) { + return JavaThreadState.UNINITIALIZED; + } else if (val == NEW) { + return JavaThreadState.NEW; + } else if (val == NEW_TRANS) { + return JavaThreadState.NEW_TRANS; + } else if (val == IN_NATIVE) { + return JavaThreadState.IN_NATIVE; + } else if (val == IN_NATIVE_TRANS) { + return JavaThreadState.IN_NATIVE_TRANS; + } else if (val == IN_VM) { + return JavaThreadState.IN_VM; + } else if (val == IN_VM_TRANS) { + return JavaThreadState.IN_VM_TRANS; + } else if (val == IN_JAVA) { + return JavaThreadState.IN_JAVA; + } else if (val == IN_JAVA_TRANS) { + return JavaThreadState.IN_JAVA_TRANS; + } else if (val == BLOCKED) { + return JavaThreadState.BLOCKED; + } else if (val == BLOCKED_TRANS) { + return JavaThreadState.BLOCKED_TRANS; + } else { + throw new RuntimeException("Illegal thread state " + val); + } + } + // FIXME: not yet implementable + // public void setThreadState(JavaThreadState s); + + // + // Miscellaneous operations + // + + public OSThread getOSThread() { + return (OSThread) VMObjectFactory.newObject(OSThread.class, osThreadField.getValue(addr)); + } + + public Address getStackBase() { + return stackBaseField.getValue(addr); + } + + public long getStackBaseValue() { + return VM.getVM().getAddressValue(getStackBase()); + } + + public long getStackSize() { + return stackSizeField.getValue(addr); + } + + public int getTerminated() { + return (int) terminatedField.getValue(addr); + } + + /** Gets the Java-side thread object for this JavaThread */ + public Oop getThreadObj() { + Oop obj = null; + try { + obj = VM.getVM().getObjectHeap().newOop(threadObjField.getValue(addr)); + } catch (Exception e) { + e.printStackTrace(); + } + return obj; + } + + /** Get the Java-side name of this thread */ + public String getThreadName() { + Oop threadObj = getThreadObj(); + if (threadObj == null) { + return ""; + } + return OopUtilities.threadOopGetName(threadObj); + } + + // + // Oop traversal + // + + public void oopsDo(AddressVisitor oopVisitor) { + super.oopsDo(oopVisitor); + + // FIXME: add in the rest of the routine from the VM + + // Traverse the execution stack + for(StackFrameStream fst = new StackFrameStream(this); !fst.isDone(); fst.next()) { + fst.getCurrent().oopsDo(oopVisitor, fst.getRegisterMap()); + } + } + + public boolean isInStack(Address a) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(VM.getVM().isDebugging(), "Not yet implemented for non-debugging system"); + } + Address sp = lastSPDbg(); + Address stackBase = getStackBase(); + // Be robust + if (sp == null) return false; + return stackBase.greaterThanOrEqual(a) && sp.lessThanOrEqual(a); + } + + public boolean isLockOwned(Address a) { + Address stackBase = getStackBase(); + Address stackLimit = stackBase.addOffsetTo(-getStackSize()); + + return stackBase.greaterThanOrEqual(a) && stackLimit.lessThanOrEqual(a); + + // FIXME: should traverse MonitorArray/MonitorChunks as in VM + } + + public Oop getCurrentParkBlocker() { + Oop threadObj = getThreadObj(); + if (threadObj != null) { + return OopUtilities.threadOopGetParkBlocker(threadObj); + } + return null; + } + + public void printInfoOn(PrintStream tty) { + + tty.println("State: " + getThreadState().toString()); + // Attempt to figure out the addresses covered by Java frames. + // NOTE: we should make this a method and let the Stackwalk panel use the result too. + // + sun.jvm.hotspot.runtime.Frame tmpFrame = getCurrentFrameGuess(); + if (tmpFrame != null ) { + Address sp = tmpFrame.getSP(); + Address maxSP = sp; + Address minSP = sp; + RegisterMap tmpMap = newRegisterMap(false); + while ((tmpFrame != null) && (!tmpFrame.isFirstFrame())) { + tmpFrame = tmpFrame.sender(tmpMap); + if (tmpFrame != null) { + sp = tmpFrame.getSP(); + maxSP = AddressOps.max(maxSP, sp); + minSP = AddressOps.min(minSP, sp); + } + } + tty.println("Stack in use by Java: " + minSP + " .. " + maxSP); + } else { + tty.println("No Java frames present"); + } + tty.println("Base of Stack: " + getStackBase()); + tty.println("Last_Java_SP: " + getLastJavaSP()); + tty.println("Last_Java_FP: " + getLastJavaFP()); + tty.println("Last_Java_PC: " + getLastJavaPC()); + // More stuff like saved_execption_pc, safepoint_state, ... + access.printInfoOn(addr, tty); + + } + + /////////////////////////////// + // // + // FIXME: add more accessors // + // // + /////////////////////////////// + + //-------------------------------------------------------------------------------- + // Internals only below this point + // + + private Frame cookLastFrame(Frame fr) { + if (fr == null) { + return null; + } + + Address pc = fr.getPC(); + + if (Assert.ASSERTS_ENABLED) { + if (pc == null) { + Assert.that(VM.getVM().isDebugging(), "must have PC"); + } + } + return fr; + } + + private Address lastSPDbg() { + return access.getLastSP(addr); + } + + + public void printThreadInfoOn(PrintStream out){ + Oop threadOop = this.getThreadObj(); + + out.print("\""); + out.print(this.getThreadName()); + out.print("\" #"); + out.print(OopUtilities.threadOopGetTID(threadOop)); + if(OopUtilities.threadOopGetDaemon(threadOop)){ + out.print(" daemon"); + } + out.print(" prio="); + out.print(OopUtilities.threadOopGetPriority(threadOop)); + out.print(" tid="); + out.print(this.getAddress()); + out.print(" nid="); + out.print(String.format("0x%x ",this.getOSThread().threadId())); + out.print(getOSThread().getThreadState().getPrintVal()); + out.print(" ["); + if(this.getLastJavaSP() == null){ + out.print(String.format(ADDRESS_FORMAT,0L)); + } else { + out.print(this.getLastJavaSP().andWithMask(~0xFFF)); + } + out.println("]"); + out.print(" java.lang.Thread.State: "); + out.println(OopUtilities.threadOopGetThreadStatusName(threadOop)); + out.print(" JavaThread state: _thread_"); + out.println(this.getThreadState().toString().toLowerCase()); + } +}