| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.intellij.debugger.engine; |
| |
| import com.intellij.Patches; |
| import com.intellij.debugger.*; |
| import com.intellij.debugger.actions.DebuggerAction; |
| import com.intellij.debugger.actions.DebuggerActions; |
| import com.intellij.debugger.apiAdapters.ConnectionServiceWrapper; |
| import com.intellij.debugger.engine.evaluation.*; |
| import com.intellij.debugger.engine.events.DebuggerCommandImpl; |
| import com.intellij.debugger.engine.events.SuspendContextCommandImpl; |
| import com.intellij.debugger.engine.jdi.ThreadReferenceProxy; |
| import com.intellij.debugger.engine.requests.MethodReturnValueWatcher; |
| import com.intellij.debugger.engine.requests.RequestManagerImpl; |
| import com.intellij.debugger.impl.DebuggerContextImpl; |
| import com.intellij.debugger.impl.DebuggerSession; |
| import com.intellij.debugger.impl.DebuggerUtilsEx; |
| import com.intellij.debugger.impl.PrioritizedTask; |
| import com.intellij.debugger.jdi.StackFrameProxyImpl; |
| import com.intellij.debugger.jdi.ThreadReferenceProxyImpl; |
| import com.intellij.debugger.jdi.VirtualMachineProxyImpl; |
| import com.intellij.debugger.settings.DebuggerSettings; |
| import com.intellij.debugger.settings.NodeRendererSettings; |
| import com.intellij.debugger.ui.breakpoints.BreakpointManager; |
| import com.intellij.debugger.ui.breakpoints.RunToCursorBreakpoint; |
| import com.intellij.debugger.ui.breakpoints.StepIntoBreakpoint; |
| import com.intellij.debugger.ui.tree.ValueDescriptor; |
| import com.intellij.debugger.ui.tree.render.*; |
| import com.intellij.execution.CantRunException; |
| import com.intellij.execution.ExecutionException; |
| import com.intellij.execution.ExecutionResult; |
| import com.intellij.execution.configurations.RemoteConnection; |
| import com.intellij.execution.process.*; |
| import com.intellij.execution.runners.ExecutionUtil; |
| import com.intellij.idea.ActionsBundle; |
| import com.intellij.openapi.Disposable; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.editor.Document; |
| import com.intellij.openapi.extensions.Extensions; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.ui.Messages; |
| import com.intellij.openapi.util.Disposer; |
| import com.intellij.openapi.util.Pair; |
| import com.intellij.openapi.util.Ref; |
| import com.intellij.openapi.util.UserDataHolderBase; |
| import com.intellij.openapi.wm.ToolWindowId; |
| import com.intellij.openapi.wm.WindowManager; |
| import com.intellij.psi.PsiDocumentManager; |
| import com.intellij.psi.PsiFile; |
| import com.intellij.psi.search.GlobalSearchScope; |
| import com.intellij.ui.classFilter.ClassFilter; |
| import com.intellij.ui.classFilter.DebuggerClassFilterProvider; |
| import com.intellij.util.Alarm; |
| import com.intellij.util.EventDispatcher; |
| import com.intellij.util.StringBuilderSpinAllocator; |
| import com.intellij.util.concurrency.Semaphore; |
| import com.intellij.util.containers.ContainerUtil; |
| import com.intellij.util.containers.HashMap; |
| import com.intellij.xdebugger.XDebugSession; |
| import com.sun.jdi.*; |
| import com.sun.jdi.connect.*; |
| import com.sun.jdi.request.EventRequest; |
| import com.sun.jdi.request.EventRequestManager; |
| import com.sun.jdi.request.StepRequest; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import javax.swing.*; |
| import java.io.IOException; |
| import java.net.UnknownHostException; |
| import java.util.*; |
| import java.util.concurrent.atomic.AtomicInteger; |
| |
| public abstract class DebugProcessImpl extends UserDataHolderBase implements DebugProcess { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.DebugProcessImpl"); |
| |
| @NonNls private static final String SOCKET_ATTACHING_CONNECTOR_NAME = "com.sun.jdi.SocketAttach"; |
| @NonNls private static final String SHMEM_ATTACHING_CONNECTOR_NAME = "com.sun.jdi.SharedMemoryAttach"; |
| @NonNls private static final String SOCKET_LISTENING_CONNECTOR_NAME = "com.sun.jdi.SocketListen"; |
| @NonNls private static final String SHMEM_LISTENING_CONNECTOR_NAME = "com.sun.jdi.SharedMemoryListen"; |
| |
| private final Project myProject; |
| private final RequestManagerImpl myRequestManager; |
| |
| private volatile VirtualMachineProxyImpl myVirtualMachineProxy = null; |
| protected EventDispatcher<DebugProcessListener> myDebugProcessDispatcher = EventDispatcher.create(DebugProcessListener.class); |
| protected EventDispatcher<EvaluationListener> myEvaluationDispatcher = EventDispatcher.create(EvaluationListener.class); |
| |
| private final List<ProcessListener> myProcessListeners = ContainerUtil.createLockFreeCopyOnWriteList(); |
| |
| protected static final int STATE_INITIAL = 0; |
| protected static final int STATE_ATTACHED = 1; |
| protected static final int STATE_DETACHING = 2; |
| protected static final int STATE_DETACHED = 3; |
| protected final AtomicInteger myState = new AtomicInteger(STATE_INITIAL); |
| |
| private ExecutionResult myExecutionResult; |
| private RemoteConnection myConnection; |
| |
| private ConnectionServiceWrapper myConnectionService; |
| private Map<String, Connector.Argument> myArguments; |
| |
| private final List<NodeRenderer> myRenderers = new ArrayList<NodeRenderer>(); |
| |
| // we use null key here |
| private final Map<Type, NodeRenderer> myNodeRenderersMap = new HashMap<Type, NodeRenderer>(); |
| |
| private final NodeRendererSettingsListener mySettingsListener = new NodeRendererSettingsListener() { |
| @Override |
| public void renderersChanged() { |
| myNodeRenderersMap.clear(); |
| myRenderers.clear(); |
| loadRenderers(); |
| } |
| }; |
| |
| private final SuspendManagerImpl mySuspendManager = new SuspendManagerImpl(this); |
| protected CompoundPositionManager myPositionManager = null; |
| private volatile DebuggerManagerThreadImpl myDebuggerManagerThread; |
| private static final int LOCAL_START_TIMEOUT = 30000; |
| |
| private final Semaphore myWaitFor = new Semaphore(); |
| private boolean myIsFailed = false; |
| protected DebuggerSession mySession; |
| @Nullable protected MethodReturnValueWatcher myReturnValueWatcher; |
| private final Disposable myDisposable = Disposer.newDisposable(); |
| private final Alarm myStatusUpdateAlarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD, myDisposable); |
| |
| protected DebugProcessImpl(Project project) { |
| myProject = project; |
| myRequestManager = new RequestManagerImpl(this); |
| NodeRendererSettings.getInstance().addListener(mySettingsListener); |
| loadRenderers(); |
| } |
| |
| private void loadRenderers() { |
| getManagerThread().invoke(new DebuggerCommandImpl() { |
| @Override |
| protected void action() throws Exception { |
| try { |
| final NodeRendererSettings rendererSettings = NodeRendererSettings.getInstance(); |
| for (final NodeRenderer renderer : rendererSettings.getAllRenderers()) { |
| if (renderer.isEnabled()) { |
| myRenderers.add(renderer); |
| } |
| } |
| } |
| finally { |
| DebuggerInvocationUtil.swingInvokeLater(myProject, new Runnable() { |
| @Override |
| public void run() { |
| final DebuggerSession session = mySession; |
| if (session != null && session.isAttached()) { |
| session.refresh(true); |
| DebuggerAction.refreshViews(mySession.getXDebugSession()); |
| } |
| } |
| }); |
| } |
| } |
| }); |
| } |
| |
| @Nullable |
| public Pair<Method, Value> getLastExecutedMethod() { |
| final MethodReturnValueWatcher watcher = myReturnValueWatcher; |
| if (watcher == null) { |
| return null; |
| } |
| final Method method = watcher.getLastExecutedMethod(); |
| if (method == null) { |
| return null; |
| } |
| return Pair.create(method, watcher.getLastMethodReturnValue()); |
| } |
| |
| public void setWatchMethodReturnValuesEnabled(boolean enabled) { |
| final MethodReturnValueWatcher watcher = myReturnValueWatcher; |
| if (watcher != null) { |
| watcher.setFeatureEnabled(enabled); |
| } |
| } |
| |
| public boolean isWatchMethodReturnValuesEnabled() { |
| final MethodReturnValueWatcher watcher = myReturnValueWatcher; |
| return watcher != null && watcher.isFeatureEnabled(); |
| } |
| |
| public boolean canGetMethodReturnValue() { |
| return myReturnValueWatcher != null; |
| } |
| |
| public NodeRenderer getAutoRenderer(ValueDescriptor descriptor) { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| final Value value = descriptor.getValue(); |
| Type type = value != null ? value.type() : null; |
| |
| // in case evaluation is not possible, force default renderer |
| if (!DebuggerManagerEx.getInstanceEx(getProject()).getContext().isEvaluationPossible()) { |
| return getDefaultRenderer(type); |
| } |
| |
| NodeRenderer renderer = myNodeRenderersMap.get(type); |
| if(renderer == null) { |
| for (final NodeRenderer nodeRenderer : myRenderers) { |
| if (nodeRenderer.isApplicable(type)) { |
| renderer = nodeRenderer; |
| break; |
| } |
| } |
| if (renderer == null) { |
| renderer = getDefaultRenderer(type); |
| } |
| myNodeRenderersMap.put(type, renderer); |
| } |
| |
| return renderer; |
| } |
| |
| public static NodeRenderer getDefaultRenderer(Value value) { |
| return getDefaultRenderer(value != null ? value.type() : null); |
| } |
| |
| public static NodeRenderer getDefaultRenderer(Type type) { |
| final NodeRendererSettings settings = NodeRendererSettings.getInstance(); |
| |
| final PrimitiveRenderer primitiveRenderer = settings.getPrimitiveRenderer(); |
| if(primitiveRenderer.isApplicable(type)) { |
| return primitiveRenderer; |
| } |
| |
| final ArrayRenderer arrayRenderer = settings.getArrayRenderer(); |
| if(arrayRenderer.isApplicable(type)) { |
| return arrayRenderer; |
| } |
| |
| final ClassRenderer classRenderer = settings.getClassRenderer(); |
| LOG.assertTrue(classRenderer.isApplicable(type), type.name()); |
| return classRenderer; |
| } |
| |
| |
| @SuppressWarnings({"HardCodedStringLiteral"}) |
| protected void commitVM(VirtualMachine vm) { |
| if (!isInInitialState()) { |
| LOG.error("State is invalid " + myState.get()); |
| } |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| myPositionManager = createPositionManager(); |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("*******************VM attached******************"); |
| } |
| checkVirtualMachineVersion(vm); |
| |
| myVirtualMachineProxy = new VirtualMachineProxyImpl(this, vm); |
| |
| String trace = System.getProperty("idea.debugger.trace"); |
| if (trace != null) { |
| int mask = 0; |
| StringTokenizer tokenizer = new StringTokenizer(trace); |
| while (tokenizer.hasMoreTokens()) { |
| String token = tokenizer.nextToken(); |
| if ("SENDS".compareToIgnoreCase(token) == 0) { |
| mask |= VirtualMachine.TRACE_SENDS; |
| } |
| else if ("RAW_SENDS".compareToIgnoreCase(token) == 0) { |
| mask |= 0x01000000; |
| } |
| else if ("RECEIVES".compareToIgnoreCase(token) == 0) { |
| mask |= VirtualMachine.TRACE_RECEIVES; |
| } |
| else if ("RAW_RECEIVES".compareToIgnoreCase(token) == 0) { |
| mask |= 0x02000000; |
| } |
| else if ("EVENTS".compareToIgnoreCase(token) == 0) { |
| mask |= VirtualMachine.TRACE_EVENTS; |
| } |
| else if ("REFTYPES".compareToIgnoreCase(token) == 0) { |
| mask |= VirtualMachine.TRACE_REFTYPES; |
| } |
| else if ("OBJREFS".compareToIgnoreCase(token) == 0) { |
| mask |= VirtualMachine.TRACE_OBJREFS; |
| } |
| else if ("ALL".compareToIgnoreCase(token) == 0) { |
| mask |= VirtualMachine.TRACE_ALL; |
| } |
| } |
| |
| vm.setDebugTraceMode(mask); |
| } |
| } |
| |
| private void stopConnecting() { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| |
| Map<String, Connector.Argument> arguments = myArguments; |
| try { |
| if (arguments == null) { |
| return; |
| } |
| if(myConnection.isServerMode()) { |
| ListeningConnector connector = (ListeningConnector)findConnector(SOCKET_LISTENING_CONNECTOR_NAME); |
| if (connector == null) { |
| LOG.error("Cannot find connector: " + SOCKET_LISTENING_CONNECTOR_NAME); |
| } |
| connector.stopListening(arguments); |
| } |
| else { |
| if(myConnectionService != null) { |
| myConnectionService.close(); |
| } |
| } |
| } |
| catch (IOException e) { |
| if (LOG.isDebugEnabled()) { |
| LOG.debug(e); |
| } |
| } |
| catch (IllegalConnectorArgumentsException e) { |
| if (LOG.isDebugEnabled()) { |
| LOG.debug(e); |
| } |
| } |
| catch (ExecutionException e) { |
| LOG.error(e); |
| } |
| finally { |
| closeProcess(true); |
| } |
| } |
| |
| protected CompoundPositionManager createPositionManager() { |
| return new CompoundPositionManager(new PositionManagerImpl(this)); |
| } |
| |
| @Override |
| public void printToConsole(final String text) { |
| myExecutionResult.getProcessHandler().notifyTextAvailable(text, ProcessOutputTypes.SYSTEM); |
| } |
| |
| @Override |
| public ProcessHandler getProcessHandler() { |
| return myExecutionResult.getProcessHandler(); |
| } |
| |
| /** |
| * |
| * @param suspendContext |
| * @param stepThread |
| * @param depth |
| * @param hint may be null |
| */ |
| protected void doStep(final SuspendContextImpl suspendContext, final ThreadReferenceProxyImpl stepThread, int depth, RequestHint hint) { |
| if (stepThread == null) { |
| return; |
| } |
| try { |
| final ThreadReference stepThreadReference = stepThread.getThreadReference(); |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("DO_STEP: creating step request for " + stepThreadReference); |
| } |
| deleteStepRequests(stepThreadReference); |
| EventRequestManager requestManager = getVirtualMachineProxy().eventRequestManager(); |
| StepRequest stepRequest = requestManager.createStepRequest(stepThreadReference, StepRequest.STEP_LINE, depth); |
| DebuggerSettings settings = DebuggerSettings.getInstance(); |
| if (!(hint != null && hint.isIgnoreFilters()) /*&& depth == StepRequest.STEP_INTO*/) { |
| final List<ClassFilter> activeFilters = new ArrayList<ClassFilter>(); |
| if (settings.TRACING_FILTERS_ENABLED) { |
| for (ClassFilter filter : settings.getSteppingFilters()) { |
| if (filter.isEnabled()) { |
| activeFilters.add(filter); |
| } |
| } |
| } |
| for (DebuggerClassFilterProvider provider : Extensions.getExtensions(DebuggerClassFilterProvider.EP_NAME)) { |
| for (ClassFilter filter : provider.getFilters()) { |
| if (filter.isEnabled()) { |
| activeFilters.add(filter); |
| } |
| } |
| } |
| |
| if (!activeFilters.isEmpty()) { |
| final String currentClassName = getCurrentClassName(stepThread); |
| if (currentClassName == null || !DebuggerUtilsEx.isFiltered(currentClassName, activeFilters)) { |
| // add class filters |
| for (ClassFilter filter : activeFilters) { |
| stepRequest.addClassExclusionFilter(filter.getPattern()); |
| } |
| } |
| } |
| } |
| |
| // suspend policy to match the suspend policy of the context: |
| // if all threads were suspended, then during stepping all the threads must be suspended |
| // if only event thread were suspended, then only this particular thread must be suspended during stepping |
| stepRequest.setSuspendPolicy(suspendContext.getSuspendPolicy() == EventRequest.SUSPEND_EVENT_THREAD? EventRequest.SUSPEND_EVENT_THREAD : EventRequest.SUSPEND_ALL); |
| |
| if (hint != null) { |
| //noinspection HardCodedStringLiteral |
| stepRequest.putProperty("hint", hint); |
| } |
| stepRequest.enable(); |
| } |
| catch (ObjectCollectedException ignored) { |
| |
| } |
| } |
| |
| void deleteStepRequests(@Nullable final ThreadReference stepThread) { |
| EventRequestManager requestManager = getVirtualMachineProxy().eventRequestManager(); |
| List<StepRequest> stepRequests = requestManager.stepRequests(); |
| if (!stepRequests.isEmpty()) { |
| final List<StepRequest> toDelete = new ArrayList<StepRequest>(stepRequests.size()); |
| for (final StepRequest request : stepRequests) { |
| ThreadReference threadReference = request.thread(); |
| // [jeka] on attempt to delete a request assigned to a thread with unknown status, a JDWP error occurs |
| try { |
| if (threadReference.status() != ThreadReference.THREAD_STATUS_UNKNOWN && (stepThread == null || stepThread.equals(threadReference))) { |
| toDelete.add(request); |
| } |
| } |
| catch (IllegalThreadStateException e) { |
| LOG.info(e); // undocumented by JDI: may be thrown when querying thread status |
| } |
| } |
| requestManager.deleteEventRequests(toDelete); |
| } |
| } |
| |
| @Nullable |
| private static String getCurrentClassName(ThreadReferenceProxyImpl thread) { |
| try { |
| if (thread != null && thread.frameCount() > 0) { |
| StackFrameProxyImpl stackFrame = thread.frame(0); |
| if (stackFrame != null) { |
| Location location = stackFrame.location(); |
| ReferenceType referenceType = location == null ? null : location.declaringType(); |
| if (referenceType != null) { |
| return referenceType.name(); |
| } |
| } |
| } |
| } |
| catch (EvaluateException ignored) { |
| } |
| return null; |
| } |
| |
| private VirtualMachine createVirtualMachineInt() throws ExecutionException { |
| try { |
| if (myArguments != null) { |
| throw new IOException(DebuggerBundle.message("error.debugger.already.listening")); |
| } |
| |
| final String address = myConnection.getAddress(); |
| if (myConnection.isServerMode()) { |
| ListeningConnector connector = (ListeningConnector)findConnector( |
| myConnection.isUseSockets() ? SOCKET_LISTENING_CONNECTOR_NAME : SHMEM_LISTENING_CONNECTOR_NAME); |
| if (connector == null) { |
| throw new CantRunException(DebuggerBundle.message("error.debug.connector.not.found", DebuggerBundle.getTransportName(myConnection))); |
| } |
| myArguments = connector.defaultArguments(); |
| if (myArguments == null) { |
| throw new CantRunException(DebuggerBundle.message("error.no.debug.listen.port")); |
| } |
| |
| if (address == null) { |
| throw new CantRunException(DebuggerBundle.message("error.no.debug.listen.port")); |
| } |
| // negative port number means the caller leaves to debugger to decide at which port to listen |
| //noinspection HardCodedStringLiteral |
| final Connector.Argument portArg = myConnection.isUseSockets() ? myArguments.get("port") : myArguments.get("name"); |
| if (portArg != null) { |
| portArg.setValue(address); |
| } |
| //noinspection HardCodedStringLiteral |
| final Connector.Argument timeoutArg = myArguments.get("timeout"); |
| if (timeoutArg != null) { |
| timeoutArg.setValue("0"); // wait forever |
| } |
| connector.startListening(myArguments); |
| myDebugProcessDispatcher.getMulticaster().connectorIsReady(); |
| try { |
| return connector.accept(myArguments); |
| } |
| finally { |
| if(myArguments != null) { |
| try { |
| connector.stopListening(myArguments); |
| } |
| catch (IllegalArgumentException ignored) { |
| // ignored |
| } |
| catch (IllegalConnectorArgumentsException ignored) { |
| // ignored |
| } |
| } |
| } |
| } |
| else { // is client mode, should attach to already running process |
| AttachingConnector connector = (AttachingConnector)findConnector( |
| myConnection.isUseSockets() ? SOCKET_ATTACHING_CONNECTOR_NAME : SHMEM_ATTACHING_CONNECTOR_NAME |
| ); |
| |
| if (connector == null) { |
| throw new CantRunException( DebuggerBundle.message("error.debug.connector.not.found", DebuggerBundle.getTransportName(myConnection))); |
| } |
| myArguments = connector.defaultArguments(); |
| if (myConnection.isUseSockets()) { |
| //noinspection HardCodedStringLiteral |
| final Connector.Argument hostnameArg = myArguments.get("hostname"); |
| if (hostnameArg != null && myConnection.getHostName() != null) { |
| hostnameArg.setValue(myConnection.getHostName()); |
| } |
| if (address == null) { |
| throw new CantRunException(DebuggerBundle.message("error.no.debug.attach.port")); |
| } |
| //noinspection HardCodedStringLiteral |
| final Connector.Argument portArg = myArguments.get("port"); |
| if (portArg != null) { |
| portArg.setValue(address); |
| } |
| } |
| else { |
| if (address == null) { |
| throw new CantRunException(DebuggerBundle.message("error.no.shmem.address")); |
| } |
| //noinspection HardCodedStringLiteral |
| final Connector.Argument nameArg = myArguments.get("name"); |
| if (nameArg != null) { |
| nameArg.setValue(address); |
| } |
| } |
| //noinspection HardCodedStringLiteral |
| final Connector.Argument timeoutArg = myArguments.get("timeout"); |
| if (timeoutArg != null) { |
| timeoutArg.setValue("0"); // wait forever |
| } |
| |
| myDebugProcessDispatcher.getMulticaster().connectorIsReady(); |
| try { |
| return connector.attach(myArguments); |
| } |
| catch (IllegalArgumentException e) { |
| throw new CantRunException(e.getLocalizedMessage()); |
| } |
| } |
| } |
| catch (IOException e) { |
| throw new ExecutionException(processError(e), e); |
| } |
| catch (IllegalConnectorArgumentsException e) { |
| throw new ExecutionException(processError(e), e); |
| } |
| finally { |
| myArguments = null; |
| myConnectionService = null; |
| } |
| } |
| |
| public void showStatusText(final String text) { |
| if (!myStatusUpdateAlarm.isDisposed()) { |
| myStatusUpdateAlarm.cancelAllRequests(); |
| myStatusUpdateAlarm.addRequest(new Runnable() { |
| @Override |
| public void run() { |
| final WindowManager wm = WindowManager.getInstance(); |
| if (wm != null) { |
| wm.getStatusBar(myProject).setInfo(text); |
| } |
| } |
| }, 50); |
| } |
| } |
| |
| static Connector findConnector(String connectorName) throws ExecutionException { |
| VirtualMachineManager virtualMachineManager; |
| try { |
| virtualMachineManager = Bootstrap.virtualMachineManager(); |
| } |
| catch (Error e) { |
| final String error = e.getClass().getName() + " : " + e.getLocalizedMessage(); |
| throw new ExecutionException(DebuggerBundle.message("debugger.jdi.bootstrap.error", error)); |
| } |
| List connectors; |
| if (SOCKET_ATTACHING_CONNECTOR_NAME.equals(connectorName) || SHMEM_ATTACHING_CONNECTOR_NAME.equals(connectorName)) { |
| connectors = virtualMachineManager.attachingConnectors(); |
| } |
| else if (SOCKET_LISTENING_CONNECTOR_NAME.equals(connectorName) || SHMEM_LISTENING_CONNECTOR_NAME.equals(connectorName)) { |
| connectors = virtualMachineManager.listeningConnectors(); |
| } |
| else { |
| return null; |
| } |
| for (Object connector1 : connectors) { |
| Connector connector = (Connector)connector1; |
| if (connectorName.equals(connector.name())) { |
| return connector; |
| } |
| } |
| return null; |
| } |
| |
| private void checkVirtualMachineVersion(VirtualMachine vm) { |
| final String version = vm.version(); |
| if ("1.4.0".equals(version)) { |
| SwingUtilities.invokeLater(new Runnable() { |
| @Override |
| public void run() { |
| Messages.showMessageDialog( |
| getProject(), |
| DebuggerBundle.message("warning.jdk140.unstable"), DebuggerBundle.message("title.jdk140.unstable"), Messages.getWarningIcon() |
| ); |
| } |
| }); |
| } |
| } |
| |
| /*Event dispatching*/ |
| public void addEvaluationListener(EvaluationListener evaluationListener) { |
| myEvaluationDispatcher.addListener(evaluationListener); |
| } |
| |
| public void removeEvaluationListener(EvaluationListener evaluationListener) { |
| myEvaluationDispatcher.removeListener(evaluationListener); |
| } |
| |
| @Override |
| public void addDebugProcessListener(DebugProcessListener listener) { |
| myDebugProcessDispatcher.addListener(listener); |
| } |
| |
| @Override |
| public void removeDebugProcessListener(DebugProcessListener listener) { |
| myDebugProcessDispatcher.removeListener(listener); |
| } |
| |
| public void addProcessListener(ProcessListener processListener) { |
| synchronized(myProcessListeners) { |
| if(getProcessHandler() != null) { |
| getProcessHandler().addProcessListener(processListener); |
| } |
| else { |
| myProcessListeners.add(processListener); |
| } |
| } |
| } |
| |
| public void removeProcessListener(ProcessListener processListener) { |
| synchronized (myProcessListeners) { |
| if(getProcessHandler() != null) { |
| getProcessHandler().removeProcessListener(processListener); |
| } |
| else { |
| myProcessListeners.remove(processListener); |
| } |
| } |
| } |
| |
| /* getters */ |
| public RemoteConnection getConnection() { |
| return myConnection; |
| } |
| |
| @Override |
| public ExecutionResult getExecutionResult() { |
| return myExecutionResult; |
| } |
| |
| @Override |
| public Project getProject() { |
| return myProject; |
| } |
| |
| public boolean canRedefineClasses() { |
| final VirtualMachineProxyImpl vm = myVirtualMachineProxy; |
| return vm != null && vm.canRedefineClasses(); |
| } |
| |
| public boolean canWatchFieldModification() { |
| final VirtualMachineProxyImpl vm = myVirtualMachineProxy; |
| return vm != null && vm.canWatchFieldModification(); |
| } |
| |
| public boolean isInInitialState() { |
| return myState.get() == STATE_INITIAL; |
| } |
| |
| @Override |
| public boolean isAttached() { |
| return myState.get() == STATE_ATTACHED; |
| } |
| |
| @Override |
| public boolean isDetached() { |
| return myState.get() == STATE_DETACHED; |
| } |
| |
| @Override |
| public boolean isDetaching() { |
| return myState.get() == STATE_DETACHING; |
| } |
| |
| @Override |
| public RequestManagerImpl getRequestsManager() { |
| return myRequestManager; |
| } |
| |
| @Override |
| public VirtualMachineProxyImpl getVirtualMachineProxy() { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| final VirtualMachineProxyImpl vm = myVirtualMachineProxy; |
| if (vm == null) { |
| throw new VMDisconnectedException(); |
| } |
| return vm; |
| } |
| |
| @Override |
| public void appendPositionManager(final PositionManager positionManager) { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| myPositionManager.appendPositionManager(positionManager); |
| } |
| |
| private volatile RunToCursorBreakpoint myRunToCursorBreakpoint; |
| |
| public void cancelRunToCursorBreakpoint() { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| final RunToCursorBreakpoint runToCursorBreakpoint = myRunToCursorBreakpoint; |
| if (runToCursorBreakpoint != null) { |
| myRunToCursorBreakpoint = null; |
| getRequestsManager().deleteRequest(runToCursorBreakpoint); |
| runToCursorBreakpoint.delete(); |
| if (runToCursorBreakpoint.isRestoreBreakpoints()) { |
| final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(getProject()).getBreakpointManager(); |
| breakpointManager.enableBreakpoints(this); |
| } |
| } |
| } |
| |
| protected void closeProcess(boolean closedByUser) { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| |
| if (myState.compareAndSet(STATE_INITIAL, STATE_DETACHING) || myState.compareAndSet(STATE_ATTACHED, STATE_DETACHING)) { |
| try { |
| getManagerThread().close(); |
| } |
| finally { |
| final VirtualMachineProxyImpl vm = myVirtualMachineProxy; |
| myVirtualMachineProxy = null; |
| myPositionManager = null; |
| myReturnValueWatcher = null; |
| myNodeRenderersMap.clear(); |
| myRenderers.clear(); |
| DebuggerUtils.cleanupAfterProcessFinish(this); |
| myState.set(STATE_DETACHED); |
| try { |
| myDebugProcessDispatcher.getMulticaster().processDetached(this, closedByUser); |
| } |
| finally { |
| //if (DebuggerSettings.getInstance().UNMUTE_ON_STOP) { |
| // XDebugSession session = mySession.getXDebugSession(); |
| // if (session != null) { |
| // session.setBreakpointMuted(false); |
| // } |
| //} |
| if (vm != null) { |
| try { |
| vm.dispose(); // to be on the safe side ensure that VM mirror, if present, is disposed and invalidated |
| } |
| catch (Throwable ignored) { |
| } |
| } |
| myWaitFor.up(); |
| } |
| } |
| |
| } |
| } |
| |
| private static String formatMessage(String message) { |
| final int lineLength = 90; |
| StringBuilder buf = new StringBuilder(message.length()); |
| int index = 0; |
| while (index < message.length()) { |
| buf.append(message.substring(index, Math.min(index + lineLength, message.length()))).append('\n'); |
| index += lineLength; |
| } |
| return buf.toString(); |
| } |
| |
| public static String processError(Exception e) { |
| String message; |
| |
| if (e instanceof VMStartException) { |
| VMStartException e1 = (VMStartException)e; |
| message = e1.getLocalizedMessage(); |
| } |
| else if (e instanceof IllegalConnectorArgumentsException) { |
| IllegalConnectorArgumentsException e1 = (IllegalConnectorArgumentsException)e; |
| final List<String> invalidArgumentNames = e1.argumentNames(); |
| message = formatMessage(DebuggerBundle.message("error.invalid.argument", invalidArgumentNames.size()) + ": "+ e1.getLocalizedMessage()) + invalidArgumentNames; |
| if (LOG.isDebugEnabled()) { |
| LOG.debug(e1); |
| } |
| } |
| else if (e instanceof CantRunException) { |
| message = e.getLocalizedMessage(); |
| } |
| else if (e instanceof VMDisconnectedException) { |
| message = DebuggerBundle.message("error.vm.disconnected"); |
| } |
| else if (e instanceof UnknownHostException) { |
| message = DebuggerBundle.message("error.unknown.host") + ":\n" + e.getLocalizedMessage(); |
| } |
| else if (e instanceof IOException) { |
| IOException e1 = (IOException)e; |
| final StringBuilder buf = StringBuilderSpinAllocator.alloc(); |
| try { |
| buf.append(DebuggerBundle.message("error.cannot.open.debugger.port")).append(" : "); |
| buf.append(e1.getClass().getName()).append(" "); |
| final String localizedMessage = e1.getLocalizedMessage(); |
| if (localizedMessage != null && !localizedMessage.isEmpty()) { |
| buf.append('"'); |
| buf.append(localizedMessage); |
| buf.append('"'); |
| } |
| if (LOG.isDebugEnabled()) { |
| LOG.debug(e1); |
| } |
| message = buf.toString(); |
| } |
| finally { |
| StringBuilderSpinAllocator.dispose(buf); |
| } |
| } |
| else if (e instanceof ExecutionException) { |
| message = e.getLocalizedMessage(); |
| } |
| else { |
| message = DebuggerBundle.message("error.exception.while.connecting", e.getClass().getName(), e.getLocalizedMessage()); |
| if (LOG.isDebugEnabled()) { |
| LOG.debug(e); |
| } |
| } |
| return message; |
| } |
| |
| public void dispose() { |
| NodeRendererSettings.getInstance().removeListener(mySettingsListener); |
| Disposer.dispose(myDisposable); |
| myRequestManager.setFilterThread(null); |
| } |
| |
| @Override |
| public DebuggerManagerThreadImpl getManagerThread() { |
| if (myDebuggerManagerThread == null) { |
| synchronized (this) { |
| if (myDebuggerManagerThread == null) { |
| myDebuggerManagerThread = new DebuggerManagerThreadImpl(myDisposable); |
| } |
| } |
| } |
| return myDebuggerManagerThread; |
| } |
| |
| private static int getInvokePolicy(SuspendContext suspendContext) { |
| //return ThreadReference.INVOKE_SINGLE_THREADED; |
| return suspendContext.getSuspendPolicy() == EventRequest.SUSPEND_EVENT_THREAD ? ObjectReference.INVOKE_SINGLE_THREADED : 0; |
| } |
| |
| @Override |
| public void waitFor() { |
| LOG.assertTrue(!DebuggerManagerThreadImpl.isManagerThread()); |
| myWaitFor.waitFor(); |
| } |
| |
| @Override |
| public void waitFor(long timeout) { |
| LOG.assertTrue(!DebuggerManagerThreadImpl.isManagerThread()); |
| myWaitFor.waitFor(timeout); |
| } |
| |
| private abstract class InvokeCommand <E extends Value> { |
| private final List myArgs; |
| |
| protected InvokeCommand(List args) { |
| if (!args.isEmpty()) { |
| myArgs = new ArrayList(args); |
| } |
| else { |
| myArgs = args; |
| } |
| } |
| |
| public String toString() { |
| return "INVOKE: " + super.toString(); |
| } |
| |
| protected abstract E invokeMethod(int invokePolicy, final List args) throws InvocationException, |
| ClassNotLoadedException, |
| IncompatibleThreadStateException, |
| InvalidTypeException; |
| |
| |
| E start(EvaluationContextImpl evaluationContext, Method method, boolean internalEvaluate) throws EvaluateException { |
| while (true) { |
| try { |
| return startInternal(evaluationContext, method, internalEvaluate); |
| } |
| catch (ClassNotLoadedException e) { |
| ReferenceType loadedClass = null; |
| try { |
| if (evaluationContext.isAutoLoadClasses()) { |
| loadedClass = loadClass(evaluationContext, e.className(), evaluationContext.getClassLoader()); |
| } |
| } |
| catch (Exception ignored) { |
| loadedClass = null; |
| } |
| if (loadedClass == null) { |
| throw EvaluateExceptionUtil.createEvaluateException(e); |
| } |
| } |
| } |
| } |
| |
| E startInternal(EvaluationContextImpl evaluationContext, Method method, boolean internalEvaluate) |
| throws EvaluateException, ClassNotLoadedException { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| SuspendContextImpl suspendContext = evaluationContext.getSuspendContext(); |
| SuspendManagerUtil.assertSuspendContext(suspendContext); |
| |
| ThreadReferenceProxyImpl invokeThread = suspendContext.getThread(); |
| |
| if (SuspendManagerUtil.isEvaluating(getSuspendManager(), invokeThread)) { |
| throw EvaluateExceptionUtil.NESTED_EVALUATION_ERROR; |
| } |
| |
| Set<SuspendContextImpl> suspendingContexts = SuspendManagerUtil.getSuspendingContexts(getSuspendManager(), invokeThread); |
| final ThreadReference invokeThreadRef = invokeThread.getThreadReference(); |
| |
| myEvaluationDispatcher.getMulticaster().evaluationStarted(suspendContext); |
| beforeMethodInvocation(suspendContext, method, internalEvaluate); |
| |
| Object resumeData = null; |
| try { |
| for (SuspendContextImpl suspendingContext : suspendingContexts) { |
| final ThreadReferenceProxyImpl suspendContextThread = suspendingContext.getThread(); |
| if (suspendContextThread != invokeThread) { |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("Resuming " + invokeThread + " that is paused by " + suspendContextThread); |
| } |
| LOG.assertTrue(suspendContextThread == null || !invokeThreadRef.equals(suspendContextThread.getThreadReference())); |
| getSuspendManager().resumeThread(suspendingContext, invokeThread); |
| } |
| } |
| |
| resumeData = SuspendManagerUtil.prepareForResume(suspendContext); |
| suspendContext.setIsEvaluating(evaluationContext); |
| |
| getVirtualMachineProxy().clearCaches(); |
| |
| return invokeMethodAndFork(suspendContext); |
| } |
| catch (InvocationException e) { |
| throw EvaluateExceptionUtil.createEvaluateException(e); |
| } |
| catch (IncompatibleThreadStateException e) { |
| throw EvaluateExceptionUtil.createEvaluateException(e); |
| } |
| catch (InvalidTypeException e) { |
| throw EvaluateExceptionUtil.createEvaluateException(e); |
| } |
| catch (ObjectCollectedException e) { |
| throw EvaluateExceptionUtil.createEvaluateException(e); |
| } |
| catch (UnsupportedOperationException e) { |
| throw EvaluateExceptionUtil.createEvaluateException(e); |
| } |
| catch (InternalException e) { |
| throw EvaluateExceptionUtil.createEvaluateException(e); |
| } |
| finally { |
| suspendContext.setIsEvaluating(null); |
| if (resumeData != null) { |
| SuspendManagerUtil.restoreAfterResume(suspendContext, resumeData); |
| } |
| for (SuspendContextImpl suspendingContext : mySuspendManager.getEventContexts()) { |
| if (suspendingContexts.contains(suspendingContext) && !suspendingContext.isEvaluating() && !suspendingContext.suspends(invokeThread)) { |
| mySuspendManager.suspendThread(suspendingContext, invokeThread); |
| } |
| } |
| |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("getVirtualMachine().clearCaches()"); |
| } |
| getVirtualMachineProxy().clearCaches(); |
| afterMethodInvocation(suspendContext, internalEvaluate); |
| |
| myEvaluationDispatcher.getMulticaster().evaluationFinished(suspendContext); |
| } |
| } |
| |
| private E invokeMethodAndFork(final SuspendContextImpl context) throws InvocationException, |
| ClassNotLoadedException, |
| IncompatibleThreadStateException, |
| InvalidTypeException { |
| final int invokePolicy = getInvokePolicy(context); |
| final Exception[] exception = new Exception[1]; |
| final Value[] result = new Value[1]; |
| getManagerThread().startLongProcessAndFork(new Runnable() { |
| @Override |
| public void run() { |
| ThreadReferenceProxyImpl thread = context.getThread(); |
| try { |
| try { |
| if (LOG.isDebugEnabled()) { |
| final VirtualMachineProxyImpl virtualMachineProxy = getVirtualMachineProxy(); |
| virtualMachineProxy.logThreads(); |
| LOG.debug("Invoke in " + thread.name()); |
| assertThreadSuspended(thread, context); |
| } |
| if (!Patches.IBM_JDK_DISABLE_COLLECTION_BUG) { |
| // ensure args are not collected |
| for (Object arg : myArgs) { |
| if (arg instanceof ObjectReference) { |
| ((ObjectReference)arg).disableCollection(); |
| } |
| } |
| } |
| result[0] = invokeMethod(invokePolicy, myArgs); |
| } |
| finally { |
| // assertThreadSuspended(thread, context); |
| if (!Patches.IBM_JDK_DISABLE_COLLECTION_BUG) { |
| // ensure args are not collected |
| for (Object arg : myArgs) { |
| if (arg instanceof ObjectReference) { |
| ((ObjectReference)arg).enableCollection(); |
| } |
| } |
| } |
| } |
| } |
| catch (Exception e) { |
| exception[0] = e; |
| } |
| } |
| }); |
| |
| if (exception[0] != null) { |
| if (exception[0] instanceof InvocationException) { |
| throw (InvocationException)exception[0]; |
| } |
| else if (exception[0] instanceof ClassNotLoadedException) { |
| throw (ClassNotLoadedException)exception[0]; |
| } |
| else if (exception[0] instanceof IncompatibleThreadStateException) { |
| throw (IncompatibleThreadStateException)exception[0]; |
| } |
| else if (exception[0] instanceof InvalidTypeException) { |
| throw (InvalidTypeException)exception[0]; |
| } |
| else if (exception[0] instanceof RuntimeException) { |
| throw (RuntimeException)exception[0]; |
| } |
| else { |
| LOG.assertTrue(false); |
| } |
| } |
| |
| return (E)result[0]; |
| } |
| |
| private void assertThreadSuspended(final ThreadReferenceProxyImpl thread, final SuspendContextImpl context) { |
| LOG.assertTrue(context.isEvaluating()); |
| try { |
| final boolean isSuspended = thread.isSuspended(); |
| LOG.assertTrue(isSuspended, thread); |
| } |
| catch (ObjectCollectedException ignored) { |
| } |
| } |
| } |
| |
| @Override |
| public Value invokeMethod(@NotNull EvaluationContext evaluationContext, @NotNull ObjectReference objRef, @NotNull Method method, final List args) throws EvaluateException { |
| return invokeInstanceMethod(evaluationContext, objRef, method, args, 0); |
| } |
| |
| @Override |
| public Value invokeInstanceMethod(@NotNull EvaluationContext evaluationContext, @NotNull final ObjectReference objRef, final Method method, |
| final List args, final int invocationOptions) throws EvaluateException { |
| final ThreadReference thread = getEvaluationThread(evaluationContext); |
| return new InvokeCommand<Value>(args) { |
| @Override |
| protected Value invokeMethod(int invokePolicy, final List args) throws InvocationException, ClassNotLoadedException, IncompatibleThreadStateException, InvalidTypeException { |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("Invoke " + method.name()); |
| } |
| return objRef.invokeMethod(thread, method, args, invokePolicy | invocationOptions); |
| } |
| }.start((EvaluationContextImpl)evaluationContext, method, false); |
| } |
| |
| private static ThreadReference getEvaluationThread(final EvaluationContext evaluationContext) throws EvaluateException { |
| ThreadReferenceProxy evaluationThread = evaluationContext.getSuspendContext().getThread(); |
| if(evaluationThread == null) { |
| throw EvaluateExceptionUtil.NULL_STACK_FRAME; |
| } |
| return evaluationThread.getThreadReference(); |
| } |
| |
| @Override |
| public Value invokeMethod(final EvaluationContext evaluationContext, final ClassType classType, |
| final Method method, |
| final List args) throws EvaluateException { |
| return invokeMethod(evaluationContext, classType, method, args, false); |
| } |
| |
| public Value invokeMethod(@NotNull EvaluationContext evaluationContext, |
| @NotNull final ClassType classType, |
| @NotNull final Method method, |
| final List args, |
| boolean internalEvaluate) throws EvaluateException { |
| final ThreadReference thread = getEvaluationThread(evaluationContext); |
| return new InvokeCommand<Value>(args) { |
| @Override |
| protected Value invokeMethod(int invokePolicy, final List args) throws InvocationException, |
| ClassNotLoadedException, |
| IncompatibleThreadStateException, |
| InvalidTypeException { |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("Invoke " + method.name()); |
| } |
| return classType.invokeMethod(thread, method, args, invokePolicy); |
| } |
| }.start((EvaluationContextImpl)evaluationContext, method, internalEvaluate); |
| } |
| |
| @Override |
| public ArrayReference newInstance(final ArrayType arrayType, |
| final int dimension) |
| throws EvaluateException { |
| try { |
| return arrayType.newInstance(dimension); |
| } |
| catch (Exception e) { |
| throw EvaluateExceptionUtil.createEvaluateException(e); |
| } |
| } |
| |
| @Override |
| public ObjectReference newInstance(final EvaluationContext evaluationContext, final ClassType classType, |
| final Method method, |
| final List args) throws EvaluateException { |
| final ThreadReference thread = getEvaluationThread(evaluationContext); |
| InvokeCommand<ObjectReference> invokeCommand = new InvokeCommand<ObjectReference>(args) { |
| @Override |
| protected ObjectReference invokeMethod(int invokePolicy, final List args) throws InvocationException, |
| ClassNotLoadedException, |
| IncompatibleThreadStateException, |
| InvalidTypeException { |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("New instance " + method.name()); |
| } |
| return classType.newInstance(thread, method, args, invokePolicy); |
| } |
| }; |
| return invokeCommand.start((EvaluationContextImpl)evaluationContext, method, false); |
| } |
| |
| public void clearCashes(int suspendPolicy) { |
| if (!isAttached()) return; |
| switch (suspendPolicy) { |
| case EventRequest.SUSPEND_ALL: |
| getVirtualMachineProxy().clearCaches(); |
| break; |
| case EventRequest.SUSPEND_EVENT_THREAD: |
| getVirtualMachineProxy().clearCaches(); |
| //suspendContext.getThread().clearAll(); |
| break; |
| } |
| } |
| |
| protected void beforeSuspend(SuspendContextImpl suspendContext) { |
| clearCashes(suspendContext.getSuspendPolicy()); |
| } |
| |
| private void beforeMethodInvocation(SuspendContextImpl suspendContext, Method method, boolean internalEvaluate) { |
| if (LOG.isDebugEnabled()) { |
| LOG.debug( |
| "before invocation in thread " + suspendContext.getThread().name() + " method " + (method == null ? "null" : method.name())); |
| } |
| |
| if (!internalEvaluate) { |
| if (method != null) { |
| showStatusText(DebuggerBundle.message("progress.evaluating", DebuggerUtilsEx.methodName(method))); |
| } |
| else { |
| showStatusText(DebuggerBundle.message("title.evaluating")); |
| } |
| } |
| } |
| |
| private void afterMethodInvocation(SuspendContextImpl suspendContext, boolean internalEvaluate) { |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("after invocation in thread " + suspendContext.getThread().name()); |
| } |
| if (!internalEvaluate) { |
| showStatusText(""); |
| } |
| } |
| |
| @Override |
| public ReferenceType findClass(EvaluationContext evaluationContext, String className, |
| ClassLoaderReference classLoader) throws EvaluateException { |
| try { |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| final VirtualMachineProxyImpl vmProxy = getVirtualMachineProxy(); |
| if (vmProxy == null) { |
| throw new VMDisconnectedException(); |
| } |
| ReferenceType result = null; |
| for (final ReferenceType refType : vmProxy.classesByName(className)) { |
| if (refType.isPrepared() && isVisibleFromClassLoader(classLoader, refType)) { |
| result = refType; |
| break; |
| } |
| } |
| final EvaluationContextImpl evalContext = (EvaluationContextImpl)evaluationContext; |
| if (result == null && evalContext.isAutoLoadClasses()) { |
| return loadClass(evalContext, className, classLoader); |
| } |
| return result; |
| } |
| catch (InvocationException e) { |
| throw EvaluateExceptionUtil.createEvaluateException(e); |
| } |
| catch (ClassNotLoadedException e) { |
| throw EvaluateExceptionUtil.createEvaluateException(e); |
| } |
| catch (IncompatibleThreadStateException e) { |
| throw EvaluateExceptionUtil.createEvaluateException(e); |
| } |
| catch (InvalidTypeException e) { |
| throw EvaluateExceptionUtil.createEvaluateException(e); |
| } |
| } |
| |
| private static boolean isVisibleFromClassLoader(final ClassLoaderReference fromLoader, final ReferenceType refType) { |
| // IMPORTANT! Even if the refType is already loaded by some parent or bootstrap loader, it may not be visible from the given loader. |
| // 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 |
| // for this refType and the refType is not visible to the loader. |
| // Attempt to evaluate method with this refType will yield ClassNotLoadedException. |
| // The only way to say for sure whether the class is _visible_ to the given loader, is to use the following API call |
| return fromLoader == null || fromLoader.equals(refType.classLoader()) || fromLoader.visibleClasses().contains(refType); |
| } |
| |
| private static String reformatArrayName(String className) { |
| if (className.indexOf('[') == -1) return className; |
| |
| int dims = 0; |
| while (className.endsWith("[]")) { |
| className = className.substring(0, className.length() - 2); |
| dims++; |
| } |
| |
| StringBuilder buffer = StringBuilderSpinAllocator.alloc(); |
| try { |
| for (int i = 0; i < dims; i++) { |
| buffer.append('['); |
| } |
| String primitiveSignature = JVMNameUtil.getPrimitiveSignature(className); |
| if(primitiveSignature != null) { |
| buffer.append(primitiveSignature); |
| } |
| else { |
| buffer.append('L'); |
| buffer.append(className); |
| buffer.append(';'); |
| } |
| return buffer.toString(); |
| } |
| finally { |
| StringBuilderSpinAllocator.dispose(buffer); |
| } |
| } |
| |
| @SuppressWarnings({"HardCodedStringLiteral", "SpellCheckingInspection"}) |
| public ReferenceType loadClass(EvaluationContextImpl evaluationContext, String qName, ClassLoaderReference classLoader) |
| throws InvocationException, ClassNotLoadedException, IncompatibleThreadStateException, InvalidTypeException, EvaluateException { |
| |
| DebuggerManagerThreadImpl.assertIsManagerThread(); |
| qName = reformatArrayName(qName); |
| ReferenceType refType = null; |
| VirtualMachineProxyImpl virtualMachine = getVirtualMachineProxy(); |
| final List classClasses = virtualMachine.classesByName("java.lang.Class"); |
| if (!classClasses.isEmpty()) { |
| ClassType classClassType = (ClassType)classClasses.get(0); |
| final Method forNameMethod; |
| if (classLoader != null) { |
| //forNameMethod = classClassType.concreteMethodByName("forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"); |
| forNameMethod = DebuggerUtils.findMethod(classClassType, "forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"); |
| } |
| else { |
| //forNameMethod = classClassType.concreteMethodByName("forName", "(Ljava/lang/String;)Ljava/lang/Class;"); |
| forNameMethod = DebuggerUtils.findMethod(classClassType, "forName", "(Ljava/lang/String;)Ljava/lang/Class;"); |
| } |
| final List<Mirror> args = new ArrayList<Mirror>(); // do not use unmodifiable lists because the list is modified by JPDA |
| final StringReference qNameMirror = virtualMachine.mirrorOf(qName); |
| args.add(qNameMirror); |
| if (classLoader != null) { |
| args.add(virtualMachine.mirrorOf(true)); |
| args.add(classLoader); |
| } |
| final Value value = invokeMethod(evaluationContext, classClassType, forNameMethod, args); |
| if (value instanceof ClassObjectReference) { |
| refType = ((ClassObjectReference)value).reflectedType(); |
| } |
| } |
| return refType; |
| } |
| |
| public void logThreads() { |
| if (LOG.isDebugEnabled()) { |
| try { |
| Collection<ThreadReferenceProxyImpl> allThreads = getVirtualMachineProxy().allThreads(); |
| for (ThreadReferenceProxyImpl threadReferenceProxy : allThreads) { |
| LOG.debug("Thread name=" + threadReferenceProxy.name() + " suspendCount()=" + threadReferenceProxy.getSuspendCount()); |
| } |
| } |
| catch (Exception e) { |
| LOG.debug(e); |
| } |
| } |
| } |
| |
| public SuspendManager getSuspendManager() { |
| return mySuspendManager; |
| } |
| |
| @Override |
| public CompoundPositionManager getPositionManager() { |
| return myPositionManager; |
| } |
| //ManagerCommands |
| |
| @Override |
| public void stop(boolean forceTerminate) { |
| getManagerThread().terminateAndInvoke(createStopCommand(forceTerminate), DebuggerManagerThreadImpl.COMMAND_TIMEOUT); |
| } |
| |
| public StopCommand createStopCommand(boolean forceTerminate) { |
| return new StopCommand(forceTerminate); |
| } |
| |
| protected class StopCommand extends DebuggerCommandImpl { |
| private final boolean myIsTerminateTargetVM; |
| |
| public StopCommand(boolean isTerminateTargetVM) { |
| myIsTerminateTargetVM = isTerminateTargetVM; |
| } |
| |
| @Override |
| public Priority getPriority() { |
| return Priority.HIGH; |
| } |
| |
| @Override |
| protected void action() throws Exception { |
| if (isAttached()) { |
| final VirtualMachineProxyImpl virtualMachineProxy = getVirtualMachineProxy(); |
| if (myIsTerminateTargetVM) { |
| virtualMachineProxy.exit(-1); |
| } |
| else { |
| // some VMs (like IBM VM 1.4.2 bundled with WebSphere) does not resume threads on dispose() like it should |
| try { |
| virtualMachineProxy.resume(); |
| } |
| finally { |
| virtualMachineProxy.dispose(); |
| } |
| } |
| } |
| else { |
| stopConnecting(); |
| } |
| } |
| } |
| |
| private class StepOutCommand extends ResumeCommand { |
| public StepOutCommand(SuspendContextImpl suspendContext) { |
| super(suspendContext); |
| } |
| |
| @Override |
| public void contextAction() { |
| showStatusText(DebuggerBundle.message("status.step.out")); |
| final SuspendContextImpl suspendContext = getSuspendContext(); |
| final ThreadReferenceProxyImpl thread = getContextThread(); |
| RequestHint hint = new RequestHint(thread, suspendContext, StepRequest.STEP_OUT); |
| hint.setIgnoreFilters(mySession.shouldIgnoreSteppingFilters()); |
| applyThreadFilter(thread); |
| final MethodReturnValueWatcher rvWatcher = myReturnValueWatcher; |
| if (rvWatcher != null) { |
| rvWatcher.enable(thread.getThreadReference()); |
| } |
| doStep(suspendContext, thread, StepRequest.STEP_OUT, hint); |
| super.contextAction(); |
| } |
| } |
| |
| private class StepIntoCommand extends ResumeCommand { |
| private final boolean myForcedIgnoreFilters; |
| private final MethodFilter mySmartStepFilter; |
| @Nullable |
| private final StepIntoBreakpoint myBreakpoint; |
| |
| public StepIntoCommand(SuspendContextImpl suspendContext, boolean ignoreFilters, @Nullable final MethodFilter methodFilter) { |
| super(suspendContext); |
| myForcedIgnoreFilters = ignoreFilters || methodFilter != null; |
| mySmartStepFilter = methodFilter; |
| myBreakpoint = methodFilter instanceof BreakpointStepMethodFilter ? |
| DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager().addStepIntoBreakpoint(((BreakpointStepMethodFilter)methodFilter)) : |
| null; |
| } |
| |
| @Override |
| public void contextAction() { |
| showStatusText(DebuggerBundle.message("status.step.into")); |
| final SuspendContextImpl suspendContext = getSuspendContext(); |
| final ThreadReferenceProxyImpl stepThread = getContextThread(); |
| final RequestHint hint = mySmartStepFilter != null? |
| new RequestHint(stepThread, suspendContext, mySmartStepFilter) : |
| new RequestHint(stepThread, suspendContext, StepRequest.STEP_INTO); |
| if (myForcedIgnoreFilters) { |
| try { |
| mySession.setIgnoreStepFiltersFlag(stepThread.frameCount()); |
| } |
| catch (EvaluateException e) { |
| LOG.info(e); |
| } |
| } |
| hint.setIgnoreFilters(myForcedIgnoreFilters || mySession.shouldIgnoreSteppingFilters()); |
| applyThreadFilter(stepThread); |
| if (myBreakpoint != null) { |
| myBreakpoint.setSuspendPolicy(suspendContext.getSuspendPolicy() == EventRequest.SUSPEND_EVENT_THREAD? DebuggerSettings.SUSPEND_THREAD : DebuggerSettings.SUSPEND_ALL); |
| myBreakpoint.createRequest(suspendContext.getDebugProcess()); |
| myRunToCursorBreakpoint = myBreakpoint; |
| } |
| doStep(suspendContext, stepThread, StepRequest.STEP_INTO, hint); |
| super.contextAction(); |
| } |
| } |
| |
| private class StepOverCommand extends ResumeCommand { |
| private final boolean myIsIgnoreBreakpoints; |
| |
| public StepOverCommand(SuspendContextImpl suspendContext, boolean ignoreBreakpoints) { |
| super(suspendContext); |
| myIsIgnoreBreakpoints = ignoreBreakpoints; |
| } |
| |
| @Override |
| public void contextAction() { |
| showStatusText(DebuggerBundle.message("status.step.over")); |
| final SuspendContextImpl suspendContext = getSuspendContext(); |
| final ThreadReferenceProxyImpl stepThread = getContextThread(); |
| // need this hint while stepping over for JSR45 support: |
| // several lines of generated java code may correspond to a single line in the source file, |
| // from which the java code was generated |
| RequestHint hint = new RequestHint(stepThread, suspendContext, StepRequest.STEP_OVER); |
| hint.setRestoreBreakpoints(myIsIgnoreBreakpoints); |
| hint.setIgnoreFilters(myIsIgnoreBreakpoints || mySession.shouldIgnoreSteppingFilters()); |
| |
| applyThreadFilter(stepThread); |
| |
| final MethodReturnValueWatcher rvWatcher = myReturnValueWatcher; |
| if (rvWatcher != null) { |
| rvWatcher.enable(stepThread.getThreadReference()); |
| } |
| |
| doStep(suspendContext, stepThread, StepRequest.STEP_OVER, hint); |
| |
| if (myIsIgnoreBreakpoints) { |
| DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager().disableBreakpoints(DebugProcessImpl.this); |
| } |
| super.contextAction(); |
| } |
| } |
| |
| private class RunToCursorCommand extends ResumeCommand { |
| private final RunToCursorBreakpoint myRunToCursorBreakpoint; |
| private final boolean myIgnoreBreakpoints; |
| |
| private RunToCursorCommand(SuspendContextImpl suspendContext, Document document, int lineIndex, final boolean ignoreBreakpoints) { |
| super(suspendContext); |
| myIgnoreBreakpoints = ignoreBreakpoints; |
| final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager(); |
| myRunToCursorBreakpoint = breakpointManager.addRunToCursorBreakpoint(document, lineIndex, ignoreBreakpoints); |
| } |
| |
| @Override |
| public void contextAction() { |
| showStatusText(DebuggerBundle.message("status.run.to.cursor")); |
| cancelRunToCursorBreakpoint(); |
| if (myRunToCursorBreakpoint == null) { |
| return; |
| } |
| if (myIgnoreBreakpoints) { |
| final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager(); |
| breakpointManager.disableBreakpoints(DebugProcessImpl.this); |
| } |
| applyThreadFilter(getContextThread()); |
| final SuspendContextImpl context = getSuspendContext(); |
| myRunToCursorBreakpoint.setSuspendPolicy(context.getSuspendPolicy() == EventRequest.SUSPEND_EVENT_THREAD? DebuggerSettings.SUSPEND_THREAD : DebuggerSettings.SUSPEND_ALL); |
| myRunToCursorBreakpoint.createRequest(context.getDebugProcess()); |
| DebugProcessImpl.this.myRunToCursorBreakpoint = myRunToCursorBreakpoint; |
| super.contextAction(); |
| } |
| } |
| |
| public abstract class ResumeCommand extends SuspendContextCommandImpl { |
| |
| private final ThreadReferenceProxyImpl myContextThread; |
| |
| public ResumeCommand(SuspendContextImpl suspendContext) { |
| super(suspendContext); |
| final ThreadReferenceProxyImpl contextThread = getDebuggerContext().getThreadProxy(); |
| myContextThread = contextThread != null ? contextThread : (suspendContext != null? suspendContext.getThread() : null); |
| } |
| |
| @Override |
| public Priority getPriority() { |
| return Priority.HIGH; |
| } |
| |
| @Override |
| public void contextAction() { |
| showStatusText(DebuggerBundle.message("status.process.resumed")); |
| getSuspendManager().resume(getSuspendContext()); |
| myDebugProcessDispatcher.getMulticaster().resumed(getSuspendContext()); |
| } |
| |
| public ThreadReferenceProxyImpl getContextThread() { |
| return myContextThread; |
| } |
| |
| protected void applyThreadFilter(ThreadReferenceProxy thread) { |
| if (getSuspendContext().getSuspendPolicy() == EventRequest.SUSPEND_ALL) { |
| // there could be explicit resume as a result of call to voteSuspend() |
| // e.g. when breakpoint was considered invalid, in that case the filter will be applied _after_ |
| // resuming and all breakpoints in other threads will be ignored. |
| // As resume() implicitly cleares the filter, the filter must be always applied _before_ any resume() action happens |
| final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(getProject()).getBreakpointManager(); |
| breakpointManager.applyThreadFilter(DebugProcessImpl.this, thread.getThreadReference()); |
| } |
| } |
| } |
| |
| private class PauseCommand extends DebuggerCommandImpl { |
| public PauseCommand() { |
| } |
| |
| @Override |
| public void action() { |
| if (!isAttached() || getVirtualMachineProxy().isPausePressed()) { |
| return; |
| } |
| logThreads(); |
| getVirtualMachineProxy().suspend(); |
| logThreads(); |
| SuspendContextImpl suspendContext = mySuspendManager.pushSuspendContext(EventRequest.SUSPEND_ALL, 0); |
| myDebugProcessDispatcher.getMulticaster().paused(suspendContext); |
| } |
| } |
| |
| private class ResumeThreadCommand extends SuspendContextCommandImpl { |
| private final ThreadReferenceProxyImpl myThread; |
| |
| public ResumeThreadCommand(SuspendContextImpl suspendContext, ThreadReferenceProxyImpl thread) { |
| super(suspendContext); |
| myThread = thread; |
| } |
| |
| @Override |
| public void contextAction() { |
| if (getSuspendManager().isFrozen(myThread)) { |
| getSuspendManager().unfreezeThread(myThread); |
| return; |
| } |
| |
| final Set<SuspendContextImpl> suspendingContexts = SuspendManagerUtil.getSuspendingContexts(getSuspendManager(), myThread); |
| for (SuspendContextImpl suspendContext : suspendingContexts) { |
| if (suspendContext.getThread() == myThread) { |
| getManagerThread().invoke(createResumeCommand(suspendContext)); |
| } |
| else { |
| getSuspendManager().resumeThread(suspendContext, myThread); |
| } |
| } |
| } |
| } |
| |
| private class FreezeThreadCommand extends DebuggerCommandImpl { |
| private final ThreadReferenceProxyImpl myThread; |
| |
| public FreezeThreadCommand(ThreadReferenceProxyImpl thread) { |
| myThread = thread; |
| } |
| |
| @Override |
| protected void action() throws Exception { |
| SuspendManager suspendManager = getSuspendManager(); |
| if (!suspendManager.isFrozen(myThread)) { |
| suspendManager.freezeThread(myThread); |
| } |
| } |
| } |
| |
| private class PopFrameCommand extends SuspendContextCommandImpl { |
| private final StackFrameProxyImpl myStackFrame; |
| |
| public PopFrameCommand(SuspendContextImpl context, StackFrameProxyImpl frameProxy) { |
| super(context); |
| myStackFrame = frameProxy; |
| } |
| |
| @Override |
| public void contextAction() { |
| final ThreadReferenceProxyImpl thread = myStackFrame.threadProxy(); |
| try { |
| if (!getSuspendManager().isSuspended(thread)) { |
| notifyCancelled(); |
| return; |
| } |
| } |
| catch (ObjectCollectedException ignored) { |
| notifyCancelled(); |
| return; |
| } |
| |
| final SuspendContextImpl suspendContext = getSuspendContext(); |
| if (!suspendContext.suspends(thread)) { |
| suspendContext.postponeCommand(this); |
| return; |
| } |
| |
| if (myStackFrame.isBottom()) { |
| DebuggerInvocationUtil.swingInvokeLater(myProject, new Runnable() { |
| @Override |
| public void run() { |
| Messages.showMessageDialog(myProject, DebuggerBundle.message("error.pop.bottom.stackframe"), ActionsBundle.actionText(DebuggerActions.POP_FRAME), Messages.getErrorIcon()); |
| } |
| }); |
| return; |
| } |
| |
| try { |
| thread.popFrames(myStackFrame); |
| } |
| catch (final EvaluateException e) { |
| DebuggerInvocationUtil.swingInvokeLater(myProject, new Runnable() { |
| @Override |
| public void run() { |
| Messages.showMessageDialog(myProject, DebuggerBundle.message("error.pop.stackframe", e.getLocalizedMessage()), ActionsBundle.actionText(DebuggerActions.POP_FRAME), Messages.getErrorIcon()); |
| } |
| }); |
| LOG.info(e); |
| } |
| finally { |
| getSuspendManager().popFrame(suspendContext); |
| } |
| } |
| } |
| |
| @Override |
| @NotNull |
| public GlobalSearchScope getSearchScope() { |
| LOG.assertTrue(mySession != null, "Accessing debug session before its initialization"); |
| return mySession.getSearchScope(); |
| } |
| |
| @Nullable |
| public ExecutionResult attachVirtualMachine(final DebugEnvironment environment, |
| final DebuggerSession session) throws ExecutionException { |
| mySession = session; |
| myWaitFor.down(); |
| |
| ApplicationManager.getApplication().assertIsDispatchThread(); |
| LOG.assertTrue(isInInitialState()); |
| |
| myConnection = environment.getRemoteConnection(); |
| |
| createVirtualMachine(environment.getSessionName(), environment.isPollConnection()); |
| |
| ExecutionResult executionResult; |
| try { |
| synchronized (myProcessListeners) { |
| executionResult = environment.createExecutionResult(); |
| myExecutionResult = executionResult; |
| if (executionResult == null) { |
| fail(); |
| return null; |
| } |
| for (ProcessListener processListener : myProcessListeners) { |
| executionResult.getProcessHandler().addProcessListener(processListener); |
| } |
| myProcessListeners.clear(); |
| } |
| } |
| catch (ExecutionException e) { |
| fail(); |
| throw e; |
| } |
| |
| // writing to volatile field ensures the other threads will see the right values in non-volatile fields |
| |
| if (ApplicationManager.getApplication().isUnitTestMode()) { |
| return executionResult; |
| } |
| |
| /* |
| final Alarm debugPortTimeout = new Alarm(Alarm.ThreadToUse.SHARED_THREAD); |
| |
| myExecutionResult.getProcessHandler().addProcessListener(new ProcessAdapter() { |
| public void processTerminated(ProcessEvent event) { |
| debugPortTimeout.cancelAllRequests(); |
| } |
| |
| public void startNotified(ProcessEvent event) { |
| debugPortTimeout.addRequest(new Runnable() { |
| public void run() { |
| if(isInInitialState()) { |
| ApplicationManager.getApplication().schedule(new Runnable() { |
| public void run() { |
| String message = DebuggerBundle.message("status.connect.failed", DebuggerBundle.getAddressDisplayName(remoteConnection), DebuggerBundle.getTransportName(remoteConnection)); |
| Messages.showErrorDialog(myProject, message, DebuggerBundle.message("title.generic.debug.dialog")); |
| } |
| }); |
| } |
| } |
| }, LOCAL_START_TIMEOUT); |
| } |
| }); |
| */ |
| |
| return executionResult; |
| } |
| |
| private void fail() { |
| synchronized (this) { |
| if (myIsFailed) { |
| // need this in order to prevent calling stop() twice |
| return; |
| } |
| myIsFailed = true; |
| } |
| stop(false); |
| } |
| |
| private void createVirtualMachine(final String sessionName, final boolean pollConnection) { |
| final Semaphore semaphore = new Semaphore(); |
| semaphore.down(); |
| |
| final Ref<Boolean> connectorIsReady = Ref.create(false); |
| myDebugProcessDispatcher.addListener(new DebugProcessAdapter() { |
| @Override |
| public void connectorIsReady() { |
| connectorIsReady.set(true); |
| semaphore.up(); |
| myDebugProcessDispatcher.removeListener(this); |
| } |
| }); |
| |
| // reload to make sure that source positions are initialized |
| DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager().reloadBreakpoints(); |
| |
| getManagerThread().schedule(new DebuggerCommandImpl() { |
| @Override |
| protected void action() { |
| VirtualMachine vm = null; |
| |
| try { |
| final long time = System.currentTimeMillis(); |
| while (System.currentTimeMillis() - time < LOCAL_START_TIMEOUT) { |
| try { |
| vm = createVirtualMachineInt(); |
| break; |
| } |
| catch (final ExecutionException e) { |
| if (pollConnection && !myConnection.isServerMode() && e.getCause() instanceof IOException) { |
| synchronized (this) { |
| try { |
| wait(500); |
| } |
| catch (InterruptedException ignored) { |
| break; |
| } |
| } |
| } |
| else { |
| fail(); |
| if (myExecutionResult != null || !connectorIsReady.get()) { |
| // propagate exception only in case we succeeded to obtain execution result, |
| // otherwise if the error is induced by the fact that there is nothing to debug, and there is no need to show |
| // this problem to the user |
| SwingUtilities.invokeLater(new Runnable() { |
| @Override |
| public void run() { |
| ExecutionUtil.handleExecutionError(myProject, ToolWindowId.DEBUG, sessionName, e); |
| } |
| }); |
| } |
| break; |
| } |
| } |
| } |
| } |
| finally { |
| semaphore.up(); |
| } |
| |
| if (vm != null) { |
| final VirtualMachine vm1 = vm; |
| afterProcessStarted(new Runnable() { |
| @Override |
| public void run() { |
| getManagerThread().schedule(new DebuggerCommandImpl() { |
| @Override |
| protected void action() throws Exception { |
| commitVM(vm1); |
| } |
| }); |
| } |
| }); |
| } |
| } |
| |
| @Override |
| protected void commandCancelled() { |
| try { |
| super.commandCancelled(); |
| } |
| finally { |
| semaphore.up(); |
| } |
| } |
| }); |
| |
| semaphore.waitFor(); |
| } |
| |
| private void afterProcessStarted(final Runnable run) { |
| class MyProcessAdapter extends ProcessAdapter { |
| private boolean alreadyRun = false; |
| |
| public synchronized void run() { |
| if(!alreadyRun) { |
| alreadyRun = true; |
| run.run(); |
| } |
| removeProcessListener(this); |
| } |
| |
| @Override |
| public void startNotified(ProcessEvent event) { |
| run(); |
| } |
| } |
| MyProcessAdapter processListener = new MyProcessAdapter(); |
| addProcessListener(processListener); |
| if (myExecutionResult != null) { |
| if (myExecutionResult.getProcessHandler().isStartNotified()) { |
| processListener.run(); |
| } |
| } |
| } |
| |
| public boolean isPausePressed() { |
| final VirtualMachineProxyImpl vm = myVirtualMachineProxy; |
| return vm != null && vm.isPausePressed(); |
| } |
| |
| public DebuggerCommandImpl createPauseCommand() { |
| return new PauseCommand(); |
| } |
| |
| public ResumeCommand createResumeCommand(SuspendContextImpl suspendContext) { |
| return createResumeCommand(suspendContext, PrioritizedTask.Priority.HIGH); |
| } |
| |
| public ResumeCommand createResumeCommand(SuspendContextImpl suspendContext, final PrioritizedTask.Priority priority) { |
| final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(getProject()).getBreakpointManager(); |
| return new ResumeCommand(suspendContext) { |
| @Override |
| public void contextAction() { |
| breakpointManager.applyThreadFilter(DebugProcessImpl.this, null); // clear the filter on resume |
| super.contextAction(); |
| } |
| |
| @Override |
| public Priority getPriority() { |
| return priority; |
| } |
| }; |
| } |
| |
| public ResumeCommand createStepOverCommand(SuspendContextImpl suspendContext, boolean ignoreBreakpoints) { |
| return new StepOverCommand(suspendContext, ignoreBreakpoints); |
| } |
| |
| public ResumeCommand createStepOutCommand(SuspendContextImpl suspendContext) { |
| return new StepOutCommand(suspendContext); |
| } |
| |
| public ResumeCommand createStepIntoCommand(SuspendContextImpl suspendContext, boolean ignoreFilters, final MethodFilter smartStepFilter) { |
| return new StepIntoCommand(suspendContext, ignoreFilters, smartStepFilter); |
| } |
| |
| public ResumeCommand createRunToCursorCommand(SuspendContextImpl suspendContext, Document document, int lineIndex, |
| final boolean ignoreBreakpoints) |
| throws EvaluateException { |
| RunToCursorCommand runToCursorCommand = new RunToCursorCommand(suspendContext, document, lineIndex, ignoreBreakpoints); |
| if(runToCursorCommand.myRunToCursorBreakpoint == null) { |
| final PsiFile psiFile = PsiDocumentManager.getInstance(getProject()).getPsiFile(document); |
| throw new EvaluateException(DebuggerBundle.message("error.running.to.cursor.no.executable.code", psiFile != null? psiFile.getName() : "<No File>", lineIndex), null); |
| } |
| return runToCursorCommand; |
| } |
| |
| public DebuggerCommandImpl createFreezeThreadCommand(ThreadReferenceProxyImpl thread) { |
| return new FreezeThreadCommand(thread); |
| } |
| |
| public SuspendContextCommandImpl createResumeThreadCommand(SuspendContextImpl suspendContext, ThreadReferenceProxyImpl thread) { |
| return new ResumeThreadCommand(suspendContext, thread); |
| } |
| |
| public SuspendContextCommandImpl createPopFrameCommand(DebuggerContextImpl context, StackFrameProxyImpl stackFrame) { |
| final SuspendContextImpl contextByThread = |
| SuspendManagerUtil.findContextByThread(context.getDebugProcess().getSuspendManager(), stackFrame.threadProxy()); |
| return new PopFrameCommand(contextByThread, stackFrame); |
| } |
| |
| //public void setBreakpointsMuted(final boolean muted) { |
| // XDebugSession session = mySession.getXDebugSession(); |
| // if (isAttached()) { |
| // getManagerThread().schedule(new DebuggerCommandImpl() { |
| // @Override |
| // protected void action() throws Exception { |
| // // set the flag before enabling/disabling cause it affects if breakpoints will create requests |
| // if (myBreakpointsMuted.getAndSet(muted) != muted) { |
| // final BreakpointManager breakpointManager = DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager(); |
| // if (muted) { |
| // breakpointManager.disableBreakpoints(DebugProcessImpl.this); |
| // } |
| // else { |
| // breakpointManager.enableBreakpoints(DebugProcessImpl.this); |
| // } |
| // } |
| // } |
| // }); |
| // } |
| // else { |
| // session.setBreakpointMuted(muted); |
| // } |
| //} |
| |
| public DebuggerContextImpl getDebuggerContext() { |
| return mySession.getContextManager().getContext(); |
| } |
| |
| @Nullable |
| public JavaDebugProcess getXdebugProcess() { |
| XDebugSession session = mySession.getXDebugSession(); |
| return session != null ? (JavaDebugProcess)session.getDebugProcess() : null; |
| } |
| |
| public boolean areBreakpointsMuted() { |
| XDebugSession session = mySession.getXDebugSession(); |
| return session != null && session.areBreakpointsMuted(); |
| } |
| |
| public DebuggerSession getSession() { |
| return mySession; |
| } |
| } |