Skip to content

Commit f1ef2a2

Browse files
committed
Handle missing stack traces in ExtendedThreadInformation
Fix ArrayIndexOutOfBoundsException on invocation of Message.getFormattedMessage() when any thread has no stack trace, which occurs on some JVM implementations.
1 parent 3709962 commit f1ef2a2

File tree

2 files changed

+57
-2
lines changed

2 files changed

+57
-2
lines changed

log4j-core-test/src/test/java/org/apache/logging/log4j/core/message/ExtendedThreadInformationTest.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,17 @@
1616
*/
1717
package org.apache.logging.log4j.core.message;
1818

19+
import static org.hamcrest.CoreMatchers.containsString;
20+
import static org.hamcrest.MatcherAssert.assertThat;
1921
import static org.junit.jupiter.api.Assertions.assertTrue;
22+
import static org.mockito.Mockito.mock;
23+
import static org.mockito.Mockito.when;
2024

25+
import java.lang.management.ThreadInfo;
2126
import org.apache.logging.log4j.message.ThreadDumpMessage;
2227
import org.junit.jupiter.api.Test;
28+
import org.junit.jupiter.params.ParameterizedTest;
29+
import org.junit.jupiter.params.provider.EnumSource;
2330

2431
/**
2532
* Tests that ThreadDumpMessage uses ExtendedThreadInformation when available.
@@ -33,4 +40,42 @@ void testMessage() {
3340
// System.out.print(message);
3441
assertTrue(message.contains(" Id="), "No header");
3542
}
43+
44+
@ParameterizedTest
45+
@EnumSource(Thread.State.class)
46+
void testMessageWithNullStackTrace(final Thread.State state) {
47+
obtainMessageWithMissingStackTrace(state, null);
48+
}
49+
50+
@ParameterizedTest
51+
@EnumSource(Thread.State.class)
52+
void testMessageWithEmptyStackTrace(final Thread.State state) {
53+
obtainMessageWithMissingStackTrace(state, new StackTraceElement[0]);
54+
}
55+
56+
private void obtainMessageWithMissingStackTrace(final Thread.State state, final StackTraceElement[] stackTrace) {
57+
// setup
58+
final String threadName = "the thread name";
59+
final long threadId = 23523L;
60+
61+
final ThreadInfo threadInfo = mock(ThreadInfo.class);
62+
when(threadInfo.getStackTrace()).thenReturn(stackTrace);
63+
when(threadInfo.getThreadName()).thenReturn(threadName);
64+
when(threadInfo.getThreadId()).thenReturn(threadId);
65+
when(threadInfo.isSuspended()).thenReturn(true);
66+
when(threadInfo.isInNative()).thenReturn(true);
67+
when(threadInfo.getThreadState()).thenReturn(state);
68+
69+
// given
70+
final ExtendedThreadInformation sut = new ExtendedThreadInformation(threadInfo);
71+
72+
// when
73+
final StringBuilder result = new StringBuilder();
74+
sut.printThreadInfo(result);
75+
76+
// then
77+
assertThat(result.toString(), containsString(threadName));
78+
assertThat(result.toString(), containsString(state.name()));
79+
assertThat(result.toString(), containsString(String.valueOf(threadId)));
80+
}
3681
}

log4j-core/src/main/java/org/apache/logging/log4j/core/message/ExtendedThreadInformation.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ private void formatState(final StringBuilder sb, final ThreadInfo info) {
119119
break;
120120
}
121121
case WAITING: {
122-
final StackTraceElement element = info.getStackTrace()[0];
122+
final StackTraceElement element = getStackTraceElement(info);
123123
final String className = element.getClassName();
124124
final String method = element.getMethodName();
125125
if (className.equals("java.lang.Object") && method.equals("wait")) {
@@ -144,7 +144,7 @@ private void formatState(final StringBuilder sb, final ThreadInfo info) {
144144
break;
145145
}
146146
case TIMED_WAITING: {
147-
final StackTraceElement element = info.getStackTrace()[0];
147+
final StackTraceElement element = getStackTraceElement(info);
148148
final String className = element.getClassName();
149149
final String method = element.getMethodName();
150150
if (className.equals("java.lang.Object") && method.equals("wait")) {
@@ -174,4 +174,14 @@ private void formatState(final StringBuilder sb, final ThreadInfo info) {
174174
break;
175175
}
176176
}
177+
178+
private static StackTraceElement getStackTraceElement(final ThreadInfo info) {
179+
final StackTraceElement[] stackTrace = info.getStackTrace();
180+
if (stackTrace == null || stackTrace.length < 1) {
181+
final String textualInfo = "Unknown";
182+
return new StackTraceElement(textualInfo, textualInfo, textualInfo, -1);
183+
}
184+
185+
return stackTrace[0];
186+
}
177187
}

0 commit comments

Comments
 (0)