Skip to content

Commit ff7fa6f

Browse files
authored
more stack frames for thread dump (#3644)
1 parent cd4077f commit ff7fa6f

File tree

1 file changed

+85
-2
lines changed

1 file changed

+85
-2
lines changed

apm-agent-core/src/main/java/co/elastic/apm/agent/util/ThreadDump.java

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,16 @@
2828
import java.lang.management.ManagementFactory;
2929
import java.lang.management.ThreadInfo;
3030
import java.lang.management.ThreadMXBean;
31+
import java.lang.management.MonitorInfo;
32+
import java.lang.management.LockInfo;
3133
import java.util.concurrent.ScheduledThreadPoolExecutor;
3234
import java.util.concurrent.TimeUnit;
3335

3436
public class ThreadDump extends AbstractLifecycleListener {
3537

36-
private Logger log = LoggerFactory.getLogger(ThreadDump.class);
38+
private static final Logger log = LoggerFactory.getLogger(ThreadDump.class);
39+
40+
private static final int MAX_FRAMES = 40;
3741

3842
@Nullable
3943
private ScheduledThreadPoolExecutor executor;
@@ -66,7 +70,7 @@ public void run() {
6670
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
6771
StringBuilder sb = new StringBuilder();
6872
for (ThreadInfo threadInfo : threadMXBean.dumpAllThreads(true, true)) {
69-
sb.append(threadInfo.toString());
73+
sb.append(ThreadDump.toString(threadInfo));
7074
}
7175

7276
log.debug("thread dump: \n\n {}", sb);
@@ -82,4 +86,83 @@ public void stop() throws Exception {
8286
}
8387
ExecutorUtils.shutdownAndWaitTermination(executor);
8488
}
89+
90+
/**
91+
* Copy of {@link ThreadInfo#toString()} with a higher frame size as default implementation.
92+
*
93+
* @param threadInfo thread info
94+
* @return thread info as string
95+
*/
96+
private static String toString(ThreadInfo threadInfo) {
97+
StringBuilder sb = new StringBuilder("\"" + threadInfo.getThreadName() + "\"" +
98+
// note: daemon and priority now available < java9
99+
" Id=" + threadInfo.getThreadId() + " " +
100+
threadInfo.getThreadState());
101+
if (threadInfo.getLockName() != null) {
102+
sb.append(" on " + threadInfo.getLockName());
103+
}
104+
if (threadInfo.getLockOwnerName() != null) {
105+
sb.append(" owned by \"" + threadInfo.getLockOwnerName() +
106+
"\" Id=" + threadInfo.getLockOwnerId());
107+
}
108+
if (threadInfo.isSuspended()) {
109+
sb.append(" (suspended)");
110+
}
111+
if (threadInfo.isInNative()) {
112+
sb.append(" (in native)");
113+
}
114+
sb.append('\n');
115+
116+
StackTraceElement[] stackTrace = threadInfo.getStackTrace();
117+
int i = 0;
118+
for (; i < stackTrace.length && i < MAX_FRAMES; i++) {
119+
StackTraceElement ste = stackTrace[i];
120+
sb.append("\tat " + ste.toString());
121+
sb.append('\n');
122+
if (i == 0 && threadInfo.getLockInfo() != null) {
123+
Thread.State ts = threadInfo.getThreadState();
124+
switch (ts) {
125+
case BLOCKED:
126+
sb.append("\t- blocked on " + threadInfo.getLockInfo());
127+
sb.append('\n');
128+
break;
129+
case WAITING:
130+
sb.append("\t- waiting on " + threadInfo.getLockInfo());
131+
sb.append('\n');
132+
break;
133+
case TIMED_WAITING:
134+
sb.append("\t- waiting on " + threadInfo.getLockInfo());
135+
sb.append('\n');
136+
break;
137+
default:
138+
}
139+
}
140+
141+
for (MonitorInfo mi : threadInfo.getLockedMonitors()) {
142+
if (mi.getLockedStackDepth() == i) {
143+
sb.append("\t- locked " + mi);
144+
sb.append('\n');
145+
}
146+
}
147+
}
148+
if (i < stackTrace.length) {
149+
sb.append("\t...");
150+
sb.append('\n');
151+
}
152+
153+
LockInfo[] locks = threadInfo.getLockedSynchronizers();
154+
if (locks.length > 0) {
155+
sb.append("\n\tNumber of locked synchronizers = " + locks.length);
156+
sb.append('\n');
157+
for (LockInfo li : locks) {
158+
sb.append("\t- " + li);
159+
sb.append('\n');
160+
}
161+
}
162+
sb.append('\n');
163+
return sb.toString();
164+
165+
}
166+
167+
85168
}

0 commit comments

Comments
 (0)