2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com
.intellij
.unscramble
;
18 import com
.intellij
.openapi
.util
.text
.StringUtil
;
19 import org
.jetbrains
.annotations
.NonNls
;
20 import org
.jetbrains
.annotations
.Nullable
;
22 import java
.util
.ArrayList
;
23 import java
.util
.Collections
;
24 import java
.util
.Comparator
;
25 import java
.util
.List
;
26 import java
.util
.regex
.Matcher
;
27 import java
.util
.regex
.Pattern
;
32 public class ThreadDumpParser
{
33 private static final Pattern ourThreadStartPattern
= Pattern
.compile("^\"(.+)\".+prio=\\d+ tid=[^\\s]+ nid=[^\\s]+ ([^\\[]+)");
34 private static final Pattern ourThreadStatePattern
= Pattern
.compile("java\\.lang\\.Thread\\.State: (.+) \\((.+)\\)");
35 private static final Pattern ourThreadStatePattern2
= Pattern
.compile("java\\.lang\\.Thread\\.State: (.+)");
36 private static final Pattern ourWaitingForLockPattern
= Pattern
.compile("- waiting (on|to lock) <(.+)>");
37 private static final Pattern ourParkingToWaitForLockPattern
= Pattern
.compile("- parking to wait for <(.+)>");
38 @NonNls private static final String PUMP_EVENT
= "java.awt.EventDispatchThread.pumpOneEventForFilters";
39 private static final Pattern ourIdleTimerThreadPattern
= Pattern
.compile("java.lang.Object.wait\\([^()]+\\)\\s+at java.util.TimerThread.mainLoop");
40 private static final Pattern ourIdleSwingTimerThreadPattern
= Pattern
.compile("java.lang.Object.wait\\([^()]+\\)\\s+at javax.swing.TimerQueue.run");
42 private ThreadDumpParser() {
45 public static List
<ThreadState
> parse(String threadDump
) {
46 List
<ThreadState
> result
= new ArrayList
<ThreadState
>();
47 StringBuilder lastThreadStack
= new StringBuilder();
48 ThreadState lastThreadState
= null;
49 boolean expectingThreadState
= false;
50 boolean haveNonEmptyStackTrace
= false;
51 for(@NonNls String line
: StringUtil
.tokenize(threadDump
, "\r\n")) {
52 if (line
.startsWith("============") || line
.contains("Java-level deadlock")) {
55 ThreadState state
= tryParseThreadStart(line
);
57 if (lastThreadState
!= null) {
58 lastThreadState
.setStackTrace(lastThreadStack
.toString(), !haveNonEmptyStackTrace
);
60 lastThreadState
= state
;
61 result
.add(lastThreadState
);
62 lastThreadStack
.setLength(0);
63 haveNonEmptyStackTrace
= false;
64 lastThreadStack
.append(line
).append("\n");
65 expectingThreadState
= true;
68 boolean parsedThreadState
= false;
69 if (expectingThreadState
) {
70 expectingThreadState
= false;
71 parsedThreadState
= tryParseThreadState(line
, lastThreadState
);
73 lastThreadStack
.append(line
).append("\n");
74 if (!parsedThreadState
&& line
.trim().startsWith("at")) {
75 haveNonEmptyStackTrace
= true;
79 if (lastThreadState
!= null) {
80 lastThreadState
.setStackTrace(lastThreadStack
.toString(), !haveNonEmptyStackTrace
);
82 for(ThreadState threadState
: result
) {
83 inferThreadStateDetail(threadState
);
85 final String s
= findWaitingForLock(threadState
.getStackTrace());
87 for(ThreadState lockOwner
: result
) {
88 if (lockOwner
== threadState
) {
91 final String marker
= "- locked <" + s
+ ">";
92 if (lockOwner
.getStackTrace().contains(marker
)) {
93 if (threadState
.isAwaitedBy(lockOwner
)) {
94 threadState
.addDeadlockedThread(lockOwner
);
95 lockOwner
.addDeadlockedThread(threadState
);
97 lockOwner
.addWaitingThread(threadState
);
107 public static void sortThreads(List
<ThreadState
> result
) {
108 Collections
.sort(result
, new Comparator
<ThreadState
>() {
109 public int compare(final ThreadState o1
, final ThreadState o2
) {
110 return getInterestLevel(o2
) - getInterestLevel(o1
);
116 private static String
findWaitingForLock(final String stackTrace
) {
117 Matcher m
= ourWaitingForLockPattern
.matcher(stackTrace
);
121 m
= ourParkingToWaitForLockPattern
.matcher(stackTrace
);
128 private static int getInterestLevel(final ThreadState state
) {
129 if (state
.isEmptyStackTrace()) return -10;
130 if (isKnownJdkThread(state
)) return -5;
131 if (state
.isSleeping()) {
134 if (state
.getOperation() == ThreadOperation
.Socket
) {
137 return state
.getStackTrace().split("\n").length
;
140 public static boolean isKnownJdkThread(final ThreadState state
) {
141 @NonNls String stackTrace
= state
.getStackTrace();
142 return stackTrace
.contains("java.lang.ref.Reference$ReferenceHandler.run") ||
143 stackTrace
.contains("java.lang.ref.Finalizer$FinalizerThread.run") ||
144 stackTrace
.contains("sun.awt.AWTAutoShutdown.run") ||
145 stackTrace
.contains("sun.java2d.Disposer.run") ||
146 stackTrace
.contains("sun.awt.windows.WToolkit.eventLoop") ||
147 ourIdleTimerThreadPattern
.matcher(stackTrace
).find() ||
148 ourIdleSwingTimerThreadPattern
.matcher(stackTrace
).find();
151 public static void inferThreadStateDetail(final ThreadState threadState
) {
152 @NonNls String stackTrace
= threadState
.getStackTrace();
153 if (stackTrace
.contains("at java.net.PlainSocketImpl.socketAccept") ||
154 stackTrace
.contains("at java.net.PlainDatagramSocketImpl.receive") ||
155 stackTrace
.contains("at java.net.SocketInputStream.socketRead") ||
156 stackTrace
.contains("at java.net.PlainSocketImpl.socketConnect")) {
157 threadState
.setOperation(ThreadOperation
.Socket
);
159 else if (stackTrace
.contains("at java.io.FileInputStream.readBytes")) {
160 threadState
.setOperation(ThreadOperation
.IO
);
162 else if (stackTrace
.contains("at java.lang.Thread.sleep")) {
163 final String javaThreadState
= threadState
.getJavaThreadState();
164 if (!Thread
.State
.RUNNABLE
.name().equals(javaThreadState
)) {
165 threadState
.setThreadStateDetail("sleeping"); // JDK 1.6 sets this explicitly, but JDK 1.5 does not
168 if (threadState
.isEDT()) {
169 if (stackTrace
.contains("java.awt.EventQueue.getNextEvent")) {
170 threadState
.setThreadStateDetail("idle");
175 pos
= stackTrace
.indexOf(PUMP_EVENT
, pos
);
178 pos
+= PUMP_EVENT
.length();
180 threadState
.setExtraState("modality level " + modality
);
185 private static ThreadState
tryParseThreadStart(final String line
) {
186 Matcher m
= ourThreadStartPattern
.matcher(line
);
188 return new ThreadState(m
.group(1), m
.group(2));
193 private static boolean tryParseThreadState(final String line
, final ThreadState threadState
) {
194 Matcher m
= ourThreadStatePattern
.matcher(line
);
196 threadState
.setJavaThreadState(m
.group(1));
197 threadState
.setThreadStateDetail(m
.group(2).trim());
200 m
= ourThreadStatePattern2
.matcher(line
);
202 threadState
.setJavaThreadState(m
.group(1));