Hi Chihiro,

It looks good to me.
However, I cannot be a sponsor because I cannot access JPRT. The change(s) for 
HotSpot should be pushed via JPRT.

Please find a sponsor for this change.
(Of course, you can list me as a reviewer (ysuenaga))


Thanks,

Yasumasa


On 2017/07/05 2:17, chihiro ito wrote:
Hi Yasumasa,

Thank you for reviewing.

I modified these following source code that you commented.
+ out.print(String.format("0x%016x",this.getAddress().asLongValue()));
+    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("0x%016x",0L));

Based on your advice, I have modified as following. Could you possibly review 
for this code ?

+  private static final String  ADDRESS_FORMAT = VM.getVM().isLP64() ? "0x%016x" : 
"0x%08x";

+    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));

Regards,
Chihiro


hg diff -g is following.

diff --git 
a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/OopUtilities.java 
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/OopUtilities.java
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/OopUtilities.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/OopUtilities.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -59,20 +59,20 @@
   // parkBlocker field is new since 1.6
   private static OopField threadParkBlockerField;

+  private static IntField threadPriorityField;
+  private static BooleanField threadDaemonField;
+
   // possible values of java_lang_Thread::ThreadStatus
   private static int THREAD_STATUS_NEW;
-  /*
-    Other enum constants are not needed as of now. Uncomment these as and when 
needed.

-    private static int THREAD_STATUS_RUNNABLE;
-    private static int THREAD_STATUS_SLEEPING;
-    private static int THREAD_STATUS_IN_OBJECT_WAIT;
-    private static int THREAD_STATUS_IN_OBJECT_WAIT_TIMED;
-    private static int THREAD_STATUS_PARKED;
-    private static int THREAD_STATUS_PARKED_TIMED;
-    private static int THREAD_STATUS_BLOCKED_ON_MONITOR_ENTER;
-    private static int THREAD_STATUS_TERMINATED;
-  */
+  private static int THREAD_STATUS_RUNNABLE;
+  private static int THREAD_STATUS_SLEEPING;
+  private static int THREAD_STATUS_IN_OBJECT_WAIT;
+  private static int THREAD_STATUS_IN_OBJECT_WAIT_TIMED;
+  private static int THREAD_STATUS_PARKED;
+  private static int THREAD_STATUS_PARKED_TIMED;
+  private static int THREAD_STATUS_BLOCKED_ON_MONITOR_ENTER;
+  private static int THREAD_STATUS_TERMINATED;

   // java.util.concurrent.locks.AbstractOwnableSynchronizer fields
   private static OopField absOwnSyncOwnerThreadField;
@@ -229,20 +229,19 @@
       threadStatusField = (IntField) k.findField("threadStatus", "I");
       threadParkBlockerField = (OopField) k.findField("parkBlocker",
                                      "Ljava/lang/Object;");
+      threadPriorityField = (IntField) k.findField("priority", "I");
+      threadDaemonField = (BooleanField) k.findField("daemon", "Z");
       TypeDataBase db = VM.getVM().getTypeDataBase();
       THREAD_STATUS_NEW = 
db.lookupIntConstant("java_lang_Thread::NEW").intValue();
-      /*
-        Other enum constants are not needed as of now. Uncomment these as and 
when needed.

-        THREAD_STATUS_RUNNABLE = 
db.lookupIntConstant("java_lang_Thread::RUNNABLE").intValue();
-        THREAD_STATUS_SLEEPING = 
db.lookupIntConstant("java_lang_Thread::SLEEPING").intValue();
-        THREAD_STATUS_IN_OBJECT_WAIT = 
db.lookupIntConstant("java_lang_Thread::IN_OBJECT_WAIT").intValue();
-        THREAD_STATUS_IN_OBJECT_WAIT_TIMED = 
db.lookupIntConstant("java_lang_Thread::IN_OBJECT_WAIT_TIMED").intValue();
-        THREAD_STATUS_PARKED = 
db.lookupIntConstant("java_lang_Thread::PARKED").intValue();
-        THREAD_STATUS_PARKED_TIMED = 
db.lookupIntConstant("java_lang_Thread::PARKED_TIMED").intValue();
-        THREAD_STATUS_BLOCKED_ON_MONITOR_ENTER = 
db.lookupIntConstant("java_lang_Thread::BLOCKED_ON_MONITOR_ENTER").intValue();
-        THREAD_STATUS_TERMINATED = 
db.lookupIntConstant("java_lang_Thread::TERMINATED").intValue();
-      */
+      THREAD_STATUS_RUNNABLE = 
db.lookupIntConstant("java_lang_Thread::RUNNABLE").intValue();
+      THREAD_STATUS_SLEEPING = 
db.lookupIntConstant("java_lang_Thread::SLEEPING").intValue();
+      THREAD_STATUS_IN_OBJECT_WAIT = 
db.lookupIntConstant("java_lang_Thread::IN_OBJECT_WAIT").intValue();
+      THREAD_STATUS_IN_OBJECT_WAIT_TIMED = 
db.lookupIntConstant("java_lang_Thread::IN_OBJECT_WAIT_TIMED").intValue();
+      THREAD_STATUS_PARKED = 
db.lookupIntConstant("java_lang_Thread::PARKED").intValue();
+      THREAD_STATUS_PARKED_TIMED = 
db.lookupIntConstant("java_lang_Thread::PARKED_TIMED").intValue();
+      THREAD_STATUS_BLOCKED_ON_MONITOR_ENTER = 
db.lookupIntConstant("java_lang_Thread::BLOCKED_ON_MONITOR_ENTER").intValue();
+      THREAD_STATUS_TERMINATED = 
db.lookupIntConstant("java_lang_Thread::TERMINATED").intValue();

       if (Assert.ASSERTS_ENABLED) {
         // it is okay to miss threadStatusField, because this was
@@ -331,4 +330,46 @@
       return absOwnSyncOwnerThreadField.getValue(oop);
     }
   }
+
+  public static int threadOopGetPriority(Oop threadOop) {
+    initThreadFields();
+    if (threadPriorityField != null) {
+      return threadPriorityField.getValue(threadOop);
+    } else {
+      return 0;
+    }
+  }
+
+  public static boolean threadOopGetDaemon(Oop threadOop) {
+    initThreadFields();
+    if (threadDaemonField != null) {
+      return threadDaemonField.getValue(threadOop);
+    } else {
+      return false;
+    }
+  }
+
+  public static String threadOopGetThreadStatusName(Oop threadOop) {
+    int status = OopUtilities.threadOopGetThreadStatus(threadOop);
+    if( status == THREAD_STATUS_NEW ){
+      return "NEW";
+    }else if(status == THREAD_STATUS_RUNNABLE ){
+      return "RUNNABLE";
+    }else if(status == THREAD_STATUS_SLEEPING ){
+      return "TIMED_WAITING (sleeping)";
+    }else if(status == THREAD_STATUS_IN_OBJECT_WAIT ){
+      return "WAITING (on object monitor)";
+    }else if(status == THREAD_STATUS_IN_OBJECT_WAIT_TIMED ){
+      return "TIMED_WAITING (on object monitor)";
+    }else if(status == THREAD_STATUS_PARKED ){
+      return "WAITING (parking)";
+    }else if(status == THREAD_STATUS_PARKED_TIMED ){
+      return "TIMED_WAITING (parking)";
+    }else if(status == THREAD_STATUS_BLOCKED_ON_MONITOR_ENTER ){
+      return "BLOCKED (on object monitor)";
+    }else if(status == THREAD_STATUS_TERMINATED ){
+      return "TERMINATED";
+    }
+    return "UNKNOWN";
+  }
 }
diff --git 
a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java 
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java
--- 
a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java
+++ 
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java
@@ -70,6 +70,8 @@
   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) {
@@ -475,4 +477,35 @@
     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());
+  }
 }
diff --git 
a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/OSThread.java 
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/OSThread.java
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/OSThread.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/OSThread.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 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
@@ -33,6 +33,19 @@
 public class OSThread extends VMObject {
     private static JIntField interruptedField;
     private static Field threadIdField;
+    private static CIntegerField threadStateField;
+
+    // ThreadStates read from underlying process
+    private static int ALLOCATED;
+    private static int INITIALIZED;
+    private static int RUNNABLE;
+    private static int MONITOR_WAIT;
+    private static int CONDVAR_WAIT;
+    private static int OBJECT_WAIT;
+    private static int BREAKPOINTED;
+    private static int SLEEPING;
+    private static int ZOMBIE;
+
     static {
         VM.registerVMInitializedObserver(new Observer() {
             public void update(Observable o, Object data) {
@@ -45,6 +58,17 @@
         Type type = db.lookupType("OSThread");
         interruptedField = type.getJIntField("_interrupted");
         threadIdField = type.getField("_thread_id");
+        threadStateField = type.getCIntegerField("_state");
+
+        ALLOCATED = db.lookupIntConstant("ALLOCATED").intValue();
+        INITIALIZED = db.lookupIntConstant("INITIALIZED").intValue();
+        RUNNABLE = db.lookupIntConstant("RUNNABLE").intValue();
+        MONITOR_WAIT = db.lookupIntConstant("MONITOR_WAIT").intValue();
+        CONDVAR_WAIT = db.lookupIntConstant("CONDVAR_WAIT").intValue();
+        OBJECT_WAIT = db.lookupIntConstant("OBJECT_WAIT").intValue();
+        BREAKPOINTED = db.lookupIntConstant("BREAKPOINTED").intValue();
+        SLEEPING = db.lookupIntConstant("SLEEPING").intValue();
+        ZOMBIE = db.lookupIntConstant("ZOMBIE").intValue();
     }

     public OSThread(Address addr) {
@@ -59,4 +83,28 @@
         return threadIdField.getJInt(addr);
     }

+    public ThreadState getThreadState() {
+        int val = (int)threadStateField.getValue(addr);
+        if (val ==  ALLOCATED) {
+            return ThreadState.ALLOCATED;
+        } else if (val ==  INITIALIZED) {
+            return ThreadState.INITIALIZED;
+        } else if (val ==  RUNNABLE) {
+            return ThreadState.RUNNABLE;
+        } else if (val ==  MONITOR_WAIT) {
+            return ThreadState.MONITOR_WAIT;
+        } else if (val ==  CONDVAR_WAIT) {
+            return ThreadState.CONDVAR_WAIT;
+        } else if (val ==  OBJECT_WAIT) {
+            return ThreadState.OBJECT_WAIT;
+        } else if (val ==  BREAKPOINTED) {
+            return ThreadState.BREAKPOINTED;
+        } else if (val ==  SLEEPING) {
+            return ThreadState.SLEEPING;
+        } else if (val ==  ZOMBIE) {
+            return ThreadState.ZOMBIE;
+        } else {
+            throw new RuntimeException("Illegal thread state " + val);
+        }
+    }
 }
diff --git 
a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ThreadState.java 
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ThreadState.java
new file mode 100644
--- /dev/null
+++ 
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ThreadState.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 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;
+
+/** This is a type-safe enum mirroring the ThreadState enum in
+ osThread.hpp. The conversion between the underlying ints
+ and these values is done in OSThread. */
+
+public class ThreadState {
+
+    private String printVal;
+
+    /** Memory has been allocated but not initialized */
+    public static final ThreadState ALLOCATED = new ThreadState("allocated");
+    /** The thread has been initialized but yet started */
+    public static final ThreadState INITIALIZED = new 
ThreadState("initialized");
+    /** Has been started and is runnable, but not necessarily running */
+    public static final ThreadState RUNNABLE = new ThreadState("runnable");
+    /** Waiting on a contended monitor lock */
+    public static final ThreadState MONITOR_WAIT = new ThreadState("waiting for 
monitor entry");
+    /** Waiting on a condition variable */
+    public static final ThreadState CONDVAR_WAIT = new ThreadState("waiting on 
condition");
+    /** Waiting on an Object.wait() call */
+    public static final ThreadState OBJECT_WAIT = new ThreadState("in 
Object.wait()");
+    /** Suspended at breakpoint */
+    public static final ThreadState BREAKPOINTED = new ThreadState("at 
breakpoint");
+    /** Thread.sleep() */
+    public static final ThreadState SLEEPING = new ThreadState("sleeping");
+    /** All done, but not reclaimed yet */
+    public static final ThreadState ZOMBIE = new ThreadState("zombie");
+
+    private ThreadState(String printVal){
+        this.printVal = printVal;
+    }
+
+    public String getPrintVal() {
+        return printVal;
+    }
+}
diff --git 
a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java 
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 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
@@ -88,6 +88,10 @@
                out.print("----------------- ");
                out.print(th);
                out.println(" -----------------");
+               JavaThread jthread = (JavaThread) proxyToThread.get(th);
+               if (jthread != null) {
+                  jthread.printThreadInfoOn(out);
+               }
                while (f != null) {
                   ClosestSymbol sym = f.closestSymbolToPC();
                   Address pc = f.pc();
diff --git 
a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java 
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 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
@@ -74,14 +74,7 @@
             int i = 1;
             for (JavaThread cur = threads.first(); cur != null; cur = 
cur.next(), i++) {
                 if (cur.isJavaThread()) {
-                    Address sp = cur.getLastJavaSP();
-                    tty.print("Thread ");
-                    cur.printThreadIDOn(tty);
-                    tty.print(": (state = " + cur.getThreadState());
-                    if (verbose) {
-                        tty.println(", current Java SP = " + sp);
-                    }
-                    tty.println(')');
+                    cur.printThreadInfoOn(tty);
                     try {
                         for (JavaVFrame vf = cur.getLastJavaVFrameDbg(); vf != 
null; vf = vf.javaSender()) {
                             Method method = vf.getMethod();
diff --git a/src/share/vm/runtime/vmStructs.cpp 
b/src/share/vm/runtime/vmStructs.cpp
--- a/src/share/vm/runtime/vmStructs.cpp
+++ b/src/share/vm/runtime/vmStructs.cpp
@@ -981,6 +981,7 @@
/************/ \
\
   volatile_nonstatic_field(OSThread, _interrupted, jint)                       
           \
+  volatile_nonstatic_field(OSThread, _state, ThreadState) \
\
/************************/ \
   /* OopMap and OopMapSet */ \
@@ -2186,6 +2187,7 @@
declare_integer_type(Generation::Name) \
declare_integer_type(InstanceKlass::ClassState) \
declare_integer_type(JavaThreadState) \
+ declare_integer_type(ThreadState) \
declare_integer_type(Location::Type) \
declare_integer_type(Location::Where) \
declare_integer_type(Flag::Flags) \
@@ -2443,6 +2445,20 @@
declare_constant(JavaThread::_not_terminated) \
declare_constant(JavaThread::_thread_exiting) \
\
+ /*******************/ \
+  /* JavaThreadState */                                                   \
+ /*******************/ \
+ \
+ declare_constant(ALLOCATED) \
+ declare_constant(INITIALIZED) \
+ declare_constant(RUNNABLE) \
+ declare_constant(MONITOR_WAIT) \
+ declare_constant(CONDVAR_WAIT) \
+ declare_constant(OBJECT_WAIT) \
+ declare_constant(BREAKPOINTED) \
+ declare_constant(SLEEPING) \
+ declare_constant(ZOMBIE) \
+ \
/******************************/ \
   /* Klass misc. enum constants */                                        \
/******************************/ \
diff --git a/test/serviceability/sa/JhsdbThreadInfoTest.java 
b/test/serviceability/sa/JhsdbThreadInfoTest.java
new file mode 100644
--- /dev/null
+++ b/test/serviceability/sa/JhsdbThreadInfoTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 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.
+ */
+
+import jdk.test.lib.apps.LingeredApp;
+import jdk.test.lib.JDKToolLauncher;
+import jdk.test.lib.Platform;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.Utils;
+
+/*
+ * @test
+ * @library /test/lib
+ * @run main JhsdbThreadInfoTest
+ */
+public class JhsdbThreadInfoTest {
+
+    public static void main(String[] args) throws Exception {
+
+        if (!Platform.shouldSAAttach()) {
+            System.out.println("SA attach not expected to work - test 
skipped.");
+            return;
+        }
+
+        LingeredApp app = null;
+
+        try {
+            app = LingeredApp.startApp(Utils.getVmOptions());
+            System.out.println("Started LingeredApp with pid " + app.getPid());
+
+            JDKToolLauncher jhsdbLauncher = 
JDKToolLauncher.createUsingTestJDK("jhsdb");
+
+            jhsdbLauncher.addToolArg("jstack");
+            jhsdbLauncher.addToolArg("--pid");
+            jhsdbLauncher.addToolArg(Long.toString(app.getPid()));
+
+            ProcessBuilder pb = new ProcessBuilder();
+            pb.command(jhsdbLauncher.getCommand());
+            Process jhsdb = pb.start();
+
+            jhsdb.waitFor();
+
+            OutputAnalyzer out = new OutputAnalyzer(jhsdb);
+
+            System.out.println(out.getStdout());
+            System.err.println(out.getStderr());
+
+            out.shouldMatch("\".+\" #\\d+ daemon prio=\\d+ tid=0x[0-9a-f]+ 
nid=0x[0-9a-f]+ .+ \\[0x[0-9a-f]+]");
+            out.shouldMatch("\"main\" #\\d+ prio=\\d+ tid=0x[0-9a-f]+ 
nid=0x[0-9a-f]+ .+ \\[0x[0-9a-f]+]");
+            out.shouldMatch("   java.lang.Thread.State: .+");
+            out.shouldMatch("   JavaThread state: _thread_.+");
+
+            out.shouldNotContain("   java.lang.Thread.State: UNKNOWN");
+            out.stderrShouldBeEmpty();
+
+            System.out.println("Test Completed");
+
+
+        } catch (InterruptedException ie) {
+            throw new Error("Problem awaiting the child process: " + ie, ie);
+        } catch (Exception attachE) {
+            throw new Error("Couldn't start jhsdb, attach to LingeredApp or match 
ThreadName: " + attachE);
+
+        } finally {
+            LingeredApp.stopApp(app);
+        }
+    }
+}




On 2017/07/04 23:44, Yasumasa Suenaga wrote:
Hi Chihiro,

Thank you for updating your patch!
I have a comment to printThreadInfoOn() in JavaThread.java:

+ out.print(String.format("0x%016x",this.getAddress().asLongValue()));
+    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("0x%016x",0L));

You set "0x%016x" to format string for address value.
However, this length is changed by pointer length e.g. "0x%08x" should be set 
on ILP32 platforms.

In case of Linux, Address#toString() generates platform-aware string value in 
DebuggerUtilities#addressValueToString(). So you can use Address#toString() if 
Address is not null.


Yasumasa


On 2017/07/04 0:32, chihiro ito wrote:
Hi Yasumasa,

Thank you for review, again.

According to JavaThread::print_on() in thread.cpp , stack address in thread 
dump shows top of page address.
If so, can we calculate it as below?

  this.getLastJavaSP().andWithMask(~0xFFF)

Yes, It is also correct. I modified source code to your simple one.

Are these regex correct?
Regex for SP "\\[0x[0-9a-f]+]" should be "\\[0x[0-9a-f]+\\]"
(Last backslash is missing.)

] that is not paired with [ does not need to be escaped.

following 2 case it print true.
System.out.println("]".matches("]"));
System.out.println("]".matches("\\]"));

The source code which modified the logical operation is as follows.

Regards,
Chihiro


diff --git 
a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/OopUtilities.java 
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/OopUtilities.java
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/OopUtilities.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/OopUtilities.java
@@ -1,5 +1,5 @@
  /*
- * Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -59,20 +59,20 @@
    // parkBlocker field is new since 1.6
    private static OopField threadParkBlockerField;

+  private static IntField threadPriorityField;
+  private static BooleanField threadDaemonField;
+
    // possible values of java_lang_Thread::ThreadStatus
    private static int THREAD_STATUS_NEW;
-  /*
-    Other enum constants are not needed as of now. Uncomment these as and when 
needed.

-    private static int THREAD_STATUS_RUNNABLE;
-    private static int THREAD_STATUS_SLEEPING;
-    private static int THREAD_STATUS_IN_OBJECT_WAIT;
-    private static int THREAD_STATUS_IN_OBJECT_WAIT_TIMED;
-    private static int THREAD_STATUS_PARKED;
-    private static int THREAD_STATUS_PARKED_TIMED;
-    private static int THREAD_STATUS_BLOCKED_ON_MONITOR_ENTER;
-    private static int THREAD_STATUS_TERMINATED;
-  */
+  private static int THREAD_STATUS_RUNNABLE;
+  private static int THREAD_STATUS_SLEEPING;
+  private static int THREAD_STATUS_IN_OBJECT_WAIT;
+  private static int THREAD_STATUS_IN_OBJECT_WAIT_TIMED;
+  private static int THREAD_STATUS_PARKED;
+  private static int THREAD_STATUS_PARKED_TIMED;
+  private static int THREAD_STATUS_BLOCKED_ON_MONITOR_ENTER;
+  private static int THREAD_STATUS_TERMINATED;

    // java.util.concurrent.locks.AbstractOwnableSynchronizer fields
    private static OopField absOwnSyncOwnerThreadField;
@@ -229,20 +229,19 @@
        threadStatusField = (IntField) k.findField("threadStatus", "I");
        threadParkBlockerField = (OopField) k.findField("parkBlocker",
                                       "Ljava/lang/Object;");
+      threadPriorityField = (IntField) k.findField("priority", "I");
+      threadDaemonField = (BooleanField) k.findField("daemon", "Z");
        TypeDataBase db = VM.getVM().getTypeDataBase();
        THREAD_STATUS_NEW = 
db.lookupIntConstant("java_lang_Thread::NEW").intValue();
-      /*
-        Other enum constants are not needed as of now. Uncomment these as and 
when needed.

-        THREAD_STATUS_RUNNABLE = 
db.lookupIntConstant("java_lang_Thread::RUNNABLE").intValue();
-        THREAD_STATUS_SLEEPING = 
db.lookupIntConstant("java_lang_Thread::SLEEPING").intValue();
-        THREAD_STATUS_IN_OBJECT_WAIT = 
db.lookupIntConstant("java_lang_Thread::IN_OBJECT_WAIT").intValue();
-        THREAD_STATUS_IN_OBJECT_WAIT_TIMED = 
db.lookupIntConstant("java_lang_Thread::IN_OBJECT_WAIT_TIMED").intValue();
-        THREAD_STATUS_PARKED = 
db.lookupIntConstant("java_lang_Thread::PARKED").intValue();
-        THREAD_STATUS_PARKED_TIMED = 
db.lookupIntConstant("java_lang_Thread::PARKED_TIMED").intValue();
-        THREAD_STATUS_BLOCKED_ON_MONITOR_ENTER = 
db.lookupIntConstant("java_lang_Thread::BLOCKED_ON_MONITOR_ENTER").intValue();
-        THREAD_STATUS_TERMINATED = 
db.lookupIntConstant("java_lang_Thread::TERMINATED").intValue();
-      */
+      THREAD_STATUS_RUNNABLE = 
db.lookupIntConstant("java_lang_Thread::RUNNABLE").intValue();
+      THREAD_STATUS_SLEEPING = 
db.lookupIntConstant("java_lang_Thread::SLEEPING").intValue();
+      THREAD_STATUS_IN_OBJECT_WAIT = 
db.lookupIntConstant("java_lang_Thread::IN_OBJECT_WAIT").intValue();
+      THREAD_STATUS_IN_OBJECT_WAIT_TIMED = 
db.lookupIntConstant("java_lang_Thread::IN_OBJECT_WAIT_TIMED").intValue();
+      THREAD_STATUS_PARKED = 
db.lookupIntConstant("java_lang_Thread::PARKED").intValue();
+      THREAD_STATUS_PARKED_TIMED = 
db.lookupIntConstant("java_lang_Thread::PARKED_TIMED").intValue();
+      THREAD_STATUS_BLOCKED_ON_MONITOR_ENTER = 
db.lookupIntConstant("java_lang_Thread::BLOCKED_ON_MONITOR_ENTER").intValue();
+      THREAD_STATUS_TERMINATED = 
db.lookupIntConstant("java_lang_Thread::TERMINATED").intValue();

        if (Assert.ASSERTS_ENABLED) {
          // it is okay to miss threadStatusField, because this was
@@ -331,4 +330,46 @@
        return absOwnSyncOwnerThreadField.getValue(oop);
      }
    }
+
+  public static int threadOopGetPriority(Oop threadOop) {
+    initThreadFields();
+    if (threadPriorityField != null) {
+      return threadPriorityField.getValue(threadOop);
+    } else {
+      return 0;
+    }
+  }
+
+  public static boolean threadOopGetDaemon(Oop threadOop) {
+    initThreadFields();
+    if (threadDaemonField != null) {
+      return threadDaemonField.getValue(threadOop);
+    } else {
+      return false;
+    }
+  }
+
+  public static String threadOopGetThreadStatusName(Oop threadOop) {
+    int status = OopUtilities.threadOopGetThreadStatus(threadOop);
+    if( status == THREAD_STATUS_NEW ){
+      return "NEW";
+    }else if(status == THREAD_STATUS_RUNNABLE ){
+      return "RUNNABLE";
+    }else if(status == THREAD_STATUS_SLEEPING ){
+      return "TIMED_WAITING (sleeping)";
+    }else if(status == THREAD_STATUS_IN_OBJECT_WAIT ){
+      return "WAITING (on object monitor)";
+    }else if(status == THREAD_STATUS_IN_OBJECT_WAIT_TIMED ){
+      return "TIMED_WAITING (on object monitor)";
+    }else if(status == THREAD_STATUS_PARKED ){
+      return "WAITING (parking)";
+    }else if(status == THREAD_STATUS_PARKED_TIMED ){
+      return "TIMED_WAITING (parking)";
+    }else if(status == THREAD_STATUS_BLOCKED_ON_MONITOR_ENTER ){
+      return "BLOCKED (on object monitor)";
+    }else if(status == THREAD_STATUS_TERMINATED ){
+      return "TERMINATED";
+    }
+    return "UNKNOWN";
+  }
  }
diff --git 
a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java 
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java
--- 
a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java
+++ 
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java
@@ -475,4 +475,35 @@
      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(String.format("0x%016x",this.getAddress().asLongValue()));
+    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("0x%016x",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());
+  }
  }
diff --git 
a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/OSThread.java 
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/OSThread.java
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/OSThread.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/OSThread.java
@@ -1,5 +1,5 @@
  /*
- * Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 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
@@ -33,6 +33,19 @@
  public class OSThread extends VMObject {
      private static JIntField interruptedField;
      private static Field threadIdField;
+    private static CIntegerField threadStateField;
+
+    // ThreadStates read from underlying process
+    private static int ALLOCATED;
+    private static int INITIALIZED;
+    private static int RUNNABLE;
+    private static int MONITOR_WAIT;
+    private static int CONDVAR_WAIT;
+    private static int OBJECT_WAIT;
+    private static int BREAKPOINTED;
+    private static int SLEEPING;
+    private static int ZOMBIE;
+
      static {
          VM.registerVMInitializedObserver(new Observer() {
              public void update(Observable o, Object data) {
@@ -45,6 +58,17 @@
          Type type = db.lookupType("OSThread");
          interruptedField = type.getJIntField("_interrupted");
          threadIdField = type.getField("_thread_id");
+        threadStateField = type.getCIntegerField("_state");
+
+        ALLOCATED = db.lookupIntConstant("ALLOCATED").intValue();
+        INITIALIZED = db.lookupIntConstant("INITIALIZED").intValue();
+        RUNNABLE = db.lookupIntConstant("RUNNABLE").intValue();
+        MONITOR_WAIT = db.lookupIntConstant("MONITOR_WAIT").intValue();
+        CONDVAR_WAIT = db.lookupIntConstant("CONDVAR_WAIT").intValue();
+        OBJECT_WAIT = db.lookupIntConstant("OBJECT_WAIT").intValue();
+        BREAKPOINTED = db.lookupIntConstant("BREAKPOINTED").intValue();
+        SLEEPING = db.lookupIntConstant("SLEEPING").intValue();
+        ZOMBIE = db.lookupIntConstant("ZOMBIE").intValue();
      }

      public OSThread(Address addr) {
@@ -59,4 +83,28 @@
          return threadIdField.getJInt(addr);
      }

+    public ThreadState getThreadState() {
+        int val = (int)threadStateField.getValue(addr);
+        if (val ==  ALLOCATED) {
+            return ThreadState.ALLOCATED;
+        } else if (val ==  INITIALIZED) {
+            return ThreadState.INITIALIZED;
+        } else if (val ==  RUNNABLE) {
+            return ThreadState.RUNNABLE;
+        } else if (val ==  MONITOR_WAIT) {
+            return ThreadState.MONITOR_WAIT;
+        } else if (val ==  CONDVAR_WAIT) {
+            return ThreadState.CONDVAR_WAIT;
+        } else if (val ==  OBJECT_WAIT) {
+            return ThreadState.OBJECT_WAIT;
+        } else if (val ==  BREAKPOINTED) {
+            return ThreadState.BREAKPOINTED;
+        } else if (val ==  SLEEPING) {
+            return ThreadState.SLEEPING;
+        } else if (val ==  ZOMBIE) {
+            return ThreadState.ZOMBIE;
+        } else {
+            throw new RuntimeException("Illegal thread state " + val);
+        }
+    }
  }
diff --git 
a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ThreadState.java 
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ThreadState.java
new file mode 100644
--- /dev/null
+++ 
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ThreadState.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 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;
+
+/** This is a type-safe enum mirroring the ThreadState enum in
+ osThread.hpp. The conversion between the underlying ints
+ and these values is done in OSThread. */
+
+public class ThreadState {
+
+    private String printVal;
+
+    /** Memory has been allocated but not initialized */
+    public static final ThreadState ALLOCATED = new ThreadState("allocated");
+    /** The thread has been initialized but yet started */
+    public static final ThreadState INITIALIZED = new 
ThreadState("initialized");
+    /** Has been started and is runnable, but not necessarily running */
+    public static final ThreadState RUNNABLE = new ThreadState("runnable");
+    /** Waiting on a contended monitor lock */
+    public static final ThreadState MONITOR_WAIT = new ThreadState("waiting for 
monitor entry");
+    /** Waiting on a condition variable */
+    public static final ThreadState CONDVAR_WAIT = new ThreadState("waiting on 
condition");
+    /** Waiting on an Object.wait() call */
+    public static final ThreadState OBJECT_WAIT = new ThreadState("in 
Object.wait()");
+    /** Suspended at breakpoint */
+    public static final ThreadState BREAKPOINTED = new ThreadState("at 
breakpoint");
+    /** Thread.sleep() */
+    public static final ThreadState SLEEPING = new ThreadState("sleeping");
+    /** All done, but not reclaimed yet */
+    public static final ThreadState ZOMBIE = new ThreadState("zombie");
+
+    private ThreadState(String printVal){
+        this.printVal = printVal;
+    }
+
+    public String getPrintVal() {
+        return printVal;
+    }
+}
diff --git 
a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java 
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java
@@ -1,5 +1,5 @@
  /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 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
@@ -88,6 +88,10 @@
                 out.print("----------------- ");
                 out.print(th);
                 out.println(" -----------------");
+               JavaThread jthread = (JavaThread) proxyToThread.get(th);
+               if (jthread != null) {
+                  jthread.printThreadInfoOn(out);
+               }
                 while (f != null) {
                    ClosestSymbol sym = f.closestSymbolToPC();
                    Address pc = f.pc();
diff --git 
a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java 
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java
@@ -1,5 +1,5 @@
  /*
- * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 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
@@ -74,14 +74,7 @@
              int i = 1;
              for (JavaThread cur = threads.first(); cur != null; cur = 
cur.next(), i++) {
                  if (cur.isJavaThread()) {
-                    Address sp = cur.getLastJavaSP();
-                    tty.print("Thread ");
-                    cur.printThreadIDOn(tty);
-                    tty.print(": (state = " + cur.getThreadState());
-                    if (verbose) {
-                        tty.println(", current Java SP = " + sp);
-                    }
-                    tty.println(')');
+                    cur.printThreadInfoOn(tty);
                      try {
                          for (JavaVFrame vf = cur.getLastJavaVFrameDbg(); vf 
!= null; vf = vf.javaSender()) {
                              Method method = vf.getMethod();
diff --git a/src/share/vm/runtime/vmStructs.cpp 
b/src/share/vm/runtime/vmStructs.cpp
--- a/src/share/vm/runtime/vmStructs.cpp
+++ b/src/share/vm/runtime/vmStructs.cpp
@@ -981,6 +981,7 @@
/************/ \
\
    volatile_nonstatic_field(OSThread, _interrupted, jint)                      
            \
+  volatile_nonstatic_field(OSThread, _state, ThreadState)                      
           \
\
/************************/ \
    /* OopMap and OopMapSet */ \
@@ -2186,6 +2187,7 @@
declare_integer_type(Generation::Name) \
declare_integer_type(InstanceKlass::ClassState) \
declare_integer_type(JavaThreadState) \
+ declare_integer_type(ThreadState) \
declare_integer_type(Location::Type) \
declare_integer_type(Location::Where) \
declare_integer_type(Flag::Flags) \
@@ -2443,6 +2445,20 @@
declare_constant(JavaThread::_not_terminated) \
declare_constant(JavaThread::_thread_exiting) \
\
+ /*******************/ \
+  /* JavaThreadState */                                                   \
+ /*******************/ \
+ \
+ declare_constant(ALLOCATED) \
+ declare_constant(INITIALIZED) \
+ declare_constant(RUNNABLE) \
+ declare_constant(MONITOR_WAIT) \
+ declare_constant(CONDVAR_WAIT) \
+ declare_constant(OBJECT_WAIT) \
+ declare_constant(BREAKPOINTED) \
+ declare_constant(SLEEPING) \
+ declare_constant(ZOMBIE) \
+ \
/******************************/ \
    /* Klass misc. enum constants */                                        \
/******************************/ \
diff --git a/test/serviceability/sa/JhsdbThreadInfoTest.java 
b/test/serviceability/sa/JhsdbThreadInfoTest.java
new file mode 100644
--- /dev/null
+++ b/test/serviceability/sa/JhsdbThreadInfoTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 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.
+ */
+
+import jdk.test.lib.apps.LingeredApp;
+import jdk.test.lib.JDKToolLauncher;
+import jdk.test.lib.Platform;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.Utils;
+
+/*
+ * @test
+ * @library /test/lib
+ * @run main JhsdbThreadInfoTest
+ */
+public class JhsdbThreadInfoTest {
+
+    public static void main(String[] args) throws Exception {
+
+        if (!Platform.shouldSAAttach()) {
+            System.out.println("SA attach not expected to work - test 
skipped.");
+            return;
+        }
+
+        LingeredApp app = null;
+
+        try {
+            app = LingeredApp.startApp(Utils.getVmOptions());
+            System.out.println("Started LingeredApp with pid " + app.getPid());
+
+            JDKToolLauncher jhsdbLauncher = 
JDKToolLauncher.createUsingTestJDK("jhsdb");
+
+            jhsdbLauncher.addToolArg("jstack");
+            jhsdbLauncher.addToolArg("--pid");
+ jhsdbLauncher.addToolArg(Long.toString(app.getPid()));
+
+            ProcessBuilder pb = new ProcessBuilder();
+            pb.command(jhsdbLauncher.getCommand());
+            Process jhsdb = pb.start();
+
+            jhsdb.waitFor();
+
+            OutputAnalyzer out = new OutputAnalyzer(jhsdb);
+
+            System.out.println(out.getStdout());
+            System.err.println(out.getStderr());
+
+            out.shouldMatch("\".+\" #\\d+ daemon prio=\\d+ tid=0x[0-9a-f]+ 
nid=0x[0-9a-f]+ .+ \\[0x[0-9a-f]+]");
+            out.shouldMatch("\"main\" #\\d+ prio=\\d+ tid=0x[0-9a-f]+ 
nid=0x[0-9a-f]+ .+ \\[0x[0-9a-f]+]");
+            out.shouldMatch("   java.lang.Thread.State: .+");
+            out.shouldMatch("   JavaThread state: _thread_.+");
+
+            out.shouldNotContain("   java.lang.Thread.State: UNKNOWN");
+            out.stderrShouldBeEmpty();
+
+            System.out.println("Test Completed");
+
+
+        } catch (InterruptedException ie) {
+            throw new Error("Problem awaiting the child process: " + ie, ie);
+        } catch (Exception attachE) {
+            throw new Error("Couldn't start jhsdb, attach to LingeredApp or match 
ThreadName: " + attachE);
+
+        } finally {
+            LingeredApp.stopApp(app);
+        }
+    }
+}




On 2017/07/02 21:44, Yasumasa Suenaga wrote:
Hi Chihiro,


printThreadInfoOn() in JavaThread.java:
+      Address maskAddress = this.getLastJavaSP().andWithMask(0xFFF);
+ out.print(this.getLastJavaSP().xorWithMask(maskAddress.asLongValue()));

IMHO it is complex a bit.
According to JavaThread::print_on() in thread.cpp , stack address in thread 
dump shows top of page address.
If so, can we calculate it as below?

  this.getLastJavaSP().andWithMask(~0xFFF)


JhsdbThreadInfoTest.java:
+            out.shouldMatch("\".+\" #\\d+ daemon prio=\\d+ tid=0x[0-9a-f]+ 
nid=0x[0-9a-f]+ .+ \\[0x[0-9a-f]+]");
+            out.shouldMatch("\"main\" #\\d+ prio=\\d+ tid=0x[0-9a-f]+ 
nid=0x[0-9a-f]+ .+ \\[0x[0-9a-f]+]");

Are these regex correct?
Regex for SP "\\[0x[0-9a-f]+]" should be "\\[0x[0-9a-f]+\\]"
(Last backslash is missing.)


Thanks,

Yasumasa


On 2017/07/02 16:43, chihiro ito wrote:
Hi Yasumasa,

Thank you for your review. I modified source code following your advice.
I applied this to latest source code and passed jtreg.

Could somebody possibly be sponsor and commit this as cito ?

Regards,
Chihiro

diff --git 
a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/OopUtilities.java 
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/OopUtilities.java
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/OopUtilities.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/OopUtilities.java
@@ -1,5 +1,5 @@
  /*
- * Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -59,20 +59,20 @@
    // parkBlocker field is new since 1.6
    private static OopField threadParkBlockerField;

+  private static IntField threadPriorityField;
+  private static BooleanField threadDaemonField;
+
    // possible values of java_lang_Thread::ThreadStatus
    private static int THREAD_STATUS_NEW;
-  /*
-    Other enum constants are not needed as of now. Uncomment these as and when 
needed.

-    private static int THREAD_STATUS_RUNNABLE;
-    private static int THREAD_STATUS_SLEEPING;
-    private static int THREAD_STATUS_IN_OBJECT_WAIT;
-    private static int THREAD_STATUS_IN_OBJECT_WAIT_TIMED;
-    private static int THREAD_STATUS_PARKED;
-    private static int THREAD_STATUS_PARKED_TIMED;
-    private static int THREAD_STATUS_BLOCKED_ON_MONITOR_ENTER;
-    private static int THREAD_STATUS_TERMINATED;
-  */
+  private static int THREAD_STATUS_RUNNABLE;
+  private static int THREAD_STATUS_SLEEPING;
+  private static int THREAD_STATUS_IN_OBJECT_WAIT;
+  private static int THREAD_STATUS_IN_OBJECT_WAIT_TIMED;
+  private static int THREAD_STATUS_PARKED;
+  private static int THREAD_STATUS_PARKED_TIMED;
+  private static int THREAD_STATUS_BLOCKED_ON_MONITOR_ENTER;
+  private static int THREAD_STATUS_TERMINATED;

    // java.util.concurrent.locks.AbstractOwnableSynchronizer fields
    private static OopField absOwnSyncOwnerThreadField;
@@ -229,20 +229,19 @@
        threadStatusField = (IntField) k.findField("threadStatus", "I");
        threadParkBlockerField = (OopField) k.findField("parkBlocker",
"Ljava/lang/Object;");
+      threadPriorityField = (IntField) k.findField("priority", "I");
+      threadDaemonField = (BooleanField) k.findField("daemon", "Z");
        TypeDataBase db = VM.getVM().getTypeDataBase();
        THREAD_STATUS_NEW = 
db.lookupIntConstant("java_lang_Thread::NEW").intValue();
-      /*
-        Other enum constants are not needed as of now. Uncomment these as and 
when needed.

-        THREAD_STATUS_RUNNABLE = 
db.lookupIntConstant("java_lang_Thread::RUNNABLE").intValue();
-        THREAD_STATUS_SLEEPING = 
db.lookupIntConstant("java_lang_Thread::SLEEPING").intValue();
-        THREAD_STATUS_IN_OBJECT_WAIT = 
db.lookupIntConstant("java_lang_Thread::IN_OBJECT_WAIT").intValue();
-        THREAD_STATUS_IN_OBJECT_WAIT_TIMED = 
db.lookupIntConstant("java_lang_Thread::IN_OBJECT_WAIT_TIMED").intValue();
-        THREAD_STATUS_PARKED = 
db.lookupIntConstant("java_lang_Thread::PARKED").intValue();
-        THREAD_STATUS_PARKED_TIMED = 
db.lookupIntConstant("java_lang_Thread::PARKED_TIMED").intValue();
-        THREAD_STATUS_BLOCKED_ON_MONITOR_ENTER = 
db.lookupIntConstant("java_lang_Thread::BLOCKED_ON_MONITOR_ENTER").intValue();
-        THREAD_STATUS_TERMINATED = 
db.lookupIntConstant("java_lang_Thread::TERMINATED").intValue();
-      */
+      THREAD_STATUS_RUNNABLE = 
db.lookupIntConstant("java_lang_Thread::RUNNABLE").intValue();
+      THREAD_STATUS_SLEEPING = 
db.lookupIntConstant("java_lang_Thread::SLEEPING").intValue();
+      THREAD_STATUS_IN_OBJECT_WAIT = 
db.lookupIntConstant("java_lang_Thread::IN_OBJECT_WAIT").intValue();
+      THREAD_STATUS_IN_OBJECT_WAIT_TIMED = 
db.lookupIntConstant("java_lang_Thread::IN_OBJECT_WAIT_TIMED").intValue();
+      THREAD_STATUS_PARKED = 
db.lookupIntConstant("java_lang_Thread::PARKED").intValue();
+      THREAD_STATUS_PARKED_TIMED = 
db.lookupIntConstant("java_lang_Thread::PARKED_TIMED").intValue();
+      THREAD_STATUS_BLOCKED_ON_MONITOR_ENTER = 
db.lookupIntConstant("java_lang_Thread::BLOCKED_ON_MONITOR_ENTER").intValue();
+      THREAD_STATUS_TERMINATED = 
db.lookupIntConstant("java_lang_Thread::TERMINATED").intValue();

        if (Assert.ASSERTS_ENABLED) {
          // it is okay to miss threadStatusField, because this was
@@ -331,4 +330,46 @@
        return absOwnSyncOwnerThreadField.getValue(oop);
      }
    }
+
+  public static int threadOopGetPriority(Oop threadOop) {
+    initThreadFields();
+    if (threadPriorityField != null) {
+      return threadPriorityField.getValue(threadOop);
+    } else {
+      return 0;
+    }
+  }
+
+  public static boolean threadOopGetDaemon(Oop threadOop) {
+    initThreadFields();
+    if (threadDaemonField != null) {
+      return threadDaemonField.getValue(threadOop);
+    } else {
+      return false;
+    }
+  }
+
+  public static String threadOopGetThreadStatusName(Oop threadOop) {
+    int status = OopUtilities.threadOopGetThreadStatus(threadOop);
+    if( status == THREAD_STATUS_NEW ){
+      return "NEW";
+    }else if(status == THREAD_STATUS_RUNNABLE ){
+      return "RUNNABLE";
+    }else if(status == THREAD_STATUS_SLEEPING ){
+      return "TIMED_WAITING (sleeping)";
+    }else if(status == THREAD_STATUS_IN_OBJECT_WAIT ){
+      return "WAITING (on object monitor)";
+    }else if(status == THREAD_STATUS_IN_OBJECT_WAIT_TIMED ){
+      return "TIMED_WAITING (on object monitor)";
+    }else if(status == THREAD_STATUS_PARKED ){
+      return "WAITING (parking)";
+    }else if(status == THREAD_STATUS_PARKED_TIMED ){
+      return "TIMED_WAITING (parking)";
+    }else if(status == THREAD_STATUS_BLOCKED_ON_MONITOR_ENTER ){
+      return "BLOCKED (on object monitor)";
+    }else if(status == THREAD_STATUS_TERMINATED ){
+      return "TERMINATED";
+    }
+    return "UNKNOWN";
+  }
  }
diff --git 
a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java 
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java
--- 
a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java
+++ 
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java
@@ -475,4 +475,36 @@
      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(String.format("0x%016x",this.getAddress().asLongValue()));
+    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("0x%016x",0L));
+    } else {
+      Address maskAddress = this.getLastJavaSP().andWithMask(0xFFF);
+ out.print(this.getLastJavaSP().xorWithMask(maskAddress.asLongValue()));
+    }
+    out.println("]");
+    out.print("   java.lang.Thread.State: ");
+ out.println(OopUtilities.threadOopGetThreadStatusName(threadOop));
+    out.print("   JavaThread state: _thread_");
+ out.println(this.getThreadState().toString().toLowerCase());
+  }
  }
diff --git 
a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/OSThread.java 
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/OSThread.java
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/OSThread.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/OSThread.java
@@ -1,5 +1,5 @@
  /*
- * Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 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
@@ -33,6 +33,19 @@
  public class OSThread extends VMObject {
      private static JIntField interruptedField;
      private static Field threadIdField;
+    private static CIntegerField threadStateField;
+
+    // ThreadStates read from underlying process
+    private static int ALLOCATED;
+    private static int INITIALIZED;
+    private static int RUNNABLE;
+    private static int MONITOR_WAIT;
+    private static int CONDVAR_WAIT;
+    private static int OBJECT_WAIT;
+    private static int BREAKPOINTED;
+    private static int SLEEPING;
+    private static int ZOMBIE;
+
      static {
          VM.registerVMInitializedObserver(new Observer() {
              public void update(Observable o, Object data) {
@@ -45,6 +58,17 @@
          Type type = db.lookupType("OSThread");
          interruptedField = type.getJIntField("_interrupted");
          threadIdField = type.getField("_thread_id");
+        threadStateField = type.getCIntegerField("_state");
+
+        ALLOCATED = db.lookupIntConstant("ALLOCATED").intValue();
+        INITIALIZED = db.lookupIntConstant("INITIALIZED").intValue();
+        RUNNABLE = db.lookupIntConstant("RUNNABLE").intValue();
+        MONITOR_WAIT = db.lookupIntConstant("MONITOR_WAIT").intValue();
+        CONDVAR_WAIT = db.lookupIntConstant("CONDVAR_WAIT").intValue();
+        OBJECT_WAIT = db.lookupIntConstant("OBJECT_WAIT").intValue();
+        BREAKPOINTED = db.lookupIntConstant("BREAKPOINTED").intValue();
+        SLEEPING = db.lookupIntConstant("SLEEPING").intValue();
+        ZOMBIE = db.lookupIntConstant("ZOMBIE").intValue();
      }

      public OSThread(Address addr) {
@@ -59,4 +83,28 @@
          return threadIdField.getJInt(addr);
      }

+    public ThreadState getThreadState() {
+        int val = (int)threadStateField.getValue(addr);
+        if (val ==  ALLOCATED) {
+            return ThreadState.ALLOCATED;
+        } else if (val ==  INITIALIZED) {
+            return ThreadState.INITIALIZED;
+        } else if (val ==  RUNNABLE) {
+            return ThreadState.RUNNABLE;
+        } else if (val ==  MONITOR_WAIT) {
+            return ThreadState.MONITOR_WAIT;
+        } else if (val ==  CONDVAR_WAIT) {
+            return ThreadState.CONDVAR_WAIT;
+        } else if (val ==  OBJECT_WAIT) {
+            return ThreadState.OBJECT_WAIT;
+        } else if (val ==  BREAKPOINTED) {
+            return ThreadState.BREAKPOINTED;
+        } else if (val ==  SLEEPING) {
+            return ThreadState.SLEEPING;
+        } else if (val ==  ZOMBIE) {
+            return ThreadState.ZOMBIE;
+        } else {
+            throw new RuntimeException("Illegal thread state " + val);
+        }
+    }
  }
diff --git 
a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ThreadState.java 
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ThreadState.java
new file mode 100644
--- /dev/null
+++ 
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ThreadState.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 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;
+
+/** This is a type-safe enum mirroring the ThreadState enum in
+ osThread.hpp. The conversion between the underlying ints
+ and these values is done in OSThread. */
+
+public class ThreadState {
+
+    private String printVal;
+
+    /** Memory has been allocated but not initialized */
+    public static final ThreadState ALLOCATED = new ThreadState("allocated");
+    /** The thread has been initialized but yet started */
+    public static final ThreadState INITIALIZED = new 
ThreadState("initialized");
+    /** Has been started and is runnable, but not necessarily running */
+    public static final ThreadState RUNNABLE = new ThreadState("runnable");
+    /** Waiting on a contended monitor lock */
+    public static final ThreadState MONITOR_WAIT = new ThreadState("waiting for 
monitor entry");
+    /** Waiting on a condition variable */
+    public static final ThreadState CONDVAR_WAIT = new ThreadState("waiting on 
condition");
+    /** Waiting on an Object.wait() call */
+    public static final ThreadState OBJECT_WAIT = new ThreadState("in 
Object.wait()");
+    /** Suspended at breakpoint */
+    public static final ThreadState BREAKPOINTED = new ThreadState("at 
breakpoint");
+    /** Thread.sleep() */
+    public static final ThreadState SLEEPING = new ThreadState("sleeping");
+    /** All done, but not reclaimed yet */
+    public static final ThreadState ZOMBIE = new ThreadState("zombie");
+
+    private ThreadState(String printVal){
+        this.printVal = printVal;
+    }
+
+    public String getPrintVal() {
+        return printVal;
+    }
+}
diff --git 
a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java 
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java
@@ -1,5 +1,5 @@
  /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 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
@@ -88,6 +88,10 @@
                 out.print("----------------- ");
                 out.print(th);
                 out.println(" -----------------");
+               JavaThread jthread = (JavaThread) proxyToThread.get(th);
+               if (jthread != null) {
+                  jthread.printThreadInfoOn(out);
+               }
                 while (f != null) {
                    ClosestSymbol sym = f.closestSymbolToPC();
                    Address pc = f.pc();
diff --git 
a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java 
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java
@@ -1,5 +1,5 @@
  /*
- * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 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
@@ -74,14 +74,7 @@
              int i = 1;
              for (JavaThread cur = threads.first(); cur != null; cur = 
cur.next(), i++) {
                  if (cur.isJavaThread()) {
-                    Address sp = cur.getLastJavaSP();
-                    tty.print("Thread ");
-                    cur.printThreadIDOn(tty);
-                    tty.print(": (state = " + cur.getThreadState());
-                    if (verbose) {
-                        tty.println(", current Java SP = " + sp);
-                    }
-                    tty.println(')');
+                    cur.printThreadInfoOn(tty);
                      try {
                          for (JavaVFrame vf = cur.getLastJavaVFrameDbg(); vf 
!= null; vf = vf.javaSender()) {
                              Method method = vf.getMethod();
diff --git a/src/share/vm/runtime/vmStructs.cpp 
b/src/share/vm/runtime/vmStructs.cpp
--- a/src/share/vm/runtime/vmStructs.cpp
+++ b/src/share/vm/runtime/vmStructs.cpp
@@ -981,6 +981,7 @@
/************/ \
\
    volatile_nonstatic_field(OSThread, _interrupted, jint)                      
            \
+  volatile_nonstatic_field(OSThread, _state, ThreadState)                      
           \
\
/************************/ \
    /* OopMap and OopMapSet */ \
@@ -2186,6 +2187,7 @@
declare_integer_type(Generation::Name) \
declare_integer_type(InstanceKlass::ClassState) \
declare_integer_type(JavaThreadState) \
+ declare_integer_type(ThreadState) \
declare_integer_type(Location::Type) \
declare_integer_type(Location::Where) \
declare_integer_type(Flag::Flags) \
@@ -2443,6 +2445,20 @@
declare_constant(JavaThread::_not_terminated) \
declare_constant(JavaThread::_thread_exiting) \
\
+ /*******************/ \
+  /* JavaThreadState */                                                   \
+ /*******************/ \
+ \
+ declare_constant(ALLOCATED) \
+ declare_constant(INITIALIZED) \
+ declare_constant(RUNNABLE) \
+ declare_constant(MONITOR_WAIT) \
+ declare_constant(CONDVAR_WAIT) \
+ declare_constant(OBJECT_WAIT) \
+ declare_constant(BREAKPOINTED) \
+ declare_constant(SLEEPING) \
+ declare_constant(ZOMBIE) \
+ \
/******************************/ \
    /* Klass misc. enum constants */                                        \
/******************************/ \
diff --git a/test/serviceability/sa/JhsdbThreadInfoTest.java 
b/test/serviceability/sa/JhsdbThreadInfoTest.java
new file mode 100644
--- /dev/null
+++ b/test/serviceability/sa/JhsdbThreadInfoTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 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.
+ */
+
+import jdk.test.lib.apps.LingeredApp;
+import jdk.test.lib.JDKToolLauncher;
+import jdk.test.lib.Platform;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.Utils;
+
+/*
+ * @test
+ * @library /test/lib
+ * @run main JhsdbThreadInfoTest
+ */
+public class JhsdbThreadInfoTest {
+
+    public static void main(String[] args) throws Exception {
+
+        if (!Platform.shouldSAAttach()) {
+            System.out.println("SA attach not expected to work - test 
skipped.");
+            return;
+        }
+
+        LingeredApp app = null;
+
+        try {
+            app = LingeredApp.startApp(Utils.getVmOptions());
+            System.out.println("Started LingeredApp with pid " + app.getPid());
+
+            JDKToolLauncher jhsdbLauncher = 
JDKToolLauncher.createUsingTestJDK("jhsdb");
+
+            jhsdbLauncher.addToolArg("jstack");
+            jhsdbLauncher.addToolArg("--pid");
+ jhsdbLauncher.addToolArg(Long.toString(app.getPid()));
+
+            ProcessBuilder pb = new ProcessBuilder();
+            pb.command(jhsdbLauncher.getCommand());
+            Process jhsdb = pb.start();
+
+            jhsdb.waitFor();
+
+            OutputAnalyzer out = new OutputAnalyzer(jhsdb);
+
+            System.out.println(out.getStdout());
+            System.err.println(out.getStderr());
+
+            out.shouldMatch("\".+\" #\\d+ daemon prio=\\d+ tid=0x[0-9a-f]+ 
nid=0x[0-9a-f]+ .+ \\[0x[0-9a-f]+]");
+            out.shouldMatch("\"main\" #\\d+ prio=\\d+ tid=0x[0-9a-f]+ 
nid=0x[0-9a-f]+ .+ \\[0x[0-9a-f]+]");
+            out.shouldMatch("   java.lang.Thread.State: .+");
+            out.shouldMatch("   JavaThread state: _thread_.+");
+
+            out.shouldNotContain(" java.lang.Thread.State: UNKNOWN");
+            out.stderrShouldBeEmpty();
+
+            System.out.println("Test Completed");
+
+
+        } catch (InterruptedException ie) {
+            throw new Error("Problem awaiting the child process: " + ie, ie);
+        } catch (Exception attachE) {
+            throw new Error("Couldn't start jhsdb, attach to LingeredApp or match 
ThreadName: " + attachE);
+
+        } finally {
+            LingeredApp.stopApp(app);
+        }
+    }
+}




On 2017/06/29 7:40, Yasumasa Suenaga wrote:
Hi chihiro,

getThreadState() in OSThread.java:

+        } else if (val == BREAKPOINTED) {
+            return ThreadState.BREAKPOINTED;
+        } else if (val ==  BREAKPOINTED) {
+            return ThreadState.BREAKPOINTED;

These conditions are duplicated.


Please upload webrev if you can :-)


Yasumasa


On 2017/06/29 0:02, chihiro ito wrote:
Hi all,

In last week, I've posted review request [1].
Could you possibly review for this following small change? If review is ok, 
please commit this as cito.

[1] 
http://mail.openjdk.java.net/pipermail/serviceability-dev/2017-June/021430.html

Thanks,
Chihiro (Contributer)

On 2017/06/18 13:02, chihiro ito wrote:
At first I thought to print just each thread name, but I tried to make it as 
similar as possible to JStack as following.

Mixed mode:
----------------- 26476 -----------------
"main" #1 prio=5 tid=0x00007f6894019000 nid=0x676c waiting on condition 
[0x00007f689b7ae000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
   JavaThread state: _thread_blocked
0x00007f689b185a82    __pthread_cond_timedwait + 0x132

No mixed mode:
"main" #1 prio=5 tid=0x00007f6894019000 nid=0x676c waiting on condition 
[0x00007f689b7ae000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
   JavaThread state: _thread_blocked
 - java.lang.Thread.sleep(long) @bci=0 (Interpreted frame)


This change passed a test by jtreg.

SOURCE_HOME=/home/user/repo/jdk10-hs
jtreg -dir:${SOURCE_HOME}/hotspot/test 
-testjdk:${SOURCE_HOME}/build/linux-x86_64-normal-server-slowdebug/jdk/ 
serviceability/sa/JhsdbThreadInfoTest.java
Test results: passed: 1


Source:
diff --git 
a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/OopUtilities.java 
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/OopUtilities.java
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/OopUtilities.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/OopUtilities.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -59,20 +59,20 @@
   // parkBlocker field is new since 1.6
   private static OopField threadParkBlockerField;

+  private static IntField threadPriorityField;
+  private static BooleanField threadDaemonField;
+
   // possible values of java_lang_Thread::ThreadStatus
   private static int THREAD_STATUS_NEW;
-  /*
-    Other enum constants are not needed as of now. Uncomment these as and when 
needed.

-    private static int THREAD_STATUS_RUNNABLE;
-    private static int THREAD_STATUS_SLEEPING;
-    private static int THREAD_STATUS_IN_OBJECT_WAIT;
-    private static int THREAD_STATUS_IN_OBJECT_WAIT_TIMED;
-    private static int THREAD_STATUS_PARKED;
-    private static int THREAD_STATUS_PARKED_TIMED;
-    private static int THREAD_STATUS_BLOCKED_ON_MONITOR_ENTER;
-    private static int THREAD_STATUS_TERMINATED;
-  */
+  private static int THREAD_STATUS_RUNNABLE;
+  private static int THREAD_STATUS_SLEEPING;
+  private static int THREAD_STATUS_IN_OBJECT_WAIT;
+  private static int THREAD_STATUS_IN_OBJECT_WAIT_TIMED;
+  private static int THREAD_STATUS_PARKED;
+  private static int THREAD_STATUS_PARKED_TIMED;
+  private static int THREAD_STATUS_BLOCKED_ON_MONITOR_ENTER;
+  private static int THREAD_STATUS_TERMINATED;

   // java.util.concurrent.locks.AbstractOwnableSynchronizer fields
   private static OopField absOwnSyncOwnerThreadField;
@@ -229,20 +229,19 @@
       threadStatusField = (IntField) k.findField("threadStatus", "I");
       threadParkBlockerField = (OopField) k.findField("parkBlocker",
"Ljava/lang/Object;");
+      threadPriorityField = (IntField) k.findField("priority", "I");
+      threadDaemonField = (BooleanField) k.findField("daemon", "Z");
       TypeDataBase db = VM.getVM().getTypeDataBase();
       THREAD_STATUS_NEW = 
db.lookupIntConstant("java_lang_Thread::NEW").intValue();
-      /*
-        Other enum constants are not needed as of now. Uncomment these as and 
when needed.

-        THREAD_STATUS_RUNNABLE = 
db.lookupIntConstant("java_lang_Thread::RUNNABLE").intValue();
-        THREAD_STATUS_SLEEPING = 
db.lookupIntConstant("java_lang_Thread::SLEEPING").intValue();
-        THREAD_STATUS_IN_OBJECT_WAIT = 
db.lookupIntConstant("java_lang_Thread::IN_OBJECT_WAIT").intValue();
-        THREAD_STATUS_IN_OBJECT_WAIT_TIMED = 
db.lookupIntConstant("java_lang_Thread::IN_OBJECT_WAIT_TIMED").intValue();
-        THREAD_STATUS_PARKED = 
db.lookupIntConstant("java_lang_Thread::PARKED").intValue();
-        THREAD_STATUS_PARKED_TIMED = 
db.lookupIntConstant("java_lang_Thread::PARKED_TIMED").intValue();
-        THREAD_STATUS_BLOCKED_ON_MONITOR_ENTER = 
db.lookupIntConstant("java_lang_Thread::BLOCKED_ON_MONITOR_ENTER").intValue();
-        THREAD_STATUS_TERMINATED = 
db.lookupIntConstant("java_lang_Thread::TERMINATED").intValue();
-      */
+      THREAD_STATUS_RUNNABLE = 
db.lookupIntConstant("java_lang_Thread::RUNNABLE").intValue();
+      THREAD_STATUS_SLEEPING = 
db.lookupIntConstant("java_lang_Thread::SLEEPING").intValue();
+      THREAD_STATUS_IN_OBJECT_WAIT = 
db.lookupIntConstant("java_lang_Thread::IN_OBJECT_WAIT").intValue();
+      THREAD_STATUS_IN_OBJECT_WAIT_TIMED = 
db.lookupIntConstant("java_lang_Thread::IN_OBJECT_WAIT_TIMED").intValue();
+      THREAD_STATUS_PARKED = 
db.lookupIntConstant("java_lang_Thread::PARKED").intValue();
+      THREAD_STATUS_PARKED_TIMED = 
db.lookupIntConstant("java_lang_Thread::PARKED_TIMED").intValue();
+      THREAD_STATUS_BLOCKED_ON_MONITOR_ENTER = 
db.lookupIntConstant("java_lang_Thread::BLOCKED_ON_MONITOR_ENTER").intValue();
+      THREAD_STATUS_TERMINATED = 
db.lookupIntConstant("java_lang_Thread::TERMINATED").intValue();

       if (Assert.ASSERTS_ENABLED) {
         // it is okay to miss threadStatusField, because this was
@@ -331,4 +330,46 @@
       return absOwnSyncOwnerThreadField.getValue(oop);
     }
   }
+
+  public static int threadOopGetPriority(Oop threadOop) {
+    initThreadFields();
+    if (threadPriorityField != null) {
+      return threadPriorityField.getValue(threadOop);
+    } else {
+      return 0;
+    }
+  }
+
+  public static boolean threadOopGetDaemon(Oop threadOop) {
+    initThreadFields();
+    if (threadDaemonField != null) {
+      return threadDaemonField.getValue(threadOop);
+    } else {
+      return false;
+    }
+  }
+
+  public static String threadOopGetThreadStatusName(Oop threadOop) {
+    int status = OopUtilities.threadOopGetThreadStatus(threadOop);
+    if( status == THREAD_STATUS_NEW ){
+      return "NEW";
+    }else if(status == THREAD_STATUS_RUNNABLE ){
+      return "RUNNABLE";
+    }else if(status == THREAD_STATUS_SLEEPING ){
+      return "TIMED_WAITING (sleeping)";
+    }else if(status == THREAD_STATUS_IN_OBJECT_WAIT ){
+      return "WAITING (on object monitor)";
+    }else if(status == THREAD_STATUS_IN_OBJECT_WAIT_TIMED ){
+      return "TIMED_WAITING (on object monitor)";
+    }else if(status == THREAD_STATUS_PARKED ){
+      return "WAITING (parking)";
+    }else if(status == THREAD_STATUS_PARKED_TIMED ){
+      return "TIMED_WAITING (parking)";
+    }else if(status == THREAD_STATUS_BLOCKED_ON_MONITOR_ENTER ){
+      return "BLOCKED (on object monitor)";
+    }else if(status == THREAD_STATUS_TERMINATED ){
+      return "TERMINATED";
+    }
+    return "UNKNOWN";
+  }
 }
diff --git 
a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java 
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java
--- 
a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java
+++ 
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java
@@ -475,4 +475,36 @@
     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(String.format("0x%016x",this.getAddress().asLongValue()));
+    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("0x%016x",0L));
+    } else {
+      Address maskAddress = this.getLastJavaSP().andWithMask(0xFFF);
+ out.print(this.getLastJavaSP().xorWithMask(maskAddress.asLongValue()));
+    }
+    out.println("]");
+    out.print("   java.lang.Thread.State: ");
+ out.println(OopUtilities.threadOopGetThreadStatusName(threadOop));
+    out.print("   JavaThread state: _thread_");
+ out.println(this.getThreadState().toString().toLowerCase());
+  }
 }
diff --git 
a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/OSThread.java 
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/OSThread.java
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/OSThread.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/OSThread.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 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
@@ -33,6 +33,19 @@
 public class OSThread extends VMObject {
     private static JIntField interruptedField;
     private static Field threadIdField;
+    private static CIntegerField threadStateField;
+
+    // ThreadStates read from underlying process
+    private static int ALLOCATED;
+    private static int INITIALIZED;
+    private static int RUNNABLE;
+    private static int MONITOR_WAIT;
+    private static int CONDVAR_WAIT;
+    private static int OBJECT_WAIT;
+    private static int BREAKPOINTED;
+    private static int SLEEPING;
+    private static int ZOMBIE;
+
     static {
         VM.registerVMInitializedObserver(new Observer() {
             public void update(Observable o, Object data) {
@@ -45,6 +58,17 @@
         Type type = db.lookupType("OSThread");
         interruptedField = type.getJIntField("_interrupted");
         threadIdField = type.getField("_thread_id");
+        threadStateField = type.getCIntegerField("_state");
+
+        ALLOCATED = db.lookupIntConstant("ALLOCATED").intValue();
+        INITIALIZED = db.lookupIntConstant("INITIALIZED").intValue();
+        RUNNABLE = db.lookupIntConstant("RUNNABLE").intValue();
+        MONITOR_WAIT = db.lookupIntConstant("MONITOR_WAIT").intValue();
+        CONDVAR_WAIT = db.lookupIntConstant("CONDVAR_WAIT").intValue();
+        OBJECT_WAIT = db.lookupIntConstant("OBJECT_WAIT").intValue();
+        BREAKPOINTED = db.lookupIntConstant("BREAKPOINTED").intValue();
+        SLEEPING = db.lookupIntConstant("SLEEPING").intValue();
+        ZOMBIE = db.lookupIntConstant("ZOMBIE").intValue();
     }

     public OSThread(Address addr) {
@@ -59,4 +83,30 @@
         return threadIdField.getJInt(addr);
     }

+    public ThreadState getThreadState() {
+        int val = (int)threadStateField.getValue(addr);
+        if (val ==  ALLOCATED) {
+            return ThreadState.ALLOCATED;
+        } else if (val ==  INITIALIZED) {
+            return ThreadState.INITIALIZED;
+        } else if (val ==  RUNNABLE) {
+            return ThreadState.RUNNABLE;
+        } else if (val ==  MONITOR_WAIT) {
+            return ThreadState.MONITOR_WAIT;
+        } else if (val ==  CONDVAR_WAIT) {
+            return ThreadState.CONDVAR_WAIT;
+        } else if (val ==  OBJECT_WAIT) {
+            return ThreadState.OBJECT_WAIT;
+        } else if (val ==  BREAKPOINTED) {
+            return ThreadState.BREAKPOINTED;
+        } else if (val ==  BREAKPOINTED) {
+            return ThreadState.BREAKPOINTED;
+        } else if (val ==  SLEEPING) {
+            return ThreadState.SLEEPING;
+        } else if (val ==  ZOMBIE) {
+            return ThreadState.ZOMBIE;
+        } else {
+            throw new RuntimeException("Illegal thread state " + val);
+        }
+    }
 }
diff --git 
a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ThreadState.java 
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ThreadState.java
new file mode 100644
--- /dev/null
+++ 
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ThreadState.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 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;
+
+/** This is a type-safe enum mirroring the ThreadState enum in
+ osThread.hpp. The conversion between the underlying ints
+ and these values is done in OSThread. */
+
+public class ThreadState {
+
+    private String printVal;
+
+    /** Memory has been allocated but not initialized */
+    public static final ThreadState ALLOCATED = new ThreadState("allocated");
+    /** The thread has been initialized but yet started */
+    public static final ThreadState INITIALIZED = new 
ThreadState("initialized");
+    /** Has been started and is runnable, but not necessarily running */
+    public static final ThreadState RUNNABLE = new ThreadState("runnable");
+    /** Waiting on a contended monitor lock */
+    public static final ThreadState MONITOR_WAIT = new ThreadState("waiting for 
monitor entry");
+    /** Waiting on a condition variable */
+    public static final ThreadState CONDVAR_WAIT = new ThreadState("waiting on 
condition");
+    /** Waiting on an Object.wait() call */
+    public static final ThreadState OBJECT_WAIT = new ThreadState("in 
Object.wait()");
+    /** Suspended at breakpoint */
+    public static final ThreadState BREAKPOINTED = new ThreadState("at 
breakpoint");
+    /** Thread.sleep() */
+    public static final ThreadState SLEEPING = new ThreadState("sleeping");
+    /** All done, but not reclaimed yet */
+    public static final ThreadState ZOMBIE = new ThreadState("zombie");
+
+    private ThreadState(String printVal){
+        this.printVal = printVal;
+    }
+
+    public String getPrintVal() {
+        return printVal;
+    }
+}
diff --git 
a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java 
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 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
@@ -88,6 +88,10 @@
                out.print("----------------- ");
                out.print(th);
                out.println(" -----------------");
+               JavaThread jthread = (JavaThread) proxyToThread.get(th);
+               if (jthread != null) {
+                  jthread.printThreadInfoOn(out);
+               }
                while (f != null) {
                   ClosestSymbol sym = f.closestSymbolToPC();
                   Address pc = f.pc();
diff --git 
a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java 
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 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
@@ -74,14 +74,7 @@
             int i = 1;
             for (JavaThread cur = threads.first(); cur != null; cur = 
cur.next(), i++) {
                 if (cur.isJavaThread()) {
-                    Address sp = cur.getLastJavaSP();
-                    tty.print("Thread ");
-                    cur.printThreadIDOn(tty);
-                    tty.print(": (state = " + cur.getThreadState());
-                    if (verbose) {
-                        tty.println(", current Java SP = " + sp);
-                    }
-                    tty.println(')');
+                    cur.printThreadInfoOn(tty);
                     try {
                         for (JavaVFrame vf = cur.getLastJavaVFrameDbg(); vf != 
null; vf = vf.javaSender()) {
                             Method method = vf.getMethod();
diff --git a/src/share/vm/runtime/vmStructs.cpp 
b/src/share/vm/runtime/vmStructs.cpp
--- a/src/share/vm/runtime/vmStructs.cpp
+++ b/src/share/vm/runtime/vmStructs.cpp
@@ -981,6 +981,7 @@
/************/ \
\
   volatile_nonstatic_field(OSThread, _interrupted, jint)                       
           \
+  volatile_nonstatic_field(OSThread, _state, ThreadState)                      
           \
\
/************************/ \
   /* OopMap and OopMapSet */ \
@@ -2186,6 +2187,7 @@
declare_integer_type(Generation::Name) \
declare_integer_type(InstanceKlass::ClassState) \
declare_integer_type(JavaThreadState) \
+ declare_integer_type(ThreadState) \
declare_integer_type(Location::Type) \
declare_integer_type(Location::Where) \
declare_integer_type(Flag::Flags) \
@@ -2443,6 +2445,20 @@
declare_constant(JavaThread::_not_terminated) \
declare_constant(JavaThread::_thread_exiting) \
\
+ /*******************/ \
+  /* JavaThreadState */                                                   \
+ /*******************/ \
+ \
+ declare_constant(ALLOCATED) \
+ declare_constant(INITIALIZED) \
+ declare_constant(RUNNABLE) \
+ declare_constant(MONITOR_WAIT) \
+ declare_constant(CONDVAR_WAIT) \
+ declare_constant(OBJECT_WAIT) \
+ declare_constant(BREAKPOINTED) \
+ declare_constant(SLEEPING) \
+ declare_constant(ZOMBIE) \
+ \
/******************************/ \
   /* Klass misc. enum constants */                                        \
/******************************/ \
diff --git a/test/serviceability/sa/JhsdbThreadInfoTest.java 
b/test/serviceability/sa/JhsdbThreadInfoTest.java
new file mode 100644
--- /dev/null
+++ b/test/serviceability/sa/JhsdbThreadInfoTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 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.
+ */
+
+import jdk.test.lib.apps.LingeredApp;
+import jdk.test.lib.JDKToolLauncher;
+import jdk.test.lib.Platform;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.Utils;
+
+/*
+ * @test
+ * @library /test/lib
+ * @run main JhsdbThreadInfoTest
+ */
+public class JhsdbThreadInfoTest {
+
+    public static void main(String[] args) throws Exception {
+
+        if (!Platform.shouldSAAttach()) {
+            System.out.println("SA attach not expected to work - test 
skipped.");
+            return;
+        }
+
+        LingeredApp app = null;
+
+        try {
+            app = LingeredApp.startApp(Utils.getVmOptions());
+            System.out.println("Started LingeredApp with pid " + app.getPid());
+
+            JDKToolLauncher jhsdbLauncher = 
JDKToolLauncher.createUsingTestJDK("jhsdb");
+
+            jhsdbLauncher.addToolArg("jstack");
+            jhsdbLauncher.addToolArg("--pid");
+ jhsdbLauncher.addToolArg(Long.toString(app.getPid()));
+
+            ProcessBuilder pb = new ProcessBuilder();
+            pb.command(jhsdbLauncher.getCommand());
+            Process jhsdb = pb.start();
+
+            jhsdb.waitFor();
+
+            OutputAnalyzer out = new OutputAnalyzer(jhsdb);
+
+            System.out.println(out.getStdout());
+            System.err.println(out.getStderr());
+
+            out.shouldMatch("\".+\" #\\d+ daemon prio=\\d+ tid=0x[0-9a-f]+ 
nid=0x[0-9a-f]+ .+ \\[0x[0-9a-f]+]");
+            out.shouldMatch("\"main\" #\\d+ prio=\\d+ tid=0x[0-9a-f]+ 
nid=0x[0-9a-f]+ .+ \\[0x[0-9a-f]+]");
+            out.shouldMatch(" java.lang.Thread.State: .+");
+            out.shouldMatch("   JavaThread state: _thread_.+");
+
+            out.shouldNotContain(" java.lang.Thread.State: UNKNOWN");
+            out.stderrShouldBeEmpty();
+
+            System.out.println("Test Completed");
+
+
+        } catch (InterruptedException ie) {
+            throw new Error("Problem awaiting the child process: " + ie, ie);
+        } catch (Exception attachE) {
+            throw new Error("Couldn't start jhsdb, attach to LingeredApp or match 
ThreadName: " + attachE);
+
+        } finally {
+            LingeredApp.stopApp(app);
+        }
+    }
+}


Regards,
Chihiro


On 2017/06/14 16:51, Bernd Eckenfels wrote:
I don't understand why this format is totally different from the normal stack 
traces? At least the header with the stack names could be similar?

Gruss
Bernd
--
https://urldefense.proofpoint.com/v2/url?u=http-3A__bernd.eckenfels.net&d=DwICaQ&c=RoP1YumCXCgaWHvlZYR8PQcxBKCX5YTpkKY057SbK10&r=0SCyhNQIV2jPt0aEruqsRB6bzVcIXHTDh1GkXLV1dyY&m=3QRaQqXxp0PcpNUQsiJOAlmzxeN3O9PyZoFxlIPpsUs&s=KMibjgBgQ9DJ0ddSGSJrvH7PZMz4zGnH-CZml-iRTMM&e=
 ------------------------------------------------------------------------
*From:* serviceability-dev <serviceability-dev-boun...@openjdk.java.net> on behalf of 
chihiro ito <chihiro....@oracle.com>
*Sent:* Wednesday, June 14, 2017 9:17:42 AM
*To:* Jini George; serviceability-dev@openjdk.java.net
*Subject:* Re: [10] RFR 8181647: jhsdb jstack could not output thread name
Hi all,

I added a test case and modified previous patch including fix the
copyright year to 2017.
I changed to output Java thread name next the separator lines in "jhsdb
jstack --mixed" case as following.

----------------- 32117 -----------------
"main"
0x00007f6c8feafa82    __pthread_cond_timedwait + 0x132

Could you possibly review for this following small change? If review is
ok, please commit this as cito.


Source:
diff --git
a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java
@@ -1,5 +1,5 @@
  /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights
reserved.
+ * Copyright (c) 2003, 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
@@ -88,6 +88,12 @@
                 out.print("----------------- ");
                 out.print(th);
                 out.println(" -----------------");
+               JavaThread jthread = (JavaThread) proxyToThread.get(th);
+               if (jthread != null) {
+                   out.print("\"");
+ out.print(jthread.getThreadName());
+                   out.println("\"");
+               }
                 while (f != null) {
                    ClosestSymbol sym = f.closestSymbolToPC();
                    Address pc = f.pc();
diff --git
a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java
---
a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java
+++
b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java
@@ -1,5 +1,5 @@
  /*
- * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights
reserved.
+ * Copyright (c) 2002, 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
@@ -75,7 +75,9 @@
              for (JavaThread cur = threads.first(); cur != null; cur =
cur.next(), i++) {
                  if (cur.isJavaThread()) {
                      Address sp = cur.getLastJavaSP();
-                    tty.print("Thread ");
+                    tty.print("\"");
+                    tty.print(cur.getThreadName());
+                    tty.print("\" nid=");
                      cur.printThreadIDOn(tty);
                      tty.print(": (state = " + cur.getThreadState());
                      if (verbose) {
diff --git a/test/serviceability/sa/JhsdbThreadNameTest.java
b/test/serviceability/sa/JhsdbThreadNameTest.java
new file mode 100644
--- /dev/null
+++ b/test/serviceability/sa/JhsdbThreadNameTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 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 <http://www.oracle.com> if you need additional 
information or have any
+ * questions.
+ */
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Consumer;
+
+import jdk.test.lib.apps.LingeredApp;
+import jdk.test.lib.JDKToolLauncher;
+import jdk.test.lib.Platform;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.Utils;
+
+/*
+ * @test
+ * @library /test/lib
+ * @run main/othervm JhsdbThreadNameTest
+ */
+public class JhsdbThreadNameTest {
+
+    private static String notMixedModeThreadNames[] =
{"Common-Cleaner", "Signal Dispatcher", "Finalizer", "Reference
Handler", "main"};
+    private static String mixedModeThreadNames[] = {"C2
CompilerThread0", "C1 CompilerThread1", "Sweeper thread", "Service Thread"};
+
+    private static void startHsdbJstack(boolean mixed) throws Exception {
+
+        LingeredApp app = null;
+
+        try {
+            List<String> vmArgs = new ArrayList<String>();
+            vmArgs.add("-Xmx10m");
+            vmArgs.addAll(Utils.getVmOptions());
+
+            app = LingeredApp.startApp(vmArgs);
+            System.out.println("Started LingeredApp with pid " +
app.getPid());
+
+            JDKToolLauncher jhsdbLauncher =
JDKToolLauncher.createUsingTestJDK("jhsdb");
+
+            jhsdbLauncher.addToolArg("jstack");
+            jhsdbLauncher.addToolArg("--pid");
+ jhsdbLauncher.addToolArg(Long.toString(app.getPid()));
+
+            if (mixed) {
+ jhsdbLauncher.addToolArg("--mixed");
+            }
+            ProcessBuilder pb = new ProcessBuilder();
+            pb.command(jhsdbLauncher.getCommand());
+            Process jhsdb = pb.start();
+
+            jhsdb.waitFor();
+
+            OutputAnalyzer out = new OutputAnalyzer(jhsdb);
+
+
Arrays.stream(notMixedModeThreadNames).map(JhsdbThreadNameTest::addQuotationMarks).forEach(out::shouldContain);
+            Consumer<String> testMethod = null;
+            if (mixed) {
+                testMethod = out::shouldContain;
+            } else {
+                testMethod = out::shouldNotContain;
+            }
+
Arrays.stream(mixedModeThreadNames).map(JhsdbThreadNameTest::addQuotationMarks).forEach(testMethod);
+
+        } catch (InterruptedException ie) {
+            throw new Error("Problem awaiting the child process: " +
ie, ie);
+        } catch (Exception attachE) {
+            throw new Error("Couldn't start jhsdb, attach to
LingeredApp or match ThreadName: " + attachE);
+
+        } finally {
+            LingeredApp.stopApp(app);
+        }
+    }
+
+    private static String addQuotationMarks(String str) {
+        return "\"" + str + "\"";
+    }
+
+    public static void main(String[] args) throws Exception {
+
+        if (!Platform.shouldSAAttach()) {
+            System.out.println("SA attach not expected to work - test
skipped.");
+            return;
+        }
+
+        startHsdbJstack(true);
+        startHsdbJstack(false);
+    }
+}


Regards,
Chihiro


On 2017/06/08 18:04, chihiro ito wrote:
> Hi Jini,
>
> Thank you for your advices. I try to add the test case and modify the
> copyright year to 2017.
> Basically, I agree with your idea, but I think that the separator line
> should finally be the same as the output of the jstack command. I
> worry which is better way.
>
> Thanks,
> Chihiro
>
> On 2017/06/08 16:42, Jini George wrote:
>> Hi Chihiro,
>>
>> Thank you for making this useful change. Your changes look good.
>>
>> It would be great though if you could add a test case for this. Could
>> you also modify the copyright year to 2017 ? One additional
>> suggestion: The addition of the thread name makes the separator lines
>> unaligned in the pstack/jstack --mixed cases. Like:
>>
>> ----------------- "Service Thread" nid=16051 -----------------
>> and
>> ----------------- nid=16052 -----------------
>>
>> So I am wondering if it would make sense to have the name printed out
>> on a separate line to keep the separator lines aligned. But this is a
>> nit, and I would leave it to you to decide on this.
>>
>> Thanks,
>> Jini (Not a (R)eviewer)
>>
>> On 6/7/2017 3:16 PM, chihiro ito wrote:
>>> Hi all,
>>>
>>> I changed to output Java thread name in jhsdb jstack as following.
>>>
>>> jhsdb jstack --pid 25879
>>> "main" nid=25884: (state = BLOCKED)
>>>
>>> jhsdb jstack --mixed --pid 25879
>>> ----------------- "main" nid=25884 -----------------
>>>
>>> Could you possibly review for this following small change? If review
>>> is ok, please commit this as cito.
>>>
>>> Source:
>>> diff --git
>>> a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java
>>> b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java
>>> ---
>>> a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java
>>> +++
>>> b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/PStack.java
>>> @@ -86,6 +86,13 @@
>>>              try {
>>>                 CFrame f = cdbg.topFrameForThread(th);
>>> out.print("----------------- ");
>>> +               JavaThread jthread = (JavaThread)
>>> proxyToThread.get(th);
>>> +               if (jthread != null) {
>>> +                   out.print("\"");
>>> + out.print(jthread.getThreadName());
>>> +                   out.print("\" ");
>>> +               }
>>> +               out.print("nid=");
>>>                 out.print(th);
>>>                 out.println(" -----------------");
>>>                 while (f != null) {
>>> diff --git
>>> a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java
>>> b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java
>>>
>>> ---
>>> a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java
>>> +++
>>> b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java
>>> @@ -75,7 +75,9 @@
>>>              for (JavaThread cur = threads.first(); cur != null; cur
>>> = cur.next(), i++) {
>>>                  if (cur.isJavaThread()) {
>>>                      Address sp = cur.getLastJavaSP();
>>> -                    tty.print("Thread ");
>>> +                    tty.print("\"");
>>> + tty.print(cur.getThreadName());
>>> +                    tty.print("\" nid=");
>>> cur.printThreadIDOn(tty);
>>>                      tty.print(": (state = " + cur.getThreadState());
>>>                      if (verbose) {
>>>
>>> Regards,
>>> Chihiro
>>>
>>
>






Reply via email to