Remember a forced step into at its call stack (IDEADEV-39365)
[fedora-idea.git] / java / debugger / impl / src / com / intellij / debugger / engine / DebugProcessImpl.java
blob427cbd919ecb5a41ee27cf7b6200f35002a11a0c
1 package com.intellij.debugger.engine;
3 import com.intellij.Patches;
4 import com.intellij.debugger.DebuggerBundle;
5 import com.intellij.debugger.DebuggerInvocationUtil;
6 import com.intellij.debugger.DebuggerManagerEx;
7 import com.intellij.debugger.PositionManager;
8 import com.intellij.debugger.actions.DebuggerActions;
9 import com.intellij.debugger.apiAdapters.ConnectionServiceWrapper;
10 import com.intellij.debugger.apiAdapters.TransportServiceWrapper;
11 import com.intellij.debugger.engine.evaluation.*;
12 import com.intellij.debugger.engine.events.DebuggerCommandImpl;
13 import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
14 import com.intellij.debugger.engine.jdi.ThreadReferenceProxy;
15 import com.intellij.debugger.engine.requests.MethodReturnValueWatcher;
16 import com.intellij.debugger.engine.requests.RequestManagerImpl;
17 import com.intellij.debugger.impl.DebuggerContextImpl;
18 import com.intellij.debugger.impl.DebuggerSession;
19 import com.intellij.debugger.impl.DebuggerUtilsEx;
20 import com.intellij.debugger.impl.PrioritizedTask;
21 import com.intellij.debugger.jdi.StackFrameProxyImpl;
22 import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
23 import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
24 import com.intellij.debugger.settings.DebuggerSettings;
25 import com.intellij.debugger.settings.NodeRendererSettings;
26 import com.intellij.debugger.ui.breakpoints.BreakpointManager;
27 import com.intellij.debugger.ui.breakpoints.RunToCursorBreakpoint;
28 import com.intellij.debugger.ui.tree.ValueDescriptor;
29 import com.intellij.debugger.ui.tree.render.*;
30 import com.intellij.execution.CantRunException;
31 import com.intellij.execution.ExecutionException;
32 import com.intellij.execution.ExecutionResult;
33 import com.intellij.execution.Executor;
34 import com.intellij.execution.configurations.RemoteConnection;
35 import com.intellij.execution.configurations.RunProfileState;
36 import com.intellij.execution.process.ProcessAdapter;
37 import com.intellij.execution.process.ProcessEvent;
38 import com.intellij.execution.process.ProcessListener;
39 import com.intellij.execution.process.ProcessOutputTypes;
40 import com.intellij.execution.runners.ProgramRunner;
41 import com.intellij.execution.runners.ProgramRunnerUtil;
42 import com.intellij.idea.ActionsBundle;
43 import com.intellij.openapi.application.ApplicationManager;
44 import com.intellij.openapi.diagnostic.Logger;
45 import com.intellij.openapi.editor.Document;
46 import com.intellij.openapi.extensions.Extensions;
47 import com.intellij.openapi.project.Project;
48 import com.intellij.openapi.ui.Messages;
49 import com.intellij.openapi.util.Comparing;
50 import com.intellij.openapi.util.Key;
51 import com.intellij.openapi.util.Pair;
52 import com.intellij.openapi.util.Ref;
53 import com.intellij.openapi.wm.WindowManager;
54 import com.intellij.psi.PsiDocumentManager;
55 import com.intellij.psi.PsiFile;
56 import com.intellij.psi.search.GlobalSearchScope;
57 import com.intellij.ui.classFilter.ClassFilter;
58 import com.intellij.ui.classFilter.DebuggerClassFilterProvider;
59 import com.intellij.util.Alarm;
60 import com.intellij.util.EventDispatcher;
61 import com.intellij.util.StringBuilderSpinAllocator;
62 import com.intellij.util.concurrency.Semaphore;
63 import com.sun.jdi.*;
64 import com.sun.jdi.connect.*;
65 import com.sun.jdi.request.EventRequest;
66 import com.sun.jdi.request.EventRequestManager;
67 import com.sun.jdi.request.StepRequest;
68 import org.jetbrains.annotations.NonNls;
69 import org.jetbrains.annotations.NotNull;
70 import org.jetbrains.annotations.Nullable;
72 import javax.swing.*;
73 import java.io.IOException;
74 import java.net.UnknownHostException;
75 import java.util.*;
76 import java.util.concurrent.atomic.AtomicBoolean;
77 import java.util.concurrent.atomic.AtomicInteger;
79 public abstract class DebugProcessImpl implements DebugProcess {
80 private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.DebugProcessImpl");
82 static final @NonNls String SOCKET_ATTACHING_CONNECTOR_NAME = "com.sun.jdi.SocketAttach";
83 static final @NonNls String SHMEM_ATTACHING_CONNECTOR_NAME = "com.sun.jdi.SharedMemoryAttach";
84 static final @NonNls String SOCKET_LISTENING_CONNECTOR_NAME = "com.sun.jdi.SocketListen";
85 static final @NonNls String SHMEM_LISTENING_CONNECTOR_NAME = "com.sun.jdi.SharedMemoryListen";
87 private final Project myProject;
88 private final RequestManagerImpl myRequestManager;
90 private VirtualMachineProxyImpl myVirtualMachineProxy = null;
91 protected EventDispatcher<DebugProcessListener> myDebugProcessDispatcher = EventDispatcher.create(DebugProcessListener.class);
92 protected EventDispatcher<EvaluationListener> myEvaluationDispatcher = EventDispatcher.create(EvaluationListener.class);
94 private final List<ProcessListener> myProcessListeners = new ArrayList<ProcessListener>();
96 protected static final int STATE_INITIAL = 0;
97 protected static final int STATE_ATTACHED = 1;
98 protected static final int STATE_DETACHING = 2;
99 protected static final int STATE_DETACHED = 3;
100 protected final AtomicInteger myState = new AtomicInteger(STATE_INITIAL);
102 private ExecutionResult myExecutionResult;
103 private RemoteConnection myConnection;
105 private ConnectionServiceWrapper myConnectionService;
106 private Map<String, Connector.Argument> myArguments;
108 private final List<NodeRenderer> myRenderers = new ArrayList<NodeRenderer>();
109 private final Map<Type, NodeRenderer> myNodeRederersMap = new com.intellij.util.containers.HashMap<Type, NodeRenderer>();
110 private final NodeRendererSettingsListener mySettingsListener = new NodeRendererSettingsListener() {
111 public void renderersChanged() {
112 myNodeRederersMap.clear();
113 myRenderers.clear();
114 loadRenderers();
118 private final SuspendManagerImpl mySuspendManager = new SuspendManagerImpl(this);
119 protected CompoundPositionManager myPositionManager = null;
120 private volatile DebuggerManagerThreadImpl myDebuggerManagerThread;
121 private final HashMap myUserData = new HashMap();
122 private static final int LOCAL_START_TIMEOUT = 15000;
124 private final Semaphore myWaitFor = new Semaphore();
125 private final AtomicBoolean myBreakpointsMuted = new AtomicBoolean(false);
126 private boolean myIsFailed = false;
127 private DebuggerSession mySession;
128 protected @Nullable MethodReturnValueWatcher myReturnValueWatcher;
129 private final Alarm myStatusUpdateAlarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD);
131 protected DebugProcessImpl(Project project) {
132 myProject = project;
133 myRequestManager = new RequestManagerImpl(this);
134 NodeRendererSettings.getInstance().addListener(mySettingsListener);
135 loadRenderers();
138 private void loadRenderers() {
139 getManagerThread().invoke(new DebuggerCommandImpl() {
140 protected void action() throws Exception {
141 final NodeRendererSettings rendererSettings = NodeRendererSettings.getInstance();
142 for (final NodeRenderer renderer : rendererSettings.getAllRenderers()) {
143 if (renderer.isEnabled()) {
144 myRenderers.add(renderer);
151 @Nullable
152 public Pair<Method, Value> getLastExecutedMethod() {
153 if (myReturnValueWatcher == null) {
154 return null;
156 final Method method = myReturnValueWatcher.getLastExecutedMethod();
157 if (method == null) {
158 return null;
160 return new Pair<Method, Value>(method, myReturnValueWatcher.getLastMethodReturnValue());
163 public void setWatchMethodReturnValuesEnabled(boolean enabled) {
164 if (myReturnValueWatcher != null) {
165 myReturnValueWatcher.setFeatureEnabled(enabled);
169 public boolean isWatchMethodReturnValuesEnabled() {
170 return myReturnValueWatcher != null && myReturnValueWatcher.isFeatureEnabled();
173 public boolean canGetMethodReturnValue() {
174 return myReturnValueWatcher != null;
177 public NodeRenderer getAutoRenderer(ValueDescriptor descriptor) {
178 DebuggerManagerThreadImpl.assertIsManagerThread();
179 final Value value = descriptor.getValue();
180 Type type = value != null ? value.type() : null;
182 // in case evaluation is not possible, force default renderer
183 if (!DebuggerManagerEx.getInstanceEx(getProject()).getContext().isEvaluationPossible()) {
184 return getDefaultRenderer(type);
187 NodeRenderer renderer = myNodeRederersMap.get(type);
188 if(renderer == null) {
189 for (final NodeRenderer nodeRenderer : myRenderers) {
190 if (nodeRenderer.isApplicable(type)) {
191 renderer = nodeRenderer;
192 break;
195 if (renderer == null) {
196 renderer = getDefaultRenderer(type);
198 myNodeRederersMap.put(type, renderer);
201 return renderer;
204 public final NodeRenderer getDefaultRenderer(Value value) {
205 return getDefaultRenderer((value != null) ? value.type() : (Type)null);
208 public final NodeRenderer getDefaultRenderer(Type type) {
209 final NodeRendererSettings settings = NodeRendererSettings.getInstance();
211 final PrimitiveRenderer primitiveRenderer = settings.getPrimitiveRenderer();
212 if(primitiveRenderer.isApplicable(type)) {
213 return primitiveRenderer;
216 final ArrayRenderer arrayRenderer = settings.getArrayRenderer();
217 if(arrayRenderer.isApplicable(type)) {
218 return arrayRenderer;
221 final ClassRenderer classRenderer = settings.getClassRenderer();
222 LOG.assertTrue(classRenderer.isApplicable(type), type.name());
223 return classRenderer;
227 @SuppressWarnings({"HardCodedStringLiteral"})
228 protected void commitVM(VirtualMachine vm) {
229 if (!isInInitialState()) {
230 LOG.assertTrue(false, "State is invalid " + myState.get());
232 DebuggerManagerThreadImpl.assertIsManagerThread();
233 myPositionManager = createPositionManager();
234 if (LOG.isDebugEnabled()) {
235 LOG.debug("*******************VM attached******************");
237 checkVirtualMachineVersion(vm);
239 myVirtualMachineProxy = new VirtualMachineProxyImpl(this, vm);
241 String trace = System.getProperty("idea.debugger.trace");
242 if (trace != null) {
243 int mask = 0;
244 StringTokenizer tokenizer = new StringTokenizer(trace);
245 while (tokenizer.hasMoreTokens()) {
246 String token = tokenizer.nextToken();
247 if ("SENDS".compareToIgnoreCase(token) == 0) {
248 mask |= VirtualMachine.TRACE_SENDS;
250 else if ("RAW_SENDS".compareToIgnoreCase(token) == 0) {
251 mask |= 0x01000000;
253 else if ("RECEIVES".compareToIgnoreCase(token) == 0) {
254 mask |= VirtualMachine.TRACE_RECEIVES;
256 else if ("RAW_RECEIVES".compareToIgnoreCase(token) == 0) {
257 mask |= 0x02000000;
259 else if ("EVENTS".compareToIgnoreCase(token) == 0) {
260 mask |= VirtualMachine.TRACE_EVENTS;
262 else if ("REFTYPES".compareToIgnoreCase(token) == 0) {
263 mask |= VirtualMachine.TRACE_REFTYPES;
265 else if ("OBJREFS".compareToIgnoreCase(token) == 0) {
266 mask |= VirtualMachine.TRACE_OBJREFS;
268 else if ("ALL".compareToIgnoreCase(token) == 0) {
269 mask |= VirtualMachine.TRACE_ALL;
273 vm.setDebugTraceMode(mask);
277 private void stopConnecting() {
278 DebuggerManagerThreadImpl.assertIsManagerThread();
280 Map arguments = myArguments;
281 try {
282 if (arguments == null) {
283 return;
285 if(myConnection.isServerMode()) {
286 ListeningConnector connector = (ListeningConnector)findConnector(SOCKET_LISTENING_CONNECTOR_NAME);
287 if (connector == null) {
288 LOG.error("Cannot find connector: " + SOCKET_LISTENING_CONNECTOR_NAME);
290 connector.stopListening(arguments);
292 else {
293 if(myConnectionService != null) {
294 myConnectionService.close();
298 catch (IOException e) {
299 if (LOG.isDebugEnabled()) {
300 LOG.debug(e);
303 catch (IllegalConnectorArgumentsException e) {
304 if (LOG.isDebugEnabled()) {
305 LOG.debug(e);
308 catch (ExecutionException e) {
309 LOG.error(e);
311 finally {
312 closeProcess(true);
316 protected CompoundPositionManager createPositionManager() {
317 return new CompoundPositionManager(new PositionManagerImpl(this));
320 public void printToConsole(final String text) {
321 myExecutionResult.getProcessHandler().notifyTextAvailable(text, ProcessOutputTypes.SYSTEM);
326 * @param suspendContext
327 * @param stepThread
328 * @param depth
329 * @param hint may be null
331 protected void doStep(final SuspendContextImpl suspendContext, final ThreadReferenceProxyImpl stepThread, int depth, RequestHint hint) {
332 if (stepThread == null) {
333 return;
335 try {
336 if (LOG.isDebugEnabled()) {
337 LOG.debug("DO_STEP: creating step request for " + stepThread.getThreadReference());
339 deleteStepRequests();
340 EventRequestManager requestManager = getVirtualMachineProxy().eventRequestManager();
341 StepRequest stepRequest = requestManager.createStepRequest(stepThread.getThreadReference(), StepRequest.STEP_LINE, depth);
342 DebuggerSettings settings = DebuggerSettings.getInstance();
343 if (!(hint != null && hint.isIgnoreFilters()) /*&& depth == StepRequest.STEP_INTO*/) {
344 final String currentClassName = getCurrentClassName(stepThread);
345 final List<ClassFilter> activeFilters = new ArrayList<ClassFilter>();
346 if (settings.TRACING_FILTERS_ENABLED) {
347 for (ClassFilter filter : settings.getSteppingFilters()) {
348 if (filter.isEnabled()) {
349 activeFilters.add(filter);
353 for (DebuggerClassFilterProvider provider : Extensions.getExtensions(DebuggerClassFilterProvider.EP_NAME)) {
354 for (ClassFilter filter : provider.getFilters()) {
355 if (filter.isEnabled()) {
356 activeFilters.add(filter);
361 if (currentClassName == null || !DebuggerUtilsEx.isFiltered(currentClassName, activeFilters)) {
362 // add class filters
363 for (ClassFilter filter : activeFilters) {
364 stepRequest.addClassExclusionFilter(filter.getPattern());
369 // suspend policy to match the suspend policy of the context:
370 // if all threads were suspended, then during stepping all the threads must be suspended
371 // if only event thread were suspended, then only this particular thread must be suspended during stepping
372 stepRequest.setSuspendPolicy(suspendContext.getSuspendPolicy() == EventRequest.SUSPEND_EVENT_THREAD? EventRequest.SUSPEND_EVENT_THREAD : EventRequest.SUSPEND_ALL);
374 if (hint != null) {
375 //noinspection HardCodedStringLiteral
376 stepRequest.putProperty("hint", hint);
378 stepRequest.enable();
380 catch (ObjectCollectedException ignored) {
385 void deleteStepRequests() {
386 EventRequestManager requestManager = getVirtualMachineProxy().eventRequestManager();
387 List<StepRequest> stepRequests = requestManager.stepRequests();
388 if (stepRequests.size() > 0) {
389 final List<StepRequest> toDelete = new ArrayList<StepRequest>(stepRequests.size());
390 for (final StepRequest request : stepRequests) {
391 ThreadReference threadReference = request.thread();
392 // [jeka] on attempt to delete a request assigned to a thread with unknown status, a JDWP error occures
393 if (threadReference.status() != ThreadReference.THREAD_STATUS_UNKNOWN) {
394 toDelete.add(request);
397 requestManager.deleteEventRequests(toDelete);
401 @Nullable
402 private static String getCurrentClassName(ThreadReferenceProxyImpl thread) {
403 try {
404 if (thread != null && thread.frameCount() > 0) {
405 StackFrameProxyImpl stackFrame = thread.frame(0);
406 Location location = stackFrame.location();
407 ReferenceType referenceType = location.declaringType();
408 if (referenceType != null) {
409 return referenceType.name();
413 catch (EvaluateException e) {
415 return null;
418 private VirtualMachine createVirtualMachineInt() throws ExecutionException {
419 try {
420 if (myArguments != null) {
421 throw new IOException(DebuggerBundle.message("error.debugger.already.listening"));
424 final String address = myConnection.getAddress();
425 if (myConnection.isServerMode()) {
426 ListeningConnector connector = (ListeningConnector)findConnector(
427 myConnection.isUseSockets() ? SOCKET_LISTENING_CONNECTOR_NAME : SHMEM_LISTENING_CONNECTOR_NAME);
428 if (connector == null) {
429 throw new CantRunException(DebuggerBundle.message("error.debug.connector.not.found", DebuggerBundle.getTransportName(myConnection)));
431 myArguments = connector.defaultArguments();
432 if (myArguments == null) {
433 throw new CantRunException(DebuggerBundle.message("error.no.debug.listen.port"));
436 if (address == null) {
437 throw new CantRunException(DebuggerBundle.message("error.no.debug.listen.port"));
439 // negative port number means the caller leaves to debugger to decide at which hport to listen
440 //noinspection HardCodedStringLiteral
441 final Connector.Argument portArg = myConnection.isUseSockets() ? myArguments.get("port") : myArguments.get("name");
442 if (portArg != null) {
443 portArg.setValue(address);
445 //noinspection HardCodedStringLiteral
446 final Connector.Argument timeoutArg = myArguments.get("timeout");
447 if (timeoutArg != null) {
448 timeoutArg.setValue("0"); // wait forever
450 connector.startListening(myArguments);
451 myDebugProcessDispatcher.getMulticaster().connectorIsReady();
452 try {
453 return connector.accept(myArguments);
455 finally {
456 if(myArguments != null) {
457 try {
458 connector.stopListening(myArguments);
460 catch (IllegalArgumentException e) {
461 // ignored
463 catch (IllegalConnectorArgumentsException e) {
464 // ignored
469 else { // is client mode, should attach to already running process
470 AttachingConnector connector = (AttachingConnector)findConnector(
471 myConnection.isUseSockets() ? SOCKET_ATTACHING_CONNECTOR_NAME : SHMEM_ATTACHING_CONNECTOR_NAME
474 if (connector == null) {
475 throw new CantRunException( DebuggerBundle.message("error.debug.connector.not.found", DebuggerBundle.getTransportName(myConnection)));
477 myArguments = connector.defaultArguments();
478 if (myConnection.isUseSockets()) {
479 //noinspection HardCodedStringLiteral
480 final Connector.Argument hostnameArg = (Connector.Argument)myArguments.get("hostname");
481 if (hostnameArg != null && myConnection.getHostName() != null) {
482 hostnameArg.setValue(myConnection.getHostName());
484 if (address == null) {
485 throw new CantRunException(DebuggerBundle.message("error.no.debug.attach.port"));
487 //noinspection HardCodedStringLiteral
488 final Connector.Argument portArg = (Connector.Argument)myArguments.get("port");
489 if (portArg != null) {
490 portArg.setValue(address);
493 else {
494 if (address == null) {
495 throw new CantRunException(DebuggerBundle.message("error.no.shmem.address"));
497 //noinspection HardCodedStringLiteral
498 final Connector.Argument nameArg = myArguments.get("name");
499 if (nameArg != null) {
500 nameArg.setValue(address);
503 //noinspection HardCodedStringLiteral
504 final Connector.Argument timeoutArg = myArguments.get("timeout");
505 if (timeoutArg != null) {
506 timeoutArg.setValue("0"); // wait forever
509 myDebugProcessDispatcher.getMulticaster().connectorIsReady();
510 try {
511 if(SOCKET_ATTACHING_CONNECTOR_NAME.equals(connector.name()) && Patches.SUN_BUG_338675) {
512 String portString = myConnection.getAddress();
513 String hostString = myConnection.getHostName();
515 if (hostString == null || hostString.length() == 0) {
516 //noinspection HardCodedStringLiteral
517 hostString = "localhost";
519 hostString = hostString + ":";
521 final TransportServiceWrapper transportServiceWrapper = TransportServiceWrapper.getTransportService(connector.transport());
522 myConnectionService = transportServiceWrapper.attach(hostString + portString);
523 return myConnectionService.createVirtualMachine();
525 else {
526 return connector.attach(myArguments);
529 catch (IllegalArgumentException e) {
530 throw new CantRunException(e.getLocalizedMessage());
534 catch (IOException e) {
535 throw new ExecutionException(processError(e), e);
537 catch (IllegalConnectorArgumentsException e) {
538 throw new ExecutionException(processError(e), e);
540 finally {
541 myArguments = null;
542 myConnectionService = null;
546 public void showStatusText(final String text) {
547 myStatusUpdateAlarm.cancelAllRequests();
548 myStatusUpdateAlarm.addRequest(new Runnable() {
549 public void run() {
550 final WindowManager wm = WindowManager.getInstance();
551 if (wm != null) {
552 wm.getStatusBar(myProject).setInfo(text);
555 }, 50);
558 static Connector findConnector(String connectorName) throws ExecutionException {
559 VirtualMachineManager virtualMachineManager = null;
560 try {
561 virtualMachineManager = Bootstrap.virtualMachineManager();
563 catch (Error e) {
564 final String error = e.getClass().getName() + " : " + e.getLocalizedMessage();
565 throw new ExecutionException(DebuggerBundle.message("debugger.jdi.bootstrap.error", error));
567 List connectors;
568 if (SOCKET_ATTACHING_CONNECTOR_NAME.equals(connectorName) || SHMEM_ATTACHING_CONNECTOR_NAME.equals(connectorName)) {
569 connectors = virtualMachineManager.attachingConnectors();
571 else if (SOCKET_LISTENING_CONNECTOR_NAME.equals(connectorName) || SHMEM_LISTENING_CONNECTOR_NAME.equals(connectorName)) {
572 connectors = virtualMachineManager.listeningConnectors();
574 else {
575 return null;
577 for (Iterator it = connectors.iterator(); it.hasNext();) {
578 Connector connector = (Connector)it.next();
579 if (connectorName.equals(connector.name())) {
580 return connector;
583 return null;
586 private void checkVirtualMachineVersion(VirtualMachine vm) {
587 final String version = vm.version();
588 if ("1.4.0".equals(version)) {
589 SwingUtilities.invokeLater(new Runnable() {
590 public void run() {
591 Messages.showMessageDialog(
592 getProject(),
593 DebuggerBundle.message("warning.jdk140.unstable"), DebuggerBundle.message("title.jdk140.unstable"), Messages.getWarningIcon()
600 /*Event dispatching*/
601 public void addEvaluationListener(EvaluationListener evaluationListener) {
602 myEvaluationDispatcher.addListener(evaluationListener);
605 public void removeEvaluationListener(EvaluationListener evaluationListener) {
606 myEvaluationDispatcher.removeListener(evaluationListener);
609 public void addDebugProcessListener(DebugProcessListener listener) {
610 myDebugProcessDispatcher.addListener(listener);
613 public void removeDebugProcessListener(DebugProcessListener listener) {
614 myDebugProcessDispatcher.removeListener(listener);
617 public void addProcessListener(ProcessListener processListener) {
618 synchronized(myProcessListeners) {
619 if(getExecutionResult() != null) {
620 getExecutionResult().getProcessHandler().addProcessListener(processListener);
622 else {
623 myProcessListeners.add(processListener);
628 public void removeProcessListener(ProcessListener processListener) {
629 synchronized (myProcessListeners) {
630 if(getExecutionResult() != null) {
631 getExecutionResult().getProcessHandler().removeProcessListener(processListener);
633 else {
634 myProcessListeners.remove(processListener);
639 /* getters */
640 public RemoteConnection getConnection() {
641 return myConnection;
644 public ExecutionResult getExecutionResult() {
645 return myExecutionResult;
648 public <T> T getUserData(Key<T> key) {
649 return (T)myUserData.get(key);
652 public <T> void putUserData(Key<T> key, T value) {
653 myUserData.put(key, value);
656 public Project getProject() {
657 return myProject;
660 public boolean canRedefineClasses() {
661 return myVirtualMachineProxy != null && myVirtualMachineProxy.canRedefineClasses();
664 public boolean canWatchFieldModification() {
665 return myVirtualMachineProxy != null && myVirtualMachineProxy.canWatchFieldModification();
668 public boolean isInInitialState() {
669 return myState.get() == STATE_INITIAL;
672 public boolean isAttached() {
673 return myState.get() == STATE_ATTACHED;
676 public boolean isDetached() {
677 return myState.get() == STATE_DETACHED;
680 public boolean isDetaching() {
681 return myState.get() == STATE_DETACHING;
684 public RequestManagerImpl getRequestsManager() {
685 return myRequestManager;
688 public VirtualMachineProxyImpl getVirtualMachineProxy() {
689 DebuggerManagerThreadImpl.assertIsManagerThread();
690 if (myVirtualMachineProxy == null) {
691 throw new VMDisconnectedException();
693 return myVirtualMachineProxy;
696 public void appendPositionManager(final PositionManager positionManager) {
697 DebuggerManagerThreadImpl.assertIsManagerThread();
698 myPositionManager.appendPositionManager(positionManager);
699 DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager().updateBreakpoints(this);
702 private RunToCursorBreakpoint myRunToCursorBreakpoint;
704 public void cancelRunToCursorBreakpoint() {
705 DebuggerManagerThreadImpl.assertIsManagerThread();
706 if (myRunToCursorBreakpoint != null) {
707 getRequestsManager().deleteRequest(myRunToCursorBreakpoint);
708 myRunToCursorBreakpoint.delete();
709 if (myRunToCursorBreakpoint.isRestoreBreakpoints()) {
710 final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(getProject()).getBreakpointManager();
711 breakpointManager.enableBreakpoints(this);
713 myRunToCursorBreakpoint = null;
717 protected void closeProcess(boolean closedByUser) {
718 DebuggerManagerThreadImpl.assertIsManagerThread();
720 if (myState.compareAndSet(STATE_INITIAL, STATE_DETACHING) || myState.compareAndSet(STATE_ATTACHED, STATE_DETACHING)) {
721 myVirtualMachineProxy = null;
722 myPositionManager = null;
724 try {
725 getManagerThread().close();
727 finally {
728 myState.set(STATE_DETACHED);
729 try {
730 myDebugProcessDispatcher.getMulticaster().processDetached(this, closedByUser);
732 finally {
733 setBreakpointsMuted(false);
734 myWaitFor.up();
741 private static String formatMessage(String message) {
742 final int lineLength = 90;
743 StringBuffer buf = new StringBuffer(message.length());
744 int index = 0;
745 while (index < message.length()) {
746 buf.append(message.substring(index, Math.min(index + lineLength, message.length()))).append('\n');
747 index += lineLength;
749 return buf.toString();
752 public static String processError(Exception e) {
753 String message;
755 if (e instanceof VMStartException) {
756 VMStartException e1 = (VMStartException)e;
757 message = e1.getLocalizedMessage();
759 else if (e instanceof IllegalConnectorArgumentsException) {
760 IllegalConnectorArgumentsException e1 = (IllegalConnectorArgumentsException)e;
761 final List<String> invalidArgumentNames = e1.argumentNames();
762 message = formatMessage(DebuggerBundle.message("error.invalid.argument", invalidArgumentNames.size()) + ": "+ e1.getLocalizedMessage()) + invalidArgumentNames;
763 if (LOG.isDebugEnabled()) {
764 LOG.debug(e1);
767 else if (e instanceof CantRunException) {
768 message = e.getLocalizedMessage();
770 else if (e instanceof VMDisconnectedException) {
771 message = DebuggerBundle.message("error.vm.disconnected");
773 else if (e instanceof UnknownHostException) {
774 message = DebuggerBundle.message("error.unknown.host") + ":\n" + e.getLocalizedMessage();
776 else if (e instanceof IOException) {
777 IOException e1 = (IOException)e;
778 final StringBuilder buf = StringBuilderSpinAllocator.alloc();
779 try {
780 buf.append(DebuggerBundle.message("error.cannot.open.debugger.port")).append(" : ");
781 buf.append(e1.getClass().getName() + " ");
782 final String localizedMessage = e1.getLocalizedMessage();
783 if (localizedMessage != null && localizedMessage.length() > 0) {
784 buf.append('"');
785 buf.append(localizedMessage);
786 buf.append('"');
788 if (LOG.isDebugEnabled()) {
789 LOG.debug(e1);
791 message = buf.toString();
793 finally {
794 StringBuilderSpinAllocator.dispose(buf);
797 else if (e instanceof ExecutionException) {
798 message = e.getLocalizedMessage();
800 else {
801 message = DebuggerBundle.message("error.exception.while.connecting", e.getClass().getName(), e.getLocalizedMessage());
802 if (LOG.isDebugEnabled()) {
803 LOG.debug(e);
806 return message;
809 public void dispose() {
810 NodeRendererSettings.getInstance().removeListener(mySettingsListener);
811 myStatusUpdateAlarm.dispose();
814 public DebuggerManagerThreadImpl getManagerThread() {
815 if (myDebuggerManagerThread == null) {
816 synchronized (this) {
817 if (myDebuggerManagerThread == null) {
818 myDebuggerManagerThread = new DebuggerManagerThreadImpl();
822 return myDebuggerManagerThread;
825 private static int getInvokePolicy(SuspendContext suspendContext) {
826 //return ThreadReference.INVOKE_SINGLE_THREADED;
827 return suspendContext.getSuspendPolicy() == EventRequest.SUSPEND_EVENT_THREAD ? ThreadReference.INVOKE_SINGLE_THREADED : 0;
830 public void waitFor() {
831 LOG.assertTrue(!DebuggerManagerThreadImpl.isManagerThread());
832 myWaitFor.waitFor();
835 public void waitFor(long timeout) {
836 LOG.assertTrue(!DebuggerManagerThreadImpl.isManagerThread());
837 myWaitFor.waitFor(timeout);
840 private abstract class InvokeCommand <E extends Value> {
841 private final List myArgs;
843 protected InvokeCommand(List args) {
844 if (args.size() > 0) {
845 myArgs = new ArrayList(args);
847 else {
848 myArgs = args;
852 public String toString() {
853 return "INVOKE: " + super.toString();
856 protected abstract E invokeMethod(int invokePolicy, final List args) throws InvocationException,
857 ClassNotLoadedException,
858 IncompatibleThreadStateException,
859 InvalidTypeException;
861 public E start(EvaluationContextImpl evaluationContext, Method method) throws EvaluateException {
862 DebuggerManagerThreadImpl.assertIsManagerThread();
863 SuspendContextImpl suspendContext = evaluationContext.getSuspendContext();
864 SuspendManagerUtil.assertSuspendContext(suspendContext);
866 ThreadReferenceProxyImpl invokeThread = suspendContext.getThread();
868 if (SuspendManagerUtil.isEvaluating(getSuspendManager(), invokeThread)) {
869 throw EvaluateExceptionUtil.NESTED_EVALUATION_ERROR;
872 Set<SuspendContextImpl> suspendingContexts = SuspendManagerUtil.getSuspendingContexts(getSuspendManager(), invokeThread);
873 final ThreadReference invokeThreadRef = invokeThread.getThreadReference();
875 myEvaluationDispatcher.getMulticaster().evaluationStarted(suspendContext);
876 beforeMethodInvocation(suspendContext, method);
878 Object resumeData = null;
879 try {
880 for (final SuspendContextImpl suspendingContext : suspendingContexts) {
881 final ThreadReferenceProxyImpl suspendContextThread = suspendingContext.getThread();
882 if (suspendContextThread != invokeThread) {
883 if (LOG.isDebugEnabled()) {
884 LOG.debug("Resuming " + invokeThread + " that is paused by " + suspendContextThread);
886 LOG.assertTrue(suspendContextThread == null || !invokeThreadRef.equals(suspendContextThread.getThreadReference()));
887 getSuspendManager().resumeThread(suspendingContext, invokeThread);
891 resumeData = SuspendManagerUtil.prepareForResume(suspendContext);
892 suspendContext.setIsEvaluating(evaluationContext);
894 getVirtualMachineProxy().clearCaches();
896 while (true) {
897 try {
898 return invokeMethodAndFork(suspendContext);
900 catch (ClassNotLoadedException e) {
901 ReferenceType loadedClass = evaluationContext.isAutoLoadClasses()? loadClass(evaluationContext, e.className(), evaluationContext.getClassLoader()) : null;
902 if (loadedClass == null) {
903 throw EvaluateExceptionUtil.createEvaluateException(e);
908 catch (ClassNotLoadedException e) {
909 throw EvaluateExceptionUtil.createEvaluateException(e);
911 catch (InvocationException e) {
912 throw EvaluateExceptionUtil.createEvaluateException(e);
914 catch (IncompatibleThreadStateException e) {
915 throw EvaluateExceptionUtil.createEvaluateException(e);
917 catch (InvalidTypeException e) {
918 throw EvaluateExceptionUtil.createEvaluateException(e);
920 catch (ObjectCollectedException e) {
921 throw EvaluateExceptionUtil.createEvaluateException(e);
923 catch (UnsupportedOperationException e) {
924 throw EvaluateExceptionUtil.createEvaluateException(e);
926 catch (InternalException e) {
927 throw EvaluateExceptionUtil.createEvaluateException(e);
929 finally {
930 suspendContext.setIsEvaluating(null);
931 if (resumeData != null) {
932 SuspendManagerUtil.restoreAfterResume(suspendContext, resumeData);
934 for (SuspendContextImpl suspendingContext : mySuspendManager.getEventContexts()) {
935 if (suspendingContexts.contains(suspendingContext) && !suspendingContext.isEvaluating() && !suspendingContext.suspends(invokeThread)) {
936 mySuspendManager.suspendThread(suspendingContext, invokeThread);
940 if (LOG.isDebugEnabled()) {
941 LOG.debug("getVirtualMachine().clearCaches()");
943 getVirtualMachineProxy().clearCaches();
944 afterMethodInvocation(suspendContext);
946 myEvaluationDispatcher.getMulticaster().evaluationFinished(suspendContext);
950 private E invokeMethodAndFork(final SuspendContextImpl context) throws InvocationException,
951 ClassNotLoadedException,
952 IncompatibleThreadStateException,
953 InvalidTypeException {
954 final int invokePolicy = getInvokePolicy(context);
955 final Exception[] exception = new Exception[1];
956 final Value[] result = new Value[1];
957 getManagerThread().startLongProcessAndFork(new Runnable() {
958 public void run() {
959 ThreadReferenceProxyImpl thread = context.getThread();
960 try {
961 try {
962 if (LOG.isDebugEnabled()) {
963 final VirtualMachineProxyImpl virtualMachineProxy = getVirtualMachineProxy();
964 virtualMachineProxy.logThreads();
965 LOG.debug("Invoke in " + thread.name());
966 assertThreadSuspended(thread, context);
968 if (!Patches.IBM_JDK_DISABLE_COLLECTION_BUG) {
969 // ensure args are not collected
970 for (Object arg : myArgs) {
971 if (arg instanceof ObjectReference) {
972 ((ObjectReference)arg).disableCollection();
976 result[0] = invokeMethod(invokePolicy, myArgs);
978 finally {
979 // assertThreadSuspended(thread, context);
980 if (!Patches.IBM_JDK_DISABLE_COLLECTION_BUG) {
981 // ensure args are not collected
982 for (Object arg : myArgs) {
983 if (arg instanceof ObjectReference) {
984 ((ObjectReference)arg).enableCollection();
990 catch (Exception e) {
991 exception[0] = e;
996 if (exception[0] != null) {
997 if (exception[0] instanceof InvocationException) {
998 throw (InvocationException)exception[0];
1000 else if (exception[0] instanceof ClassNotLoadedException) {
1001 throw (ClassNotLoadedException)exception[0];
1003 else if (exception[0] instanceof IncompatibleThreadStateException) {
1004 throw (IncompatibleThreadStateException)exception[0];
1006 else if (exception[0] instanceof InvalidTypeException) {
1007 throw (InvalidTypeException)exception[0];
1009 else if (exception[0] instanceof RuntimeException) {
1010 throw (RuntimeException)exception[0];
1012 else {
1013 LOG.assertTrue(false);
1017 return (E)result[0];
1020 private void assertThreadSuspended(final ThreadReferenceProxyImpl thread, final SuspendContextImpl context) {
1021 LOG.assertTrue(context.isEvaluating());
1022 try {
1023 final boolean isSuspended = thread.isSuspended();
1024 LOG.assertTrue(isSuspended, thread);
1026 catch (ObjectCollectedException ignored) {
1031 public Value invokeMethod(final EvaluationContext evaluationContext, final ObjectReference objRef, final Method method, final List args) throws EvaluateException {
1032 return invokeInstanceMethod(evaluationContext, objRef, method, args, 0);
1035 public Value invokeInstanceMethod(final EvaluationContext evaluationContext, final ObjectReference objRef, final Method method,
1036 final List args, final int invocationOptions) throws EvaluateException {
1037 final ThreadReference thread = getEvaluationThread(evaluationContext);
1038 InvokeCommand<Value> invokeCommand = new InvokeCommand<Value>(args) {
1039 protected Value invokeMethod(int invokePolicy, final List args) throws InvocationException, ClassNotLoadedException, IncompatibleThreadStateException, InvalidTypeException {
1040 if (LOG.isDebugEnabled()) {
1041 LOG.debug("Invoke " + method.name());
1043 //try {
1044 // if (!Patches.IBM_JDK_DISABLE_COLLECTION_BUG) {
1045 // // ensure target object wil not be collected
1046 // objRef.disableCollection();
1047 // }
1048 return objRef.invokeMethod(thread, method, args, invokePolicy | invocationOptions);
1050 //finally {
1051 // if (!Patches.IBM_JDK_DISABLE_COLLECTION_BUG) {
1052 // objRef.enableCollection();
1053 // }
1057 return invokeCommand.start((EvaluationContextImpl)evaluationContext, method);
1060 private static ThreadReference getEvaluationThread(final EvaluationContext evaluationContext) throws EvaluateException {
1061 ThreadReferenceProxy evaluationThread = evaluationContext.getSuspendContext().getThread();
1062 if(evaluationThread == null) {
1063 throw EvaluateExceptionUtil.NULL_STACK_FRAME;
1065 return evaluationThread.getThreadReference();
1068 public Value invokeMethod(final EvaluationContext evaluationContext, final ClassType classType,
1069 final Method method,
1070 final List args) throws EvaluateException {
1072 final ThreadReference thread = getEvaluationThread(evaluationContext);
1073 InvokeCommand<Value> invokeCommand = new InvokeCommand<Value>(args) {
1074 protected Value invokeMethod(int invokePolicy, final List args) throws InvocationException,
1075 ClassNotLoadedException,
1076 IncompatibleThreadStateException,
1077 InvalidTypeException {
1078 if (LOG.isDebugEnabled()) {
1079 LOG.debug("Invoke " + method.name());
1081 return classType.invokeMethod(thread, method, args, invokePolicy);
1084 return invokeCommand.start((EvaluationContextImpl)evaluationContext, method);
1087 public ArrayReference newInstance(final ArrayType arrayType,
1088 final int dimension)
1089 throws EvaluateException {
1090 return arrayType.newInstance(dimension);
1093 public ObjectReference newInstance(final EvaluationContext evaluationContext, final ClassType classType,
1094 final Method method,
1095 final List args) throws EvaluateException {
1096 final ThreadReference thread = getEvaluationThread(evaluationContext);
1097 InvokeCommand<ObjectReference> invokeCommand = new InvokeCommand<ObjectReference>(args) {
1098 protected ObjectReference invokeMethod(int invokePolicy, final List args) throws InvocationException,
1099 ClassNotLoadedException,
1100 IncompatibleThreadStateException,
1101 InvalidTypeException {
1102 if (LOG.isDebugEnabled()) {
1103 LOG.debug("New instance " + method.name());
1105 return classType.newInstance(thread, method, args, invokePolicy);
1108 return invokeCommand.start((EvaluationContextImpl)evaluationContext, method);
1111 public void clearCashes(int suspendPolicy) {
1112 if (!isAttached()) return;
1113 switch (suspendPolicy) {
1114 case EventRequest.SUSPEND_ALL:
1115 getVirtualMachineProxy().clearCaches();
1116 break;
1117 case EventRequest.SUSPEND_EVENT_THREAD:
1118 getVirtualMachineProxy().clearCaches();
1119 //suspendContext.getThread().clearAll();
1120 break;
1124 protected void beforeSuspend(SuspendContextImpl suspendContext) {
1125 clearCashes(suspendContext.getSuspendPolicy());
1128 private void beforeMethodInvocation(SuspendContextImpl suspendContext, Method method) {
1129 if (LOG.isDebugEnabled()) {
1130 LOG.debug(
1131 "before invocation in thread " + suspendContext.getThread().name() + " method " + (method == null ? "null" : method.name()));
1134 if (method != null) {
1135 showStatusText(DebuggerBundle.message("progress.evaluating", DebuggerUtilsEx.methodName(method)));
1137 else {
1138 showStatusText(DebuggerBundle.message("title.evaluating"));
1142 private void afterMethodInvocation(SuspendContextImpl suspendContext) {
1143 if (LOG.isDebugEnabled()) {
1144 LOG.debug("after invocation in thread " + suspendContext.getThread().name());
1146 showStatusText("");
1149 public ReferenceType findClass(EvaluationContext evaluationContext, String className,
1150 ClassLoaderReference classLoader) throws EvaluateException {
1151 try {
1152 DebuggerManagerThreadImpl.assertIsManagerThread();
1153 ReferenceType result = null;
1154 final VirtualMachineProxyImpl vmProxy = getVirtualMachineProxy();
1155 if (vmProxy == null) {
1156 throw new VMDisconnectedException();
1158 for (final ReferenceType refType : vmProxy.classesByName(className)) {
1159 if (refType.isPrepared() && isVisibleFromClassLoader(classLoader, refType)) {
1160 result = refType;
1161 break;
1164 final EvaluationContextImpl evalContext = (EvaluationContextImpl)evaluationContext;
1165 if (result == null && evalContext.isAutoLoadClasses()) {
1166 return loadClass(evalContext, className, classLoader);
1168 return result;
1170 catch (InvocationException e) {
1171 throw EvaluateExceptionUtil.createEvaluateException(e);
1173 catch (ClassNotLoadedException e) {
1174 throw EvaluateExceptionUtil.createEvaluateException(e);
1176 catch (IncompatibleThreadStateException e) {
1177 throw EvaluateExceptionUtil.createEvaluateException(e);
1179 catch (InvalidTypeException e) {
1180 throw EvaluateExceptionUtil.createEvaluateException(e);
1184 private static boolean isVisibleFromClassLoader(final ClassLoaderReference fromLoader, final ReferenceType refType) {
1185 final ClassLoaderReference typeLoader = refType.classLoader();
1186 if (typeLoader == null) {
1187 return true; // optimization: if class is loaded by a bootstrap loader, it is visible from every other loader
1189 for (ClassLoaderReference checkLoader = fromLoader; checkLoader != null; checkLoader = getParentLoader(checkLoader)) {
1190 if (Comparing.equal(typeLoader, checkLoader)) {
1191 return true;
1194 return false;
1197 @SuppressWarnings({"HardCodedStringLiteral"})
1198 private static ClassLoaderReference getParentLoader(final ClassLoaderReference fromLoader) {
1199 final ReferenceType refType = fromLoader.referenceType();
1200 Field field = refType.fieldByName("parent");
1201 if (field == null) {
1202 final List allFields = refType.allFields();
1203 for (Iterator it = allFields.iterator(); it.hasNext();) {
1204 final Field candidateField = (Field)it.next();
1205 try {
1206 final Type checkedType = candidateField.type();
1207 if (checkedType instanceof ReferenceType && DebuggerUtilsEx.isAssignableFrom("java.lang.ClassLoader", (ReferenceType)checkedType)) {
1208 field = candidateField;
1209 break;
1212 catch (ClassNotLoadedException e) {
1213 // ignore this and continue,
1214 // java.lang.ClassLoader must be loaded at the moment of check, so if this happens, the field's type is definitely not java.lang.ClassLoader
1218 return field != null? (ClassLoaderReference)fromLoader.getValue(field) : null;
1221 private String reformatArrayName(String className) {
1222 if (className.indexOf('[') == -1) return className;
1224 int dims = 0;
1225 while (className.endsWith("[]")) {
1226 className = className.substring(0, className.length() - 2);
1227 dims++;
1230 StringBuilder buffer = StringBuilderSpinAllocator.alloc();
1231 try {
1232 for (int i = 0; i < dims; i++) {
1233 buffer.append('[');
1235 String primitiveSignature = JVMNameUtil.getPrimitiveSignature(className);
1236 if(primitiveSignature != null) {
1237 buffer.append(primitiveSignature);
1239 else {
1240 buffer.append('L');
1241 buffer.append(className);
1242 buffer.append(';');
1244 return buffer.toString();
1246 finally {
1247 StringBuilderSpinAllocator.dispose(buffer);
1251 @SuppressWarnings({"HardCodedStringLiteral"})
1252 public ReferenceType loadClass(EvaluationContextImpl evaluationContext, String qName, ClassLoaderReference classLoader)
1253 throws InvocationException, ClassNotLoadedException, IncompatibleThreadStateException, InvalidTypeException, EvaluateException {
1255 DebuggerManagerThreadImpl.assertIsManagerThread();
1256 qName = reformatArrayName(qName);
1257 ReferenceType refType = null;
1258 VirtualMachineProxyImpl virtualMachine = getVirtualMachineProxy();
1259 final List classClasses = virtualMachine.classesByName("java.lang.Class");
1260 if (classClasses.size() > 0) {
1261 ClassType classClassType = (ClassType)classClasses.get(0);
1262 final Method forNameMethod;
1263 if (classLoader != null) {
1264 //forNameMethod = classClassType.concreteMethodByName("forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;");
1265 forNameMethod = DebuggerUtils.findMethod(classClassType, "forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;");
1267 else {
1268 //forNameMethod = classClassType.concreteMethodByName("forName", "(Ljava/lang/String;)Ljava/lang/Class;");
1269 forNameMethod = DebuggerUtils.findMethod(classClassType, "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
1271 final List<Mirror> args = new ArrayList<Mirror>(); // do not use unmodifiable lists because the list is modified by JPDA
1272 final StringReference qNameMirror = virtualMachine.mirrorOf(qName);
1273 args.add(qNameMirror);
1274 if (classLoader != null) {
1275 args.add(virtualMachine.mirrorOf(true));
1276 args.add(classLoader);
1278 final Value value = invokeMethod(evaluationContext, classClassType, forNameMethod, args);
1279 if (value instanceof ClassObjectReference) {
1280 refType = ((ClassObjectReference)value).reflectedType();
1283 return refType;
1286 public void logThreads() {
1287 if (LOG.isDebugEnabled()) {
1288 try {
1289 Collection<ThreadReferenceProxyImpl> allThreads = getVirtualMachineProxy().allThreads();
1290 for (ThreadReferenceProxyImpl threadReferenceProxy : allThreads) {
1291 LOG.debug("Thread name=" + threadReferenceProxy.name() + " suspendCount()=" + threadReferenceProxy.getSuspendCount());
1294 catch (Exception e) {
1295 LOG.debug(e);
1300 public SuspendManager getSuspendManager() {
1301 return mySuspendManager;
1304 public CompoundPositionManager getPositionManager() {
1305 return myPositionManager;
1307 //ManagerCommands
1309 public void stop(boolean forceTerminate) {
1310 this.getManagerThread().terminateAndInvoke(createStopCommand(forceTerminate), DebuggerManagerThreadImpl.COMMAND_TIMEOUT);
1313 public StopCommand createStopCommand(boolean forceTerminate) {
1314 return new StopCommand(forceTerminate);
1317 protected class StopCommand extends DebuggerCommandImpl {
1318 private final boolean myIsTerminateTargetVM;
1320 public StopCommand(boolean isTerminateTargetVM) {
1321 myIsTerminateTargetVM = isTerminateTargetVM;
1324 public Priority getPriority() {
1325 return Priority.HIGH;
1328 protected void action() throws Exception {
1329 if (isAttached()) {
1330 final VirtualMachineProxyImpl virtualMachineProxy = getVirtualMachineProxy();
1331 if (myIsTerminateTargetVM) {
1332 virtualMachineProxy.exit(-1);
1334 else {
1335 // some VM's (like IBM VM 1.4.2 bundled with WebSpere) does not
1336 // resume threads on dispose() like it should
1337 virtualMachineProxy.resume();
1338 virtualMachineProxy.dispose();
1341 else {
1342 stopConnecting();
1347 private class StepOutCommand extends ResumeCommand {
1348 public StepOutCommand(SuspendContextImpl suspendContext) {
1349 super(suspendContext);
1352 public void contextAction() {
1353 showStatusText(DebuggerBundle.message("status.step.out"));
1354 final SuspendContextImpl suspendContext = getSuspendContext();
1355 final ThreadReferenceProxyImpl thread = suspendContext.getThread();
1356 RequestHint hint = new RequestHint(thread, suspendContext, StepRequest.STEP_OUT);
1357 hint.setIgnoreFilters(mySession.shouldIgnoreSteppingFilters());
1358 if (myReturnValueWatcher != null) {
1359 myReturnValueWatcher.setTrackingEnabled(true);
1361 doStep(suspendContext, thread, StepRequest.STEP_OUT, hint);
1362 super.contextAction();
1366 private class StepIntoCommand extends ResumeCommand {
1367 private final boolean myForcedIgnoreFilters;
1368 private final RequestHint.SmartStepFilter mySmartStepFilter;
1370 public StepIntoCommand(SuspendContextImpl suspendContext, boolean ignoreFilters, final @Nullable RequestHint.SmartStepFilter smartStepFilter) {
1371 super(suspendContext);
1372 myForcedIgnoreFilters = ignoreFilters || smartStepFilter != null;
1373 mySmartStepFilter = smartStepFilter;
1376 public void contextAction() {
1377 showStatusText(DebuggerBundle.message("status.step.into"));
1378 final SuspendContextImpl suspendContext = getSuspendContext();
1379 final ThreadReferenceProxyImpl stepThread = suspendContext.getThread();
1380 final RequestHint hint = mySmartStepFilter != null?
1381 new RequestHint(stepThread, suspendContext, mySmartStepFilter) :
1382 new RequestHint(stepThread, suspendContext, StepRequest.STEP_INTO);
1383 if (myForcedIgnoreFilters) {
1384 try {
1385 mySession.setIgnoreStepFiltersFlag(stepThread.frameCount());
1387 catch (EvaluateException e) {
1388 LOG.info(e);
1391 hint.setIgnoreFilters(myForcedIgnoreFilters || mySession.shouldIgnoreSteppingFilters());
1392 doStep(suspendContext, stepThread, StepRequest.STEP_INTO, hint);
1393 super.contextAction();
1397 private class StepOverCommand extends ResumeCommand {
1398 private final boolean myIsIgnoreBreakpoints;
1400 public StepOverCommand(SuspendContextImpl suspendContext, boolean ignoreBreakpoints) {
1401 super(suspendContext);
1402 myIsIgnoreBreakpoints = ignoreBreakpoints;
1405 public void contextAction() {
1406 showStatusText(DebuggerBundle.message("status.step.over"));
1407 final SuspendContextImpl suspendContext = getSuspendContext();
1408 final ThreadReferenceProxyImpl steppingThread = suspendContext.getThread();
1409 // need this hint whil stepping over for JSR45 support:
1410 // several lines of generated java code may correspond to a single line in the source file,
1411 // from which the java code was generated
1412 RequestHint hint = new RequestHint(steppingThread, suspendContext, StepRequest.STEP_OVER);
1413 hint.setRestoreBreakpoints(myIsIgnoreBreakpoints);
1414 hint.setIgnoreFilters(myIsIgnoreBreakpoints || mySession.shouldIgnoreSteppingFilters());
1416 if (myReturnValueWatcher != null) {
1417 myReturnValueWatcher.setTrackingEnabled(true);
1419 doStep(suspendContext, steppingThread, StepRequest.STEP_OVER, hint);
1421 if (myIsIgnoreBreakpoints) {
1422 DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager().disableBreakpoints(DebugProcessImpl.this);
1424 super.contextAction();
1428 private class RunToCursorCommand extends ResumeCommand {
1429 private final RunToCursorBreakpoint myRunToCursorBreakpoint;
1430 private final boolean myIgnoreBreakpoints;
1432 private RunToCursorCommand(SuspendContextImpl suspendContext, Document document, int lineIndex, final boolean ignoreBreakpoints) {
1433 super(suspendContext);
1434 myIgnoreBreakpoints = ignoreBreakpoints;
1435 final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager();
1436 myRunToCursorBreakpoint = breakpointManager.addRunToCursorBreakpoint(document, lineIndex, ignoreBreakpoints);
1439 public void contextAction() {
1440 showStatusText(DebuggerBundle.message("status.run.to.cursor"));
1441 cancelRunToCursorBreakpoint();
1442 if (myRunToCursorBreakpoint == null) {
1443 return;
1445 if (myIgnoreBreakpoints) {
1446 final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager();
1447 breakpointManager.disableBreakpoints(DebugProcessImpl.this);
1449 myRunToCursorBreakpoint.SUSPEND_POLICY = DebuggerSettings.SUSPEND_ALL;
1450 myRunToCursorBreakpoint.LOG_ENABLED = false;
1451 myRunToCursorBreakpoint.createRequest(getSuspendContext().getDebugProcess());
1452 DebugProcessImpl.this.myRunToCursorBreakpoint = myRunToCursorBreakpoint;
1453 super.contextAction();
1457 public abstract class ResumeCommand extends SuspendContextCommandImpl {
1459 public ResumeCommand(SuspendContextImpl suspendContext) {
1460 super(suspendContext);
1463 public Priority getPriority() {
1464 return Priority.HIGH;
1467 public void contextAction() {
1468 showStatusText(DebuggerBundle.message("status.process.resumed"));
1469 getSuspendManager().resume(getSuspendContext());
1470 myDebugProcessDispatcher.getMulticaster().resumed(getSuspendContext());
1474 private class PauseCommand extends DebuggerCommandImpl {
1475 public PauseCommand() {
1478 public void action() {
1479 if (!isAttached() || getVirtualMachineProxy().isPausePressed()) {
1480 return;
1482 logThreads();
1483 getVirtualMachineProxy().suspend();
1484 logThreads();
1485 SuspendContextImpl suspendContext = mySuspendManager.pushSuspendContext(EventRequest.SUSPEND_ALL, 0);
1486 myDebugProcessDispatcher.getMulticaster().paused(suspendContext);
1490 private class ResumeThreadCommand extends SuspendContextCommandImpl {
1491 private final ThreadReferenceProxyImpl myThread;
1493 public ResumeThreadCommand(SuspendContextImpl suspendContext, ThreadReferenceProxyImpl thread) {
1494 super(suspendContext);
1495 myThread = thread;
1498 public void contextAction() {
1499 if (getSuspendManager().isFrozen(myThread)) {
1500 getSuspendManager().unfreezeThread(myThread);
1501 return;
1504 final Set<SuspendContextImpl> suspendingContexts = SuspendManagerUtil.getSuspendingContexts(getSuspendManager(), myThread);
1505 for (Iterator<SuspendContextImpl> iterator = suspendingContexts.iterator(); iterator.hasNext();) {
1506 SuspendContextImpl suspendContext = iterator.next();
1507 if (suspendContext.getThread() == myThread) {
1508 DebugProcessImpl.this.getManagerThread().invoke(createResumeCommand(suspendContext));
1510 else {
1511 getSuspendManager().resumeThread(suspendContext, myThread);
1517 private class FreezeThreadCommand extends DebuggerCommandImpl {
1518 private final ThreadReferenceProxyImpl myThread;
1520 public FreezeThreadCommand(ThreadReferenceProxyImpl thread) {
1521 myThread = thread;
1524 protected void action() throws Exception {
1525 SuspendManager suspendManager = getSuspendManager();
1526 if (!suspendManager.isFrozen(myThread)) {
1527 suspendManager.freezeThread(myThread);
1532 private class PopFrameCommand extends SuspendContextCommandImpl {
1533 private final StackFrameProxyImpl myStackFrame;
1535 public PopFrameCommand(SuspendContextImpl context, StackFrameProxyImpl frameProxy) {
1536 super(context);
1537 myStackFrame = frameProxy;
1540 public void contextAction() {
1541 final ThreadReferenceProxyImpl thread = myStackFrame.threadProxy();
1542 try {
1543 if (!getSuspendManager().isSuspended(thread)) {
1544 notifyCancelled();
1545 return;
1548 catch (ObjectCollectedException e) {
1549 notifyCancelled();
1550 return;
1553 final SuspendContextImpl suspendContext = getSuspendContext();
1554 if (!suspendContext.suspends(thread)) {
1555 suspendContext.postponeCommand(this);
1556 return;
1559 if (myStackFrame.isBottom()) {
1560 DebuggerInvocationUtil.swingInvokeLater(myProject, new Runnable() {
1561 public void run() {
1562 Messages.showMessageDialog(myProject, DebuggerBundle.message("error.pop.bottom.stackframe"), ActionsBundle.actionText(DebuggerActions.POP_FRAME), Messages.getErrorIcon());
1565 return;
1568 try {
1569 thread.popFrames(myStackFrame);
1571 catch (final EvaluateException e) {
1572 DebuggerInvocationUtil.swingInvokeLater(myProject, new Runnable() {
1573 public void run() {
1574 Messages.showMessageDialog(myProject, DebuggerBundle.message("error.pop.stackframe", e.getLocalizedMessage()), ActionsBundle.actionText(DebuggerActions.POP_FRAME), Messages.getErrorIcon());
1577 LOG.info(e);
1579 finally {
1580 getSuspendManager().popFrame(suspendContext);
1585 @NotNull
1586 public GlobalSearchScope getSearchScope() {
1587 LOG.assertTrue(mySession != null, "Accessing debug session before its initialization");
1588 return mySession.getSearchScope();
1591 public @Nullable ExecutionResult attachVirtualMachine(final Executor executor,
1592 final ProgramRunner runner,
1593 final DebuggerSession session,
1594 final RunProfileState state,
1595 final RemoteConnection remoteConnection,
1596 boolean pollConnection) throws ExecutionException {
1597 mySession = session;
1598 myWaitFor.down();
1600 ApplicationManager.getApplication().assertIsDispatchThread();
1601 LOG.assertTrue(isInInitialState());
1603 myConnection = remoteConnection;
1605 createVirtualMachine(state, pollConnection);
1607 try {
1608 synchronized (myProcessListeners) {
1609 myExecutionResult = state.execute(executor, runner);
1610 if (myExecutionResult == null) {
1611 fail();
1612 return null;
1614 for (ProcessListener processListener : myProcessListeners) {
1615 myExecutionResult.getProcessHandler().addProcessListener(processListener);
1617 myProcessListeners.clear();
1620 catch (ExecutionException e) {
1621 fail();
1622 throw e;
1625 if (ApplicationManager.getApplication().isUnitTestMode()) {
1626 return myExecutionResult;
1630 final Alarm debugPortTimeout = new Alarm(Alarm.ThreadToUse.SHARED_THREAD);
1632 myExecutionResult.getProcessHandler().addProcessListener(new ProcessAdapter() {
1633 public void processTerminated(ProcessEvent event) {
1634 debugPortTimeout.cancelAllRequests();
1637 public void startNotified(ProcessEvent event) {
1638 debugPortTimeout.addRequest(new Runnable() {
1639 public void run() {
1640 if(isInInitialState()) {
1641 ApplicationManager.getApplication().schedule(new Runnable() {
1642 public void run() {
1643 String message = DebuggerBundle.message("status.connect.failed", DebuggerBundle.getAddressDisplayName(remoteConnection), DebuggerBundle.getTransportName(remoteConnection));
1644 Messages.showErrorDialog(myProject, message, DebuggerBundle.message("title.generic.debug.dialog"));
1649 }, LOCAL_START_TIMEOUT);
1654 return myExecutionResult;
1657 private void fail() {
1658 synchronized (this) {
1659 if (myIsFailed) {
1660 // need this in order to prevent calling stop() twice
1661 return;
1663 myIsFailed = true;
1665 stop(false);
1668 private void createVirtualMachine(final RunProfileState state, final boolean pollConnection) {
1669 final Semaphore semaphore = new Semaphore();
1670 semaphore.down();
1672 final Ref<Boolean> connectorIsReady = Ref.create(false);
1673 myDebugProcessDispatcher.addListener(new DebugProcessAdapter() {
1674 public void connectorIsReady() {
1675 connectorIsReady.set(true);
1676 semaphore.up();
1677 myDebugProcessDispatcher.removeListener(this);
1682 this.getManagerThread().schedule(new DebuggerCommandImpl() {
1683 protected void action() {
1684 VirtualMachine vm = null;
1686 try {
1687 final long time = System.currentTimeMillis();
1688 while (System.currentTimeMillis() - time < LOCAL_START_TIMEOUT) {
1689 try {
1690 vm = createVirtualMachineInt();
1691 break;
1693 catch (final ExecutionException e) {
1694 if (pollConnection && !myConnection.isServerMode() && e.getCause() instanceof IOException) {
1695 synchronized (this) {
1696 try {
1697 wait(500);
1699 catch (InterruptedException ie) {
1700 break;
1704 else {
1705 fail();
1706 if (myExecutionResult != null || !connectorIsReady.get()) {
1707 // propagate exception only in case we succeded to obtain execution result,
1708 // otherwise it the error is induced by the fact that there is nothing to debug, and there is no need to show
1709 // this problem to the user
1710 SwingUtilities.invokeLater(new Runnable() {
1711 public void run() {
1712 ProgramRunnerUtil.handleExecutionError(myProject, state.getRunnerSettings().getRunProfile(), e);
1716 break;
1721 finally {
1722 semaphore.up();
1725 if (vm != null) {
1726 final VirtualMachine vm1 = vm;
1727 afterProcessStarted(new Runnable() {
1728 public void run() {
1729 getManagerThread().schedule(new DebuggerCommandImpl() {
1730 protected void action() throws Exception {
1731 commitVM(vm1);
1739 protected void commandCancelled() {
1740 try {
1741 super.commandCancelled();
1743 finally {
1744 semaphore.up();
1749 semaphore.waitFor();
1752 private void afterProcessStarted(final Runnable run) {
1753 class MyProcessAdapter extends ProcessAdapter {
1754 private boolean alreadyRun = false;
1756 public synchronized void run() {
1757 if(!alreadyRun) {
1758 alreadyRun = true;
1759 run.run();
1761 removeProcessListener(this);
1764 public void startNotified(ProcessEvent event) {
1765 run();
1768 MyProcessAdapter processListener = new MyProcessAdapter();
1769 addProcessListener(processListener);
1770 if(myExecutionResult != null) {
1771 if(myExecutionResult.getProcessHandler().isStartNotified()) {
1772 processListener.run();
1777 public boolean isPausePressed() {
1778 return myVirtualMachineProxy != null && myVirtualMachineProxy.isPausePressed();
1781 public DebuggerCommandImpl createPauseCommand() {
1782 return new PauseCommand();
1785 public ResumeCommand createResumeCommand(SuspendContextImpl suspendContext) {
1786 return createResumeCommand(suspendContext, PrioritizedTask.Priority.HIGH);
1789 public ResumeCommand createResumeCommand(SuspendContextImpl suspendContext, final PrioritizedTask.Priority priority) {
1790 final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(getProject()).getBreakpointManager();
1791 return new ResumeCommand(suspendContext) {
1792 public void contextAction() {
1793 breakpointManager.applyThreadFilter(DebugProcessImpl.this, null); // clear the filter on resume
1794 super.contextAction();
1797 public Priority getPriority() {
1798 return priority;
1803 public ResumeCommand createStepOverCommand(SuspendContextImpl suspendContext, boolean ignoreBreakpoints) {
1804 return new StepOverCommand(suspendContext, ignoreBreakpoints);
1807 public ResumeCommand createStepOutCommand(SuspendContextImpl suspendContext) {
1808 return new StepOutCommand(suspendContext);
1811 public ResumeCommand createStepIntoCommand(SuspendContextImpl suspendContext, boolean ignoreFilters, final RequestHint.SmartStepFilter smartStepFilter) {
1812 return new StepIntoCommand(suspendContext, ignoreFilters, smartStepFilter);
1815 public ResumeCommand createRunToCursorCommand(SuspendContextImpl suspendContext, Document document, int lineIndex,
1816 final boolean ignoreBreakpoints)
1817 throws EvaluateException {
1818 RunToCursorCommand runToCursorCommand = new RunToCursorCommand(suspendContext, document, lineIndex, ignoreBreakpoints);
1819 if(runToCursorCommand.myRunToCursorBreakpoint == null) {
1820 final PsiFile psiFile = PsiDocumentManager.getInstance(getProject()).getPsiFile(document);
1821 throw new EvaluateException(DebuggerBundle.message("error.running.to.cursor.no.executable.code", psiFile != null? psiFile.getName() : "<No File>", lineIndex), null);
1823 return runToCursorCommand;
1826 public DebuggerCommandImpl createFreezeThreadCommand(ThreadReferenceProxyImpl thread) {
1827 return new FreezeThreadCommand(thread);
1830 public SuspendContextCommandImpl createResumeThreadCommand(SuspendContextImpl suspendContext, ThreadReferenceProxyImpl thread) {
1831 return new ResumeThreadCommand(suspendContext, thread);
1834 public SuspendContextCommandImpl createPopFrameCommand(DebuggerContextImpl context, StackFrameProxyImpl stackFrame) {
1835 final SuspendContextImpl contextByThread =
1836 SuspendManagerUtil.findContextByThread(context.getDebugProcess().getSuspendManager(), stackFrame.threadProxy());
1837 return new PopFrameCommand(contextByThread, stackFrame);
1840 public void setBreakpointsMuted(final boolean muted) {
1841 if (isAttached()) {
1842 getManagerThread().schedule(new DebuggerCommandImpl() {
1843 protected void action() throws Exception {
1844 // set the flag before enabling/disabling cause it affects if breakpoints will create requests
1845 if (myBreakpointsMuted.getAndSet(muted) != muted) {
1846 final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager();
1847 if (muted) {
1848 breakpointManager.disableBreakpoints(DebugProcessImpl.this);
1850 else {
1851 breakpointManager.enableBreakpoints(DebugProcessImpl.this);
1857 else {
1858 myBreakpointsMuted.set(muted);
1863 public boolean areBreakpointsMuted() {
1864 return myBreakpointsMuted.get();