2 * Copyright 2000-2014 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.debugger.engine;
18 import com.intellij.Patches;
19 import com.intellij.debugger.*;
20 import com.intellij.debugger.actions.DebuggerAction;
21 import com.intellij.debugger.actions.DebuggerActions;
22 import com.intellij.debugger.apiAdapters.ConnectionServiceWrapper;
23 import com.intellij.debugger.engine.evaluation.*;
24 import com.intellij.debugger.engine.events.DebuggerCommandImpl;
25 import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
26 import com.intellij.debugger.engine.jdi.ThreadReferenceProxy;
27 import com.intellij.debugger.engine.requests.MethodReturnValueWatcher;
28 import com.intellij.debugger.engine.requests.RequestManagerImpl;
29 import com.intellij.debugger.impl.DebuggerContextImpl;
30 import com.intellij.debugger.impl.DebuggerSession;
31 import com.intellij.debugger.impl.DebuggerUtilsEx;
32 import com.intellij.debugger.impl.PrioritizedTask;
33 import com.intellij.debugger.jdi.StackFrameProxyImpl;
34 import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
35 import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
36 import com.intellij.debugger.settings.DebuggerSettings;
37 import com.intellij.debugger.settings.NodeRendererSettings;
38 import com.intellij.debugger.ui.breakpoints.BreakpointManager;
39 import com.intellij.debugger.ui.breakpoints.RunToCursorBreakpoint;
40 import com.intellij.debugger.ui.breakpoints.StepIntoBreakpoint;
41 import com.intellij.debugger.ui.tree.ValueDescriptor;
42 import com.intellij.debugger.ui.tree.render.*;
43 import com.intellij.execution.CantRunException;
44 import com.intellij.execution.ExecutionException;
45 import com.intellij.execution.ExecutionResult;
46 import com.intellij.execution.configurations.RemoteConnection;
47 import com.intellij.execution.process.*;
48 import com.intellij.execution.runners.ExecutionUtil;
49 import com.intellij.idea.ActionsBundle;
50 import com.intellij.openapi.Disposable;
51 import com.intellij.openapi.application.ApplicationManager;
52 import com.intellij.openapi.diagnostic.Logger;
53 import com.intellij.openapi.editor.Document;
54 import com.intellij.openapi.extensions.Extensions;
55 import com.intellij.openapi.project.Project;
56 import com.intellij.openapi.ui.Messages;
57 import com.intellij.openapi.util.Disposer;
58 import com.intellij.openapi.util.Pair;
59 import com.intellij.openapi.util.Ref;
60 import com.intellij.openapi.util.UserDataHolderBase;
61 import com.intellij.openapi.util.text.StringUtil;
62 import com.intellij.openapi.wm.ToolWindowId;
63 import com.intellij.openapi.wm.impl.status.StatusBarUtil;
64 import com.intellij.psi.PsiDocumentManager;
65 import com.intellij.psi.PsiFile;
66 import com.intellij.psi.search.GlobalSearchScope;
67 import com.intellij.ui.classFilter.ClassFilter;
68 import com.intellij.ui.classFilter.DebuggerClassFilterProvider;
69 import com.intellij.util.Alarm;
70 import com.intellij.util.EventDispatcher;
71 import com.intellij.util.StringBuilderSpinAllocator;
72 import com.intellij.util.concurrency.Semaphore;
73 import com.intellij.util.containers.ContainerUtil;
74 import com.intellij.util.containers.HashMap;
75 import com.intellij.util.ui.UIUtil;
76 import com.intellij.xdebugger.XDebugSession;
77 import com.intellij.xdebugger.impl.actions.XDebuggerActions;
79 import com.sun.jdi.connect.*;
80 import com.sun.jdi.request.EventRequest;
81 import com.sun.jdi.request.EventRequestManager;
82 import com.sun.jdi.request.StepRequest;
83 import org.jetbrains.annotations.NonNls;
84 import org.jetbrains.annotations.NotNull;
85 import org.jetbrains.annotations.Nullable;
88 import java.io.IOException;
89 import java.net.UnknownHostException;
91 import java.util.concurrent.atomic.AtomicInteger;
93 public abstract class DebugProcessImpl extends UserDataHolderBase implements DebugProcess {
94 private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.DebugProcessImpl");
96 @NonNls private static final String SOCKET_ATTACHING_CONNECTOR_NAME = "com.sun.jdi.SocketAttach";
97 @NonNls private static final String SHMEM_ATTACHING_CONNECTOR_NAME = "com.sun.jdi.SharedMemoryAttach";
98 @NonNls private static final String SOCKET_LISTENING_CONNECTOR_NAME = "com.sun.jdi.SocketListen";
99 @NonNls private static final String SHMEM_LISTENING_CONNECTOR_NAME = "com.sun.jdi.SharedMemoryListen";
101 private final Project myProject;
102 private final RequestManagerImpl myRequestManager;
104 private volatile VirtualMachineProxyImpl myVirtualMachineProxy = null;
105 protected EventDispatcher<DebugProcessListener> myDebugProcessDispatcher = EventDispatcher.create(DebugProcessListener.class);
106 protected EventDispatcher<EvaluationListener> myEvaluationDispatcher = EventDispatcher.create(EvaluationListener.class);
108 private final List<ProcessListener> myProcessListeners = ContainerUtil.createLockFreeCopyOnWriteList();
110 protected static final int STATE_INITIAL = 0;
111 protected static final int STATE_ATTACHED = 1;
112 protected static final int STATE_DETACHING = 2;
113 protected static final int STATE_DETACHED = 3;
114 protected final AtomicInteger myState = new AtomicInteger(STATE_INITIAL);
116 private ExecutionResult myExecutionResult;
117 private RemoteConnection myConnection;
118 private JavaDebugProcess myXDebugProcess;
120 private ConnectionServiceWrapper myConnectionService;
121 private Map<String, Connector.Argument> myArguments;
123 private final List<NodeRenderer> myRenderers = new ArrayList<NodeRenderer>();
125 // we use null key here
126 private final Map<Type, NodeRenderer> myNodeRenderersMap = new HashMap<Type, NodeRenderer>();
128 private final NodeRendererSettingsListener mySettingsListener = new NodeRendererSettingsListener() {
130 public void renderersChanged() {
131 myNodeRenderersMap.clear();
137 private final SuspendManagerImpl mySuspendManager = new SuspendManagerImpl(this);
138 protected CompoundPositionManager myPositionManager = null;
139 private volatile DebuggerManagerThreadImpl myDebuggerManagerThread;
140 private static final int LOCAL_START_TIMEOUT = 30000;
142 private final Semaphore myWaitFor = new Semaphore();
143 private boolean myIsFailed = false;
144 protected DebuggerSession mySession;
145 @Nullable protected MethodReturnValueWatcher myReturnValueWatcher;
146 private final Disposable myDisposable = Disposer.newDisposable();
147 private final Alarm myStatusUpdateAlarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD, myDisposable);
149 protected DebugProcessImpl(Project project) {
151 myRequestManager = new RequestManagerImpl(this);
152 NodeRendererSettings.getInstance().addListener(mySettingsListener);
156 private void loadRenderers() {
157 getManagerThread().invoke(new DebuggerCommandImpl() {
159 protected void action() throws Exception {
161 final NodeRendererSettings rendererSettings = NodeRendererSettings.getInstance();
162 for (final NodeRenderer renderer : rendererSettings.getAllRenderers()) {
163 if (renderer.isEnabled()) {
164 myRenderers.add(renderer);
169 DebuggerInvocationUtil.swingInvokeLater(myProject, new Runnable() {
172 final DebuggerSession session = mySession;
173 if (session != null && session.isAttached()) {
174 session.refresh(true);
175 DebuggerAction.refreshViews(mySession.getXDebugSession());
185 public Pair<Method, Value> getLastExecutedMethod() {
186 final MethodReturnValueWatcher watcher = myReturnValueWatcher;
187 if (watcher == null) {
190 final Method method = watcher.getLastExecutedMethod();
191 if (method == null) {
194 return Pair.create(method, watcher.getLastMethodReturnValue());
197 public void setWatchMethodReturnValuesEnabled(boolean enabled) {
198 final MethodReturnValueWatcher watcher = myReturnValueWatcher;
199 if (watcher != null) {
200 watcher.setFeatureEnabled(enabled);
204 public boolean isWatchMethodReturnValuesEnabled() {
205 final MethodReturnValueWatcher watcher = myReturnValueWatcher;
206 return watcher != null && watcher.isFeatureEnabled();
209 public boolean canGetMethodReturnValue() {
210 return myReturnValueWatcher != null;
213 public NodeRenderer getAutoRenderer(ValueDescriptor descriptor) {
214 DebuggerManagerThreadImpl.assertIsManagerThread();
215 final Value value = descriptor.getValue();
216 Type type = value != null ? value.type() : null;
218 // in case evaluation is not possible, force default renderer
219 if (!DebuggerManagerEx.getInstanceEx(getProject()).getContext().isEvaluationPossible()) {
220 return getDefaultRenderer(type);
223 NodeRenderer renderer = myNodeRenderersMap.get(type);
224 if(renderer == null) {
225 for (final NodeRenderer nodeRenderer : myRenderers) {
226 if (nodeRenderer.isApplicable(type)) {
227 renderer = nodeRenderer;
231 if (renderer == null) {
232 renderer = getDefaultRenderer(type);
234 myNodeRenderersMap.put(type, renderer);
240 public static NodeRenderer getDefaultRenderer(Value value) {
241 return getDefaultRenderer(value != null ? value.type() : null);
244 public static NodeRenderer getDefaultRenderer(Type type) {
245 final NodeRendererSettings settings = NodeRendererSettings.getInstance();
247 final PrimitiveRenderer primitiveRenderer = settings.getPrimitiveRenderer();
248 if(primitiveRenderer.isApplicable(type)) {
249 return primitiveRenderer;
252 final ArrayRenderer arrayRenderer = settings.getArrayRenderer();
253 if(arrayRenderer.isApplicable(type)) {
254 return arrayRenderer;
257 final ClassRenderer classRenderer = settings.getClassRenderer();
258 LOG.assertTrue(classRenderer.isApplicable(type), type.name());
259 return classRenderer;
263 @SuppressWarnings({"HardCodedStringLiteral"})
264 protected void commitVM(VirtualMachine vm) {
265 if (!isInInitialState()) {
266 LOG.error("State is invalid " + myState.get());
268 DebuggerManagerThreadImpl.assertIsManagerThread();
269 myPositionManager = createPositionManager();
270 if (LOG.isDebugEnabled()) {
271 LOG.debug("*******************VM attached******************");
273 checkVirtualMachineVersion(vm);
275 myVirtualMachineProxy = new VirtualMachineProxyImpl(this, vm);
277 String trace = System.getProperty("idea.debugger.trace");
280 StringTokenizer tokenizer = new StringTokenizer(trace);
281 while (tokenizer.hasMoreTokens()) {
282 String token = tokenizer.nextToken();
283 if ("SENDS".compareToIgnoreCase(token) == 0) {
284 mask |= VirtualMachine.TRACE_SENDS;
286 else if ("RAW_SENDS".compareToIgnoreCase(token) == 0) {
289 else if ("RECEIVES".compareToIgnoreCase(token) == 0) {
290 mask |= VirtualMachine.TRACE_RECEIVES;
292 else if ("RAW_RECEIVES".compareToIgnoreCase(token) == 0) {
295 else if ("EVENTS".compareToIgnoreCase(token) == 0) {
296 mask |= VirtualMachine.TRACE_EVENTS;
298 else if ("REFTYPES".compareToIgnoreCase(token) == 0) {
299 mask |= VirtualMachine.TRACE_REFTYPES;
301 else if ("OBJREFS".compareToIgnoreCase(token) == 0) {
302 mask |= VirtualMachine.TRACE_OBJREFS;
304 else if ("ALL".compareToIgnoreCase(token) == 0) {
305 mask |= VirtualMachine.TRACE_ALL;
309 vm.setDebugTraceMode(mask);
313 private void stopConnecting() {
314 DebuggerManagerThreadImpl.assertIsManagerThread();
316 Map<String, Connector.Argument> arguments = myArguments;
318 if (arguments == null) {
321 if(myConnection.isServerMode()) {
322 ListeningConnector connector = (ListeningConnector)findConnector(SOCKET_LISTENING_CONNECTOR_NAME);
323 if (connector == null) {
324 LOG.error("Cannot find connector: " + SOCKET_LISTENING_CONNECTOR_NAME);
327 connector.stopListening(arguments);
331 if(myConnectionService != null) {
332 myConnectionService.close();
336 catch (IOException e) {
337 if (LOG.isDebugEnabled()) {
341 catch (IllegalConnectorArgumentsException e) {
342 if (LOG.isDebugEnabled()) {
346 catch (ExecutionException e) {
354 protected CompoundPositionManager createPositionManager() {
355 return new CompoundPositionManager(new PositionManagerImpl(this));
359 public void printToConsole(final String text) {
360 myExecutionResult.getProcessHandler().notifyTextAvailable(text, ProcessOutputTypes.SYSTEM);
364 public ProcessHandler getProcessHandler() {
365 return myExecutionResult.getProcessHandler();
370 * @param suspendContext
372 * @param size the step size. One of {@link StepRequest#STEP_LINE} or {@link StepRequest#STEP_MIN}
374 * @param hint may be null
376 protected void doStep(final SuspendContextImpl suspendContext, final ThreadReferenceProxyImpl stepThread, int size, int depth,
378 if (stepThread == null) {
382 final ThreadReference stepThreadReference = stepThread.getThreadReference();
383 if (LOG.isDebugEnabled()) {
384 LOG.debug("DO_STEP: creating step request for " + stepThreadReference);
386 deleteStepRequests(stepThreadReference);
387 EventRequestManager requestManager = getVirtualMachineProxy().eventRequestManager();
388 StepRequest stepRequest = requestManager.createStepRequest(stepThreadReference, size, depth);
389 if (!(hint != null && hint.isIgnoreFilters()) /*&& depth == StepRequest.STEP_INTO*/) {
390 final List<ClassFilter> activeFilters = new ArrayList<ClassFilter>();
391 DebuggerSettings settings = DebuggerSettings.getInstance();
392 if (settings.TRACING_FILTERS_ENABLED) {
393 for (ClassFilter filter : settings.getSteppingFilters()) {
394 if (filter.isEnabled()) {
395 activeFilters.add(filter);
399 for (DebuggerClassFilterProvider provider : Extensions.getExtensions(DebuggerClassFilterProvider.EP_NAME)) {
400 for (ClassFilter filter : provider.getFilters()) {
401 if (filter.isEnabled()) {
402 activeFilters.add(filter);
407 if (!activeFilters.isEmpty()) {
408 final String currentClassName = getCurrentClassName(stepThread);
409 if (currentClassName == null || !DebuggerUtilsEx.isFiltered(currentClassName, activeFilters)) {
411 for (ClassFilter filter : activeFilters) {
412 stepRequest.addClassExclusionFilter(filter.getPattern());
418 // suspend policy to match the suspend policy of the context:
419 // if all threads were suspended, then during stepping all the threads must be suspended
420 // if only event thread were suspended, then only this particular thread must be suspended during stepping
421 stepRequest.setSuspendPolicy(suspendContext.getSuspendPolicy() == EventRequest.SUSPEND_EVENT_THREAD? EventRequest.SUSPEND_EVENT_THREAD : EventRequest.SUSPEND_ALL);
424 //noinspection HardCodedStringLiteral
425 stepRequest.putProperty("hint", hint);
427 stepRequest.enable();
429 catch (ObjectCollectedException ignored) {
434 void deleteStepRequests(@Nullable final ThreadReference stepThread) {
435 EventRequestManager requestManager = getVirtualMachineProxy().eventRequestManager();
436 List<StepRequest> stepRequests = requestManager.stepRequests();
437 if (!stepRequests.isEmpty()) {
438 final List<StepRequest> toDelete = new ArrayList<StepRequest>(stepRequests.size());
439 for (final StepRequest request : stepRequests) {
440 ThreadReference threadReference = request.thread();
441 // [jeka] on attempt to delete a request assigned to a thread with unknown status, a JDWP error occurs
443 if (threadReference.status() != ThreadReference.THREAD_STATUS_UNKNOWN && (stepThread == null || stepThread.equals(threadReference))) {
444 toDelete.add(request);
447 catch (IllegalThreadStateException e) {
448 LOG.info(e); // undocumented by JDI: may be thrown when querying thread status
451 requestManager.deleteEventRequests(toDelete);
456 private static String getCurrentClassName(ThreadReferenceProxyImpl thread) {
458 if (thread != null && thread.frameCount() > 0) {
459 StackFrameProxyImpl stackFrame = thread.frame(0);
460 if (stackFrame != null) {
461 Location location = stackFrame.location();
462 ReferenceType referenceType = location == null ? null : location.declaringType();
463 if (referenceType != null) {
464 return referenceType.name();
469 catch (EvaluateException ignored) {
474 private VirtualMachine createVirtualMachineInt() throws ExecutionException {
476 if (myArguments != null) {
477 throw new IOException(DebuggerBundle.message("error.debugger.already.listening"));
480 final String address = myConnection.getAddress();
481 if (myConnection.isServerMode()) {
482 ListeningConnector connector = (ListeningConnector)findConnector(
483 myConnection.isUseSockets() ? SOCKET_LISTENING_CONNECTOR_NAME : SHMEM_LISTENING_CONNECTOR_NAME);
484 if (connector == null) {
485 throw new CantRunException(DebuggerBundle.message("error.debug.connector.not.found", DebuggerBundle.getTransportName(myConnection)));
487 myArguments = connector.defaultArguments();
488 if (myArguments == null) {
489 throw new CantRunException(DebuggerBundle.message("error.no.debug.listen.port"));
492 if (address == null) {
493 throw new CantRunException(DebuggerBundle.message("error.no.debug.listen.port"));
495 // negative port number means the caller leaves to debugger to decide at which port to listen
496 //noinspection HardCodedStringLiteral
497 final Connector.Argument portArg = myConnection.isUseSockets() ? myArguments.get("port") : myArguments.get("name");
498 if (portArg != null) {
499 portArg.setValue(address);
501 //noinspection HardCodedStringLiteral
502 final Connector.Argument timeoutArg = myArguments.get("timeout");
503 if (timeoutArg != null) {
504 timeoutArg.setValue("0"); // wait forever
506 connector.startListening(myArguments);
507 myDebugProcessDispatcher.getMulticaster().connectorIsReady();
509 return connector.accept(myArguments);
512 if(myArguments != null) {
514 connector.stopListening(myArguments);
516 catch (IllegalArgumentException ignored) {
519 catch (IllegalConnectorArgumentsException ignored) {
525 else { // is client mode, should attach to already running process
526 AttachingConnector connector = (AttachingConnector)findConnector(
527 myConnection.isUseSockets() ? SOCKET_ATTACHING_CONNECTOR_NAME : SHMEM_ATTACHING_CONNECTOR_NAME
530 if (connector == null) {
531 throw new CantRunException( DebuggerBundle.message("error.debug.connector.not.found", DebuggerBundle.getTransportName(myConnection)));
533 myArguments = connector.defaultArguments();
534 if (myConnection.isUseSockets()) {
535 //noinspection HardCodedStringLiteral
536 final Connector.Argument hostnameArg = myArguments.get("hostname");
537 if (hostnameArg != null && myConnection.getHostName() != null) {
538 hostnameArg.setValue(myConnection.getHostName());
540 if (address == null) {
541 throw new CantRunException(DebuggerBundle.message("error.no.debug.attach.port"));
543 //noinspection HardCodedStringLiteral
544 final Connector.Argument portArg = myArguments.get("port");
545 if (portArg != null) {
546 portArg.setValue(address);
550 if (address == null) {
551 throw new CantRunException(DebuggerBundle.message("error.no.shmem.address"));
553 //noinspection HardCodedStringLiteral
554 final Connector.Argument nameArg = myArguments.get("name");
555 if (nameArg != null) {
556 nameArg.setValue(address);
559 //noinspection HardCodedStringLiteral
560 final Connector.Argument timeoutArg = myArguments.get("timeout");
561 if (timeoutArg != null) {
562 timeoutArg.setValue("0"); // wait forever
565 myDebugProcessDispatcher.getMulticaster().connectorIsReady();
567 return connector.attach(myArguments);
569 catch (IllegalArgumentException e) {
570 throw new CantRunException(e.getLocalizedMessage());
574 catch (IOException e) {
575 throw new ExecutionException(processIOException(e, DebuggerBundle.getAddressDisplayName(myConnection)), e);
577 catch (IllegalConnectorArgumentsException e) {
578 throw new ExecutionException(processError(e), e);
582 myConnectionService = null;
586 public void showStatusText(final String text) {
587 if (!myStatusUpdateAlarm.isDisposed()) {
588 myStatusUpdateAlarm.cancelAllRequests();
589 myStatusUpdateAlarm.addRequest(new Runnable() {
592 StatusBarUtil.setStatusBarInfo(myProject, text);
598 static Connector findConnector(String connectorName) throws ExecutionException {
599 VirtualMachineManager virtualMachineManager;
601 virtualMachineManager = Bootstrap.virtualMachineManager();
604 final String error = e.getClass().getName() + " : " + e.getLocalizedMessage();
605 throw new ExecutionException(DebuggerBundle.message("debugger.jdi.bootstrap.error", error));
608 if (SOCKET_ATTACHING_CONNECTOR_NAME.equals(connectorName) || SHMEM_ATTACHING_CONNECTOR_NAME.equals(connectorName)) {
609 connectors = virtualMachineManager.attachingConnectors();
611 else if (SOCKET_LISTENING_CONNECTOR_NAME.equals(connectorName) || SHMEM_LISTENING_CONNECTOR_NAME.equals(connectorName)) {
612 connectors = virtualMachineManager.listeningConnectors();
617 for (Object connector1 : connectors) {
618 Connector connector = (Connector)connector1;
619 if (connectorName.equals(connector.name())) {
626 private void checkVirtualMachineVersion(VirtualMachine vm) {
627 final String version = vm.version();
628 if ("1.4.0".equals(version)) {
629 SwingUtilities.invokeLater(new Runnable() {
632 Messages.showMessageDialog(
634 DebuggerBundle.message("warning.jdk140.unstable"), DebuggerBundle.message("title.jdk140.unstable"), Messages.getWarningIcon()
641 /*Event dispatching*/
642 public void addEvaluationListener(EvaluationListener evaluationListener) {
643 myEvaluationDispatcher.addListener(evaluationListener);
646 public void removeEvaluationListener(EvaluationListener evaluationListener) {
647 myEvaluationDispatcher.removeListener(evaluationListener);
651 public void addDebugProcessListener(DebugProcessListener listener) {
652 myDebugProcessDispatcher.addListener(listener);
656 public void removeDebugProcessListener(DebugProcessListener listener) {
657 myDebugProcessDispatcher.removeListener(listener);
660 public void addProcessListener(ProcessListener processListener) {
661 synchronized(myProcessListeners) {
662 if(getProcessHandler() != null) {
663 getProcessHandler().addProcessListener(processListener);
666 myProcessListeners.add(processListener);
671 public void removeProcessListener(ProcessListener processListener) {
672 synchronized (myProcessListeners) {
673 if(getProcessHandler() != null) {
674 getProcessHandler().removeProcessListener(processListener);
677 myProcessListeners.remove(processListener);
683 public RemoteConnection getConnection() {
688 public ExecutionResult getExecutionResult() {
689 return myExecutionResult;
693 public Project getProject() {
697 public boolean canRedefineClasses() {
698 final VirtualMachineProxyImpl vm = myVirtualMachineProxy;
699 return vm != null && vm.canRedefineClasses();
702 public boolean canWatchFieldModification() {
703 final VirtualMachineProxyImpl vm = myVirtualMachineProxy;
704 return vm != null && vm.canWatchFieldModification();
707 public boolean isInInitialState() {
708 return myState.get() == STATE_INITIAL;
712 public boolean isAttached() {
713 return myState.get() == STATE_ATTACHED;
717 public boolean isDetached() {
718 return myState.get() == STATE_DETACHED;
722 public boolean isDetaching() {
723 return myState.get() == STATE_DETACHING;
727 public RequestManagerImpl getRequestsManager() {
728 return myRequestManager;
732 public VirtualMachineProxyImpl getVirtualMachineProxy() {
733 DebuggerManagerThreadImpl.assertIsManagerThread();
734 final VirtualMachineProxyImpl vm = myVirtualMachineProxy;
736 throw new VMDisconnectedException();
742 public void appendPositionManager(final PositionManager positionManager) {
743 DebuggerManagerThreadImpl.assertIsManagerThread();
744 myPositionManager.appendPositionManager(positionManager);
747 private volatile RunToCursorBreakpoint myRunToCursorBreakpoint;
749 public void cancelRunToCursorBreakpoint() {
750 DebuggerManagerThreadImpl.assertIsManagerThread();
751 final RunToCursorBreakpoint runToCursorBreakpoint = myRunToCursorBreakpoint;
752 if (runToCursorBreakpoint != null) {
753 myRunToCursorBreakpoint = null;
754 getRequestsManager().deleteRequest(runToCursorBreakpoint);
755 if (runToCursorBreakpoint.isRestoreBreakpoints()) {
756 final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(getProject()).getBreakpointManager();
757 breakpointManager.enableBreakpoints(this);
762 protected void closeProcess(boolean closedByUser) {
763 DebuggerManagerThreadImpl.assertIsManagerThread();
765 if (myState.compareAndSet(STATE_INITIAL, STATE_DETACHING) || myState.compareAndSet(STATE_ATTACHED, STATE_DETACHING)) {
767 getManagerThread().close();
770 final VirtualMachineProxyImpl vm = myVirtualMachineProxy;
771 myVirtualMachineProxy = null;
772 myPositionManager = null;
773 myReturnValueWatcher = null;
774 myNodeRenderersMap.clear();
776 DebuggerUtils.cleanupAfterProcessFinish(this);
777 myState.set(STATE_DETACHED);
779 myDebugProcessDispatcher.getMulticaster().processDetached(this, closedByUser);
782 //if (DebuggerSettings.getInstance().UNMUTE_ON_STOP) {
783 // XDebugSession session = mySession.getXDebugSession();
784 // if (session != null) {
785 // session.setBreakpointMuted(false);
790 vm.dispose(); // to be on the safe side ensure that VM mirror, if present, is disposed and invalidated
792 catch (Throwable ignored) {
802 private static String formatMessage(String message) {
803 final int lineLength = 90;
804 StringBuilder buf = new StringBuilder(message.length());
806 while (index < message.length()) {
807 buf.append(message.substring(index, Math.min(index + lineLength, message.length()))).append('\n');
810 return buf.toString();
813 public static String processError(Exception e) {
816 if (e instanceof VMStartException) {
817 VMStartException e1 = (VMStartException)e;
818 message = e1.getLocalizedMessage();
820 else if (e instanceof IllegalConnectorArgumentsException) {
821 IllegalConnectorArgumentsException e1 = (IllegalConnectorArgumentsException)e;
822 final List<String> invalidArgumentNames = e1.argumentNames();
823 message = formatMessage(DebuggerBundle.message("error.invalid.argument", invalidArgumentNames.size()) + ": "+ e1.getLocalizedMessage()) + invalidArgumentNames;
824 if (LOG.isDebugEnabled()) {
828 else if (e instanceof CantRunException) {
829 message = e.getLocalizedMessage();
831 else if (e instanceof VMDisconnectedException) {
832 message = DebuggerBundle.message("error.vm.disconnected");
834 else if (e instanceof IOException) {
835 message = processIOException((IOException)e, null);
837 else if (e instanceof ExecutionException) {
838 message = e.getLocalizedMessage();
841 message = DebuggerBundle.message("error.exception.while.connecting", e.getClass().getName(), e.getLocalizedMessage());
842 if (LOG.isDebugEnabled()) {
850 public static String processIOException(@NotNull IOException e, @Nullable String address) {
851 if (e instanceof UnknownHostException) {
852 return DebuggerBundle.message("error.unknown.host") + (address != null ? " (" + address + ")" : "") + ":\n" + e.getLocalizedMessage();
856 final StringBuilder buf = StringBuilderSpinAllocator.alloc();
858 buf.append(DebuggerBundle.message("error.cannot.open.debugger.port"));
859 if (address != null) {
860 buf.append(" (").append(address).append(")");
863 buf.append(e.getClass().getName()).append(" ");
864 final String localizedMessage = e.getLocalizedMessage();
865 if (!StringUtil.isEmpty(localizedMessage)) {
867 buf.append(localizedMessage);
870 if (LOG.isDebugEnabled()) {
873 message = buf.toString();
876 StringBuilderSpinAllocator.dispose(buf);
881 public void dispose() {
882 NodeRendererSettings.getInstance().removeListener(mySettingsListener);
883 Disposer.dispose(myDisposable);
884 myRequestManager.setFilterThread(null);
888 public DebuggerManagerThreadImpl getManagerThread() {
889 if (myDebuggerManagerThread == null) {
890 synchronized (this) {
891 if (myDebuggerManagerThread == null) {
892 myDebuggerManagerThread = new DebuggerManagerThreadImpl(myDisposable, getProject());
896 return myDebuggerManagerThread;
899 private static int getInvokePolicy(SuspendContext suspendContext) {
900 //return ThreadReference.INVOKE_SINGLE_THREADED;
901 return suspendContext.getSuspendPolicy() == EventRequest.SUSPEND_EVENT_THREAD ? ObjectReference.INVOKE_SINGLE_THREADED : 0;
905 public void waitFor() {
906 LOG.assertTrue(!DebuggerManagerThreadImpl.isManagerThread());
911 public void waitFor(long timeout) {
912 LOG.assertTrue(!DebuggerManagerThreadImpl.isManagerThread());
913 myWaitFor.waitFor(timeout);
916 private abstract class InvokeCommand <E extends Value> {
917 private final List myArgs;
919 protected InvokeCommand(List args) {
920 if (!args.isEmpty()) {
921 myArgs = new ArrayList(args);
928 public String toString() {
929 return "INVOKE: " + super.toString();
932 protected abstract E invokeMethod(int invokePolicy, final List args) throws InvocationException,
933 ClassNotLoadedException,
934 IncompatibleThreadStateException,
935 InvalidTypeException;
938 E start(EvaluationContextImpl evaluationContext, Method method, boolean internalEvaluate) throws EvaluateException {
941 return startInternal(evaluationContext, method, internalEvaluate);
943 catch (ClassNotLoadedException e) {
944 ReferenceType loadedClass = null;
946 if (evaluationContext.isAutoLoadClasses()) {
947 loadedClass = loadClass(evaluationContext, e.className(), evaluationContext.getClassLoader());
950 catch (Exception ignored) {
953 if (loadedClass == null) {
954 throw EvaluateExceptionUtil.createEvaluateException(e);
960 E startInternal(EvaluationContextImpl evaluationContext, Method method, boolean internalEvaluate)
961 throws EvaluateException, ClassNotLoadedException {
962 DebuggerManagerThreadImpl.assertIsManagerThread();
963 SuspendContextImpl suspendContext = evaluationContext.getSuspendContext();
964 SuspendManagerUtil.assertSuspendContext(suspendContext);
966 ThreadReferenceProxyImpl invokeThread = suspendContext.getThread();
968 if (SuspendManagerUtil.isEvaluating(getSuspendManager(), invokeThread)) {
969 throw EvaluateExceptionUtil.NESTED_EVALUATION_ERROR;
972 Set<SuspendContextImpl> suspendingContexts = SuspendManagerUtil.getSuspendingContexts(getSuspendManager(), invokeThread);
973 final ThreadReference invokeThreadRef = invokeThread.getThreadReference();
975 myEvaluationDispatcher.getMulticaster().evaluationStarted(suspendContext);
976 beforeMethodInvocation(suspendContext, method, internalEvaluate);
978 Object resumeData = null;
980 for (SuspendContextImpl suspendingContext : suspendingContexts) {
981 final ThreadReferenceProxyImpl suspendContextThread = suspendingContext.getThread();
982 if (suspendContextThread != invokeThread) {
983 if (LOG.isDebugEnabled()) {
984 LOG.debug("Resuming " + invokeThread + " that is paused by " + suspendContextThread);
986 LOG.assertTrue(suspendContextThread == null || !invokeThreadRef.equals(suspendContextThread.getThreadReference()));
987 getSuspendManager().resumeThread(suspendingContext, invokeThread);
991 resumeData = SuspendManagerUtil.prepareForResume(suspendContext);
992 suspendContext.setIsEvaluating(evaluationContext);
994 getVirtualMachineProxy().clearCaches();
996 return invokeMethodAndFork(suspendContext);
998 catch (InvocationException e) {
999 throw EvaluateExceptionUtil.createEvaluateException(e);
1001 catch (IncompatibleThreadStateException e) {
1002 throw EvaluateExceptionUtil.createEvaluateException(e);
1004 catch (InvalidTypeException e) {
1005 throw EvaluateExceptionUtil.createEvaluateException(e);
1007 catch (ObjectCollectedException e) {
1008 throw EvaluateExceptionUtil.createEvaluateException(e);
1010 catch (UnsupportedOperationException e) {
1011 throw EvaluateExceptionUtil.createEvaluateException(e);
1013 catch (InternalException e) {
1014 throw EvaluateExceptionUtil.createEvaluateException(e);
1017 suspendContext.setIsEvaluating(null);
1018 if (resumeData != null) {
1019 SuspendManagerUtil.restoreAfterResume(suspendContext, resumeData);
1021 for (SuspendContextImpl suspendingContext : mySuspendManager.getEventContexts()) {
1022 if (suspendingContexts.contains(suspendingContext) && !suspendingContext.isEvaluating() && !suspendingContext.suspends(invokeThread)) {
1023 mySuspendManager.suspendThread(suspendingContext, invokeThread);
1027 if (LOG.isDebugEnabled()) {
1028 LOG.debug("getVirtualMachine().clearCaches()");
1030 getVirtualMachineProxy().clearCaches();
1031 afterMethodInvocation(suspendContext, internalEvaluate);
1033 myEvaluationDispatcher.getMulticaster().evaluationFinished(suspendContext);
1037 private E invokeMethodAndFork(final SuspendContextImpl context) throws InvocationException,
1038 ClassNotLoadedException,
1039 IncompatibleThreadStateException,
1040 InvalidTypeException {
1041 final int invokePolicy = getInvokePolicy(context);
1042 final Exception[] exception = new Exception[1];
1043 final Value[] result = new Value[1];
1044 getManagerThread().startLongProcessAndFork(new Runnable() {
1047 ThreadReferenceProxyImpl thread = context.getThread();
1050 if (LOG.isDebugEnabled()) {
1051 final VirtualMachineProxyImpl virtualMachineProxy = getVirtualMachineProxy();
1052 virtualMachineProxy.logThreads();
1053 LOG.debug("Invoke in " + thread.name());
1054 assertThreadSuspended(thread, context);
1056 if (!Patches.IBM_JDK_DISABLE_COLLECTION_BUG) {
1057 // ensure args are not collected
1058 for (Object arg : myArgs) {
1059 if (arg instanceof ObjectReference) {
1060 ((ObjectReference)arg).disableCollection();
1064 result[0] = invokeMethod(invokePolicy, myArgs);
1067 // assertThreadSuspended(thread, context);
1068 if (!Patches.IBM_JDK_DISABLE_COLLECTION_BUG) {
1069 // ensure args are not collected
1070 for (Object arg : myArgs) {
1071 if (arg instanceof ObjectReference) {
1072 ((ObjectReference)arg).enableCollection();
1078 catch (Exception e) {
1084 if (exception[0] != null) {
1085 if (exception[0] instanceof InvocationException) {
1086 throw (InvocationException)exception[0];
1088 else if (exception[0] instanceof ClassNotLoadedException) {
1089 throw (ClassNotLoadedException)exception[0];
1091 else if (exception[0] instanceof IncompatibleThreadStateException) {
1092 throw (IncompatibleThreadStateException)exception[0];
1094 else if (exception[0] instanceof InvalidTypeException) {
1095 throw (InvalidTypeException)exception[0];
1097 else if (exception[0] instanceof RuntimeException) {
1098 throw (RuntimeException)exception[0];
1101 LOG.assertTrue(false);
1105 return (E)result[0];
1108 private void assertThreadSuspended(final ThreadReferenceProxyImpl thread, final SuspendContextImpl context) {
1109 LOG.assertTrue(context.isEvaluating());
1111 final boolean isSuspended = thread.isSuspended();
1112 LOG.assertTrue(isSuspended, thread);
1114 catch (ObjectCollectedException ignored) {
1120 public Value invokeMethod(@NotNull EvaluationContext evaluationContext, @NotNull ObjectReference objRef, @NotNull Method method, final List args) throws EvaluateException {
1121 return invokeInstanceMethod(evaluationContext, objRef, method, args, 0);
1125 public Value invokeInstanceMethod(@NotNull EvaluationContext evaluationContext, @NotNull final ObjectReference objRef, final Method method,
1126 final List args, final int invocationOptions) throws EvaluateException {
1127 final ThreadReference thread = getEvaluationThread(evaluationContext);
1128 return new InvokeCommand<Value>(args) {
1130 protected Value invokeMethod(int invokePolicy, final List args) throws InvocationException, ClassNotLoadedException, IncompatibleThreadStateException, InvalidTypeException {
1131 if (LOG.isDebugEnabled()) {
1132 LOG.debug("Invoke " + method.name());
1134 return objRef.invokeMethod(thread, method, args, invokePolicy | invocationOptions);
1136 }.start((EvaluationContextImpl)evaluationContext, method, false);
1139 private static ThreadReference getEvaluationThread(final EvaluationContext evaluationContext) throws EvaluateException {
1140 ThreadReferenceProxy evaluationThread = evaluationContext.getSuspendContext().getThread();
1141 if(evaluationThread == null) {
1142 throw EvaluateExceptionUtil.NULL_STACK_FRAME;
1144 return evaluationThread.getThreadReference();
1148 public Value invokeMethod(final EvaluationContext evaluationContext, final ClassType classType,
1149 final Method method,
1150 final List args) throws EvaluateException {
1151 return invokeMethod(evaluationContext, classType, method, args, false);
1154 public Value invokeMethod(@NotNull EvaluationContext evaluationContext,
1155 @NotNull final ClassType classType,
1156 @NotNull final Method method,
1158 boolean internalEvaluate) throws EvaluateException {
1159 final ThreadReference thread = getEvaluationThread(evaluationContext);
1160 return new InvokeCommand<Value>(args) {
1162 protected Value invokeMethod(int invokePolicy, final List args) throws InvocationException,
1163 ClassNotLoadedException,
1164 IncompatibleThreadStateException,
1165 InvalidTypeException {
1166 if (LOG.isDebugEnabled()) {
1167 LOG.debug("Invoke " + method.name());
1169 return classType.invokeMethod(thread, method, args, invokePolicy);
1171 }.start((EvaluationContextImpl)evaluationContext, method, internalEvaluate);
1175 public ArrayReference newInstance(final ArrayType arrayType,
1176 final int dimension)
1177 throws EvaluateException {
1179 return arrayType.newInstance(dimension);
1181 catch (Exception e) {
1182 throw EvaluateExceptionUtil.createEvaluateException(e);
1187 public ObjectReference newInstance(final EvaluationContext evaluationContext, final ClassType classType,
1188 final Method method,
1189 final List args) throws EvaluateException {
1190 final ThreadReference thread = getEvaluationThread(evaluationContext);
1191 InvokeCommand<ObjectReference> invokeCommand = new InvokeCommand<ObjectReference>(args) {
1193 protected ObjectReference invokeMethod(int invokePolicy, final List args) throws InvocationException,
1194 ClassNotLoadedException,
1195 IncompatibleThreadStateException,
1196 InvalidTypeException {
1197 if (LOG.isDebugEnabled()) {
1198 LOG.debug("New instance " + method.name());
1200 return classType.newInstance(thread, method, args, invokePolicy);
1203 return invokeCommand.start((EvaluationContextImpl)evaluationContext, method, false);
1206 public void clearCashes(int suspendPolicy) {
1207 if (!isAttached()) return;
1208 switch (suspendPolicy) {
1209 case EventRequest.SUSPEND_ALL:
1210 getVirtualMachineProxy().clearCaches();
1212 case EventRequest.SUSPEND_EVENT_THREAD:
1213 getVirtualMachineProxy().clearCaches();
1214 //suspendContext.getThread().clearAll();
1219 protected void beforeSuspend(SuspendContextImpl suspendContext) {
1220 clearCashes(suspendContext.getSuspendPolicy());
1223 private void beforeMethodInvocation(SuspendContextImpl suspendContext, Method method, boolean internalEvaluate) {
1224 if (LOG.isDebugEnabled()) {
1226 "before invocation in thread " + suspendContext.getThread().name() + " method " + (method == null ? "null" : method.name()));
1229 if (!internalEvaluate) {
1230 if (method != null) {
1231 showStatusText(DebuggerBundle.message("progress.evaluating", DebuggerUtilsEx.methodName(method)));
1234 showStatusText(DebuggerBundle.message("title.evaluating"));
1239 private void afterMethodInvocation(SuspendContextImpl suspendContext, boolean internalEvaluate) {
1240 if (LOG.isDebugEnabled()) {
1241 LOG.debug("after invocation in thread " + suspendContext.getThread().name());
1243 if (!internalEvaluate) {
1249 public ReferenceType findClass(EvaluationContext evaluationContext, String className,
1250 ClassLoaderReference classLoader) throws EvaluateException {
1252 DebuggerManagerThreadImpl.assertIsManagerThread();
1253 final VirtualMachineProxyImpl vmProxy = getVirtualMachineProxy();
1254 if (vmProxy == null) {
1255 throw new VMDisconnectedException();
1257 ReferenceType result = null;
1258 for (final ReferenceType refType : vmProxy.classesByName(className)) {
1259 if (refType.isPrepared() && isVisibleFromClassLoader(classLoader, refType)) {
1264 final EvaluationContextImpl evalContext = (EvaluationContextImpl)evaluationContext;
1265 if (result == null && evalContext.isAutoLoadClasses()) {
1266 return loadClass(evalContext, className, classLoader);
1270 catch (InvocationException e) {
1271 throw EvaluateExceptionUtil.createEvaluateException(e);
1273 catch (ClassNotLoadedException e) {
1274 throw EvaluateExceptionUtil.createEvaluateException(e);
1276 catch (IncompatibleThreadStateException e) {
1277 throw EvaluateExceptionUtil.createEvaluateException(e);
1279 catch (InvalidTypeException e) {
1280 throw EvaluateExceptionUtil.createEvaluateException(e);
1284 private static boolean isVisibleFromClassLoader(final ClassLoaderReference fromLoader, final ReferenceType refType) {
1285 // IMPORTANT! Even if the refType is already loaded by some parent or bootstrap loader, it may not be visible from the given loader.
1286 // For example because there were no accesses yet from this loader to this class. So the loader is not in the list of "initialing" loaders
1287 // for this refType and the refType is not visible to the loader.
1288 // Attempt to evaluate method with this refType will yield ClassNotLoadedException.
1289 // The only way to say for sure whether the class is _visible_ to the given loader, is to use the following API call
1290 return fromLoader == null || fromLoader.equals(refType.classLoader()) || fromLoader.visibleClasses().contains(refType);
1293 private static String reformatArrayName(String className) {
1294 if (className.indexOf('[') == -1) return className;
1297 while (className.endsWith("[]")) {
1298 className = className.substring(0, className.length() - 2);
1302 StringBuilder buffer = StringBuilderSpinAllocator.alloc();
1304 for (int i = 0; i < dims; i++) {
1307 String primitiveSignature = JVMNameUtil.getPrimitiveSignature(className);
1308 if(primitiveSignature != null) {
1309 buffer.append(primitiveSignature);
1313 buffer.append(className);
1316 return buffer.toString();
1319 StringBuilderSpinAllocator.dispose(buffer);
1323 @SuppressWarnings({"HardCodedStringLiteral", "SpellCheckingInspection"})
1324 public ReferenceType loadClass(EvaluationContextImpl evaluationContext, String qName, ClassLoaderReference classLoader)
1325 throws InvocationException, ClassNotLoadedException, IncompatibleThreadStateException, InvalidTypeException, EvaluateException {
1327 DebuggerManagerThreadImpl.assertIsManagerThread();
1328 qName = reformatArrayName(qName);
1329 ReferenceType refType = null;
1330 VirtualMachineProxyImpl virtualMachine = getVirtualMachineProxy();
1331 final List classClasses = virtualMachine.classesByName("java.lang.Class");
1332 if (!classClasses.isEmpty()) {
1333 ClassType classClassType = (ClassType)classClasses.get(0);
1334 final Method forNameMethod;
1335 if (classLoader != null) {
1336 //forNameMethod = classClassType.concreteMethodByName("forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;");
1337 forNameMethod = DebuggerUtils.findMethod(classClassType, "forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;");
1340 //forNameMethod = classClassType.concreteMethodByName("forName", "(Ljava/lang/String;)Ljava/lang/Class;");
1341 forNameMethod = DebuggerUtils.findMethod(classClassType, "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
1343 final List<Mirror> args = new ArrayList<Mirror>(); // do not use unmodifiable lists because the list is modified by JPDA
1344 final StringReference qNameMirror = virtualMachine.mirrorOf(qName);
1345 args.add(qNameMirror);
1346 if (classLoader != null) {
1347 args.add(virtualMachine.mirrorOf(true));
1348 args.add(classLoader);
1350 final Value value = invokeMethod(evaluationContext, classClassType, forNameMethod, args);
1351 if (value instanceof ClassObjectReference) {
1352 refType = ((ClassObjectReference)value).reflectedType();
1358 public void logThreads() {
1359 if (LOG.isDebugEnabled()) {
1361 Collection<ThreadReferenceProxyImpl> allThreads = getVirtualMachineProxy().allThreads();
1362 for (ThreadReferenceProxyImpl threadReferenceProxy : allThreads) {
1363 LOG.debug("Thread name=" + threadReferenceProxy.name() + " suspendCount()=" + threadReferenceProxy.getSuspendCount());
1366 catch (Exception e) {
1372 public SuspendManager getSuspendManager() {
1373 return mySuspendManager;
1377 public CompoundPositionManager getPositionManager() {
1378 return myPositionManager;
1383 public void stop(boolean forceTerminate) {
1384 getManagerThread().terminateAndInvoke(createStopCommand(forceTerminate), DebuggerManagerThreadImpl.COMMAND_TIMEOUT);
1387 public StopCommand createStopCommand(boolean forceTerminate) {
1388 return new StopCommand(forceTerminate);
1391 protected class StopCommand extends DebuggerCommandImpl {
1392 private final boolean myIsTerminateTargetVM;
1394 public StopCommand(boolean isTerminateTargetVM) {
1395 myIsTerminateTargetVM = isTerminateTargetVM;
1399 public Priority getPriority() {
1400 return Priority.HIGH;
1404 protected void action() throws Exception {
1406 final VirtualMachineProxyImpl virtualMachineProxy = getVirtualMachineProxy();
1407 if (myIsTerminateTargetVM) {
1408 virtualMachineProxy.exit(-1);
1411 // some VMs (like IBM VM 1.4.2 bundled with WebSphere) does not resume threads on dispose() like it should
1413 virtualMachineProxy.resume();
1416 virtualMachineProxy.dispose();
1426 private class StepOutCommand extends ResumeCommand {
1427 private final int myStepSize;
1429 public StepOutCommand(SuspendContextImpl suspendContext, int stepSize) {
1430 super(suspendContext);
1431 myStepSize = stepSize;
1435 public void contextAction() {
1436 showStatusText(DebuggerBundle.message("status.step.out"));
1437 final SuspendContextImpl suspendContext = getSuspendContext();
1438 final ThreadReferenceProxyImpl thread = getContextThread();
1439 RequestHint hint = new RequestHint(thread, suspendContext, StepRequest.STEP_OUT);
1440 hint.setIgnoreFilters(mySession.shouldIgnoreSteppingFilters());
1441 applyThreadFilter(thread);
1442 final MethodReturnValueWatcher rvWatcher = myReturnValueWatcher;
1443 if (rvWatcher != null) {
1444 rvWatcher.enable(thread.getThreadReference());
1446 doStep(suspendContext, thread, myStepSize, StepRequest.STEP_OUT, hint);
1447 super.contextAction();
1451 private class StepIntoCommand extends ResumeCommand {
1452 private final boolean myForcedIgnoreFilters;
1453 private final MethodFilter mySmartStepFilter;
1455 private final StepIntoBreakpoint myBreakpoint;
1456 private final int myStepSize;
1458 public StepIntoCommand(SuspendContextImpl suspendContext, boolean ignoreFilters, @Nullable final MethodFilter methodFilter,
1460 super(suspendContext);
1461 myForcedIgnoreFilters = ignoreFilters || methodFilter != null;
1462 mySmartStepFilter = methodFilter;
1463 myBreakpoint = methodFilter instanceof BreakpointStepMethodFilter ?
1464 DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager().addStepIntoBreakpoint(((BreakpointStepMethodFilter)methodFilter)) :
1466 myStepSize = stepSize;
1470 public void contextAction() {
1471 showStatusText(DebuggerBundle.message("status.step.into"));
1472 final SuspendContextImpl suspendContext = getSuspendContext();
1473 final ThreadReferenceProxyImpl stepThread = getContextThread();
1474 final RequestHint hint = mySmartStepFilter != null?
1475 new RequestHint(stepThread, suspendContext, mySmartStepFilter) :
1476 new RequestHint(stepThread, suspendContext, StepRequest.STEP_INTO);
1477 if (myForcedIgnoreFilters) {
1479 mySession.setIgnoreStepFiltersFlag(stepThread.frameCount());
1481 catch (EvaluateException e) {
1485 hint.setIgnoreFilters(myForcedIgnoreFilters || mySession.shouldIgnoreSteppingFilters());
1486 applyThreadFilter(stepThread);
1487 if (myBreakpoint != null) {
1488 myBreakpoint.setSuspendPolicy(suspendContext.getSuspendPolicy() == EventRequest.SUSPEND_EVENT_THREAD? DebuggerSettings.SUSPEND_THREAD : DebuggerSettings.SUSPEND_ALL);
1489 myBreakpoint.createRequest(suspendContext.getDebugProcess());
1490 myRunToCursorBreakpoint = myBreakpoint;
1492 doStep(suspendContext, stepThread, myStepSize, StepRequest.STEP_INTO, hint);
1493 super.contextAction();
1497 private class StepOverCommand extends ResumeCommand {
1498 private final boolean myIsIgnoreBreakpoints;
1499 private final int myStepSize;
1501 public StepOverCommand(SuspendContextImpl suspendContext, boolean ignoreBreakpoints, int stepSize) {
1502 super(suspendContext);
1503 myIsIgnoreBreakpoints = ignoreBreakpoints;
1504 myStepSize = stepSize;
1508 public void contextAction() {
1509 showStatusText(DebuggerBundle.message("status.step.over"));
1510 final SuspendContextImpl suspendContext = getSuspendContext();
1511 final ThreadReferenceProxyImpl stepThread = getContextThread();
1512 // need this hint while stepping over for JSR45 support:
1513 // several lines of generated java code may correspond to a single line in the source file,
1514 // from which the java code was generated
1515 RequestHint hint = new RequestHint(stepThread, suspendContext, StepRequest.STEP_OVER);
1516 hint.setRestoreBreakpoints(myIsIgnoreBreakpoints);
1517 hint.setIgnoreFilters(myIsIgnoreBreakpoints || mySession.shouldIgnoreSteppingFilters());
1519 applyThreadFilter(stepThread);
1521 final MethodReturnValueWatcher rvWatcher = myReturnValueWatcher;
1522 if (rvWatcher != null) {
1523 rvWatcher.enable(stepThread.getThreadReference());
1526 doStep(suspendContext, stepThread, myStepSize, StepRequest.STEP_OVER, hint);
1528 if (myIsIgnoreBreakpoints) {
1529 DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager().disableBreakpoints(DebugProcessImpl.this);
1531 super.contextAction();
1535 private class RunToCursorCommand extends ResumeCommand {
1536 private final RunToCursorBreakpoint myRunToCursorBreakpoint;
1537 private final boolean myIgnoreBreakpoints;
1539 private RunToCursorCommand(SuspendContextImpl suspendContext, Document document, int lineIndex, final boolean ignoreBreakpoints) {
1540 super(suspendContext);
1541 myIgnoreBreakpoints = ignoreBreakpoints;
1542 final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager();
1543 myRunToCursorBreakpoint = breakpointManager.addRunToCursorBreakpoint(document, lineIndex, ignoreBreakpoints);
1547 public void contextAction() {
1548 showStatusText(DebuggerBundle.message("status.run.to.cursor"));
1549 cancelRunToCursorBreakpoint();
1550 if (myRunToCursorBreakpoint == null) {
1553 if (myIgnoreBreakpoints) {
1554 final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager();
1555 breakpointManager.disableBreakpoints(DebugProcessImpl.this);
1557 applyThreadFilter(getContextThread());
1558 final SuspendContextImpl context = getSuspendContext();
1559 myRunToCursorBreakpoint.setSuspendPolicy(context.getSuspendPolicy() == EventRequest.SUSPEND_EVENT_THREAD? DebuggerSettings.SUSPEND_THREAD : DebuggerSettings.SUSPEND_ALL);
1560 DebugProcessImpl debugProcess = context.getDebugProcess();
1561 myRunToCursorBreakpoint.createRequest(debugProcess);
1562 DebugProcessImpl.this.myRunToCursorBreakpoint = myRunToCursorBreakpoint;
1564 if (debugProcess.getRequestsManager().getWarning(myRunToCursorBreakpoint) == null) {
1565 super.contextAction();
1568 myDebugProcessDispatcher.getMulticaster().resumed(getSuspendContext());
1569 DebuggerInvocationUtil.swingInvokeLater(myProject, new Runnable() {
1572 SourcePosition position = myRunToCursorBreakpoint.getSourcePosition();
1573 String name = position != null ? position.getFile().getName() : "<No File>";
1574 Messages.showErrorDialog(
1575 DebuggerBundle.message("error.running.to.cursor.no.executable.code", name, myRunToCursorBreakpoint.getLineIndex()+1),
1576 UIUtil.removeMnemonic(ActionsBundle.actionText(XDebuggerActions.RUN_TO_CURSOR)));
1583 public abstract class ResumeCommand extends SuspendContextCommandImpl {
1585 private final ThreadReferenceProxyImpl myContextThread;
1587 public ResumeCommand(SuspendContextImpl suspendContext) {
1588 super(suspendContext);
1589 final ThreadReferenceProxyImpl contextThread = getDebuggerContext().getThreadProxy();
1590 myContextThread = contextThread != null ? contextThread : (suspendContext != null? suspendContext.getThread() : null);
1594 public Priority getPriority() {
1595 return Priority.HIGH;
1599 public void contextAction() {
1600 showStatusText(DebuggerBundle.message("status.process.resumed"));
1601 getSuspendManager().resume(getSuspendContext());
1602 myDebugProcessDispatcher.getMulticaster().resumed(getSuspendContext());
1605 public ThreadReferenceProxyImpl getContextThread() {
1606 return myContextThread;
1609 protected void applyThreadFilter(ThreadReferenceProxy thread) {
1610 if (getSuspendContext().getSuspendPolicy() == EventRequest.SUSPEND_ALL) {
1611 // there could be explicit resume as a result of call to voteSuspend()
1612 // e.g. when breakpoint was considered invalid, in that case the filter will be applied _after_
1613 // resuming and all breakpoints in other threads will be ignored.
1614 // As resume() implicitly cleares the filter, the filter must be always applied _before_ any resume() action happens
1615 final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(getProject()).getBreakpointManager();
1616 breakpointManager.applyThreadFilter(DebugProcessImpl.this, thread.getThreadReference());
1621 private class PauseCommand extends DebuggerCommandImpl {
1622 public PauseCommand() {
1626 public void action() {
1627 if (!isAttached() || getVirtualMachineProxy().isPausePressed()) {
1631 getVirtualMachineProxy().suspend();
1633 SuspendContextImpl suspendContext = mySuspendManager.pushSuspendContext(EventRequest.SUSPEND_ALL, 0);
1634 myDebugProcessDispatcher.getMulticaster().paused(suspendContext);
1638 private class ResumeThreadCommand extends SuspendContextCommandImpl {
1639 private final ThreadReferenceProxyImpl myThread;
1641 public ResumeThreadCommand(SuspendContextImpl suspendContext, ThreadReferenceProxyImpl thread) {
1642 super(suspendContext);
1647 public void contextAction() {
1648 if (getSuspendManager().isFrozen(myThread)) {
1649 getSuspendManager().unfreezeThread(myThread);
1653 final Set<SuspendContextImpl> suspendingContexts = SuspendManagerUtil.getSuspendingContexts(getSuspendManager(), myThread);
1654 for (SuspendContextImpl suspendContext : suspendingContexts) {
1655 if (suspendContext.getThread() == myThread) {
1656 getManagerThread().invoke(createResumeCommand(suspendContext));
1659 getSuspendManager().resumeThread(suspendContext, myThread);
1665 private class FreezeThreadCommand extends DebuggerCommandImpl {
1666 private final ThreadReferenceProxyImpl myThread;
1668 public FreezeThreadCommand(ThreadReferenceProxyImpl thread) {
1673 protected void action() throws Exception {
1674 SuspendManager suspendManager = getSuspendManager();
1675 if (!suspendManager.isFrozen(myThread)) {
1676 suspendManager.freezeThread(myThread);
1681 private class PopFrameCommand extends SuspendContextCommandImpl {
1682 private final StackFrameProxyImpl myStackFrame;
1684 public PopFrameCommand(SuspendContextImpl context, StackFrameProxyImpl frameProxy) {
1686 myStackFrame = frameProxy;
1690 public void contextAction() {
1691 final ThreadReferenceProxyImpl thread = myStackFrame.threadProxy();
1693 if (!getSuspendManager().isSuspended(thread)) {
1698 catch (ObjectCollectedException ignored) {
1703 final SuspendContextImpl suspendContext = getSuspendContext();
1704 if (!suspendContext.suspends(thread)) {
1705 suspendContext.postponeCommand(this);
1709 if (myStackFrame.isBottom()) {
1710 DebuggerInvocationUtil.swingInvokeLater(myProject, new Runnable() {
1713 Messages.showMessageDialog(myProject, DebuggerBundle.message("error.pop.bottom.stackframe"), ActionsBundle.actionText(DebuggerActions.POP_FRAME), Messages.getErrorIcon());
1720 thread.popFrames(myStackFrame);
1722 catch (final EvaluateException e) {
1723 DebuggerInvocationUtil.swingInvokeLater(myProject, new Runnable() {
1726 Messages.showMessageDialog(myProject, DebuggerBundle.message("error.pop.stackframe", e.getLocalizedMessage()), ActionsBundle.actionText(DebuggerActions.POP_FRAME), Messages.getErrorIcon());
1732 getSuspendManager().popFrame(suspendContext);
1739 public GlobalSearchScope getSearchScope() {
1740 LOG.assertTrue(mySession != null, "Accessing debug session before its initialization");
1741 return mySession.getSearchScope();
1745 public ExecutionResult attachVirtualMachine(final DebugEnvironment environment,
1746 final DebuggerSession session) throws ExecutionException {
1747 mySession = session;
1750 ApplicationManager.getApplication().assertIsDispatchThread();
1751 LOG.assertTrue(isInInitialState());
1753 myConnection = environment.getRemoteConnection();
1755 createVirtualMachine(environment.getSessionName(), environment.isPollConnection());
1757 ExecutionResult executionResult;
1759 synchronized (myProcessListeners) {
1760 executionResult = environment.createExecutionResult();
1761 myExecutionResult = executionResult;
1762 if (executionResult == null) {
1766 for (ProcessListener processListener : myProcessListeners) {
1767 executionResult.getProcessHandler().addProcessListener(processListener);
1769 myProcessListeners.clear();
1772 catch (ExecutionException e) {
1777 // writing to volatile field ensures the other threads will see the right values in non-volatile fields
1779 if (ApplicationManager.getApplication().isUnitTestMode()) {
1780 return executionResult;
1784 final Alarm debugPortTimeout = new Alarm(Alarm.ThreadToUse.SHARED_THREAD);
1786 myExecutionResult.getProcessHandler().addProcessListener(new ProcessAdapter() {
1787 public void processTerminated(ProcessEvent event) {
1788 debugPortTimeout.cancelAllRequests();
1791 public void startNotified(ProcessEvent event) {
1792 debugPortTimeout.addRequest(new Runnable() {
1794 if(isInInitialState()) {
1795 ApplicationManager.getApplication().schedule(new Runnable() {
1797 String message = DebuggerBundle.message("status.connect.failed", DebuggerBundle.getAddressDisplayName(remoteConnection), DebuggerBundle.getTransportName(remoteConnection));
1798 Messages.showErrorDialog(myProject, message, DebuggerBundle.message("title.generic.debug.dialog"));
1803 }, LOCAL_START_TIMEOUT);
1808 return executionResult;
1811 private void fail() {
1812 synchronized (this) {
1814 // need this in order to prevent calling stop() twice
1822 private void createVirtualMachine(final String sessionName, final boolean pollConnection) {
1823 final Semaphore semaphore = new Semaphore();
1826 final Ref<Boolean> connectorIsReady = Ref.create(false);
1827 myDebugProcessDispatcher.addListener(new DebugProcessAdapter() {
1829 public void connectorIsReady() {
1830 connectorIsReady.set(true);
1832 myDebugProcessDispatcher.removeListener(this);
1836 // reload to make sure that source positions are initialized
1837 DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager().reloadBreakpoints();
1839 getManagerThread().schedule(new DebuggerCommandImpl() {
1841 protected void action() {
1842 VirtualMachine vm = null;
1845 final long time = System.currentTimeMillis();
1846 while (System.currentTimeMillis() - time < LOCAL_START_TIMEOUT) {
1848 vm = createVirtualMachineInt();
1851 catch (final ExecutionException e) {
1852 if (pollConnection && !myConnection.isServerMode() && e.getCause() instanceof IOException) {
1853 synchronized (this) {
1857 catch (InterruptedException ignored) {
1864 if (myExecutionResult != null || !connectorIsReady.get()) {
1865 // propagate exception only in case we succeeded to obtain execution result,
1866 // otherwise if the error is induced by the fact that there is nothing to debug, and there is no need to show
1867 // this problem to the user
1868 SwingUtilities.invokeLater(new Runnable() {
1871 ExecutionUtil.handleExecutionError(myProject, ToolWindowId.DEBUG, sessionName, e);
1885 final VirtualMachine vm1 = vm;
1886 afterProcessStarted(new Runnable() {
1889 getManagerThread().schedule(new DebuggerCommandImpl() {
1891 protected void action() throws Exception {
1901 protected void commandCancelled() {
1903 super.commandCancelled();
1911 semaphore.waitFor();
1914 private void afterProcessStarted(final Runnable run) {
1915 class MyProcessAdapter extends ProcessAdapter {
1916 private boolean alreadyRun = false;
1918 public synchronized void run() {
1923 removeProcessListener(this);
1927 public void startNotified(ProcessEvent event) {
1931 MyProcessAdapter processListener = new MyProcessAdapter();
1932 addProcessListener(processListener);
1933 if (myExecutionResult != null) {
1934 if (myExecutionResult.getProcessHandler().isStartNotified()) {
1935 processListener.run();
1940 public boolean isPausePressed() {
1941 final VirtualMachineProxyImpl vm = myVirtualMachineProxy;
1942 return vm != null && vm.isPausePressed();
1945 public DebuggerCommandImpl createPauseCommand() {
1946 return new PauseCommand();
1949 public ResumeCommand createResumeCommand(SuspendContextImpl suspendContext) {
1950 return createResumeCommand(suspendContext, PrioritizedTask.Priority.HIGH);
1953 public ResumeCommand createResumeCommand(SuspendContextImpl suspendContext, final PrioritizedTask.Priority priority) {
1954 final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(getProject()).getBreakpointManager();
1955 return new ResumeCommand(suspendContext) {
1957 public void contextAction() {
1958 breakpointManager.applyThreadFilter(DebugProcessImpl.this, null); // clear the filter on resume
1959 super.contextAction();
1963 public Priority getPriority() {
1969 public ResumeCommand createStepOverCommand(SuspendContextImpl suspendContext, boolean ignoreBreakpoints) {
1970 return createStepOverCommand(suspendContext, ignoreBreakpoints, StepRequest.STEP_LINE);
1973 public ResumeCommand createStepOverCommand(SuspendContextImpl suspendContext, boolean ignoreBreakpoints, int stepSize) {
1974 return new StepOverCommand(suspendContext, ignoreBreakpoints, stepSize);
1977 public ResumeCommand createStepOutCommand(SuspendContextImpl suspendContext) {
1978 return createStepOutCommand(suspendContext, StepRequest.STEP_LINE);
1981 public ResumeCommand createStepOutCommand(SuspendContextImpl suspendContext, int stepSize) {
1982 return new StepOutCommand(suspendContext, stepSize);
1985 public ResumeCommand createStepIntoCommand(SuspendContextImpl suspendContext, boolean ignoreFilters, final MethodFilter smartStepFilter) {
1986 return createStepIntoCommand(suspendContext, ignoreFilters, smartStepFilter, StepRequest.STEP_LINE);
1989 public ResumeCommand createStepIntoCommand(SuspendContextImpl suspendContext, boolean ignoreFilters, final MethodFilter smartStepFilter,
1991 return new StepIntoCommand(suspendContext, ignoreFilters, smartStepFilter, stepSize);
1994 public ResumeCommand createRunToCursorCommand(SuspendContextImpl suspendContext, Document document, int lineIndex,
1995 final boolean ignoreBreakpoints)
1996 throws EvaluateException {
1997 RunToCursorCommand runToCursorCommand = new RunToCursorCommand(suspendContext, document, lineIndex, ignoreBreakpoints);
1998 if(runToCursorCommand.myRunToCursorBreakpoint == null) {
1999 final PsiFile psiFile = PsiDocumentManager.getInstance(getProject()).getPsiFile(document);
2000 throw new EvaluateException(DebuggerBundle.message("error.running.to.cursor.no.executable.code", psiFile != null? psiFile.getName() : "<No File>", lineIndex), null);
2002 return runToCursorCommand;
2005 public DebuggerCommandImpl createFreezeThreadCommand(ThreadReferenceProxyImpl thread) {
2006 return new FreezeThreadCommand(thread);
2009 public SuspendContextCommandImpl createResumeThreadCommand(SuspendContextImpl suspendContext, ThreadReferenceProxyImpl thread) {
2010 return new ResumeThreadCommand(suspendContext, thread);
2013 public SuspendContextCommandImpl createPopFrameCommand(DebuggerContextImpl context, StackFrameProxyImpl stackFrame) {
2014 final SuspendContextImpl contextByThread =
2015 SuspendManagerUtil.findContextByThread(context.getDebugProcess().getSuspendManager(), stackFrame.threadProxy());
2016 return new PopFrameCommand(contextByThread, stackFrame);
2019 //public void setBreakpointsMuted(final boolean muted) {
2020 // XDebugSession session = mySession.getXDebugSession();
2021 // if (isAttached()) {
2022 // getManagerThread().schedule(new DebuggerCommandImpl() {
2024 // protected void action() throws Exception {
2025 // // set the flag before enabling/disabling cause it affects if breakpoints will create requests
2026 // if (myBreakpointsMuted.getAndSet(muted) != muted) {
2027 // final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager();
2029 // breakpointManager.disableBreakpoints(DebugProcessImpl.this);
2032 // breakpointManager.enableBreakpoints(DebugProcessImpl.this);
2039 // session.setBreakpointMuted(muted);
2043 public DebuggerContextImpl getDebuggerContext() {
2044 return mySession.getContextManager().getContext();
2047 public void setXDebugProcess(JavaDebugProcess XDebugProcess) {
2048 myXDebugProcess = XDebugProcess;
2052 public JavaDebugProcess getXdebugProcess() {
2053 return myXDebugProcess;
2056 public boolean areBreakpointsMuted() {
2057 XDebugSession session = mySession.getXDebugSession();
2058 return session != null && session.areBreakpointsMuted();
2061 public DebuggerSession getSession() {