/* * Copyright (c) 1998-2010 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source 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, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */ package com.caucho.util; import javax.annotation.*; import javax.management.*; import java.lang.management.*; import java.io.IOException; import java.lang.reflect.*; import java.util.*; import java.util.concurrent.atomic.AtomicLong; import java.util.logging.Logger; /** * Configuration for management. */ public class ThreadDump { private static Logger log = Logger.getLogger(ThreadDump.class.getName()); private static final ThreadDump _threadDump = new ThreadDump(); private final AtomicLong _lastDump = new AtomicLong(); private ThreadDump() { } public static void dumpThreads() { long timeout = 3600L * 1000L; _threadDump.threadDump(timeout); } private void threadDump(long timeout) { long now = Alarm.getCurrentTime(); long lastDump = _lastDump.get(); if (lastDump + timeout < now && _lastDump.compareAndSet(lastDump, now)) { threadDumpImpl(); } } private void threadDumpImpl() { ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); long []ids = threadBean.getAllThreadIds(); ThreadInfo []info = threadBean.getThreadInfo(ids, 32); StringBuilder sb = new StringBuilder(); sb.append("Thread Dump:\n"); Arrays.sort(info, new ThreadCompare()); buildThreads(sb, info, Thread.State.RUNNABLE, false); buildThreads(sb, info, Thread.State.RUNNABLE, true); buildThreads(sb, info, Thread.State.BLOCKED, false); buildThreads(sb, info, Thread.State.WAITING, false); buildThreads(sb, info, Thread.State.TIMED_WAITING, false); buildThreads(sb, info, null, false); log.info(sb.toString()); } private void buildThreads(StringBuilder sb, ThreadInfo []infoArray, Thread.State matchState, boolean isNative) { for (ThreadInfo info : infoArray) { if (info == null) continue; Thread.State state = info.getThreadState(); if (matchState == Thread.State.RUNNABLE && (isNative != info.isInNative())) { continue; } if (state == matchState) buildThread(sb, info); else if (state == null && matchState != Thread.State.RUNNABLE && matchState != Thread.State.BLOCKED && matchState != Thread.State.WAITING && matchState != Thread.State.TIMED_WAITING) { buildThread(sb, info); } } } private void buildThread(StringBuilder sb, ThreadInfo info) { sb.append("\n\""); sb.append(info.getThreadName()); sb.append("\" id=" + info.getThreadId()); sb.append(" " + info.getThreadState()); if (info.isInNative()) sb.append(" (in native)"); if (info.isSuspended()) sb.append(" (suspended)"); String lockName = info.getLockName(); if (lockName != null) { sb.append("\n waiting on "); sb.append(lockName); if (info.getLockOwnerName() != null) { sb.append("\n owned by \""); sb.append(info.getLockOwnerName()); sb.append("\""); } } sb.append("\n"); /* Server server = Server.getCurrent(); if (server != null) { TcpConnection conn = server.findConnectionByThreadId(info.getThreadId()); if (conn != null && conn.getRequest() instanceof AbstractHttpRequest) { AbstractHttpRequest req = (AbstractHttpRequest) conn.getRequest(); if (req.getRequestURI() != null) { sb.append(" ").append(req.getRequestURI()).append("\n"); } } } */ StackTraceElement []stackList = info.getStackTrace(); if (stackList == null) return; for (StackTraceElement stack : stackList) { sb.append(" at "); sb.append(stack.getClassName()); sb.append("."); sb.append(stack.getMethodName()); if (stack.getFileName() != null) { sb.append(" ("); sb.append(stack.getFileName()); if (stack.getLineNumber() > 0) { sb.append(":"); sb.append(stack.getLineNumber()); } sb.append(")"); } if (stack.isNativeMethod()) sb.append(" (native)"); sb.append("\n"); } } static class ThreadCompare implements Comparator<ThreadInfo> { public int compare(ThreadInfo a, ThreadInfo b) { if (a == b) return 0; else if (a == null) return -1; else if (b == null) return 1; else if (a.getThreadState() != b.getThreadState()) return a.getThreadState().ordinal() - b.getThreadState().ordinal(); else if (a.isInNative() && ! b.isInNative()) return 1; else if (b.isInNative() && ! a.isInNative()) return -1; else return a.getThreadName().compareTo(b.getThreadName()); } } }