/*
* ThreadDumpInfo.java
*
* This file is part of TDA - Thread Dump Analysis Tool.
*
* TDA is free software; you can redistribute it and/or modify
* it under the terms of the Lesser GNU General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* TDA 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
* Lesser GNU General Public License for more details.
*
* You should have received a copy of the Lesser GNU General Public License
* along with TDA; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* $Id: ThreadDumpInfo.java,v 1.11 2008-08-13 15:52:19 irockel Exp $
*/
package com.pironet.tda;
/**
* Thread Dump Information Node. It stores structural data about the thread dump
* and provides methods for generating html information for displaying infos about
* the thread dump.
*
* @author irockel
*/
public class ThreadDumpInfo extends AbstractInfo {
private int logLine;
private int overallThreadsWaitingWithoutLocksCount;
private String startTime;
private String overview;
private Analyzer dumpAnalyzer;
private Category waitingThreads;
private Category sleepingThreads;
private Category lockingThreads;
private Category monitors;
private Category monitorsWithoutLocks;
private Category blockingMonitors;
private Category threads;
private Category deadlocks;
private HeapInfo heapInfo;
public ThreadDumpInfo(String name, int lineCount) {
setName(name);
this.logLine = lineCount;
}
/**
* get the log line where to find the starting
* point of this thread dump in the log file
* @return starting point of thread dump in logfile, 0 if none set.
*/
public int getLogLine() {
return logLine;
}
/**
* set the log line where to find the dump in the logfile.
* @param logLine
*/
public void setLogLine(int logLine) {
this.logLine = logLine;
}
/**
* get the approx. start time of the dump represented by this
* node.
* @return start time as string, format may differ as it is just
* parsed from the log file.
*/
public String getStartTime() {
return startTime;
}
/**
* set the start time as string, can be of any format.
* @param startTime the start time as string.
*/
public void setStartTime(String startTime) {
this.startTime = startTime;
}
/**
* get the overview information of this thread dump.
* @return overview information.
*/
public String getOverview() {
if(overview == null) {
createOverview();
}
return overview;
}
/**
* creates the overview information for this thread dump.
*/
private void createOverview() {
StringBuffer statData = new StringBuffer("<body bgcolor=\"#ffffff\"><font face=System " +
"><table border=0><tr bgcolor=\"#dddddd\"><td><font face=System " +
">Overall Thread Count</td><td width=\"150\"></td><td><b><font face=System>");
statData.append(getThreads() == null? 0 : getThreads().getNodeCount());
statData.append("</b></td></tr>\n\n<tr bgcolor=\"#eeeeee\"><td><font face=System" +
">Overall Monitor Count</td><td></td><td><b><font face=System>");
statData.append(getMonitors() == null? 0 : getMonitors().getNodeCount());
statData.append("</b></td></tr>\n\n<tr bgcolor=\"#dddddd\"><td><font face=System " +
">Number of threads waiting for a monitor</td><td></td><td><b><font face=System>");
statData.append(getWaitingThreads() == null? 0 : getWaitingThreads().getNodeCount());
statData.append("</b></td></tr>\n\n<tr bgcolor=\"#eeeeee\"><td><font face=System " +
">Number of threads locking a monitor</td><td></td><td><b><font face=System size>");
statData.append(getLockingThreads() == null? 0 : getLockingThreads().getNodeCount());
statData.append("</b></td></tr>\n\n<tr bgcolor=\"#dddddd\"><td><font face=System " +
">Number of threads sleeping on a monitor</td><td></td><td><b><font face=System>");
statData.append(getSleepingThreads() == null? 0 : getSleepingThreads().getNodeCount());
statData.append("</b></td></tr>\n\n<tr bgcolor=\"#eeeeee\"><td><font face=System " +
">Number of deadlocks</td><td></td><td><b><font face=System>");
statData.append(getDeadlocks() == null? 0 : getDeadlocks().getNodeCount());
statData.append("</b></td></tr>\n\n<tr bgcolor=\"#dddddd\"><td><font face=System " +
">Number of Monitors without locking threads</td><td></td><td><b><font face=System>");
statData.append(getMonitorsWithoutLocks() == null? 0 : getMonitorsWithoutLocks().getNodeCount());
statData.append("</b></td></tr>");
// add hints concerning possible hot spots found in this thread dump.
statData.append(getDumpAnalyzer().analyzeDump());
if(getHeapInfo() != null) {
statData.append(getHeapInfo());
}
statData.append("</table>");
setOverview(statData.toString());
}
/**
* generate a monitor info node from the given information.
* @param locks how many locks are on this monitor?
* @param waits how many threads are waiting for this monitor?
* @param sleeps how many threads have a lock on this monitor and are sleeping?
* @return a info node for the monitor.
*/
public static String getMonitorInfo(int locks, int waits, int sleeps ) {
StringBuffer statData = new StringBuffer("<body bgcolor=\"ffffff\"><table border=0 bgcolor=\"#dddddd\"><tr><td><font face=System" +
">Threads locking monitor</td><td><b><font face=System>");
statData.append(locks);
statData.append("</b></td></tr>\n\n<tr bgcolor=\"#eeeeee\"><td>");
statData.append("<font face=System>Threads sleeping on monitor</td><td><b><font face=System>");
statData.append(sleeps);
statData.append("</b></td></tr>\n\n<tr><td>");
statData.append("<font face=System>Threads waiting to lock monitor</td><td><b><font face=System>");
statData.append(waits);
statData.append("</b></td></tr>\n\n");
if (locks == 0) {
statData.append("<tr bgcolor=\"#ffffff\"<td></td></tr>");
// See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5086475
statData.append("<tr bgcolor=\"#cccccc\"><td><font face=System> " +
"<p>This monitor doesn't have a thread locking it. This means one of the following is true:</p>" +
"<ul><li>a VM Thread is holding it." +
"<li>This lock is a <tt>java.util.concurrent</tt> lock and the thread holding it is not reported in the stack trace" +
"because the JVM option -XX:+PrintConcurrentLocks is not present." +
"<li>This lock is a custom java.util.concurrent lock either not based off of" +
" <tt>AbstractOwnableSynchronizer</tt> or not setting the exclusive owner when a lock is granted.</ul>");
statData.append("If you see many monitors having no locking thread (and the latter two conditions above do" +
"not apply), this usually means the garbage collector is running.<br>");
statData.append("In this case you should consider analyzing the Garbage Collector output. If the dump has many monitors with no locking thread<br>");
statData.append("a click on the <a href=\"dump://\">dump node</a> will give you additional information.<br></td></tr>");
}
if (areALotOfWaiting(waits)) {
statData.append("<tr bgcolor=\"#ffffff\"<td></td></tr>");
statData.append("<tr bgcolor=\"#cccccc\"><td><font face=System " +
"<p>A lot of threads are waiting for this monitor to become available again.</p><br>");
statData.append("This might indicate a congestion. You also should analyze other locks blocked by threads waiting<br>");
statData.append("for this monitor as there might be much more threads waiting for it.<br></td></tr>");
}
statData.append("</table>");
return (statData.toString());
}
/**
* checks if a lot of threads are waiting
* @param waits the wait to check
* @return true if a lot of threads are waiting.
*/
public static boolean areALotOfWaiting(int waits) {
return(waits > 5);
}
/**
* set the overview information of this thread dump.
* @param overview the infos to be displayed (in html)
*/
public void setOverview(String overview) {
this.overview = overview;
}
public Category getWaitingThreads() {
return waitingThreads;
}
public void setWaitingThreads(Category waitingThreads) {
this.waitingThreads = waitingThreads;
}
public Category getSleepingThreads() {
return sleepingThreads;
}
public void setSleepingThreads(Category sleepingThreads) {
this.sleepingThreads = sleepingThreads;
}
public Category getLockingThreads() {
return lockingThreads;
}
public void setLockingThreads(Category lockingThreads) {
this.lockingThreads = lockingThreads;
}
public Category getMonitors() {
return monitors;
}
public void setMonitors(Category monitors) {
this.monitors = monitors;
}
public Category getBlockingMonitors() {
return blockingMonitors;
}
public void setBlockingMonitors(Category blockingMonitors) {
this.blockingMonitors = blockingMonitors;
}
public Category getMonitorsWithoutLocks() {
return monitorsWithoutLocks;
}
public void setMonitorsWithoutLocks(Category monitorsWithoutLocks) {
this.monitorsWithoutLocks = monitorsWithoutLocks;
}
public Category getThreads() {
return threads;
}
public void setThreads(Category threads) {
this.threads = threads;
}
public Category getDeadlocks() {
return deadlocks;
}
public void setDeadlocks(Category deadlocks) {
this.deadlocks = deadlocks;
}
private Analyzer getDumpAnalyzer() {
if(dumpAnalyzer == null) {
setDumpAnalyzer(new Analyzer(this));
}
return dumpAnalyzer;
}
private void setDumpAnalyzer(Analyzer dumpAnalyzer) {
this.dumpAnalyzer = dumpAnalyzer;
}
public int getOverallThreadsWaitingWithoutLocksCount() {
return overallThreadsWaitingWithoutLocksCount;
}
public void setOverallThreadsWaitingWithoutLocksCount(int overallThreadsWaitingWithoutLocksCount) {
this.overallThreadsWaitingWithoutLocksCount = overallThreadsWaitingWithoutLocksCount;
}
/**
* add given category to the custom category.
* @param cat
*/
public void addToCustomCategories(Category cat) {
}
/**
* get the set heap info
* @return the set heap info object (only available if the thread
* dump is from Sun JDK 1.6 so far.
*/
public HeapInfo getHeapInfo() {
return(heapInfo);
}
/**
* set the heap information for this thread dump.
* @param value the heap information as string.
*/
public void setHeapInfo(HeapInfo value) {
heapInfo = value;
}
/**
* string representation of this node, is used to displayed the node info
* in the tree.
* @return the thread dump information (one line).
*/
@Override
public String toString() {
StringBuffer postFix = new StringBuffer();
if(logLine > 0) {
postFix.append(" at line " + getLogLine());
}
if(startTime != null) {
postFix.append(" around " + startTime);
}
return(getName() + postFix);
}
}