get rid of intellij.build.toolbox.litegen parameter and use BuildOptions.TOOLBOX_LITE...
[idea/community.git] / java / java-impl / src / com / intellij / unscramble / ThreadDumpParser.java
1 /*
2  * Copyright 2000-2010 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.intellij.unscramble;
17
18 import com.intellij.openapi.util.text.StringUtil;
19 import org.jetbrains.annotations.NonNls;
20 import org.jetbrains.annotations.Nullable;
21
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.List;
25 import java.util.regex.Matcher;
26 import java.util.regex.Pattern;
27
28 /**
29  * @author yole
30  */
31 public class ThreadDumpParser {
32   private static final Pattern ourThreadStartPattern = Pattern.compile("^\"(.+)\".+(prio=\\d+ (?:os_prio=[^\\s]+ )?.*tid=[^\\s]+ nid=[^\\s]+|[Ii][Dd]=\\d+) ([^\\[]+)");
33   private static final Pattern ourForcedThreadStartPattern = Pattern.compile("^Thread (\\d+): \\(state = (.+)\\)");
34   private static final Pattern ourYourkitThreadStartPattern = Pattern.compile("(.+) \\[([A-Z_, ]*)]");
35   private static final Pattern ourYourkitThreadStartPattern2 = Pattern.compile("(.+) (?:State:)? (.+) CPU usage on sample: .+");
36   private static final Pattern ourThreadStatePattern = Pattern.compile("java\\.lang\\.Thread\\.State: (.+) \\((.+)\\)");
37   private static final Pattern ourThreadStatePattern2 = Pattern.compile("java\\.lang\\.Thread\\.State: (.+)");
38   private static final Pattern ourWaitingForLockPattern = Pattern.compile("- waiting (on|to lock) <(.+)>");
39   private static final Pattern ourParkingToWaitForLockPattern = Pattern.compile("- parking to wait for  <(.+)>");
40   @NonNls private static final String PUMP_EVENT = "java.awt.EventDispatchThread.pumpOneEventForFilters";
41   private static final Pattern ourIdleTimerThreadPattern = Pattern.compile("java.lang.Object.wait\\([^()]+\\)\\s+at java.util.TimerThread.mainLoop");
42   private static final Pattern ourIdleSwingTimerThreadPattern = Pattern.compile("java.lang.Object.wait\\([^()]+\\)\\s+at javax.swing.TimerQueue.run");
43   private static final String AT_JAVA_LANG_OBJECT_WAIT = "at java.lang.Object.wait(";
44
45   private ThreadDumpParser() {
46   }
47
48   public static List<ThreadState> parse(String threadDump) {
49     List<ThreadState> result = new ArrayList<>();
50     StringBuilder lastThreadStack = new StringBuilder();
51     ThreadState lastThreadState = null;
52     boolean expectingThreadState = false;
53     boolean haveNonEmptyStackTrace = false;
54     for(@NonNls String line: StringUtil.tokenize(threadDump, "\r\n")) {
55       if (line.startsWith("============") || line.contains("Java-level deadlock")) {
56         break;
57       }
58       ThreadState state = tryParseThreadStart(line.trim());
59       if (state != null) {
60         if (lastThreadState != null) {
61           lastThreadState.setStackTrace(lastThreadStack.toString(), !haveNonEmptyStackTrace);
62         }
63         lastThreadState = state;
64         result.add(lastThreadState);
65         lastThreadStack.setLength(0);
66         haveNonEmptyStackTrace = false;
67         lastThreadStack.append(line).append("\n");
68         expectingThreadState = true;
69       }
70       else {
71         boolean parsedThreadState = false;
72         if (expectingThreadState) {
73           expectingThreadState = false;
74           parsedThreadState = tryParseThreadState(line, lastThreadState);
75         }
76         lastThreadStack.append(line).append("\n");
77         if (!parsedThreadState && line.trim().startsWith("at")) {
78           haveNonEmptyStackTrace = true;
79         }
80       }
81     }
82     if (lastThreadState != null) {
83       lastThreadState.setStackTrace(lastThreadStack.toString(), !haveNonEmptyStackTrace);
84     }
85     for(ThreadState threadState: result) {
86       inferThreadStateDetail(threadState);
87
88       String lockId = findWaitingForLock(threadState.getStackTrace());
89       ThreadState lockOwner = findLockOwner(result, lockId, true);
90       if (lockOwner == null) {
91         lockOwner = findLockOwner(result, lockId, false);
92       }
93       if (lockOwner != null) {
94         if (threadState.isAwaitedBy(lockOwner)) {
95           threadState.addDeadlockedThread(lockOwner);
96           lockOwner.addDeadlockedThread(threadState);
97         }
98         lockOwner.addWaitingThread(threadState);
99       }
100     }
101     sortThreads(result);
102     return result;
103   }
104
105   @Nullable
106   private static ThreadState findLockOwner(List<? extends ThreadState> result, @Nullable String lockId, boolean ignoreWaiting) {
107     if (lockId == null) return null;
108
109     final String marker = "- locked <" + lockId + ">";
110     for(ThreadState lockOwner : result) {
111       String trace = lockOwner.getStackTrace();
112       if (trace.contains(marker) && (!ignoreWaiting || !trace.contains(AT_JAVA_LANG_OBJECT_WAIT))) {
113         return lockOwner;
114       }
115     }
116     return null;
117   }
118
119   public static void sortThreads(List<? extends ThreadState> result) {
120     Collections.sort(result, (o1, o2) -> getInterestLevel(o2) - getInterestLevel(o1));
121   }
122
123   @Nullable
124   private static String findWaitingForLock(final String stackTrace) {
125     Matcher m = ourWaitingForLockPattern.matcher(stackTrace);
126     if (m.find()) {
127       return m.group(2);
128     }
129     m = ourParkingToWaitForLockPattern.matcher(stackTrace);
130     if (m.find()) {
131       return m.group(1);
132     }
133     return null;
134   }
135
136   private static int getInterestLevel(final ThreadState state) {
137     if (state.isEmptyStackTrace()) return -10;
138     if (isKnownJdkThread(state)) return -5;
139     if (state.isSleeping()) {
140       return -2;
141     }
142     if (state.getOperation() == ThreadOperation.Socket) {
143       return -1;
144     }
145     return state.getStackTrace().split("\n").length;
146   }
147
148   public static boolean isKnownJdkThread(final ThreadState state) {
149     @NonNls String stackTrace = state.getStackTrace();
150     return stackTrace.contains("java.lang.ref.Reference$ReferenceHandler.run") ||
151         stackTrace.contains("java.lang.ref.Finalizer$FinalizerThread.run") ||
152         stackTrace.contains("sun.awt.AWTAutoShutdown.run") ||
153         stackTrace.contains("sun.java2d.Disposer.run") ||
154         stackTrace.contains("sun.awt.windows.WToolkit.eventLoop") ||
155         ourIdleTimerThreadPattern.matcher(stackTrace).find() ||
156         ourIdleSwingTimerThreadPattern.matcher(stackTrace).find();
157   }
158
159   public static void inferThreadStateDetail(final ThreadState threadState) {
160     @NonNls String stackTrace = threadState.getStackTrace();
161     if (stackTrace.contains("at java.net.PlainSocketImpl.socketAccept") ||
162         stackTrace.contains("at java.net.PlainDatagramSocketImpl.receive") ||
163         stackTrace.contains("at java.net.SocketInputStream.socketRead") ||
164         stackTrace.contains("at java.net.PlainSocketImpl.socketConnect")) {
165       threadState.setOperation(ThreadOperation.Socket);
166     }
167     else if (stackTrace.contains("at java.io.FileInputStream.readBytes")) {
168       threadState.setOperation(ThreadOperation.IO);
169     }
170     else if (stackTrace.contains("at java.lang.Thread.sleep")) {
171       final String javaThreadState = threadState.getJavaThreadState();
172       if (!Thread.State.RUNNABLE.name().equals(javaThreadState)) {
173         threadState.setThreadStateDetail("sleeping");   // JDK 1.6 sets this explicitly, but JDK 1.5 does not
174       }
175     }
176     if (threadState.isEDT()) {
177       if (stackTrace.contains("java.awt.EventQueue.getNextEvent")) {
178         threadState.setThreadStateDetail("idle");
179       }
180       int modality = 0;
181       int pos = 0;
182       while(true) {
183         pos = stackTrace.indexOf(PUMP_EVENT, pos);
184         if (pos < 0) break;
185         modality++;
186         pos += PUMP_EVENT.length();
187       }
188       threadState.setExtraState("modality level " + modality);
189     }
190   }
191
192   @Nullable
193   private static ThreadState tryParseThreadStart(String line) {
194     Matcher m = ourThreadStartPattern.matcher(line);
195     if (m.find()) {
196       final ThreadState state = new ThreadState(m.group(1), m.group(3));
197       if (line.contains(" daemon ")) {
198         state.setDaemon(true);
199       }
200       return state;
201     }
202
203     m = ourForcedThreadStartPattern.matcher(line);
204     if (m.matches()) {
205       return new ThreadState(m.group(1), m.group(2));
206     }
207
208     boolean daemon = line.contains(" [DAEMON]");
209     if (daemon) {
210       line = StringUtil.replace(line, " [DAEMON]", "");
211     }
212
213     m = matchYourKit(line);
214     if (m != null) {
215       ThreadState state = new ThreadState(m.group(1), m.group(2));
216       state.setDaemon(daemon);
217       return state;
218     }
219     return null;
220   }
221
222   @Nullable
223   private static Matcher matchYourKit(String line) {
224     if (line.contains("[")) {
225       Matcher m = ourYourkitThreadStartPattern.matcher(line);
226       if (m.matches()) return m;
227     }
228
229     if (line.contains("CPU usage on sample:")) {
230       Matcher m = ourYourkitThreadStartPattern2.matcher(line);
231       if (m.matches()) return m;
232     }
233
234     return null;
235   }
236
237   private static boolean tryParseThreadState(final String line, final ThreadState threadState) {
238     Matcher m = ourThreadStatePattern.matcher(line);
239     if (m.find()) {
240       threadState.setJavaThreadState(m.group(1));
241       threadState.setThreadStateDetail(m.group(2).trim());
242       return true;
243     }
244     m = ourThreadStatePattern2.matcher(line);
245     if (m.find()) {
246       threadState.setJavaThreadState(m.group(1));
247       return true;
248     }
249     return false;
250   }
251 }