2 * Copyright 2000-2015 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.
18 * class ExportThreadsAction
19 * @author Eugene Zhuravlev
20 * @author Sascha Weinreuter
22 package com.intellij.debugger.actions;
24 import com.intellij.debugger.DebuggerBundle;
25 import com.intellij.debugger.DebuggerManagerEx;
26 import com.intellij.debugger.engine.DebugProcessImpl;
27 import com.intellij.debugger.engine.events.DebuggerCommandImpl;
28 import com.intellij.debugger.impl.DebuggerContextImpl;
29 import com.intellij.debugger.impl.DebuggerSession;
30 import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
31 import com.intellij.debugger.ui.DebuggerSessionTab;
32 import com.intellij.openapi.actionSystem.AnAction;
33 import com.intellij.openapi.actionSystem.AnActionEvent;
34 import com.intellij.openapi.actionSystem.CommonDataKeys;
35 import com.intellij.openapi.actionSystem.Presentation;
36 import com.intellij.openapi.application.ApplicationManager;
37 import com.intellij.openapi.application.ModalityState;
38 import com.intellij.openapi.project.Project;
39 import com.intellij.unscramble.ThreadDumpParser;
40 import com.intellij.unscramble.ThreadState;
41 import com.intellij.util.SmartList;
42 import com.intellij.xdebugger.XDebugSession;
44 import gnu.trove.TIntObjectHashMap;
46 import java.util.ArrayList;
47 import java.util.HashMap;
48 import java.util.List;
51 public class ThreadDumpAction extends AnAction implements AnAction.TransparentUpdate {
53 public void actionPerformed(AnActionEvent e) {
54 final Project project = CommonDataKeys.PROJECT.getData(e.getDataContext());
55 if (project == null) {
58 DebuggerContextImpl context = (DebuggerManagerEx.getInstanceEx(project)).getContext();
60 final DebuggerSession session = context.getDebuggerSession();
61 if(session != null && session.isAttached()) {
62 final DebugProcessImpl process = context.getDebugProcess();
63 process.getManagerThread().invoke(new DebuggerCommandImpl() {
64 protected void action() throws Exception {
65 final VirtualMachineProxyImpl vm = process.getVirtualMachineProxy();
68 final List<ThreadState> threads = buildThreadStates(vm);
69 ApplicationManager.getApplication().invokeLater(new Runnable() {
71 XDebugSession xSession = session.getXDebugSession();
72 if (xSession != null) {
73 DebuggerSessionTab.addThreadDump(project, threads, xSession.getUI(), session);
76 }, ModalityState.NON_MODAL);
86 static List<ThreadState> buildThreadStates(VirtualMachineProxyImpl vmProxy) {
87 final List<ThreadReference> threads = vmProxy.getVirtualMachine().allThreads();
88 final List<ThreadState> result = new ArrayList<ThreadState>();
89 final Map<String, ThreadState> nameToThreadMap = new HashMap<String, ThreadState>();
90 final Map<String, String> waitingMap = new HashMap<String, String>(); // key 'waits_for' value
91 for (ThreadReference threadReference : threads) {
92 final StringBuilder buffer = new StringBuilder();
93 boolean hasEmptyStack = true;
94 final int threadStatus = threadReference.status();
95 if (threadStatus == ThreadReference.THREAD_STATUS_ZOMBIE) {
98 final String threadName = threadName(threadReference);
99 final ThreadState threadState = new ThreadState(threadName, threadStatusToState(threadStatus));
100 nameToThreadMap.put(threadName, threadState);
101 result.add(threadState);
102 threadState.setJavaThreadState(threadStatusToJavaThreadState(threadStatus));
104 buffer.append("\"").append(threadName).append("\"");
105 ReferenceType referenceType = threadReference.referenceType();
106 if (referenceType != null) {
107 //noinspection HardCodedStringLiteral
108 Field daemon = referenceType.fieldByName("daemon");
109 if (daemon != null) {
110 Value value = threadReference.getValue(daemon);
111 if (value instanceof BooleanValue && ((BooleanValue)value).booleanValue()) {
112 buffer.append(" ").append(DebuggerBundle.message("threads.export.attribute.label.daemon"));
113 threadState.setDaemon(true);
117 //noinspection HardCodedStringLiteral
118 Field priority = referenceType.fieldByName("priority");
119 if (priority != null) {
120 Value value = threadReference.getValue(priority);
121 if (value instanceof IntegerValue) {
122 buffer.append(" ").append(DebuggerBundle.message("threads.export.attribute.label.priority", ((IntegerValue)value).intValue()));
126 Field tid = referenceType.fieldByName("tid");
128 Value value = threadReference.getValue(tid);
129 if (value instanceof LongValue) {
130 buffer.append(" ").append(DebuggerBundle.message("threads.export.attribute.label.tid", Long.toHexString(((LongValue)value).longValue())));
131 buffer.append(" nid=NA");
135 //ThreadGroupReference groupReference = threadReference.threadGroup();
136 //if (groupReference != null) {
137 // buffer.append(", ").append(DebuggerBundle.message("threads.export.attribute.label.group", groupReference.name()));
139 final String state = threadState.getState();
141 buffer.append(" ").append(state);
144 buffer.append("\n java.lang.Thread.State: ").append(threadState.getJavaThreadState());
147 if (vmProxy.canGetOwnedMonitorInfo() && vmProxy.canGetMonitorInfo()) {
148 List<ObjectReference> list = threadReference.ownedMonitors();
149 for (ObjectReference reference : list) {
150 if (!vmProxy.canGetMonitorFrameInfo()) { // java 5 and earlier
151 buffer.append("\n\t ").append(renderLockedObject(reference));
153 final List<ThreadReference> waiting = reference.waitingThreads();
154 for (ThreadReference thread : waiting) {
155 final String waitingThreadName = threadName(thread);
156 waitingMap.put(waitingThreadName, threadName);
157 buffer.append("\n\t ").append(DebuggerBundle.message("threads.export.attribute.label.blocks.thread", waitingThreadName));
162 ObjectReference waitedMonitor = vmProxy.canGetCurrentContendedMonitor() ? threadReference.currentContendedMonitor() : null;
163 if (waitedMonitor != null) {
164 if (vmProxy.canGetMonitorInfo()) {
165 ThreadReference waitedMonitorOwner = waitedMonitor.owningThread();
166 if (waitedMonitorOwner != null) {
167 final String monitorOwningThreadName = threadName(waitedMonitorOwner);
168 waitingMap.put(threadName, monitorOwningThreadName);
169 buffer.append("\n\t ")
170 .append(DebuggerBundle.message("threads.export.attribute.label.waiting.for.thread", monitorOwningThreadName, renderObject(waitedMonitor)));
175 final List<StackFrame> frames = threadReference.frames();
176 hasEmptyStack = frames.size() == 0;
178 final TIntObjectHashMap<List<ObjectReference>> lockedAt = new TIntObjectHashMap<List<ObjectReference>>();
179 if (vmProxy.canGetMonitorFrameInfo()) {
180 for (MonitorInfo info : threadReference.ownedMonitorsAndFrames()) {
181 final int stackDepth = info.stackDepth();
182 List<ObjectReference> monitors;
183 if ((monitors = lockedAt.get(stackDepth)) == null) {
184 lockedAt.put(stackDepth, monitors = new SmartList<ObjectReference>());
186 monitors.add(info.monitor());
190 for (int i = 0, framesSize = frames.size(); i < framesSize; i++) {
191 final StackFrame stackFrame = frames.get(i);
193 final Location location = stackFrame.location();
194 buffer.append("\n\t ").append(renderLocation(location));
196 final List<ObjectReference> monitors = lockedAt.get(i);
197 if (monitors != null) {
198 for (ObjectReference monitor : monitors) {
199 buffer.append("\n\t - ").append(renderLockedObject(monitor));
203 catch (InvalidStackFrameException e) {
204 buffer.append("\n\t Invalid stack frame: ").append(e.getMessage());
208 catch (IncompatibleThreadStateException e) {
209 buffer.append("\n\t ").append(DebuggerBundle.message("threads.export.attribute.error.incompatible.state"));
211 threadState.setStackTrace(buffer.toString(), hasEmptyStack);
212 ThreadDumpParser.inferThreadStateDetail(threadState);
215 for (String waiting : waitingMap.keySet()) {
216 final ThreadState waitingThread = nameToThreadMap.get(waiting);
217 final ThreadState awaitedThread = nameToThreadMap.get(waitingMap.get(waiting));
218 awaitedThread.addWaitingThread(waitingThread);
221 // detect simple deadlocks
222 for (ThreadState thread : result) {
223 for (ThreadState awaitingThread : thread.getAwaitingThreads()) {
224 if (awaitingThread.isAwaitedBy(thread)) {
225 thread.addDeadlockedThread(awaitingThread);
226 awaitingThread.addDeadlockedThread(thread);
231 ThreadDumpParser.sortThreads(result);
235 private static String renderLockedObject(ObjectReference monitor) {
236 return DebuggerBundle.message("threads.export.attribute.label.locked", renderObject(monitor));
239 public static String renderObject(ObjectReference monitor) {
240 String monitorTypeName;
242 monitorTypeName = monitor.referenceType().name();
244 catch (Throwable e) {
245 monitorTypeName = "Error getting object type: '" + e.getMessage() + "'";
247 return DebuggerBundle.message("threads.export.attribute.label.object-id", Long.toHexString(monitor.uniqueID()), monitorTypeName);
250 private static String threadStatusToJavaThreadState(int status) {
252 case ThreadReference.THREAD_STATUS_MONITOR:
253 return Thread.State.BLOCKED.name();
254 case ThreadReference.THREAD_STATUS_NOT_STARTED:
255 return Thread.State.NEW.name();
256 case ThreadReference.THREAD_STATUS_RUNNING:
257 return Thread.State.RUNNABLE.name();
258 case ThreadReference.THREAD_STATUS_SLEEPING:
259 return Thread.State.TIMED_WAITING.name();
260 case ThreadReference.THREAD_STATUS_WAIT:
261 return Thread.State.WAITING.name();
262 case ThreadReference.THREAD_STATUS_ZOMBIE:
263 return Thread.State.TERMINATED.name();
264 case ThreadReference.THREAD_STATUS_UNKNOWN:
271 private static String threadStatusToState(int status) {
273 case ThreadReference.THREAD_STATUS_MONITOR:
274 return "waiting for monitor entry";
275 case ThreadReference.THREAD_STATUS_NOT_STARTED:
276 return "not started";
277 case ThreadReference.THREAD_STATUS_RUNNING:
279 case ThreadReference.THREAD_STATUS_SLEEPING:
281 case ThreadReference.THREAD_STATUS_WAIT:
283 case ThreadReference.THREAD_STATUS_ZOMBIE:
285 case ThreadReference.THREAD_STATUS_UNKNOWN:
292 public static String renderLocation(final Location location) {
295 sourceName = location.sourceName();
297 catch (Throwable e) {
298 sourceName = "Unknown Source";
301 final StringBuilder methodName = new StringBuilder();
303 methodName.append(location.declaringType().name());
305 catch (Throwable e) {
306 methodName.append(e.getMessage());
308 methodName.append(".");
310 methodName.append(location.method().name());
312 catch (Throwable e) {
313 methodName.append(e.getMessage());
318 lineNumber = location.lineNumber();
320 catch (Throwable e) {
323 return DebuggerBundle.message("export.threads.stackframe.format", methodName.toString(), sourceName, lineNumber);
326 private static String threadName(ThreadReference threadReference) {
327 return threadReference.name() + "@" + threadReference.uniqueID();
331 public void update(AnActionEvent event){
332 Presentation presentation = event.getPresentation();
333 Project project = CommonDataKeys.PROJECT.getData(event.getDataContext());
334 if (project == null) {
335 presentation.setEnabled(false);
338 DebuggerSession debuggerSession = (DebuggerManagerEx.getInstanceEx(project)).getContext().getDebuggerSession();
339 presentation.setEnabled(debuggerSession != null && debuggerSession.isAttached());