Skip to content

Instantly share code, notes, and snippets.

@stoerr
Last active October 8, 2019 14:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stoerr/65aa40f262558d9c0453f43d96250e7c to your computer and use it in GitHub Desktop.
Save stoerr/65aa40f262558d9c0453f43d96250e7c to your computer and use it in GitHub Desktop.
Configurable dump of all threads with a configurable set of stati, equivalent stacktraces collected
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<%--
This JSP lists all threads with stati as configured in a checklist, optionally whose names match a specified regex.
If two threads have the same stacktrace, they are shown together and the stacktrace is printed only once.
--%>
<%@page import="java.util.*"%>
<%@page import="javax.servlet.*"%>
<%@page import="java.io.*"%>
<%@page import="java.util.regex.*"%>
<%@page import="java.util.stream.Collectors"%>
<%@page import="org.apache.commons.lang3.tuple.Pair"%>
<%@page import="org.apache.commons.lang3.StringUtils"%>
<%
final String PARAM_STATE = "state";
final String PARAM_NAMEREGEX = "nameregex";
class ThreaddumpRunner {
protected final JspWriter writer;
protected final HttpServletRequest request;
protected Set<Thread.State> stati = Collections.singleton(Thread.State.RUNNABLE);
protected String nameRegexStr = "";
protected Pattern nameRegex = Pattern.compile(nameRegexStr);
public ThreaddumpRunner(JspWriter writer, HttpServletRequest request) throws Exception {
this.writer = writer;
this.request = request;
String[] statiArray = request.getParameterValues(PARAM_STATE);
if (statiArray != null && statiArray.length > 0) {
stati = new HashSet<>();
for (String stateStr : statiArray) {
Thread.State state = Thread.State.valueOf(stateStr);
stati.add(state);
}
}
if (StringUtils.isNotBlank(request.getParameter(PARAM_NAMEREGEX))) {
nameRegexStr = request.getParameter(PARAM_NAMEREGEX);
try {
nameRegex = Pattern.compile(nameRegexStr);
} catch (PatternSyntaxException e) {
writer.println("<p><strong>Regex syntax error: " + e + "</strong></p>");
}
}
}
public void print() throws Exception {
printForm();
writer.println("<p>Since many threads share the same stacktrace, threads with the " +
"same stacktrace are grouped together.</p><br>");
printThreads();
}
protected void printThreads() throws Exception {
writer.println("<ul>");
Map<Thread, StackTraceElement[]> traceMap = Thread.getAllStackTraces();
List<Pair<Thread, String>> traces = traceMap.entrySet().stream()
.map((e) -> Pair.of(e.getKey(), stackTrace(e.getValue())))
.filter((e) -> nameRegex.matcher(e.getLeft().getName()).find())
.filter((e) -> stati.contains(e.getLeft().getState()))
.collect(Collectors.toList());
Collections.sort(traces, Comparator.comparing((e) -> e.getLeft().getName()));
// Since many many traces have the same stacktrace, we group them by stacktrace.
Map<String, List<Pair<Thread, String>>> tracesGrouped = new TreeMap<>(traces.stream()
.collect(Collectors.groupingBy((e) -> e.getRight())));
List<Pair<String, List<Thread>>> tracesGroupedByStacktrace = tracesGrouped.entrySet().stream()
.map(e -> Pair.of(e.getKey(), extractThreads(e.getValue())))
.collect(Collectors.toList());
Collections.sort(tracesGroupedByStacktrace, Comparator.comparing((e) -> e.getRight().get(0).getName()));
for (Pair<String, List<Thread>> traceAndThreads : tracesGroupedByStacktrace) {
writer.println("<li>");
for (Thread t : traceAndThreads.getRight()) {
writer.print(t.getId());
writer.print(" (" + t.getState() + ")");
writer.print(" : ");
writer.print(t.getName());
writer.println("<br/>");
}
writer.println("<pre>");
writer.println(traceAndThreads.getLeft());
writer.println("</pre>");
writer.println("</li>");
}
writer.println("</ul>");
}
private List<Thread> extractThreads(List<Pair<Thread, String>> threadTraces) {
return threadTraces.stream()
.map((e) -> e.getLeft())
.collect(Collectors.toList());
}
protected String stackTrace(StackTraceElement[] stackTraceElements) {
StringBuffer buf = new StringBuffer();
for (StackTraceElement stackTraceElement : stackTraceElements) {
buf.append(" at ")
.append(stackTraceElement)
.append("\n");
}
return buf.toString();
}
protected void printForm() throws Exception {
writer.println("<form action=\"" + request.getRequestURL() + "\" method=\"get\">");
writer.println("Print only threads of stati ");
for (Thread.State value : Thread.State.values()) {
String checked = stati.contains(value) ? "checked" : "";
writer.println(" <input type=\"checkbox\" name=\"state\" value=\"" + value.name() + "\" "
+ checked + "> " + value);
}
writer.println(" with names matching regex <input type=\"text\" name=\"nameregex\" value=\"" + nameRegex + "\">");
writer.println(" <input type=\"submit\">\n");
writer.println("</form>");
}
}
out.println("<html><body><h2>Thread dump</h2>");
new ThreaddumpRunner(out, request).print();
out.println("</body></html>");
%>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment