2 * Copyright 2000-2015 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
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.extensions.Extensions;
54 import com.intellij.openapi.project.Project;
55 import com.intellij.openapi.ui.Messages;
56 import com.intellij.openapi.util.Disposer;
57 import com.intellij.openapi.util.Pair;
58 import com.intellij.openapi.util.UserDataHolderBase;
59 import com.intellij.openapi.util.registry.Registry;
60 import com.intellij.openapi.util.text.StringUtil;
61 import com.intellij.openapi.wm.ToolWindowId;
62 import com.intellij.openapi.wm.impl.status.StatusBarUtil;
63 import com.intellij.psi.CommonClassNames;
64 import com.intellij.psi.PsiFile;
65 import com.intellij.psi.PsiManager;
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.XSourcePosition;
78 import com.intellij.xdebugger.impl.XDebugSessionImpl;
79 import com.intellij.xdebugger.impl.actions.XDebuggerActions;
81 import com.sun.jdi.connect.*;
82 import com.sun.jdi.request.EventRequest;
83 import com.sun.jdi.request.EventRequestManager;
84 import com.sun.jdi.request.StepRequest;
85 import org.jetbrains.annotations.NonNls;
86 import org.jetbrains.annotations.NotNull;
87 import org.jetbrains.annotations.Nullable;
90 import java.io.IOException;
91 import java.net.UnknownHostException;
93 import java.util.concurrent.atomic.AtomicBoolean;
94 import java.util.concurrent.atomic.AtomicInteger;
96 public abstract class DebugProcessImpl extends UserDataHolderBase implements DebugProcess {
97 private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.DebugProcessImpl");
99 @NonNls private static final String SOCKET_ATTACHING_CONNECTOR_NAME = "com.sun.jdi.SocketAttach";
100 @NonNls private static final String SHMEM_ATTACHING_CONNECTOR_NAME = "com.sun.jdi.SharedMemoryAttach";
101 @NonNls private static final String SOCKET_LISTENING_CONNECTOR_NAME = "com.sun.jdi.SocketListen";
102 @NonNls private static final String SHMEM_LISTENING_CONNECTOR_NAME = "com.sun.jdi.SharedMemoryListen";
104 private final Project myProject;
105 private final RequestManagerImpl myRequestManager;
107 private volatile VirtualMachineProxyImpl myVirtualMachineProxy = null;
108 protected EventDispatcher<DebugProcessListener> myDebugProcessDispatcher = EventDispatcher.create(DebugProcessListener.class);
109 protected EventDispatcher<EvaluationListener> myEvaluationDispatcher = EventDispatcher.create(EvaluationListener.class);
111 private final List<ProcessListener> myProcessListeners = ContainerUtil.createLockFreeCopyOnWriteList();
113 protected static final int STATE_INITIAL = 0;
114 protected static final int STATE_ATTACHED = 1;
115 protected static final int STATE_DETACHING = 2;
116 protected static final int STATE_DETACHED = 3;
117 protected final AtomicInteger myState = new AtomicInteger(STATE_INITIAL);
119 private volatile ExecutionResult myExecutionResult;
120 private RemoteConnection myConnection;
121 private JavaDebugProcess myXDebugProcess;
123 private ConnectionServiceWrapper myConnectionService;
124 private Map<String, Connector.Argument> myArguments;
126 private final List<NodeRenderer> myRenderers = new ArrayList<NodeRenderer>();
128 // we use null key here
129 private final Map<Type, NodeRenderer> myNodeRenderersMap = new HashMap<Type, NodeRenderer>();
131 private final NodeRendererSettingsListener mySettingsListener = new NodeRendererSettingsListener() {
133 public void renderersChanged() {
134 myNodeRenderersMap.clear();
140 private final SuspendManagerImpl mySuspendManager = new SuspendManagerImpl(this);
141 protected CompoundPositionManager myPositionManager = null;
142 private final DebuggerManagerThreadImpl myDebuggerManagerThread;
143 private static final int LOCAL_START_TIMEOUT = 30000;
145 private final Semaphore myWaitFor = new Semaphore();
146 private final AtomicBoolean myIsFailed = new AtomicBoolean(false);
147 protected DebuggerSession mySession;
148 @Nullable protected MethodReturnValueWatcher myReturnValueWatcher;
149 private final Disposable myDisposable = Disposer.newDisposable();
150 private final Alarm myStatusUpdateAlarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD, myDisposable);
152 protected DebugProcessImpl(Project project) {
154 myDebuggerManagerThread = new DebuggerManagerThreadImpl(myDisposable, myProject);
155 myRequestManager = new RequestManagerImpl(this);
156 NodeRendererSettings.getInstance().addListener(mySettingsListener);
160 private void loadRenderers() {
161 getManagerThread().invoke(new DebuggerCommandImpl() {
163 protected void action() throws Exception {
165 final NodeRendererSettings rendererSettings = NodeRendererSettings.getInstance();
166 for (final NodeRenderer renderer : rendererSettings.getAllRenderers()) {
167 if (renderer.isEnabled()) {
168 myRenderers.add(renderer);
173 DebuggerInvocationUtil.swingInvokeLater(myProject, new Runnable() {
176 final DebuggerSession session = mySession;
177 if (session != null && session.isAttached()) {
178 session.refresh(true);
179 DebuggerAction.refreshViews(mySession.getXDebugSession());
189 public Pair<Method, Value> getLastExecutedMethod() {
190 final MethodReturnValueWatcher watcher = myReturnValueWatcher;
191 if (watcher == null) {
194 final Method method = watcher.getLastExecutedMethod();
195 if (method == null) {
198 return Pair.create(method, watcher.getLastMethodReturnValue());
201 public void setWatchMethodReturnValuesEnabled(boolean enabled) {
202 final MethodReturnValueWatcher watcher = myReturnValueWatcher;
203 if (watcher != null) {
204 watcher.setFeatureEnabled(enabled);
208 public boolean isWatchMethodReturnValuesEnabled() {
209 final MethodReturnValueWatcher watcher = myReturnValueWatcher;
210 return watcher != null && watcher.isFeatureEnabled();
213 public boolean canGetMethodReturnValue() {
214 return myReturnValueWatcher != null;
217 public NodeRenderer getAutoRenderer(ValueDescriptor descriptor) {
218 DebuggerManagerThreadImpl.assertIsManagerThread();
219 final Value value = descriptor.getValue();
220 Type type = value != null ? value.type() : null;
222 // in case evaluation is not possible, force default renderer
223 if (!DebuggerManagerEx.getInstanceEx(getProject()).getContext().isEvaluationPossible()) {
224 return getDefaultRenderer(type);
227 NodeRenderer renderer = myNodeRenderersMap.get(type);
228 if(renderer == null) {
229 for (final NodeRenderer nodeRenderer : myRenderers) {
230 if (nodeRenderer.isApplicable(type)) {
231 renderer = nodeRenderer;
235 if (renderer == null) {
236 renderer = getDefaultRenderer(type);
238 myNodeRenderersMap.put(type, renderer);
244 public static NodeRenderer getDefaultRenderer(Value value) {
245 return getDefaultRenderer(value != null ? value.type() : null);
248 public static NodeRenderer getDefaultRenderer(Type type) {
249 final NodeRendererSettings settings = NodeRendererSettings.getInstance();
251 final PrimitiveRenderer primitiveRenderer = settings.getPrimitiveRenderer();
252 if(primitiveRenderer.isApplicable(type)) {
253 return primitiveRenderer;
256 final ArrayRenderer arrayRenderer = settings.getArrayRenderer();
257 if(arrayRenderer.isApplicable(type)) {
258 return arrayRenderer;
261 final ClassRenderer classRenderer = settings.getClassRenderer();
262 LOG.assertTrue(classRenderer.isApplicable(type), type.name());
263 return classRenderer;
266 private static final String ourTrace = System.getProperty("idea.debugger.trace");
268 @SuppressWarnings({"HardCodedStringLiteral"})
269 protected void commitVM(VirtualMachine vm) {
270 if (!isInInitialState()) {
271 LOG.error("State is invalid " + myState.get());
273 DebuggerManagerThreadImpl.assertIsManagerThread();
274 myPositionManager = createPositionManager();
275 if (LOG.isDebugEnabled()) {
276 LOG.debug("*******************VM attached******************");
278 checkVirtualMachineVersion(vm);
280 myVirtualMachineProxy = new VirtualMachineProxyImpl(this, vm);
282 if (!StringUtil.isEmpty(ourTrace)) {
284 StringTokenizer tokenizer = new StringTokenizer(ourTrace);
285 while (tokenizer.hasMoreTokens()) {
286 String token = tokenizer.nextToken();
287 if ("SENDS".compareToIgnoreCase(token) == 0) {
288 mask |= VirtualMachine.TRACE_SENDS;
290 else if ("RAW_SENDS".compareToIgnoreCase(token) == 0) {
293 else if ("RECEIVES".compareToIgnoreCase(token) == 0) {
294 mask |= VirtualMachine.TRACE_RECEIVES;
296 else if ("RAW_RECEIVES".compareToIgnoreCase(token) == 0) {
299 else if ("EVENTS".compareToIgnoreCase(token) == 0) {
300 mask |= VirtualMachine.TRACE_EVENTS;
302 else if ("REFTYPES".compareToIgnoreCase(token) == 0) {
303 mask |= VirtualMachine.TRACE_REFTYPES;
305 else if ("OBJREFS".compareToIgnoreCase(token) == 0) {
306 mask |= VirtualMachine.TRACE_OBJREFS;
308 else if ("ALL".compareToIgnoreCase(token) == 0) {
309 mask |= VirtualMachine.TRACE_ALL;
313 vm.setDebugTraceMode(mask);
317 private void stopConnecting() {
318 DebuggerManagerThreadImpl.assertIsManagerThread();
320 Map<String, Connector.Argument> arguments = myArguments;
322 if (arguments == null) {
325 if(myConnection.isServerMode()) {
326 ListeningConnector connector = (ListeningConnector)findConnector(SOCKET_LISTENING_CONNECTOR_NAME);
327 if (connector == null) {
328 LOG.error("Cannot find connector: " + SOCKET_LISTENING_CONNECTOR_NAME);
331 connector.stopListening(arguments);
335 if(myConnectionService != null) {
336 myConnectionService.close();
340 catch (IOException e) {
341 if (LOG.isDebugEnabled()) {
345 catch (IllegalConnectorArgumentsException e) {
346 if (LOG.isDebugEnabled()) {
350 catch (ExecutionException e) {
358 protected CompoundPositionManager createPositionManager() {
359 return new CompoundPositionManager(new PositionManagerImpl(this));
363 public void printToConsole(final String text) {
364 myExecutionResult.getProcessHandler().notifyTextAvailable(text, ProcessOutputTypes.SYSTEM);
368 public ProcessHandler getProcessHandler() {
369 return myExecutionResult != null ? myExecutionResult.getProcessHandler() : null;
374 * @param suspendContext
376 * @param size the step size. One of {@link StepRequest#STEP_LINE} or {@link StepRequest#STEP_MIN}
378 * @param hint may be null
380 protected void doStep(final SuspendContextImpl suspendContext, final ThreadReferenceProxyImpl stepThread, int size, int depth,
382 if (stepThread == null) {
386 final ThreadReference stepThreadReference = stepThread.getThreadReference();
387 if (LOG.isDebugEnabled()) {
388 LOG.debug("DO_STEP: creating step request for " + stepThreadReference);
390 deleteStepRequests(stepThreadReference);
391 EventRequestManager requestManager = getVirtualMachineProxy().eventRequestManager();
392 StepRequest stepRequest = requestManager.createStepRequest(stepThreadReference, size, depth);
393 if (!(hint != null && hint.isIgnoreFilters()) /*&& depth == StepRequest.STEP_INTO*/) {
394 List<ClassFilter> activeFilters = getActiveFilters();
396 if (!activeFilters.isEmpty()) {
397 final String currentClassName = getCurrentClassName(stepThread);
398 if (currentClassName == null || !DebuggerUtilsEx.isFiltered(currentClassName, activeFilters)) {
400 for (ClassFilter filter : activeFilters) {
401 stepRequest.addClassExclusionFilter(filter.getPattern());
407 // suspend policy to match the suspend policy of the context:
408 // if all threads were suspended, then during stepping all the threads must be suspended
409 // if only event thread were suspended, then only this particular thread must be suspended during stepping
410 stepRequest.setSuspendPolicy(Registry.is("debugger.step.resumes.one.thread") ? EventRequest.SUSPEND_EVENT_THREAD
411 : suspendContext.getSuspendPolicy());
414 //noinspection HardCodedStringLiteral
415 stepRequest.putProperty("hint", hint);
417 stepRequest.enable();
419 catch (ObjectCollectedException ignored) {
425 static List<ClassFilter> getActiveFilters() {
426 List<ClassFilter> activeFilters = new ArrayList<ClassFilter>();
427 DebuggerSettings settings = DebuggerSettings.getInstance();
428 if (settings.TRACING_FILTERS_ENABLED) {
429 for (ClassFilter filter : settings.getSteppingFilters()) {
430 if (filter.isEnabled()) {
431 activeFilters.add(filter);
435 for (DebuggerClassFilterProvider provider : Extensions.getExtensions(DebuggerClassFilterProvider.EP_NAME)) {
436 for (ClassFilter filter : provider.getFilters()) {
437 if (filter.isEnabled()) {
438 activeFilters.add(filter);
442 return activeFilters;
445 void deleteStepRequests(@Nullable final ThreadReference stepThread) {
446 EventRequestManager requestManager = getVirtualMachineProxy().eventRequestManager();
447 List<StepRequest> stepRequests = requestManager.stepRequests();
448 if (!stepRequests.isEmpty()) {
449 final List<StepRequest> toDelete = new ArrayList<StepRequest>(stepRequests.size());
450 for (final StepRequest request : stepRequests) {
451 ThreadReference threadReference = request.thread();
452 // [jeka] on attempt to delete a request assigned to a thread with unknown status, a JDWP error occurs
454 if (threadReference.status() != ThreadReference.THREAD_STATUS_UNKNOWN && (stepThread == null || stepThread.equals(threadReference))) {
455 toDelete.add(request);
458 catch (IllegalThreadStateException e) {
459 LOG.info(e); // undocumented by JDI: may be thrown when querying thread status
461 catch (ObjectCollectedException ignored) {
464 requestManager.deleteEventRequests(toDelete);
469 static String getCurrentClassName(ThreadReferenceProxyImpl thread) {
471 if (thread != null && thread.frameCount() > 0) {
472 StackFrameProxyImpl stackFrame = thread.frame(0);
473 if (stackFrame != null) {
474 Location location = stackFrame.location();
475 ReferenceType referenceType = location == null ? null : location.declaringType();
476 if (referenceType != null) {
477 return referenceType.name();
482 catch (EvaluateException ignored) {
487 private VirtualMachine createVirtualMachineInt() throws ExecutionException {
489 if (myArguments != null) {
490 throw new IOException(DebuggerBundle.message("error.debugger.already.listening"));
493 final String address = myConnection.getAddress();
494 if (myConnection.isServerMode()) {
495 ListeningConnector connector = (ListeningConnector)findConnector(
496 myConnection.isUseSockets() ? SOCKET_LISTENING_CONNECTOR_NAME : SHMEM_LISTENING_CONNECTOR_NAME);
497 if (connector == null) {
498 throw new CantRunException(DebuggerBundle.message("error.debug.connector.not.found", DebuggerBundle.getTransportName(myConnection)));
500 myArguments = connector.defaultArguments();
501 if (myArguments == null) {
502 throw new CantRunException(DebuggerBundle.message("error.no.debug.listen.port"));
505 if (address == null) {
506 throw new CantRunException(DebuggerBundle.message("error.no.debug.listen.port"));
508 // negative port number means the caller leaves to debugger to decide at which port to listen
509 //noinspection HardCodedStringLiteral
510 final Connector.Argument portArg = myConnection.isUseSockets() ? myArguments.get("port") : myArguments.get("name");
511 if (portArg != null) {
512 portArg.setValue(address);
514 //noinspection HardCodedStringLiteral
515 final Connector.Argument timeoutArg = myArguments.get("timeout");
516 if (timeoutArg != null) {
517 timeoutArg.setValue("0"); // wait forever
519 connector.startListening(myArguments);
520 myDebugProcessDispatcher.getMulticaster().connectorIsReady();
522 return connector.accept(myArguments);
525 if(myArguments != null) {
527 connector.stopListening(myArguments);
529 catch (IllegalArgumentException ignored) {
532 catch (IllegalConnectorArgumentsException ignored) {
538 else { // is client mode, should attach to already running process
539 AttachingConnector connector = (AttachingConnector)findConnector(
540 myConnection.isUseSockets() ? SOCKET_ATTACHING_CONNECTOR_NAME : SHMEM_ATTACHING_CONNECTOR_NAME
543 if (connector == null) {
544 throw new CantRunException( DebuggerBundle.message("error.debug.connector.not.found", DebuggerBundle.getTransportName(myConnection)));
546 myArguments = connector.defaultArguments();
547 if (myConnection.isUseSockets()) {
548 //noinspection HardCodedStringLiteral
549 final Connector.Argument hostnameArg = myArguments.get("hostname");
550 if (hostnameArg != null && myConnection.getHostName() != null) {
551 hostnameArg.setValue(myConnection.getHostName());
553 if (address == null) {
554 throw new CantRunException(DebuggerBundle.message("error.no.debug.attach.port"));
556 //noinspection HardCodedStringLiteral
557 final Connector.Argument portArg = myArguments.get("port");
558 if (portArg != null) {
559 portArg.setValue(address);
563 if (address == null) {
564 throw new CantRunException(DebuggerBundle.message("error.no.shmem.address"));
566 //noinspection HardCodedStringLiteral
567 final Connector.Argument nameArg = myArguments.get("name");
568 if (nameArg != null) {
569 nameArg.setValue(address);
572 //noinspection HardCodedStringLiteral
573 final Connector.Argument timeoutArg = myArguments.get("timeout");
574 if (timeoutArg != null) {
575 timeoutArg.setValue("0"); // wait forever
578 myDebugProcessDispatcher.getMulticaster().connectorIsReady();
580 return connector.attach(myArguments);
582 catch (IllegalArgumentException e) {
583 throw new CantRunException(e.getLocalizedMessage());
587 catch (IOException e) {
588 throw new ExecutionException(processIOException(e, DebuggerBundle.getAddressDisplayName(myConnection)), e);
590 catch (IllegalConnectorArgumentsException e) {
591 throw new ExecutionException(processError(e), e);
595 myConnectionService = null;
599 public void showStatusText(final String text) {
600 if (!myStatusUpdateAlarm.isDisposed()) {
601 myStatusUpdateAlarm.cancelAllRequests();
602 myStatusUpdateAlarm.addRequest(new Runnable() {
605 StatusBarUtil.setStatusBarInfo(myProject, text);
611 static Connector findConnector(String connectorName) throws ExecutionException {
612 VirtualMachineManager virtualMachineManager;
614 virtualMachineManager = Bootstrap.virtualMachineManager();
617 final String error = e.getClass().getName() + " : " + e.getLocalizedMessage();
618 throw new ExecutionException(DebuggerBundle.message("debugger.jdi.bootstrap.error", error));
621 if (SOCKET_ATTACHING_CONNECTOR_NAME.equals(connectorName) || SHMEM_ATTACHING_CONNECTOR_NAME.equals(connectorName)) {
622 connectors = virtualMachineManager.attachingConnectors();
624 else if (SOCKET_LISTENING_CONNECTOR_NAME.equals(connectorName) || SHMEM_LISTENING_CONNECTOR_NAME.equals(connectorName)) {
625 connectors = virtualMachineManager.listeningConnectors();
630 for (Object connector1 : connectors) {
631 Connector connector = (Connector)connector1;
632 if (connectorName.equals(connector.name())) {
639 private void checkVirtualMachineVersion(VirtualMachine vm) {
640 final String version = vm.version();
641 if ("1.4.0".equals(version)) {
642 SwingUtilities.invokeLater(new Runnable() {
645 Messages.showMessageDialog(
647 DebuggerBundle.message("warning.jdk140.unstable"), DebuggerBundle.message("title.jdk140.unstable"), Messages.getWarningIcon()
654 /*Event dispatching*/
655 public void addEvaluationListener(EvaluationListener evaluationListener) {
656 myEvaluationDispatcher.addListener(evaluationListener);
659 public void removeEvaluationListener(EvaluationListener evaluationListener) {
660 myEvaluationDispatcher.removeListener(evaluationListener);
664 public void addDebugProcessListener(DebugProcessListener listener) {
665 myDebugProcessDispatcher.addListener(listener);
669 public void removeDebugProcessListener(DebugProcessListener listener) {
670 myDebugProcessDispatcher.removeListener(listener);
673 public void addProcessListener(ProcessListener processListener) {
674 synchronized(myProcessListeners) {
675 if(getProcessHandler() != null) {
676 getProcessHandler().addProcessListener(processListener);
679 myProcessListeners.add(processListener);
684 public void removeProcessListener(ProcessListener processListener) {
685 synchronized (myProcessListeners) {
686 if(getProcessHandler() != null) {
687 getProcessHandler().removeProcessListener(processListener);
690 myProcessListeners.remove(processListener);
696 public RemoteConnection getConnection() {
701 public ExecutionResult getExecutionResult() {
702 return myExecutionResult;
706 public Project getProject() {
710 public boolean canRedefineClasses() {
711 final VirtualMachineProxyImpl vm = myVirtualMachineProxy;
712 return vm != null && vm.canRedefineClasses();
715 public boolean canWatchFieldModification() {
716 final VirtualMachineProxyImpl vm = myVirtualMachineProxy;
717 return vm != null && vm.canWatchFieldModification();
720 public boolean isInInitialState() {
721 return myState.get() == STATE_INITIAL;
725 public boolean isAttached() {
726 return myState.get() == STATE_ATTACHED;
730 public boolean isDetached() {
731 return myState.get() == STATE_DETACHED;
735 public boolean isDetaching() {
736 return myState.get() == STATE_DETACHING;
740 public RequestManagerImpl getRequestsManager() {
741 return myRequestManager;
745 public VirtualMachineProxyImpl getVirtualMachineProxy() {
746 DebuggerManagerThreadImpl.assertIsManagerThread();
747 final VirtualMachineProxyImpl vm = myVirtualMachineProxy;
749 throw new VMDisconnectedException();
755 public void appendPositionManager(final PositionManager positionManager) {
756 DebuggerManagerThreadImpl.assertIsManagerThread();
757 myPositionManager.appendPositionManager(positionManager);
760 private volatile RunToCursorBreakpoint myRunToCursorBreakpoint;
762 public void cancelRunToCursorBreakpoint() {
763 DebuggerManagerThreadImpl.assertIsManagerThread();
764 final RunToCursorBreakpoint runToCursorBreakpoint = myRunToCursorBreakpoint;
765 if (runToCursorBreakpoint != null) {
766 myRunToCursorBreakpoint = null;
767 getRequestsManager().deleteRequest(runToCursorBreakpoint);
768 if (runToCursorBreakpoint.isRestoreBreakpoints()) {
769 final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(getProject()).getBreakpointManager();
770 breakpointManager.enableBreakpoints(this);
775 protected void closeProcess(boolean closedByUser) {
776 DebuggerManagerThreadImpl.assertIsManagerThread();
778 if (myState.compareAndSet(STATE_INITIAL, STATE_DETACHING) || myState.compareAndSet(STATE_ATTACHED, STATE_DETACHING)) {
780 getManagerThread().close();
783 final VirtualMachineProxyImpl vm = myVirtualMachineProxy;
784 myVirtualMachineProxy = null;
785 myPositionManager = null;
786 myReturnValueWatcher = null;
787 myNodeRenderersMap.clear();
789 DebuggerUtils.cleanupAfterProcessFinish(this);
790 myState.set(STATE_DETACHED);
792 myDebugProcessDispatcher.getMulticaster().processDetached(this, closedByUser);
795 //if (DebuggerSettings.getInstance().UNMUTE_ON_STOP) {
796 // XDebugSession session = mySession.getXDebugSession();
797 // if (session != null) {
798 // session.setBreakpointMuted(false);
803 vm.dispose(); // to be on the safe side ensure that VM mirror, if present, is disposed and invalidated
805 catch (Throwable ignored) {
815 private static String formatMessage(String message) {
816 final int lineLength = 90;
817 StringBuilder buf = new StringBuilder(message.length());
819 while (index < message.length()) {
820 buf.append(message.substring(index, Math.min(index + lineLength, message.length()))).append('\n');
823 return buf.toString();
826 public static String processError(Exception e) {
829 if (e instanceof VMStartException) {
830 VMStartException e1 = (VMStartException)e;
831 message = e1.getLocalizedMessage();
833 else if (e instanceof IllegalConnectorArgumentsException) {
834 IllegalConnectorArgumentsException e1 = (IllegalConnectorArgumentsException)e;
835 final List<String> invalidArgumentNames = e1.argumentNames();
836 message = formatMessage(DebuggerBundle.message("error.invalid.argument", invalidArgumentNames.size()) + ": "+ e1.getLocalizedMessage()) + invalidArgumentNames;
837 if (LOG.isDebugEnabled()) {
841 else if (e instanceof CantRunException) {
842 message = e.getLocalizedMessage();
844 else if (e instanceof VMDisconnectedException) {
845 message = DebuggerBundle.message("error.vm.disconnected");
847 else if (e instanceof IOException) {
848 message = processIOException((IOException)e, null);
850 else if (e instanceof ExecutionException) {
851 message = e.getLocalizedMessage();
854 message = DebuggerBundle.message("error.exception.while.connecting", e.getClass().getName(), e.getLocalizedMessage());
855 if (LOG.isDebugEnabled()) {
863 public static String processIOException(@NotNull IOException e, @Nullable String address) {
864 if (e instanceof UnknownHostException) {
865 return DebuggerBundle.message("error.unknown.host") + (address != null ? " (" + address + ")" : "") + ":\n" + e.getLocalizedMessage();
869 final StringBuilder buf = StringBuilderSpinAllocator.alloc();
871 buf.append(DebuggerBundle.message("error.cannot.open.debugger.port"));
872 if (address != null) {
873 buf.append(" (").append(address).append(")");
876 buf.append(e.getClass().getName()).append(" ");
877 final String localizedMessage = e.getLocalizedMessage();
878 if (!StringUtil.isEmpty(localizedMessage)) {
880 buf.append(localizedMessage);
883 if (LOG.isDebugEnabled()) {
886 message = buf.toString();
889 StringBuilderSpinAllocator.dispose(buf);
894 public void dispose() {
895 NodeRendererSettings.getInstance().removeListener(mySettingsListener);
896 Disposer.dispose(myDisposable);
897 myRequestManager.setFilterThread(null);
901 public DebuggerManagerThreadImpl getManagerThread() {
902 return myDebuggerManagerThread;
905 private static int getInvokePolicy(SuspendContext suspendContext) {
906 //return ThreadReference.INVOKE_SINGLE_THREADED;
907 return suspendContext.getSuspendPolicy() == EventRequest.SUSPEND_EVENT_THREAD ? ObjectReference.INVOKE_SINGLE_THREADED : 0;
911 public void waitFor() {
912 LOG.assertTrue(!DebuggerManagerThreadImpl.isManagerThread());
917 public void waitFor(long timeout) {
918 LOG.assertTrue(!DebuggerManagerThreadImpl.isManagerThread());
919 myWaitFor.waitFor(timeout);
922 private abstract class InvokeCommand <E extends Value> {
923 private final Method myMethod;
924 private final List myArgs;
926 protected InvokeCommand(@NotNull Method method, @NotNull List args) {
928 if (!args.isEmpty()) {
929 myArgs = new ArrayList(args);
936 public String toString() {
937 return "INVOKE: " + super.toString();
940 protected abstract E invokeMethod(int invokePolicy, Method method, final List args) throws InvocationException,
941 ClassNotLoadedException,
942 IncompatibleThreadStateException,
943 InvalidTypeException;
946 E start(EvaluationContextImpl evaluationContext, boolean internalEvaluate) throws EvaluateException {
949 return startInternal(evaluationContext, internalEvaluate);
951 catch (ClassNotLoadedException e) {
952 ReferenceType loadedClass = null;
954 if (evaluationContext.isAutoLoadClasses()) {
955 loadedClass = loadClass(evaluationContext, e.className(), evaluationContext.getClassLoader());
958 catch (Exception ignored) {
961 if (loadedClass == null) {
962 throw EvaluateExceptionUtil.createEvaluateException(e);
968 E startInternal(EvaluationContextImpl evaluationContext, boolean internalEvaluate)
969 throws EvaluateException, ClassNotLoadedException {
970 DebuggerManagerThreadImpl.assertIsManagerThread();
971 SuspendContextImpl suspendContext = evaluationContext.getSuspendContext();
972 SuspendManagerUtil.assertSuspendContext(suspendContext);
974 ThreadReferenceProxyImpl invokeThread = suspendContext.getThread();
976 if (SuspendManagerUtil.isEvaluating(getSuspendManager(), invokeThread)) {
977 throw EvaluateExceptionUtil.NESTED_EVALUATION_ERROR;
980 Set<SuspendContextImpl> suspendingContexts = SuspendManagerUtil.getSuspendingContexts(getSuspendManager(), invokeThread);
981 final ThreadReference invokeThreadRef = invokeThread.getThreadReference();
983 myEvaluationDispatcher.getMulticaster().evaluationStarted(suspendContext);
984 beforeMethodInvocation(suspendContext, myMethod, internalEvaluate);
986 Object resumeData = null;
988 for (SuspendContextImpl suspendingContext : suspendingContexts) {
989 final ThreadReferenceProxyImpl suspendContextThread = suspendingContext.getThread();
990 if (suspendContextThread != invokeThread) {
991 if (LOG.isDebugEnabled()) {
992 LOG.debug("Resuming " + invokeThread + " that is paused by " + suspendContextThread);
994 LOG.assertTrue(suspendContextThread == null || !invokeThreadRef.equals(suspendContextThread.getThreadReference()));
995 getSuspendManager().resumeThread(suspendingContext, invokeThread);
999 resumeData = SuspendManagerUtil.prepareForResume(suspendContext);
1000 suspendContext.setIsEvaluating(evaluationContext);
1002 getVirtualMachineProxy().clearCaches();
1004 return invokeMethodAndFork(suspendContext);
1006 catch (InvocationException e) {
1007 throw EvaluateExceptionUtil.createEvaluateException(e);
1009 catch (IncompatibleThreadStateException e) {
1010 throw EvaluateExceptionUtil.createEvaluateException(e);
1012 catch (InvalidTypeException e) {
1013 throw EvaluateExceptionUtil.createEvaluateException(e);
1015 catch (ObjectCollectedException e) {
1016 throw EvaluateExceptionUtil.createEvaluateException(e);
1018 catch (UnsupportedOperationException e) {
1019 throw EvaluateExceptionUtil.createEvaluateException(e);
1021 catch (InternalException e) {
1022 throw EvaluateExceptionUtil.createEvaluateException(e);
1025 suspendContext.setIsEvaluating(null);
1026 if (resumeData != null) {
1027 SuspendManagerUtil.restoreAfterResume(suspendContext, resumeData);
1029 for (SuspendContextImpl suspendingContext : mySuspendManager.getEventContexts()) {
1030 if (suspendingContexts.contains(suspendingContext) && !suspendingContext.isEvaluating() && !suspendingContext.suspends(invokeThread)) {
1031 mySuspendManager.suspendThread(suspendingContext, invokeThread);
1035 if (LOG.isDebugEnabled()) {
1036 LOG.debug("getVirtualMachine().clearCaches()");
1038 getVirtualMachineProxy().clearCaches();
1039 afterMethodInvocation(suspendContext, internalEvaluate);
1041 myEvaluationDispatcher.getMulticaster().evaluationFinished(suspendContext);
1045 private E invokeMethodAndFork(final SuspendContextImpl context) throws InvocationException,
1046 ClassNotLoadedException,
1047 IncompatibleThreadStateException,
1048 InvalidTypeException {
1049 final int invokePolicy = getInvokePolicy(context);
1050 final Exception[] exception = new Exception[1];
1051 final Value[] result = new Value[1];
1052 getManagerThread().startLongProcessAndFork(new Runnable() {
1055 ThreadReferenceProxyImpl thread = context.getThread();
1058 if (LOG.isDebugEnabled()) {
1059 final VirtualMachineProxyImpl virtualMachineProxy = getVirtualMachineProxy();
1060 virtualMachineProxy.logThreads();
1061 LOG.debug("Invoke in " + thread.name());
1062 assertThreadSuspended(thread, context);
1065 if (myMethod.isVarArgs()) {
1067 // if vararg parameter array is of interface type and Object[] is expected, JDI wrap it into another array,
1068 // in this case we have to unroll the array manually and pass its elements to the method instead of array object
1069 int lastIndex = myArgs.size() - 1;
1070 if (lastIndex >= 0) {
1071 final Object lastArg = myArgs.get(lastIndex);
1072 if (lastArg instanceof ArrayReference) {
1073 final ArrayReference arrayRef = (ArrayReference)lastArg;
1074 if (((ArrayType)arrayRef.referenceType()).componentType() instanceof InterfaceType) {
1075 List<String> argTypes = myMethod.argumentTypeNames();
1076 if (argTypes.size() > lastIndex && argTypes.get(lastIndex).startsWith(CommonClassNames.JAVA_LANG_OBJECT)) {
1077 // unwrap array of interfaces for vararg param
1078 myArgs.remove(lastIndex);
1079 myArgs.addAll(arrayRef.getValues());
1086 if (!Patches.IBM_JDK_DISABLE_COLLECTION_BUG) {
1087 // ensure args are not collected
1088 for (Object arg : myArgs) {
1089 if (arg instanceof ObjectReference) {
1090 DebuggerUtilsEx.disableCollection((ObjectReference)arg);
1095 // workaround for jdi hang in trace mode
1096 if (!StringUtil.isEmpty(ourTrace)) {
1097 for (Object arg : myArgs) {
1098 //noinspection ResultOfMethodCallIgnored
1103 result[0] = invokeMethod(invokePolicy, myMethod, myArgs);
1106 // assertThreadSuspended(thread, context);
1107 if (!Patches.IBM_JDK_DISABLE_COLLECTION_BUG) {
1108 // ensure args are not collected
1109 for (Object arg : myArgs) {
1110 if (arg instanceof ObjectReference) {
1111 DebuggerUtilsEx.enableCollection((ObjectReference)arg);
1117 catch (Exception e) {
1123 if (exception[0] != null) {
1124 if (exception[0] instanceof InvocationException) {
1125 throw (InvocationException)exception[0];
1127 else if (exception[0] instanceof ClassNotLoadedException) {
1128 throw (ClassNotLoadedException)exception[0];
1130 else if (exception[0] instanceof IncompatibleThreadStateException) {
1131 throw (IncompatibleThreadStateException)exception[0];
1133 else if (exception[0] instanceof InvalidTypeException) {
1134 throw (InvalidTypeException)exception[0];
1136 else if (exception[0] instanceof RuntimeException) {
1137 throw (RuntimeException)exception[0];
1140 LOG.error("Unexpected exception", new Throwable().initCause(exception[0]));
1144 return (E)result[0];
1147 private void assertThreadSuspended(final ThreadReferenceProxyImpl thread, final SuspendContextImpl context) {
1148 LOG.assertTrue(context.isEvaluating());
1150 final boolean isSuspended = thread.isSuspended();
1151 LOG.assertTrue(isSuspended, thread);
1153 catch (ObjectCollectedException ignored) {
1159 public Value invokeMethod(@NotNull EvaluationContext evaluationContext, @NotNull ObjectReference objRef, @NotNull Method method, final List args) throws EvaluateException {
1160 return invokeInstanceMethod(evaluationContext, objRef, method, args, 0);
1164 public Value invokeInstanceMethod(@NotNull EvaluationContext evaluationContext,
1165 @NotNull final ObjectReference objRef,
1166 @NotNull Method method,
1168 final int invocationOptions) throws EvaluateException {
1169 final ThreadReference thread = getEvaluationThread(evaluationContext);
1170 return new InvokeCommand<Value>(method, args) {
1172 protected Value invokeMethod(int invokePolicy, Method method, final List args) throws InvocationException, ClassNotLoadedException, IncompatibleThreadStateException, InvalidTypeException {
1173 if (LOG.isDebugEnabled()) {
1174 LOG.debug("Invoke " + method.name());
1176 return objRef.invokeMethod(thread, method, args, invokePolicy | invocationOptions);
1178 }.start((EvaluationContextImpl)evaluationContext, false);
1181 private static ThreadReference getEvaluationThread(final EvaluationContext evaluationContext) throws EvaluateException {
1182 ThreadReferenceProxy evaluationThread = evaluationContext.getSuspendContext().getThread();
1183 if(evaluationThread == null) {
1184 throw EvaluateExceptionUtil.NULL_STACK_FRAME;
1186 return evaluationThread.getThreadReference();
1190 public Value invokeMethod(final EvaluationContext evaluationContext, final ClassType classType,
1191 final Method method,
1192 final List args) throws EvaluateException {
1193 return invokeMethod(evaluationContext, classType, method, args, false);
1196 public Value invokeMethod(@NotNull EvaluationContext evaluationContext,
1197 @NotNull final ClassType classType,
1198 @NotNull Method method,
1200 boolean internalEvaluate) throws EvaluateException {
1201 final ThreadReference thread = getEvaluationThread(evaluationContext);
1202 return new InvokeCommand<Value>(method, args) {
1204 protected Value invokeMethod(int invokePolicy, Method method, List args) throws InvocationException,
1205 ClassNotLoadedException,
1206 IncompatibleThreadStateException,
1207 InvalidTypeException {
1208 if (LOG.isDebugEnabled()) {
1209 LOG.debug("Invoke " + method.name());
1211 return classType.invokeMethod(thread, method, args, invokePolicy);
1213 }.start((EvaluationContextImpl)evaluationContext, internalEvaluate);
1217 public ArrayReference newInstance(final ArrayType arrayType,
1218 final int dimension)
1219 throws EvaluateException {
1221 return arrayType.newInstance(dimension);
1223 catch (Exception e) {
1224 throw EvaluateExceptionUtil.createEvaluateException(e);
1229 public ObjectReference newInstance(@NotNull final EvaluationContext evaluationContext,
1230 @NotNull final ClassType classType,
1231 @NotNull Method method,
1232 @NotNull List args) throws EvaluateException {
1233 final ThreadReference thread = getEvaluationThread(evaluationContext);
1234 InvokeCommand<ObjectReference> invokeCommand = new InvokeCommand<ObjectReference>(method, args) {
1236 protected ObjectReference invokeMethod(int invokePolicy, Method method, List args) throws InvocationException,
1237 ClassNotLoadedException,
1238 IncompatibleThreadStateException,
1239 InvalidTypeException {
1240 if (LOG.isDebugEnabled()) {
1241 LOG.debug("New instance " + method.name());
1243 return classType.newInstance(thread, method, args, invokePolicy);
1246 return invokeCommand.start((EvaluationContextImpl)evaluationContext, false);
1249 public void clearCashes(int suspendPolicy) {
1250 if (!isAttached()) return;
1251 switch (suspendPolicy) {
1252 case EventRequest.SUSPEND_ALL:
1253 getVirtualMachineProxy().clearCaches();
1255 case EventRequest.SUSPEND_EVENT_THREAD:
1256 getVirtualMachineProxy().clearCaches();
1257 //suspendContext.getThread().clearAll();
1262 protected void beforeSuspend(SuspendContextImpl suspendContext) {
1263 clearCashes(suspendContext.getSuspendPolicy());
1266 private void beforeMethodInvocation(SuspendContextImpl suspendContext, Method method, boolean internalEvaluate) {
1267 if (LOG.isDebugEnabled()) {
1269 "before invocation in thread " + suspendContext.getThread().name() + " method " + (method == null ? "null" : method.name()));
1272 if (!internalEvaluate) {
1273 if (method != null) {
1274 showStatusText(DebuggerBundle.message("progress.evaluating", DebuggerUtilsEx.methodName(method)));
1277 showStatusText(DebuggerBundle.message("title.evaluating"));
1282 private void afterMethodInvocation(SuspendContextImpl suspendContext, boolean internalEvaluate) {
1283 if (LOG.isDebugEnabled()) {
1284 LOG.debug("after invocation in thread " + suspendContext.getThread().name());
1286 if (!internalEvaluate) {
1292 public ReferenceType findClass(EvaluationContext evaluationContext, String className,
1293 ClassLoaderReference classLoader) throws EvaluateException {
1295 DebuggerManagerThreadImpl.assertIsManagerThread();
1296 final VirtualMachineProxyImpl vmProxy = getVirtualMachineProxy();
1297 if (vmProxy == null) {
1298 throw new VMDisconnectedException();
1300 ReferenceType result = null;
1301 for (final ReferenceType refType : vmProxy.classesByName(className)) {
1302 if (refType.isPrepared() && isVisibleFromClassLoader(classLoader, refType)) {
1307 final EvaluationContextImpl evalContext = (EvaluationContextImpl)evaluationContext;
1308 if (result == null && evalContext.isAutoLoadClasses()) {
1309 return loadClass(evalContext, className, classLoader);
1313 catch (InvocationException e) {
1314 throw EvaluateExceptionUtil.createEvaluateException(e);
1316 catch (ClassNotLoadedException e) {
1317 throw EvaluateExceptionUtil.createEvaluateException(e);
1319 catch (IncompatibleThreadStateException e) {
1320 throw EvaluateExceptionUtil.createEvaluateException(e);
1322 catch (InvalidTypeException e) {
1323 throw EvaluateExceptionUtil.createEvaluateException(e);
1327 private static boolean isVisibleFromClassLoader(final ClassLoaderReference fromLoader, final ReferenceType refType) {
1328 // IMPORTANT! Even if the refType is already loaded by some parent or bootstrap loader, it may not be visible from the given loader.
1329 // 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
1330 // for this refType and the refType is not visible to the loader.
1331 // Attempt to evaluate method with this refType will yield ClassNotLoadedException.
1332 // The only way to say for sure whether the class is _visible_ to the given loader, is to use the following API call
1333 return fromLoader == null || fromLoader.equals(refType.classLoader()) || fromLoader.visibleClasses().contains(refType);
1336 private static String reformatArrayName(String className) {
1337 if (className.indexOf('[') == -1) return className;
1340 while (className.endsWith("[]")) {
1341 className = className.substring(0, className.length() - 2);
1345 StringBuilder buffer = StringBuilderSpinAllocator.alloc();
1347 for (int i = 0; i < dims; i++) {
1350 String primitiveSignature = JVMNameUtil.getPrimitiveSignature(className);
1351 if(primitiveSignature != null) {
1352 buffer.append(primitiveSignature);
1356 buffer.append(className);
1359 return buffer.toString();
1362 StringBuilderSpinAllocator.dispose(buffer);
1366 @SuppressWarnings({"HardCodedStringLiteral", "SpellCheckingInspection"})
1367 public ReferenceType loadClass(EvaluationContextImpl evaluationContext, String qName, ClassLoaderReference classLoader)
1368 throws InvocationException, ClassNotLoadedException, IncompatibleThreadStateException, InvalidTypeException, EvaluateException {
1370 DebuggerManagerThreadImpl.assertIsManagerThread();
1371 qName = reformatArrayName(qName);
1372 ReferenceType refType = null;
1373 VirtualMachineProxyImpl virtualMachine = getVirtualMachineProxy();
1374 final List classClasses = virtualMachine.classesByName("java.lang.Class");
1375 if (!classClasses.isEmpty()) {
1376 ClassType classClassType = (ClassType)classClasses.get(0);
1377 final Method forNameMethod;
1378 if (classLoader != null) {
1379 //forNameMethod = classClassType.concreteMethodByName("forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;");
1380 forNameMethod = DebuggerUtils.findMethod(classClassType, "forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;");
1383 //forNameMethod = classClassType.concreteMethodByName("forName", "(Ljava/lang/String;)Ljava/lang/Class;");
1384 forNameMethod = DebuggerUtils.findMethod(classClassType, "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
1386 final List<Mirror> args = new ArrayList<Mirror>(); // do not use unmodifiable lists because the list is modified by JPDA
1387 final StringReference qNameMirror = virtualMachine.mirrorOf(qName);
1388 args.add(qNameMirror);
1389 if (classLoader != null) {
1390 args.add(virtualMachine.mirrorOf(true));
1391 args.add(classLoader);
1393 final Value value = invokeMethod(evaluationContext, classClassType, forNameMethod, args);
1394 if (value instanceof ClassObjectReference) {
1395 refType = ((ClassObjectReference)value).reflectedType();
1401 public void logThreads() {
1402 if (LOG.isDebugEnabled()) {
1404 Collection<ThreadReferenceProxyImpl> allThreads = getVirtualMachineProxy().allThreads();
1405 for (ThreadReferenceProxyImpl threadReferenceProxy : allThreads) {
1406 LOG.debug("Thread name=" + threadReferenceProxy.name() + " suspendCount()=" + threadReferenceProxy.getSuspendCount());
1409 catch (Exception e) {
1415 public SuspendManager getSuspendManager() {
1416 return mySuspendManager;
1420 public CompoundPositionManager getPositionManager() {
1421 return myPositionManager;
1426 public void stop(boolean forceTerminate) {
1427 getManagerThread().terminateAndInvoke(createStopCommand(forceTerminate), DebuggerManagerThreadImpl.COMMAND_TIMEOUT);
1430 public StopCommand createStopCommand(boolean forceTerminate) {
1431 return new StopCommand(forceTerminate);
1434 protected class StopCommand extends DebuggerCommandImpl {
1435 private final boolean myIsTerminateTargetVM;
1437 public StopCommand(boolean isTerminateTargetVM) {
1438 myIsTerminateTargetVM = isTerminateTargetVM;
1442 public Priority getPriority() {
1443 return Priority.HIGH;
1447 protected void action() throws Exception {
1449 final VirtualMachineProxyImpl virtualMachineProxy = getVirtualMachineProxy();
1450 if (myIsTerminateTargetVM) {
1451 virtualMachineProxy.exit(-1);
1454 // some VMs (like IBM VM 1.4.2 bundled with WebSphere) does not resume threads on dispose() like it should
1456 virtualMachineProxy.resume();
1459 virtualMachineProxy.dispose();
1469 private class StepOutCommand extends StepCommand {
1470 private final int myStepSize;
1472 public StepOutCommand(SuspendContextImpl suspendContext, int stepSize) {
1473 super(suspendContext);
1474 myStepSize = stepSize;
1478 public void contextAction() {
1479 showStatusText(DebuggerBundle.message("status.step.out"));
1480 final SuspendContextImpl suspendContext = getSuspendContext();
1481 final ThreadReferenceProxyImpl thread = getContextThread();
1482 RequestHint hint = new RequestHint(thread, suspendContext, StepRequest.STEP_OUT);
1483 hint.setIgnoreFilters(mySession.shouldIgnoreSteppingFilters());
1484 applyThreadFilter(thread);
1485 final MethodReturnValueWatcher rvWatcher = myReturnValueWatcher;
1486 if (rvWatcher != null) {
1487 rvWatcher.enable(thread.getThreadReference());
1489 doStep(suspendContext, thread, myStepSize, StepRequest.STEP_OUT, hint);
1490 super.contextAction();
1494 private class StepIntoCommand extends StepCommand {
1495 private final boolean myForcedIgnoreFilters;
1496 private final MethodFilter mySmartStepFilter;
1498 private final StepIntoBreakpoint myBreakpoint;
1499 private final int myStepSize;
1501 public StepIntoCommand(SuspendContextImpl suspendContext, boolean ignoreFilters, @Nullable final MethodFilter methodFilter,
1503 super(suspendContext);
1504 myForcedIgnoreFilters = ignoreFilters || methodFilter != null;
1505 mySmartStepFilter = methodFilter;
1506 myBreakpoint = methodFilter instanceof BreakpointStepMethodFilter ?
1507 DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager().addStepIntoBreakpoint(((BreakpointStepMethodFilter)methodFilter)) :
1509 myStepSize = stepSize;
1513 public void contextAction() {
1514 showStatusText(DebuggerBundle.message("status.step.into"));
1515 final SuspendContextImpl suspendContext = getSuspendContext();
1516 final ThreadReferenceProxyImpl stepThread = getContextThread();
1517 final RequestHint hint = mySmartStepFilter != null?
1518 new RequestHint(stepThread, suspendContext, mySmartStepFilter) :
1519 new RequestHint(stepThread, suspendContext, StepRequest.STEP_INTO);
1520 hint.setResetIgnoreFilters(mySmartStepFilter != null && !mySession.shouldIgnoreSteppingFilters());
1521 if (myForcedIgnoreFilters) {
1523 mySession.setIgnoreStepFiltersFlag(stepThread.frameCount());
1525 catch (EvaluateException e) {
1529 hint.setIgnoreFilters(myForcedIgnoreFilters || mySession.shouldIgnoreSteppingFilters());
1530 applyThreadFilter(stepThread);
1531 if (myBreakpoint != null) {
1532 myBreakpoint.setSuspendPolicy(suspendContext.getSuspendPolicy() == EventRequest.SUSPEND_EVENT_THREAD? DebuggerSettings.SUSPEND_THREAD : DebuggerSettings.SUSPEND_ALL);
1533 myBreakpoint.createRequest(suspendContext.getDebugProcess());
1534 myRunToCursorBreakpoint = myBreakpoint;
1536 doStep(suspendContext, stepThread, myStepSize, StepRequest.STEP_INTO, hint);
1537 super.contextAction();
1541 private class StepOverCommand extends StepCommand {
1542 private final boolean myIsIgnoreBreakpoints;
1543 private final int myStepSize;
1545 public StepOverCommand(SuspendContextImpl suspendContext, boolean ignoreBreakpoints, int stepSize) {
1546 super(suspendContext);
1547 myIsIgnoreBreakpoints = ignoreBreakpoints;
1548 myStepSize = stepSize;
1552 public void contextAction() {
1553 showStatusText(DebuggerBundle.message("status.step.over"));
1554 final SuspendContextImpl suspendContext = getSuspendContext();
1555 final ThreadReferenceProxyImpl stepThread = getContextThread();
1556 // need this hint while stepping over for JSR45 support:
1557 // several lines of generated java code may correspond to a single line in the source file,
1558 // from which the java code was generated
1559 RequestHint hint = new RequestHint(stepThread, suspendContext, StepRequest.STEP_OVER);
1560 hint.setRestoreBreakpoints(myIsIgnoreBreakpoints);
1561 hint.setIgnoreFilters(myIsIgnoreBreakpoints || mySession.shouldIgnoreSteppingFilters());
1563 applyThreadFilter(stepThread);
1565 final MethodReturnValueWatcher rvWatcher = myReturnValueWatcher;
1566 if (rvWatcher != null) {
1567 rvWatcher.enable(stepThread.getThreadReference());
1570 doStep(suspendContext, stepThread, myStepSize, StepRequest.STEP_OVER, hint);
1572 if (myIsIgnoreBreakpoints) {
1573 DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager().disableBreakpoints(DebugProcessImpl.this);
1575 super.contextAction();
1579 private class RunToCursorCommand extends StepCommand {
1580 private final RunToCursorBreakpoint myRunToCursorBreakpoint;
1581 private final boolean myIgnoreBreakpoints;
1583 private RunToCursorCommand(SuspendContextImpl suspendContext, @NotNull XSourcePosition position, final boolean ignoreBreakpoints) {
1584 super(suspendContext);
1585 myIgnoreBreakpoints = ignoreBreakpoints;
1586 BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager();
1587 myRunToCursorBreakpoint = breakpointManager.addRunToCursorBreakpoint(position, ignoreBreakpoints);
1591 public void contextAction() {
1592 showStatusText(DebuggerBundle.message("status.run.to.cursor"));
1593 cancelRunToCursorBreakpoint();
1594 if (myRunToCursorBreakpoint == null) {
1597 if (myIgnoreBreakpoints) {
1598 final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager();
1599 breakpointManager.disableBreakpoints(DebugProcessImpl.this);
1601 applyThreadFilter(getContextThread());
1602 final SuspendContextImpl context = getSuspendContext();
1603 myRunToCursorBreakpoint.setSuspendPolicy(context.getSuspendPolicy() == EventRequest.SUSPEND_EVENT_THREAD? DebuggerSettings.SUSPEND_THREAD : DebuggerSettings.SUSPEND_ALL);
1604 DebugProcessImpl debugProcess = context.getDebugProcess();
1605 myRunToCursorBreakpoint.createRequest(debugProcess);
1606 DebugProcessImpl.this.myRunToCursorBreakpoint = myRunToCursorBreakpoint;
1608 if (debugProcess.getRequestsManager().getWarning(myRunToCursorBreakpoint) != null) {
1609 DebuggerInvocationUtil.swingInvokeLater(myProject, new Runnable() {
1612 SourcePosition position = myRunToCursorBreakpoint.getSourcePosition();
1613 String name = position != null ? position.getFile().getName() : "<No File>";
1614 Messages.showErrorDialog(
1615 DebuggerBundle.message("error.running.to.cursor.no.executable.code", name, myRunToCursorBreakpoint.getLineIndex() + 1),
1616 UIUtil.removeMnemonic(ActionsBundle.actionText(XDebuggerActions.RUN_TO_CURSOR)));
1620 super.contextAction();
1624 private abstract class StepCommand extends ResumeCommand {
1625 public StepCommand(SuspendContextImpl suspendContext) {
1626 super(suspendContext);
1630 protected void resumeAction() {
1631 SuspendContextImpl context = getSuspendContext();
1633 && Registry.is("debugger.step.resumes.one.thread")
1634 && context.getSuspendPolicy() == EventRequest.SUSPEND_ALL) {
1635 getSuspendManager().resumeThread(context, myContextThread);
1638 super.resumeAction();
1643 public abstract class ResumeCommand extends SuspendContextCommandImpl {
1644 protected final ThreadReferenceProxyImpl myContextThread;
1646 public ResumeCommand(SuspendContextImpl suspendContext) {
1647 super(suspendContext);
1648 final ThreadReferenceProxyImpl contextThread = getDebuggerContext().getThreadProxy();
1649 myContextThread = contextThread != null ? contextThread : (suspendContext != null? suspendContext.getThread() : null);
1653 public Priority getPriority() {
1654 return Priority.HIGH;
1658 public void contextAction() {
1659 showStatusText(DebuggerBundle.message("status.process.resumed"));
1661 myDebugProcessDispatcher.getMulticaster().resumed(getSuspendContext());
1664 protected void resumeAction() {
1665 getSuspendManager().resume(getSuspendContext());
1669 public ThreadReferenceProxyImpl getContextThread() {
1670 return myContextThread;
1673 protected void applyThreadFilter(ThreadReferenceProxy thread) {
1674 if (getSuspendContext().getSuspendPolicy() == EventRequest.SUSPEND_ALL) {
1675 // there could be explicit resume as a result of call to voteSuspend()
1676 // e.g. when breakpoint was considered invalid, in that case the filter will be applied _after_
1677 // resuming and all breakpoints in other threads will be ignored.
1678 // As resume() implicitly cleares the filter, the filter must be always applied _before_ any resume() action happens
1679 final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(getProject()).getBreakpointManager();
1680 breakpointManager.applyThreadFilter(DebugProcessImpl.this, thread.getThreadReference());
1685 private class PauseCommand extends DebuggerCommandImpl {
1686 public PauseCommand() {
1690 public void action() {
1691 if (!isAttached() || getVirtualMachineProxy().isPausePressed()) {
1695 getVirtualMachineProxy().suspend();
1697 SuspendContextImpl suspendContext = mySuspendManager.pushSuspendContext(EventRequest.SUSPEND_ALL, 0);
1698 myDebugProcessDispatcher.getMulticaster().paused(suspendContext);
1702 private class ResumeThreadCommand extends SuspendContextCommandImpl {
1703 private final ThreadReferenceProxyImpl myThread;
1705 public ResumeThreadCommand(SuspendContextImpl suspendContext, ThreadReferenceProxyImpl thread) {
1706 super(suspendContext);
1711 public void contextAction() {
1712 // handle unfreeze through the regular context resume
1713 if (false && getSuspendManager().isFrozen(myThread)) {
1714 getSuspendManager().unfreezeThread(myThread);
1718 final Set<SuspendContextImpl> suspendingContexts = SuspendManagerUtil.getSuspendingContexts(getSuspendManager(), myThread);
1719 for (SuspendContextImpl suspendContext : suspendingContexts) {
1720 if (suspendContext.getThread() == myThread) {
1721 getSession().getXDebugSession().sessionResumed();
1722 getManagerThread().invoke(createResumeCommand(suspendContext));
1725 getSuspendManager().resumeThread(suspendContext, myThread);
1731 private class FreezeThreadCommand extends DebuggerCommandImpl {
1732 private final ThreadReferenceProxyImpl myThread;
1734 public FreezeThreadCommand(ThreadReferenceProxyImpl thread) {
1739 protected void action() throws Exception {
1740 SuspendManager suspendManager = getSuspendManager();
1741 if (!suspendManager.isFrozen(myThread)) {
1742 suspendManager.freezeThread(myThread);
1743 SuspendContextImpl suspendContext = mySuspendManager.pushSuspendContext(EventRequest.SUSPEND_EVENT_THREAD, 0);
1744 suspendContext.setThread(myThread.getThreadReference());
1745 myDebugProcessDispatcher.getMulticaster().paused(suspendContext);
1750 private class PopFrameCommand extends SuspendContextCommandImpl {
1751 private final StackFrameProxyImpl myStackFrame;
1753 public PopFrameCommand(SuspendContextImpl context, StackFrameProxyImpl frameProxy) {
1755 myStackFrame = frameProxy;
1759 public void contextAction() {
1760 final ThreadReferenceProxyImpl thread = myStackFrame.threadProxy();
1762 if (!getSuspendManager().isSuspended(thread)) {
1767 catch (ObjectCollectedException ignored) {
1772 final SuspendContextImpl suspendContext = getSuspendContext();
1773 if (!suspendContext.suspends(thread)) {
1774 suspendContext.postponeCommand(this);
1778 if (myStackFrame.isBottom()) {
1779 DebuggerInvocationUtil.swingInvokeLater(myProject, new Runnable() {
1782 Messages.showMessageDialog(myProject, DebuggerBundle.message("error.pop.bottom.stackframe"), ActionsBundle.actionText(DebuggerActions.POP_FRAME), Messages.getErrorIcon());
1789 thread.popFrames(myStackFrame);
1791 catch (final EvaluateException e) {
1792 DebuggerInvocationUtil.swingInvokeLater(myProject, new Runnable() {
1795 Messages.showMessageDialog(myProject, DebuggerBundle.message("error.pop.stackframe", e.getLocalizedMessage()), ActionsBundle.actionText(DebuggerActions.POP_FRAME), Messages.getErrorIcon());
1801 getSuspendManager().popFrame(suspendContext);
1808 public GlobalSearchScope getSearchScope() {
1809 LOG.assertTrue(mySession != null, "Accessing debug session before its initialization");
1810 return mySession.getSearchScope();
1813 public void reattach(final DebugEnvironment environment) throws ExecutionException {
1814 ApplicationManager.getApplication().assertIsDispatchThread(); //TODO: remove this requirement
1815 ((XDebugSessionImpl)getXdebugProcess().getSession()).reset();
1816 myState.set(STATE_INITIAL);
1817 getManagerThread().schedule(new DebuggerCommandImpl() {
1819 protected void action() throws Exception {
1820 myRequestManager.processDetached(DebugProcessImpl.this, false);
1823 myConnection = environment.getRemoteConnection();
1824 getManagerThread().restartIfNeeded();
1825 createVirtualMachine(environment.getSessionName(), environment.isPollConnection());
1829 public ExecutionResult attachVirtualMachine(final DebugEnvironment environment,
1830 final DebuggerSession session) throws ExecutionException {
1831 mySession = session;
1834 ApplicationManager.getApplication().assertIsDispatchThread();
1835 LOG.assertTrue(isInInitialState());
1837 myConnection = environment.getRemoteConnection();
1839 createVirtualMachine(environment.getSessionName(), environment.isPollConnection());
1841 ExecutionResult executionResult;
1843 synchronized (myProcessListeners) {
1844 executionResult = environment.createExecutionResult();
1845 myExecutionResult = executionResult;
1846 if (executionResult == null) {
1850 for (ProcessListener processListener : myProcessListeners) {
1851 executionResult.getProcessHandler().addProcessListener(processListener);
1853 myProcessListeners.clear();
1856 catch (ExecutionException e) {
1861 // writing to volatile field ensures the other threads will see the right values in non-volatile fields
1863 if (ApplicationManager.getApplication().isUnitTestMode()) {
1864 return executionResult;
1868 final Alarm debugPortTimeout = new Alarm(Alarm.ThreadToUse.SHARED_THREAD);
1870 myExecutionResult.getProcessHandler().addProcessListener(new ProcessAdapter() {
1871 public void processTerminated(ProcessEvent event) {
1872 debugPortTimeout.cancelAllRequests();
1875 public void startNotified(ProcessEvent event) {
1876 debugPortTimeout.addRequest(new Runnable() {
1878 if(isInInitialState()) {
1879 ApplicationManager.getApplication().schedule(new Runnable() {
1881 String message = DebuggerBundle.message("status.connect.failed", DebuggerBundle.getAddressDisplayName(remoteConnection), DebuggerBundle.getTransportName(remoteConnection));
1882 Messages.showErrorDialog(myProject, message, DebuggerBundle.message("title.generic.debug.dialog"));
1887 }, LOCAL_START_TIMEOUT);
1892 return executionResult;
1895 private void fail() {
1896 // need this in order to prevent calling stop() twice
1897 if (myIsFailed.compareAndSet(false, true)) {
1902 private void createVirtualMachine(final String sessionName, final boolean pollConnection) {
1903 final Semaphore semaphore = new Semaphore();
1906 final AtomicBoolean connectorIsReady = new AtomicBoolean(false);
1907 myDebugProcessDispatcher.addListener(new DebugProcessAdapter() {
1909 public void connectorIsReady() {
1910 connectorIsReady.set(true);
1912 myDebugProcessDispatcher.removeListener(this);
1916 // reload to make sure that source positions are initialized
1917 DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager().reloadBreakpoints();
1919 getManagerThread().schedule(new DebuggerCommandImpl() {
1921 protected void action() {
1922 VirtualMachine vm = null;
1925 final long time = System.currentTimeMillis();
1926 while (System.currentTimeMillis() - time < LOCAL_START_TIMEOUT) {
1928 vm = createVirtualMachineInt();
1931 catch (final ExecutionException e) {
1932 if (pollConnection && !myConnection.isServerMode() && e.getCause() instanceof IOException) {
1933 synchronized (this) {
1937 catch (InterruptedException ignored) {
1944 DebuggerInvocationUtil.swingInvokeLater(myProject, new Runnable() {
1947 // propagate exception only in case we succeeded to obtain execution result,
1948 // otherwise if the error is induced by the fact that there is nothing to debug, and there is no need to show
1949 // this problem to the user
1950 if (myExecutionResult != null || !connectorIsReady.get()) {
1951 ExecutionUtil.handleExecutionError(myProject, ToolWindowId.DEBUG, sessionName, e);
1965 final VirtualMachine vm1 = vm;
1966 afterProcessStarted(new Runnable() {
1969 getManagerThread().schedule(new DebuggerCommandImpl() {
1971 protected void action() throws Exception {
1981 protected void commandCancelled() {
1983 super.commandCancelled();
1991 semaphore.waitFor();
1994 private void afterProcessStarted(final Runnable run) {
1995 class MyProcessAdapter extends ProcessAdapter {
1996 private boolean alreadyRun = false;
1998 public synchronized void run() {
2003 removeProcessListener(this);
2007 public void startNotified(ProcessEvent event) {
2011 MyProcessAdapter processListener = new MyProcessAdapter();
2012 addProcessListener(processListener);
2013 if (myExecutionResult != null) {
2014 if (myExecutionResult.getProcessHandler().isStartNotified()) {
2015 processListener.run();
2020 public boolean isPausePressed() {
2021 final VirtualMachineProxyImpl vm = myVirtualMachineProxy;
2022 return vm != null && vm.isPausePressed();
2025 public DebuggerCommandImpl createPauseCommand() {
2026 return new PauseCommand();
2029 public ResumeCommand createResumeCommand(SuspendContextImpl suspendContext) {
2030 return createResumeCommand(suspendContext, PrioritizedTask.Priority.HIGH);
2033 public ResumeCommand createResumeCommand(SuspendContextImpl suspendContext, final PrioritizedTask.Priority priority) {
2034 final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(getProject()).getBreakpointManager();
2035 return new ResumeCommand(suspendContext) {
2037 public void contextAction() {
2038 breakpointManager.applyThreadFilter(DebugProcessImpl.this, null); // clear the filter on resume
2039 super.contextAction();
2043 public Priority getPriority() {
2049 public ResumeCommand createStepOverCommand(SuspendContextImpl suspendContext, boolean ignoreBreakpoints) {
2050 return createStepOverCommand(suspendContext, ignoreBreakpoints, StepRequest.STEP_LINE);
2053 public ResumeCommand createStepOverCommand(SuspendContextImpl suspendContext, boolean ignoreBreakpoints, int stepSize) {
2054 return new StepOverCommand(suspendContext, ignoreBreakpoints, stepSize);
2057 public ResumeCommand createStepOutCommand(SuspendContextImpl suspendContext) {
2058 return createStepOutCommand(suspendContext, StepRequest.STEP_LINE);
2061 public ResumeCommand createStepOutCommand(SuspendContextImpl suspendContext, int stepSize) {
2062 return new StepOutCommand(suspendContext, stepSize);
2065 public ResumeCommand createStepIntoCommand(SuspendContextImpl suspendContext, boolean ignoreFilters, final MethodFilter smartStepFilter) {
2066 return createStepIntoCommand(suspendContext, ignoreFilters, smartStepFilter, StepRequest.STEP_LINE);
2069 public ResumeCommand createStepIntoCommand(SuspendContextImpl suspendContext, boolean ignoreFilters, final MethodFilter smartStepFilter,
2071 return new StepIntoCommand(suspendContext, ignoreFilters, smartStepFilter, stepSize);
2074 public ResumeCommand createRunToCursorCommand(SuspendContextImpl suspendContext,
2075 @NotNull XSourcePosition position,
2076 boolean ignoreBreakpoints)
2077 throws EvaluateException {
2078 RunToCursorCommand runToCursorCommand = new RunToCursorCommand(suspendContext, position, ignoreBreakpoints);
2079 if (runToCursorCommand.myRunToCursorBreakpoint == null) {
2080 PsiFile psiFile = PsiManager.getInstance(myProject).findFile(position.getFile());
2081 throw new EvaluateException(DebuggerBundle.message("error.running.to.cursor.no.executable.code", psiFile != null? psiFile.getName() : "<No File>",
2082 position.getLine()), null);
2084 return runToCursorCommand;
2087 public DebuggerCommandImpl createFreezeThreadCommand(ThreadReferenceProxyImpl thread) {
2088 return new FreezeThreadCommand(thread);
2091 public SuspendContextCommandImpl createResumeThreadCommand(SuspendContextImpl suspendContext, ThreadReferenceProxyImpl thread) {
2092 return new ResumeThreadCommand(suspendContext, thread);
2095 public SuspendContextCommandImpl createPopFrameCommand(DebuggerContextImpl context, StackFrameProxyImpl stackFrame) {
2096 final SuspendContextImpl contextByThread =
2097 SuspendManagerUtil.findContextByThread(context.getDebugProcess().getSuspendManager(), stackFrame.threadProxy());
2098 return new PopFrameCommand(contextByThread, stackFrame);
2101 //public void setBreakpointsMuted(final boolean muted) {
2102 // XDebugSession session = mySession.getXDebugSession();
2103 // if (isAttached()) {
2104 // getManagerThread().schedule(new DebuggerCommandImpl() {
2106 // protected void action() throws Exception {
2107 // // set the flag before enabling/disabling cause it affects if breakpoints will create requests
2108 // if (myBreakpointsMuted.getAndSet(muted) != muted) {
2109 // final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager();
2111 // breakpointManager.disableBreakpoints(DebugProcessImpl.this);
2114 // breakpointManager.enableBreakpoints(DebugProcessImpl.this);
2121 // session.setBreakpointMuted(muted);
2125 public DebuggerContextImpl getDebuggerContext() {
2126 return mySession.getContextManager().getContext();
2129 public void setXDebugProcess(JavaDebugProcess XDebugProcess) {
2130 myXDebugProcess = XDebugProcess;
2134 public JavaDebugProcess getXdebugProcess() {
2135 return myXDebugProcess;
2138 public boolean areBreakpointsMuted() {
2139 XDebugSession session = mySession.getXDebugSession();
2140 return session != null && session.areBreakpointsMuted();
2143 public DebuggerSession getSession() {