src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java
changeset 47216 71c04702a3d5
parent 46636 26b8de0359a0
child 54955 46409371a691
--- /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 "<null>";
+    }
+    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());
+  }
+}