JDIでスレッドダンプを取得する。
JDI(Java Debug Interface)でスレッドダンプをとります。
- JDIは、その名の通り、仮想マシンのデバッガや類似システムに有用な情報を提供するJava API です。
- リモートからのデバッグを標準でサポートしています。
- JDK添付ですが、実装はtools.jar内にあります。利用するにはtools.jarをクラスパスに通す必要があります。(
/lib以下にあります。)
プログラム
import java.io.IOException; import java.io.PrintStream; import java.util.Iterator; import java.util.List; import java.util.Map; import com.sun.jdi.Bootstrap; import com.sun.jdi.IncompatibleThreadStateException; import com.sun.jdi.ObjectReference; import com.sun.jdi.ReferenceType; import com.sun.jdi.StackFrame; import com.sun.jdi.ThreadReference; import com.sun.jdi.VirtualMachine; import com.sun.jdi.VirtualMachineManager; import com.sun.jdi.connect.AttachingConnector; import com.sun.jdi.connect.IllegalConnectorArgumentsException; import com.sun.jdi.connect.Connector.IntegerArgument; import com.sun.jdi.connect.Connector.StringArgument; /** * JDIが提供するAPIを使用してスレッドダンプをとります。<br/> * 結果は標準出力に出力されます。 * <p> * usage : java ThreadDump [host] [port] * </p> * <p> * <b>使用方法</b> * </p> * <ol> * <li> * 以下のVM引数を指定して、ダンプ対象となるJavaVMを起動しておく。 * <pre>-Xrunjdwp:transport=dt_socket,address=8009,server=y,suspend=n</pre> * ※ポート番号は重複しなければ任意の値でOK。 * </li> * <li>VMが起動しているホスト名、使用するポートを指定してツールを起動する。</li> * </ol> */ public class ThreadDump { /** vm */ private VirtualMachine vm; /** ログの出力先 */ private final PrintStream out; /*** * コンストラクタ * @param out 出力先 */ public ThreadDump ( PrintStream out ) { this.out = out; } /*** * メイン関数 * @param args 引数 * @throws IncompatibleThreadStateException * @throws IllegalConnectorArgumentsException * @throws IOException * @throws NumberFormatException */ public static void main ( String[] args ) throws NumberFormatException, IOException, IllegalConnectorArgumentsException, IncompatibleThreadStateException { ThreadDump td = new ThreadDump(System.out); td.connect(args[0], Integer.parseInt(args[1])); td.dump(); } /** * デバッグ対象vmに接続します。 * @param host 接続先ホスト * @param port 接続先ポート * @throws IOException * @throws IllegalConnectorArgumentsException * @throws IncompatibleThreadStateException */ public void connect(String host, int port) throws IOException, IllegalConnectorArgumentsException, IncompatibleThreadStateException { VirtualMachineManager vmm = Bootstrap.virtualMachineManager(); List acs = vmm.attachingConnectors(); // 一覧からソケットコネクタを探す。 AttachingConnector ac = null; Iterator it = acs.iterator(); while (it.hasNext()) { AttachingConnector tmp = (AttachingConnector) it.next(); // ソケットコネクタを探す。 if ("com.sun.jdi.SocketAttach".equals(tmp.name())) { ac = tmp; break; } } //引数を設定して接続 Map arg = ac.defaultArguments(); IntegerArgument portNumber = ( IntegerArgument ) arg.get("port"); portNumber.setValue(port); StringArgument hostname = ( StringArgument ) arg.get("hostname"); hostname.setValue(host); vm = ac.attach( arg ); } /** * スレッドダンプを取ります。 */ void dump() { List list = vm.allThreads(); Iterator it = list.iterator(); while (it.hasNext()) { ThreadReference tr = (ThreadReference) it.next(); try { tr.suspend(); printThreadInfo(tr); } finally { if (tr.isSuspended()) { tr.resume(); } } } } /** * スレッドの情報を標準出力に出力します。 * @param tr スレッド参照 */ private void printThreadInfo( ThreadReference tr ) { out.println( "" ); out.println( "----"); printInfo("name", tr.name()); // 状態 String status = convStatusToString(tr.status()); printInfo("status", status); // カレントのロックを取得 ObjectReference or = null; try { or = tr.currentContendedMonitor(); } catch ( IncompatibleThreadStateException e ) { e.printStackTrace(); return; } String lock = toTypeStr(or); printInfo("current contended monitor", lock != null ? lock : "none.."); // ロックを保持しているオブジェクトの一覧を取得 List list = null; try { list = tr.ownedMonitors(); } catch ( IncompatibleThreadStateException e ) { e.printStackTrace(); return; } Iterator oit = list.iterator(); StringBuffer locked = new StringBuffer(); while (oit.hasNext()) { or = ( ObjectReference) oit.next(); lock = toTypeStr(or); locked.append(lock != null ? lock : "unknown.."); if (!oit.hasNext()) { locked.append("\n "); } } if (locked.toString().length() <= 0) { locked.append("none.."); } printInfo("owned monitors", locked.toString()); // フレーム一覧を出力 out.println( "stack-trace :" ); List frames = null; try { frames = tr.frames(); } catch ( IncompatibleThreadStateException e ) { e.printStackTrace(); return; } Iterator fit = frames.iterator(); while (fit.hasNext()) { StackFrame sf = ( StackFrame ) fit.next(); out.println( " " + sf.location() ); } } /** * ステータスIDを表示用文字列に変換します。 * @param status ステータスID * @return ステータスの表示用文字列 */ private static String convStatusToString ( int status ) { String str = ""; switch (status) { case ThreadReference.THREAD_STATUS_MONITOR : str = "waiting monitor"; break; case ThreadReference.THREAD_STATUS_NOT_STARTED : str = "not started"; break; case ThreadReference.THREAD_STATUS_RUNNING : str = "running"; break; case ThreadReference.THREAD_STATUS_SLEEPING : str = "sleeping"; break; case ThreadReference.THREAD_STATUS_WAIT : str = "wait"; break; case ThreadReference.THREAD_STATUS_ZOMBIE: str = "zombie"; break; case ThreadReference.THREAD_STATUS_UNKNOWN : default: str = "unknown"; } return str; } /** * オブジェクト参照の種類を示す文字列を取得します。 * @param or オブジェクト参照 * @return オブジェクトの種類を示す文字列 */ private static String toTypeStr(ObjectReference or) { if (or != null) { ReferenceType rt = or.referenceType(); return rt.name() + " (id=" +or.uniqueID() + ")"; } else { return null; } } private void printInfo(String title, String value) { out.println( title + " :\n " + value); } }
使用方法
プログラム中のコメントの通りです。
出力例
---- name : DestroyJavaVM status : running current contended monitor : none.. owned monitors : none.. stack-trace : ---- name : AWT-EventQueue-0 status : wait current contended monitor : java.awt.EventQueue (id=10) owned monitors : none.. stack-trace : java.lang.Object.wait(long)+-1 java.lang.Object:474 java.awt.EventQueue:345 java.awt.EventDispatchThread:189 java.awt.EventDispatchThread:163 java.awt.EventDispatchThread:157 java.awt.EventDispatchThread:149 java.awt.EventDispatchThread:110 ---- name : AWT-Shutdown status : wait current contended monitor : java.lang.Object (id=14) owned monitors : none.. stack-trace : java.lang.Object.wait(long)+-1 java.lang.Object:474 sun.awt.AWTAutoShutdown:259 java.lang.Thread:595 ---- name : Java2D Disposer status : wait current contended monitor : java.lang.ref.ReferenceQueue$Lock (id=17) owned monitors : none.. stack-trace : java.lang.Object.wait(long)+-1 java.lang.ref.ReferenceQueue:116 java.lang.ref.ReferenceQueue:132 sun.java2d.Disposer:107 java.lang.Thread:595 ---- name : AWT-Windows status : running current contended monitor : none.. owned monitors : none.. stack-trace : sun.awt.windows.WToolkit.eventLoop()+-1 sun.awt.windows.WToolkit:269 java.lang.Thread:595 ---- name : Signal Dispatcher status : running current contended monitor : none.. owned monitors : none.. stack-trace : ---- name : Finalizer status : wait current contended monitor : java.lang.ref.ReferenceQueue$Lock (id=22) owned monitors : none.. stack-trace : java.lang.Object.wait(long)+-1 java.lang.ref.ReferenceQueue:116 java.lang.ref.ReferenceQueue:132 java.lang.ref.Finalizer$FinalizerThread:159 ---- name : Reference Handler status : wait current contended monitor : java.lang.ref.Reference$Lock (id=24) owned monitors : none.. stack-trace : java.lang.Object.wait(long)+-1 java.lang.Object:474 java.lang.ref.Reference$ReferenceHandler:116