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 >>> >> >