Last active
October 8, 2019 14:09
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<%@ 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