IDEA-147000 Support YourKit thread dump format in Analyze stacktrace

This commit is contained in:
peter
2017-05-03 16:55:49 +02:00
parent 60a37218b3
commit 487e04ef3c
4 changed files with 82 additions and 26 deletions
@@ -21,7 +21,6 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -31,6 +30,7 @@ import java.util.regex.Pattern;
*/
public class ThreadDumpParser {
private static final Pattern ourThreadStartPattern = Pattern.compile("^\\s*\"(.+)\".+(prio=\\d+ (?:os_prio=[^\\s]+ )?tid=[^\\s]+ nid=[^\\s]+|[Ii][Dd]=\\d+) ([^\\[]+)");
private static final Pattern ourYourkitThreadStartPattern = Pattern.compile("(.+) \\[([^\\[]*)]");
private static final Pattern ourThreadStatePattern = Pattern.compile("java\\.lang\\.Thread\\.State: (.+) \\((.+)\\)");
private static final Pattern ourThreadStatePattern2 = Pattern.compile("java\\.lang\\.Thread\\.State: (.+)");
private static final Pattern ourWaitingForLockPattern = Pattern.compile("- waiting (on|to lock) <(.+)>");
@@ -197,6 +197,16 @@ public class ThreadDumpParser {
}
return state;
}
String noDaemon = StringUtil.trimEnd(line, " [DAEMON]");
m = ourYourkitThreadStartPattern.matcher(noDaemon);
if (m.find()) {
ThreadState state = new ThreadState(m.group(1), m.group(2));
if (noDaemon.length() < line.length()) {
state.setDaemon(true);
}
return state;
}
return null;
}
@@ -62,18 +62,28 @@ public class ExceptionWorkerTest extends LightCodeInsightFixtureTestCase {
public void testAnomalyParenthesisParsing() {
String[][] data = new String[][]{
{"at youtrack.jetbrains.com.Issue.IDEA_125137()(FooTest.groovy:2)", "youtrack.jetbrains.com.Issue", "IDEA_125137()",
{"at youtrack.jetbrains.com.Issue.IDEA_125137()(FooTest.groovy:2)\n", "youtrack.jetbrains.com.Issue", "IDEA_125137()",
"FooTest.groovy:2"},
{"at youtrack.jetbrains.com.Issue.IDEA_125137()Hmm(FooTest.groovy:2)", "youtrack.jetbrains.com.Issue", "IDEA_125137()Hmm",
{"at youtrack.jetbrains.com.Issue.IDEA_125137()Hmm(FooTest.groovy:2)\n", "youtrack.jetbrains.com.Issue", "IDEA_125137()Hmm",
"FooTest.groovy:2"},
{"p1.Cl.mee(p1.Cl.java:87) (A MESSAGE) IDEA-133794 (BUG START WITH 1)", "p1.Cl", "mee", "p1.Cl.java:87"}
{"p1.Cl.mee(p1.Cl.java:87) (A MESSAGE) IDEA-133794 (BUG START WITH 1)\n", "p1.Cl", "mee", "p1.Cl.java:87"}
};
for (String[] datum : data) {
Trinity<TextRange, TextRange, TextRange> trinity = ExceptionWorker.parseExceptionLine(datum[0]);
assertNotNull(trinity);
assertEquals(datum[1], trinity.first.subSequence(datum[0]));
assertEquals(datum[2], trinity.second.subSequence(datum[0]));
assertEquals(datum[3], trinity.third.subSequence(datum[0]));
assertParsed(datum[0], datum[1], datum[2], datum[3]);
}
}
private static void assertParsed(String line, String className, String methodName, String fileLine) {
assertTrue(line.endsWith("\n"));
Trinity<TextRange, TextRange, TextRange> trinity = ExceptionWorker.parseExceptionLine(line);
assertNotNull(trinity);
assertEquals(className, trinity.first.subSequence(line));
assertEquals(methodName, trinity.second.subSequence(line));
assertEquals(fileLine, trinity.third.subSequence(line));
}
public void testYourKitFormat() {
assertParsed("com.intellij.util.concurrency.Semaphore.waitFor(long) Semaphore.java:89\n",
"com.intellij.util.concurrency.Semaphore", "waitFor", "Semaphore.java:89");
}
}
@@ -96,5 +96,28 @@ class ThreadDumpParserTest extends TestCase {
assert threads[0].isAwaitedBy(threads[1])
}
void "test YourKit format"() {
def text = """
Stacks at 2017-05-03 01:07:25 PM (uptime 4h 21m 28s) Threads shown: 38 of 46
ApplicationImpl pooled thread 228 [WAITING]
java.lang.Thread.run() Thread.java:745
ApplicationImpl pooled thread 234 [WAITING] [DAEMON]
java.lang.Thread.run() Thread.java:745
ApplicationImpl pooled thread 6 [RUNNABLE, IN_NATIVE]
java.net.DatagramSocket.receive(DatagramPacket) DatagramSocket.java:812
com.intellij.a.f.a.c.a() c.java:60
com.intellij.a.f.a.d.run() d.java:20
java.lang.Thread.run() Thread.java:745
"""
def threads = ThreadDumpParser.parse(text)
assert threads.collect { it.name } == ['ApplicationImpl pooled thread 228', 'ApplicationImpl pooled thread 234', 'ApplicationImpl pooled thread 6']
assert threads.collect { it.state } == ['WAITING', 'WAITING', 'RUNNABLE, IN_NATIVE']
assert threads.collect { it.daemon } == [false, true, false]
assert threads.collect { it.stackTrace.readLines().size() } == [2, 2, 5] // thread name is included into stack trace
}
}
@@ -167,24 +167,14 @@ public class ExceptionWorker {
return myInfo;
}
//todo [roma] regexp
@Nullable
static Trinity<TextRange, TextRange, TextRange> parseExceptionLine(final String line) {
int startIdx;
if (line.startsWith(AT_PREFIX)){
startIdx = 0;
}
else{
startIdx = line.indexOf(STANDALONE_AT);
if (startIdx < 0) {
startIdx = line.indexOf(AT_PREFIX);
}
private static int findAtPrefix(String line) {
if (line.startsWith(AT_PREFIX)) return 0;
if (startIdx < 0) {
startIdx = -1;
}
}
int startIdx = line.indexOf(STANDALONE_AT);
return startIdx < 0 ? line.indexOf(AT_PREFIX) : startIdx;
}
private static int findFirstRParenAfterDigit(String line) {
int rParenIdx = -1;
int rParenCandidate = line.lastIndexOf(')');
//Looking for minimal position for ')' after a digit
@@ -194,6 +184,15 @@ public class ExceptionWorker {
}
rParenCandidate = line.lastIndexOf(')', rParenCandidate - 1);
}
return rParenIdx;
}
@Nullable
static Trinity<TextRange, TextRange, TextRange> parseExceptionLine(final String line) {
int startIdx = findAtPrefix(line);
TextRange yourKitLink = startIdx < 0 ? getYourKitLinkRange(line) : null;
int rParenIdx = yourKitLink != null ? yourKitLink.getEndOffset() - 2 : findFirstRParenAfterDigit(line);
if (rParenIdx < 0) return null;
final int lParenIdx = line.lastIndexOf('(', rParenIdx);
@@ -207,7 +206,21 @@ public class ExceptionWorker {
// class, method, link
return Trinity.create(new TextRange(classNameIdx, handleSpaces(line, dotIdx, -1)),
new TextRange(handleSpaces(line, dotIdx + 1, 1), handleSpaces(line, lParenIdx, -1)),
new TextRange(lParenIdx + 1, rParenIdx));
yourKitLink != null ? yourKitLink : new TextRange(lParenIdx + 1, rParenIdx));
}
@Nullable
private static TextRange getYourKitLinkRange(String line) {
int lineEnd = line.length() - 1;
if (lineEnd > 0 && line.charAt(lineEnd) == '\n') lineEnd--;
if (lineEnd > 0 && Character.isDigit(line.charAt(lineEnd))) {
int spaceIndex = line.lastIndexOf(' ');
int rParenIdx = line.lastIndexOf(')');
if (rParenIdx > 0 && spaceIndex == rParenIdx + 1) {
return new TextRange(spaceIndex + 1, lineEnd + 1);
}
}
return null;
}
private static int handleSpaces(String line, int pos, int delta) {