update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / unscramble / ThreadDumpParser.java
blobfa8962c7ac1fd2ef234d464c816b6241207d8d1d
1 /*
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;
29 /**
30 * @author yole
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")) {
53 break;
55 ThreadState state = tryParseThreadStart(line);
56 if (state != null) {
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;
67 else {
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());
86 if (s != null) {
87 for(ThreadState lockOwner : result) {
88 if (lockOwner == threadState) {
89 continue;
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);
98 break;
103 sortThreads(result);
104 return result;
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);
115 @Nullable
116 private static String findWaitingForLock(final String stackTrace) {
117 Matcher m = ourWaitingForLockPattern.matcher(stackTrace);
118 if (m.find()) {
119 return m.group(2);
121 m = ourParkingToWaitForLockPattern.matcher(stackTrace);
122 if (m.find()) {
123 return m.group(1);
125 return null;
128 private static int getInterestLevel(final ThreadState state) {
129 if (state.isEmptyStackTrace()) return -10;
130 if (isKnownJdkThread(state)) return -5;
131 if (state.isSleeping()) {
132 return -2;
134 if (state.getOperation() == ThreadOperation.Socket) {
135 return -1;
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");
172 int modality = 0;
173 int pos = 0;
174 while(true) {
175 pos = stackTrace.indexOf(PUMP_EVENT, pos);
176 if (pos < 0) break;
177 modality++;
178 pos += PUMP_EVENT.length();
180 threadState.setExtraState("modality level " + modality);
184 @Nullable
185 private static ThreadState tryParseThreadStart(final String line) {
186 Matcher m = ourThreadStartPattern.matcher(line);
187 if (m.find()) {
188 return new ThreadState(m.group(1), m.group(2));
190 return null;
193 private static boolean tryParseThreadState(final String line, final ThreadState threadState) {
194 Matcher m = ourThreadStatePattern.matcher(line);
195 if (m.find()) {
196 threadState.setJavaThreadState(m.group(1));
197 threadState.setThreadStateDetail(m.group(2).trim());
198 return true;
200 m = ourThreadStatePattern2.matcher(line);
201 if (m.find()) {
202 threadState.setJavaThreadState(m.group(1));
203 return true;
205 return false;