/*
* Copyright 2014, The Sporting Exchange Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.betfair.cougar.core.impl.jmx;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.betfair.cougar.core.api.jmx.JMXHttpParser;
/**
* Borrowed from Joe's dodgy JSP. Totally uncleaned for posterity so everyone
* can see some original Stapleton hackery...
*
*/
public class ThreadDumper implements JMXHttpParser {
@Override
public String getPath() {
return "threaddump.jsp";
}
@Override
public String process(Map<String, String> params) {
ThreadMXBean mx = ManagementFactory.getThreadMXBean();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream out = new PrintStream(baos);
// process commands
String command = params.get("command");
if (command != null) {
if (command.equals("toggleCM")) {
if (mx.isThreadContentionMonitoringEnabled()) {
mx.setThreadContentionMonitoringEnabled(false);
} else {
mx.setThreadContentionMonitoringEnabled(true);
}
} else if (command.equals("resetPeak")) {
mx.resetPeakThreadCount();
}
}
out.println("Live thread count: " + mx.getThreadCount() + "(of which " + mx.getDaemonThreadCount() + " are daemons)<br>");
out.println("Peak thread count: " + mx.getPeakThreadCount() + "(<a href='?command=resetPeak'>reset</a>)<br>");
out.println("Total started thread count: " + mx.getTotalStartedThreadCount() + "<br>");
out.println("Contention monitoring enabled: "
+ mx.isThreadContentionMonitoringEnabled()
+ "("
+ (mx.isThreadContentionMonitoringSupported() ? ("<a href='?command=toggleCM'>toggle</a> - switch off then on again to reset thread timings")
: "not supported") + ") <br>");
out.println("\tThread CPU monitoring enabled: " + mx.isThreadCpuTimeEnabled() + "(" + (mx.isThreadCpuTimeSupported() ? "" : "not ") + "supported)"
+ "<br>");
out.println("<pre>");
out.println("Report generated at " + new java.util.Date());
long[] deadLockedIds = mx.findMonitorDeadlockedThreads();
ThreadInfo[] infos;
if (deadLockedIds != null) {
infos = mx.getThreadInfo(deadLockedIds, Integer.MAX_VALUE);
out.println("\nDead locks:");
for (int i = 0; i < infos.length; i++) {
out.println(toStringThreadInfo(mx, infos[i]));
}
}
infos = mx.getThreadInfo(mx.getAllThreadIds(), Integer.MAX_VALUE);
Map blockers = new HashMap();
StringBuffer buf = new StringBuffer();
buf.append("\nAll threads:\n");
for (int i = 0; i < infos.length; i++) {
buf.append(toStringThreadInfo(mx, infos[i])).append("\n");
String lockOwner = infos[i].getLockOwnerName();
if (lockOwner != null) {
List blockees = (List) blockers.get(lockOwner);
if (blockees == null) {
blockees = new ArrayList();
blockers.put(lockOwner, blockees);
}
blockees.add(infos[i].getThreadName());
}
}
if (blockers.size() > 0) {
out.println("\nBlockers:");
Iterator i = blockers.keySet().iterator();
while (i.hasNext()) {
String blocker = (String) i.next();
out.println("\t" + blocker + " is blocking:");
Iterator j = ((ArrayList) blockers.get(blocker)).iterator();
while (j.hasNext()) {
out.println("\t\t" + j.next());
}
}
}
out.println(buf.toString());
out.println("</pre>");
return new String(baos.toByteArray());
}
private static String toStringThreadInfo(ThreadMXBean mx, ThreadInfo info) {
StringBuffer buf = new StringBuffer();
buf.append("\tid:").append(info.getThreadId()).append(" name:'").append(info.getThreadName()).append("'").append(" state:").append(
info.getThreadState());
if (info.getLockName() != null) {
buf.append(" monitor:'").append(info.getLockName()).append("'").append(" ownerId:").append(info.getLockOwnerId()).append(" ownerName:'").append(
info.getLockOwnerName()).append("'");
}
buf.append("\n\tCPUTime(ns): ").append(mx.getThreadCpuTime(info.getThreadId())).append("\tUserTime(ns): ").append(
mx.getThreadUserTime(info.getThreadId())).append(", inNative: ").append(info.isInNative()).append(", suspended: ").append(info.isSuspended())
.append(", blockedCount: ").append(info.getBlockedCount()).append(", blockedTime(ms)").append(info.getBlockedTime()).append(", waitedCount: ")
.append(info.getWaitedCount()).append(", waitedTime(ms): ").append(info.getWaitedTime());
StackTraceElement[] traces = info.getStackTrace();
if (traces != null) {
buf.append("\n");
for (int i = 0; i < traces.length; i++) {
buf.append("\t\t").append(traces[i]).append("\n");
}
}
return buf.toString();
}
}