mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-02-05 08:06:56 +07:00
1517 lines
55 KiB
Java
1517 lines
55 KiB
Java
package com.intellij.debugger.engine;
|
|
|
|
import com.intellij.Patches;
|
|
import com.intellij.debugger.ClassFilter;
|
|
import com.intellij.debugger.DebuggerInvocationUtil;
|
|
import com.intellij.debugger.DebuggerManagerEx;
|
|
import com.intellij.debugger.PositionManager;
|
|
import com.intellij.debugger.engine.evaluation.*;
|
|
import com.intellij.debugger.engine.events.DebuggerCommandImpl;
|
|
import com.intellij.debugger.engine.events.DebuggerContextCommandImpl;
|
|
import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
|
|
import com.intellij.debugger.engine.jdi.ThreadReferenceProxy;
|
|
import com.intellij.debugger.engine.requests.RequestManagerImpl;
|
|
import com.intellij.debugger.impl.DebuggerContextImpl;
|
|
import com.intellij.debugger.impl.DebuggerUtilsEx;
|
|
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.ui.DebuggerSmoothManager;
|
|
import com.intellij.debugger.ui.DescriptorHistoryManagerImpl;
|
|
import com.intellij.debugger.ui.breakpoints.LineBreakpoint;
|
|
import com.intellij.debugger.ui.impl.watch.DescriptorHistoryManager;
|
|
import com.intellij.debugger.ui.impl.watch.render.NodeRendererManagerImpl;
|
|
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.configurations.RunProfileState;
|
|
import com.intellij.execution.process.ProcessAdapter;
|
|
import com.intellij.execution.process.ProcessEvent;
|
|
import com.intellij.execution.process.ProcessListener;
|
|
import com.intellij.execution.process.ProcessOutputTypes;
|
|
import com.intellij.openapi.application.ApplicationManager;
|
|
import com.intellij.openapi.diagnostic.Logger;
|
|
import com.intellij.openapi.editor.Document;
|
|
import com.intellij.openapi.project.Project;
|
|
import com.intellij.openapi.project.ex.ProjectManagerEx;
|
|
import com.intellij.openapi.ui.Messages;
|
|
import com.intellij.openapi.util.Comparing;
|
|
import com.intellij.openapi.util.Key;
|
|
import com.intellij.openapi.wm.WindowManager;
|
|
import com.intellij.openapi.wm.ex.StatusBarEx;
|
|
import com.intellij.psi.PsiDocumentManager;
|
|
import com.intellij.psi.PsiFile;
|
|
import com.intellij.util.Alarm;
|
|
import com.intellij.util.EventDispatcher;
|
|
import com.intellij.util.concurrency.Semaphore;
|
|
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 com.sun.tools.jdi.ConnectionService;
|
|
import com.sun.tools.jdi.TransportService;
|
|
import com.sun.tools.jdi.VirtualMachineManagerService;
|
|
|
|
import javax.swing.*;
|
|
import java.io.IOException;
|
|
import java.net.UnknownHostException;
|
|
import java.util.*;
|
|
|
|
public abstract class DebugProcessImpl implements DebugProcess {
|
|
private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.DebugProcessImpl");
|
|
|
|
private static final String SOCKET_ATTACHING_CONNECTOR_NAME = "com.sun.jdi.SocketAttach";
|
|
private static final String SHMEM_ATTACHING_CONNECTOR_NAME = "com.sun.jdi.SharedMemoryAttach";
|
|
private static final String SOCKET_LISTENING_CONNECTOR_NAME = "com.sun.jdi.SocketListen";
|
|
private static final String SHMEM_LISTENING_CONNECTOR_NAME = "com.sun.jdi.SharedMemoryListen";
|
|
|
|
public static final String ALWAYS_FORCE_CLASSIC_VM = "always";
|
|
public static final String NEVER_FORCE_CLASSIC_VM = "never";
|
|
public static final String IF_SELECTED_FORCE_CLASSIC_VM = "if_selected";
|
|
|
|
public static final String JAVA_STRATUM = "Java";
|
|
|
|
private final Project myProject;
|
|
private final RequestManagerImpl myRequestManager;
|
|
|
|
private VirtualMachineProxyImpl myVirtualMachineProxy = null;
|
|
protected EventDispatcher<DebugProcessListener> myDebugProcessDispatcher = EventDispatcher.create(DebugProcessListener.class, false);
|
|
protected EventDispatcher<EvaluationListener> myEvaluationDispatcher = EventDispatcher.create(EvaluationListener.class, false);
|
|
|
|
private List<ProcessListener> myProcessListeners = new ArrayList<ProcessListener>();
|
|
|
|
private static final int STATE_INITIAL = 0;
|
|
private static final int STATE_ATTACHED = 1;
|
|
private static final int STATE_DETACHING = 2;
|
|
private static final int STATE_DETACHED = 3;
|
|
private int myState = STATE_INITIAL;
|
|
|
|
private boolean myCanRedefineClasses;
|
|
private boolean myCanWatchFieldModification;
|
|
|
|
private ExecutionResult myExecution;
|
|
private RemoteConnection myConnection;
|
|
|
|
private ConnectionService myConnectionService;
|
|
private Map myArguments;
|
|
|
|
private LinkedList<String> myStatusStack = new LinkedList<String>();
|
|
private String myStatusText;
|
|
private int mySuspendPolicy = DebuggerSettings.getInstance().isSuspendAllThreads()
|
|
? EventRequest.SUSPEND_ALL
|
|
: EventRequest.SUSPEND_EVENT_THREAD;
|
|
|
|
private final DescriptorHistoryManager myDescriptorHistoryManager;
|
|
private final NodeRendererManagerImpl myNodeRendererManager = new NodeRendererManagerImpl(this);
|
|
private final SuspendManagerImpl mySuspendManager = new SuspendManagerImpl(this);
|
|
protected CompoundPositionManager myPositionManager = null;
|
|
DebuggerManagerThreadImpl myDebuggerManagerThread;
|
|
public static final String MSG_FAILD_TO_CONNECT = "Failed to establish connection to the target VM";
|
|
private HashMap myUserData = new HashMap();
|
|
private static int LOCAL_START_TIMEOUT = 15000;
|
|
|
|
private final Semaphore myWaitFor = new Semaphore();
|
|
|
|
protected DebugProcessImpl(Project project) {
|
|
myProject = project;
|
|
myRequestManager = new RequestManagerImpl(this);
|
|
myDescriptorHistoryManager = new DescriptorHistoryManagerImpl(project);
|
|
setSuspendPolicy(DebuggerSettings.getInstance().isSuspendAllThreads());
|
|
}
|
|
|
|
protected void commitVM(VirtualMachine vm) {
|
|
LOG.assertTrue(myState == STATE_INITIAL, "State is invalid " + myState);
|
|
DebuggerManagerThreadImpl.assertIsManagerThread();
|
|
myPositionManager = createPositionManager();
|
|
if (LOG.isDebugEnabled()) {
|
|
LOG.debug("*******************VM attached******************");
|
|
}
|
|
checkVirtualMachineVersion(vm);
|
|
|
|
myVirtualMachineProxy = new VirtualMachineProxyImpl(this, vm);
|
|
myCanRedefineClasses = myVirtualMachineProxy.canRedefineClasses();
|
|
myCanWatchFieldModification = myVirtualMachineProxy.canWatchFieldModification();
|
|
|
|
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 ("RECEIVES".compareToIgnoreCase(token) == 0) {
|
|
mask |= VirtualMachine.TRACE_RECEIVES;
|
|
}
|
|
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 arguments = myArguments;
|
|
try {
|
|
if (arguments == null) {
|
|
return;
|
|
}
|
|
if(myConnection.isServerMode()) {
|
|
ListeningConnector connector = (ListeningConnector)findConnector(SOCKET_LISTENING_CONNECTOR_NAME);
|
|
LOG.assertTrue(connector != null, "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(false);
|
|
}
|
|
}
|
|
|
|
protected CompoundPositionManager createPositionManager() {
|
|
return new CompoundPositionManager(new PositionManagerImpl(this));
|
|
}
|
|
|
|
public void printToConsole(final String text) {
|
|
myExecution.getProcessHandler().notifyTextAvailable(text, ProcessOutputTypes.SYSTEM);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param suspendContext
|
|
* @param depth
|
|
* @param hint may be null
|
|
* @return
|
|
*/
|
|
protected boolean doStep(SuspendContextImpl suspendContext, int depth, RequestHint hint) {
|
|
final ThreadReferenceProxyImpl currentThreadProxy = suspendContext.getThread();
|
|
if (currentThreadProxy == null || !currentThreadProxy.isSuspended()) return false;
|
|
if (LOG.isDebugEnabled()) {
|
|
LOG.debug("DO_STEP: creating step request for " + currentThreadProxy.getThreadReference());
|
|
}
|
|
deleteStepRequests(suspendContext.getThread());
|
|
EventRequestManager requestManager = getVirtualMachineProxy().eventRequestManager();
|
|
StepRequest stepRequest = requestManager.createStepRequest(currentThreadProxy.getThreadReference(), StepRequest.STEP_LINE, depth);
|
|
DebuggerSettings settings = DebuggerSettings.getInstance();
|
|
if (!(hint != null && hint.isIgnoreFilters()) && depth == StepRequest.STEP_INTO) {
|
|
if (settings.TRACING_FILTERS_ENABLED) {
|
|
String currentClassName = getCurrentClassName(currentThreadProxy);
|
|
if (currentClassName == null || !settings.isNameFiltered(currentClassName)) {
|
|
// add class filters
|
|
ClassFilter[] filters = settings.getFilters();
|
|
for (int idx = 0; idx < filters.length; idx++) {
|
|
if (filters[idx].isEnabled()) {
|
|
stepRequest.addClassExclusionFilter(filters[idx].getPattern());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
stepRequest.setSuspendPolicy(getSuspendPolicy());
|
|
|
|
if (hint != null) {
|
|
stepRequest.putProperty("hint", hint);
|
|
}
|
|
stepRequest.enable();
|
|
return true;
|
|
}
|
|
|
|
void deleteStepRequests(ThreadReferenceProxy requestsInThread) {
|
|
EventRequestManager requestManager = getVirtualMachineProxy().eventRequestManager();
|
|
List stepRequests = requestManager.stepRequests();
|
|
if (stepRequests.size() > 0) {
|
|
List toDelete = new ArrayList();
|
|
for (Iterator iterator = stepRequests.iterator(); iterator.hasNext();) {
|
|
StepRequest request = (StepRequest)iterator.next();
|
|
ThreadReference threadReference = request.thread();
|
|
|
|
if (threadReference.status() == ThreadReference.THREAD_STATUS_UNKNOWN) {
|
|
// [jeka] on attempt to delete a request assigned to a thread with unknown status, a JDWP error occures
|
|
continue;
|
|
}
|
|
else if(threadReference.equals(requestsInThread.getThreadReference())) {
|
|
toDelete.add(request);
|
|
}
|
|
}
|
|
for (Iterator iterator = toDelete.iterator(); iterator.hasNext();) {
|
|
StepRequest request = (StepRequest)iterator.next();
|
|
requestManager.deleteEventRequest(request);
|
|
}
|
|
}
|
|
}
|
|
|
|
private String getCurrentClassName(ThreadReferenceProxyImpl thread) {
|
|
try {
|
|
final ThreadReferenceProxyImpl currentThreadProxy = thread;
|
|
if (currentThreadProxy != null) {
|
|
if (currentThreadProxy.frameCount() > 0) {
|
|
StackFrameProxyImpl stackFrame = currentThreadProxy.frame(0);
|
|
Location location = stackFrame.location();
|
|
ReferenceType referenceType = location.declaringType();
|
|
if (referenceType != null) {
|
|
return referenceType.name();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (EvaluateException e) {
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private VirtualMachine createVirtualMachineInt()
|
|
throws ExecutionException {
|
|
|
|
try {
|
|
if (myArguments != null) {
|
|
throw new IOException("DebugProcessImpl is already listening");
|
|
}
|
|
|
|
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("Cannot listen using " +
|
|
(!myConnection.isUseSockets() ? "shared memory" : "socket") +
|
|
" transport: required connector not found. Check your JDK installation.");
|
|
}
|
|
myArguments = connector.defaultArguments();
|
|
if (myArguments == null) {
|
|
throw new CantRunException("The port to listen at unspecified");
|
|
}
|
|
|
|
if (address == null) {
|
|
throw new CantRunException("The port to listen at unspecified");
|
|
}
|
|
// negative port number means the caller leaves to debugger to decide at which hport to listen
|
|
Connector.Argument portArg = myConnection.isUseSockets()
|
|
? (Connector.Argument)myArguments.get("port")
|
|
: (Connector.Argument)myArguments.get("name");
|
|
if (portArg != null) {
|
|
portArg.setValue(address);
|
|
}
|
|
connector.startListening(myArguments);
|
|
myDebugProcessDispatcher.getMulticaster().connectorIsReady();
|
|
try {
|
|
return connector.accept(myArguments);
|
|
}
|
|
finally {
|
|
if(myArguments != null) connector.stopListening(myArguments);
|
|
}
|
|
}
|
|
else {
|
|
AttachingConnector connector = (AttachingConnector)findConnector(
|
|
myConnection.isUseSockets() ? SOCKET_ATTACHING_CONNECTOR_NAME : SHMEM_ATTACHING_CONNECTOR_NAME);
|
|
|
|
if (connector == null) {
|
|
throw new CantRunException("Cannot connect using " +
|
|
(myConnection.isUseSockets() ? "socket" : "shared memory") +
|
|
" transport: required connector not found. Check your JDK installation.");
|
|
}
|
|
myArguments = connector.defaultArguments();
|
|
Connector.Argument argument;
|
|
if (myConnection.isUseSockets()) {
|
|
argument = (Connector.Argument)myArguments.get("hostname");
|
|
if (argument != null && myConnection.getHostName() != null) {
|
|
argument.setValue(myConnection.getHostName());
|
|
}
|
|
if (address == null) {
|
|
throw new CantRunException("The port to attach to unspecified");
|
|
}
|
|
argument = (Connector.Argument)myArguments.get("port");
|
|
if (argument != null) {
|
|
argument.setValue(address);
|
|
}
|
|
}
|
|
else {
|
|
if (address == null) {
|
|
throw new CantRunException("Shared memory address unspecified");
|
|
}
|
|
argument = (Connector.Argument)myArguments.get("name");
|
|
if (argument != null) {
|
|
argument.setValue(address);
|
|
}
|
|
}
|
|
myDebugProcessDispatcher.getMulticaster().connectorIsReady();
|
|
try {
|
|
if(SOCKET_ATTACHING_CONNECTOR_NAME.equals(connector.name()) && Patches.SUN_BUG_338675) {
|
|
String portString = myConnection.getAddress();
|
|
String hostString = myConnection.getHostName();
|
|
|
|
if (hostString == null || hostString.length() == 0) {
|
|
hostString = "localhost";
|
|
}
|
|
hostString = hostString + ":";
|
|
|
|
myConnectionService = ((TransportService) connector.transport()).attach(hostString + portString);
|
|
return ((VirtualMachineManagerService) Bootstrap.virtualMachineManager()).createVirtualMachine(myConnectionService);
|
|
}
|
|
else {
|
|
return connector.attach(myArguments);
|
|
}
|
|
}
|
|
catch (IllegalArgumentException e) {
|
|
throw new CantRunException("Connector myArguments invalid : " + e.getMessage());
|
|
}
|
|
}
|
|
}
|
|
catch (IOException e) {
|
|
throw new ExecutionException(createConnectionStatusMessage(processError(e), myConnection), e);
|
|
}
|
|
catch (IllegalConnectorArgumentsException e) {
|
|
throw new ExecutionException(createConnectionStatusMessage(processError(e), myConnection), e);
|
|
}
|
|
finally {
|
|
myArguments = null;
|
|
myConnectionService = null;
|
|
}
|
|
}
|
|
|
|
private void pushStatisText(String text) {
|
|
if (myStatusText == null) {
|
|
myStatusText = ((StatusBarEx)WindowManager.getInstance().getStatusBar(getProject())).getInfo();
|
|
}
|
|
|
|
myStatusStack.addLast(myStatusText);
|
|
showStatusText(text);
|
|
}
|
|
|
|
private void popStatisText() {
|
|
if (!myStatusStack.isEmpty()) {
|
|
showStatusText(myStatusStack.removeFirst());
|
|
}
|
|
}
|
|
|
|
public void showStatusText(final String text) {
|
|
myStatusText = text;
|
|
DebuggerSmoothManager.getInstanceEx().action("DebugProcessImpl.showStatusText", new Runnable() {
|
|
public void run() {
|
|
if (ProjectManagerEx.getInstanceEx().isProjectOpened(getProject())) {
|
|
WindowManager.getInstance().getStatusBar(getProject()).setInfo(text);
|
|
myStatusText = null;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
private static Connector findConnector(String connectorName) throws ExecutionException {
|
|
VirtualMachineManager virtualMachineManager = null;
|
|
try {
|
|
virtualMachineManager = Bootstrap.virtualMachineManager();
|
|
}
|
|
catch (Error e) {
|
|
throw new ExecutionException(e.getClass().getName() + " : " + e.getMessage() + ". Check your JDK installation.");
|
|
}
|
|
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 (Iterator it = connectors.iterator(); it.hasNext();) {
|
|
Connector connector = (Connector)it.next();
|
|
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() {
|
|
public void run() {
|
|
Messages.showMessageDialog(getProject(),
|
|
"The debuggee VM version is \"" + version +
|
|
"\".\nJ2SDK 1.4.0 documented bugs may cause unstable debugger behavior.\nWe recommend you using J2SDK 1.4.0_01 or higher.",
|
|
"VM Version Warning",
|
|
Messages.getWarningIcon());
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
/*Event dispatching*/
|
|
public void addEvaluationListener(EvaluationListener evaluationListener) {
|
|
myEvaluationDispatcher.addListener(evaluationListener);
|
|
}
|
|
|
|
public void removeEvaluationListener(EvaluationListener evaluationListener) {
|
|
myEvaluationDispatcher.removeListener(evaluationListener);
|
|
}
|
|
|
|
public void addDebugProcessListener(DebugProcessListener listener) {
|
|
myDebugProcessDispatcher.addListener(listener);
|
|
}
|
|
|
|
public void removeDebugProcessListener(DebugProcessListener listener) {
|
|
myDebugProcessDispatcher.removeListener(listener);
|
|
}
|
|
|
|
public void addProcessListener(ProcessListener processListener) {
|
|
synchronized(myProcessListeners) {
|
|
if(getExecutionResult() != null) {
|
|
getExecutionResult().getProcessHandler().addProcessListener(processListener);
|
|
}
|
|
else {
|
|
myProcessListeners.add(processListener);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void removeProcessListener(ProcessListener processListener) {
|
|
synchronized (myProcessListeners) {
|
|
if(getExecutionResult() != null) {
|
|
getExecutionResult().getProcessHandler().removeProcessListener(processListener);
|
|
}
|
|
else {
|
|
myProcessListeners.remove(processListener);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* getters */
|
|
public RemoteConnection getConnection() {
|
|
return myConnection;
|
|
}
|
|
|
|
public ExecutionResult getExecutionResult() {
|
|
return myExecution;
|
|
}
|
|
|
|
public <T> T getUserData(Key<T> key) {
|
|
return (T)myUserData.get(key);
|
|
}
|
|
|
|
public <T> void putUserData(Key<T> key, T value) {
|
|
myUserData.put(key, value);
|
|
}
|
|
|
|
public Project getProject() {
|
|
return myProject;
|
|
}
|
|
|
|
public boolean canRedefineClasses() {
|
|
return myCanRedefineClasses;
|
|
}
|
|
|
|
public boolean canWatchFieldModification() {
|
|
return myCanWatchFieldModification;
|
|
}
|
|
|
|
public boolean isAttached() {
|
|
return myState == STATE_ATTACHED;
|
|
}
|
|
|
|
public boolean isDetached() {
|
|
return myState == STATE_DETACHED;
|
|
}
|
|
|
|
public boolean isDetaching() {
|
|
return myState == STATE_DETACHING;
|
|
}
|
|
|
|
protected void setIsAttached() {
|
|
DebuggerManagerThreadImpl.assertIsManagerThread();
|
|
myState = STATE_ATTACHED;
|
|
}
|
|
|
|
protected void setIsDetaching() {
|
|
DebuggerManagerThreadImpl.assertIsManagerThread();
|
|
myState = STATE_DETACHING;
|
|
}
|
|
|
|
protected void setIsDetached() {
|
|
DebuggerManagerThreadImpl.assertIsManagerThread();
|
|
myState = STATE_DETACHED;
|
|
}
|
|
|
|
public RequestManagerImpl getRequestsManager() {
|
|
return myRequestManager;
|
|
}
|
|
|
|
public VirtualMachineProxyImpl getVirtualMachineProxy() {
|
|
DebuggerManagerThreadImpl.assertIsManagerThread();
|
|
if (myVirtualMachineProxy == null) throw new VMDisconnectedException();
|
|
return myVirtualMachineProxy;
|
|
}
|
|
|
|
public void appendPositionManager(final PositionManager positionManager) {
|
|
DebuggerManagerThreadImpl.assertIsManagerThread();
|
|
myPositionManager.appendPositionManager(positionManager);
|
|
DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager().updateBreakpoints(this);
|
|
}
|
|
|
|
private LineBreakpoint myRunToCursorBreakpoint;
|
|
|
|
public void cancelRunToCursorBreakpoint() {
|
|
DebuggerManagerThreadImpl.assertIsManagerThread();
|
|
if (myRunToCursorBreakpoint != null) {
|
|
getRequestsManager().deleteRequest(myRunToCursorBreakpoint);
|
|
myRunToCursorBreakpoint.delete();
|
|
myRunToCursorBreakpoint = null;
|
|
}
|
|
}
|
|
|
|
protected void closeProcess(boolean fireDetached) {
|
|
DebuggerManagerThreadImpl.assertIsManagerThread();
|
|
|
|
if (isDetached() || isDetaching()) return;
|
|
|
|
setIsDetaching();
|
|
myVirtualMachineProxy = null;
|
|
myPositionManager = null;
|
|
|
|
DebugProcessImpl.this.getManagerThread().close();
|
|
|
|
setIsDetached();
|
|
if(fireDetached) {
|
|
myDebugProcessDispatcher.getMulticaster().processDetached(DebugProcessImpl.this);
|
|
}
|
|
myWaitFor.up();
|
|
}
|
|
|
|
private static String formatMessage(String message) {
|
|
final int lineLength = 90;
|
|
StringBuffer buf = new StringBuffer(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.getMessage();
|
|
}
|
|
else if (e instanceof IllegalConnectorArgumentsException) {
|
|
IllegalConnectorArgumentsException e1 = (IllegalConnectorArgumentsException)e;
|
|
message = formatMessage("Bad Argument : " + e1.getMessage()) + e1.argumentNames();
|
|
if (LOG.isDebugEnabled()) {
|
|
LOG.debug(e1);
|
|
}
|
|
}
|
|
else if (e instanceof CantRunException) {
|
|
message = "Error Launching Debuggee.\n" + ((CantRunException)e).getMessage();
|
|
}
|
|
else if (e instanceof VMDisconnectedException) {
|
|
message = "VM Disconnected.\n" + "Target virtual machine closed connection.";
|
|
}
|
|
else if (e instanceof UnknownHostException) {
|
|
message = "Cannot Connect to Remote Process.\n" +
|
|
"Host unknown: " + ((UnknownHostException)e).getMessage();
|
|
}
|
|
else if (e instanceof IOException) {
|
|
IOException e1 = (IOException)e;
|
|
StringBuffer buf = new StringBuffer("Unable to open debugger port : ");
|
|
buf.append(e1.getClass().getName() + " ");
|
|
if (e1.getMessage() != null && e1.getMessage().length() > 0) {
|
|
buf.append('"');
|
|
buf.append(e1.getMessage());
|
|
buf.append('"');
|
|
}
|
|
if (LOG.isDebugEnabled()) {
|
|
LOG.debug(e1);
|
|
}
|
|
message = buf.toString();
|
|
}
|
|
else if (e instanceof ExecutionException) {
|
|
message = ((ExecutionException)e).getMessage();
|
|
}
|
|
else {
|
|
Exception e1 = (Exception)e;
|
|
message = "Error Connecting to Remote Process.\n" +
|
|
"Exception occured: " + e1.getClass().getName() + "\n" +
|
|
"Exception message: " + e1.getMessage();
|
|
if (LOG.isDebugEnabled()) {
|
|
LOG.debug(e1);
|
|
}
|
|
}
|
|
return message;
|
|
}
|
|
|
|
public NodeRendererManagerImpl getNodeRendererManager() {
|
|
return myNodeRendererManager;
|
|
}
|
|
|
|
public DescriptorHistoryManager getDescriptorHistoryManager() {
|
|
return myDescriptorHistoryManager;
|
|
}
|
|
|
|
public static String createConnectionStatusMessage(String connectStatus, RemoteConnection connection) {
|
|
StringBuffer message = new StringBuffer(128);
|
|
message.append(connectStatus);
|
|
if (connection.isUseSockets()) {
|
|
message.append(" at '");
|
|
message.append(connection.getHostName());
|
|
message.append(':');
|
|
message.append(connection.getAddress());
|
|
message.append("' using socket transport");
|
|
}
|
|
else {
|
|
message.append(" at address '");
|
|
message.append(connection.getAddress());
|
|
message.append("' using shared memory transport");
|
|
}
|
|
message.append(". ");
|
|
String _msg = message.toString();
|
|
return _msg;
|
|
}
|
|
|
|
public void dispose() {
|
|
LOG.assertTrue(!isAttached());
|
|
myNodeRendererManager.dispose();
|
|
myDescriptorHistoryManager.dispose();
|
|
}
|
|
|
|
public DebuggerManagerThreadImpl getManagerThread() {
|
|
synchronized (this) {
|
|
if (myDebuggerManagerThread == null) {
|
|
myDebuggerManagerThread = new DebuggerManagerThreadImpl();
|
|
}
|
|
return myDebuggerManagerThread;
|
|
}
|
|
}
|
|
|
|
private static int getInvokePolicy(SuspendContext suspendContext) {
|
|
return suspendContext.getSuspendPolicy() == EventRequest.SUSPEND_EVENT_THREAD ? ThreadReference.INVOKE_SINGLE_THREADED : 0;
|
|
}
|
|
|
|
public void waitFor() {
|
|
LOG.assertTrue(!DebuggerManagerThreadImpl.isManagerThread());
|
|
myWaitFor.waitFor();
|
|
}
|
|
|
|
private abstract class InvokeCommand <E extends Value> {
|
|
protected abstract E invokeMethod(int invokePolicy) throws InvocationException,
|
|
ClassNotLoadedException,
|
|
IncompatibleThreadStateException,
|
|
InvalidTypeException;
|
|
|
|
public E start(EvaluationContextImpl evaluationContext, Method method) throws EvaluateException {
|
|
DebuggerManagerThreadImpl.assertIsManagerThread();
|
|
SuspendContextImpl suspendContext = evaluationContext.getSuspendContext();
|
|
SuspendManagerUtil.assertSuspendContext(suspendContext);
|
|
|
|
myEvaluationDispatcher.getMulticaster().evaluationStarted(suspendContext);
|
|
|
|
beforeMethodInvocation(suspendContext, method);
|
|
ThreadReferenceProxyImpl invokeThread = suspendContext.getThread();
|
|
|
|
if (SuspendManagerUtil.isEvaluating(getSuspendManager(), invokeThread)) {
|
|
throw EvaluateExceptionUtil.NESTED_EVALUATION_ERROR;
|
|
}
|
|
|
|
Set<SuspendContextImpl> suspendingContexts = SuspendManagerUtil.getSuspendingContexts(getSuspendManager(), invokeThread);
|
|
for (Iterator<SuspendContextImpl> iterator = suspendingContexts.iterator(); iterator.hasNext();) {
|
|
SuspendContextImpl suspendingContext = iterator.next();
|
|
if (suspendingContext.getThread() != invokeThread) {
|
|
if (LOG.isDebugEnabled()) {
|
|
LOG.debug("Resuming " + invokeThread + "that is paused by " + suspendingContext.getThread());
|
|
}
|
|
LOG.assertTrue(!suspendingContext.getThread().getThreadReference().equals(invokeThread.getThreadReference()));
|
|
getSuspendManager().resumeThread(suspendingContext, invokeThread);
|
|
}
|
|
}
|
|
|
|
Object resumeData = SuspendManagerUtil.prepareForResume(suspendContext);
|
|
suspendContext.setIsEvaluating(evaluationContext);
|
|
|
|
getVirtualMachineProxy().clearCaches();
|
|
|
|
try {
|
|
for (; ;) {
|
|
try {
|
|
return invokeMethodAndFork(suspendContext);
|
|
}
|
|
catch (ClassNotLoadedException e) {
|
|
ReferenceType loadedClass = loadClass(evaluationContext, e.className(), evaluationContext.getClassLoader());
|
|
if (loadedClass == null) throw EvaluateExceptionUtil.createEvaluateException(e);
|
|
}
|
|
}
|
|
}
|
|
catch (ClassNotLoadedException e) {
|
|
throw EvaluateExceptionUtil.createEvaluateException(e);
|
|
}
|
|
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);
|
|
}
|
|
finally {
|
|
suspendContext.setIsEvaluating(null);
|
|
SuspendManagerUtil.restoreAfterResume(suspendContext, resumeData);
|
|
for (Iterator<SuspendContextImpl> iterator = getSuspendManager().getEventContexts().iterator(); iterator.hasNext();) {
|
|
SuspendContextImpl suspendingContext = iterator.next();
|
|
if (suspendingContexts.contains(suspendingContext) && !suspendingContext.isEvaluating() && !suspendingContext.suspends(invokeThread)) {
|
|
getSuspendManager().suspendThread(suspendingContext, invokeThread);
|
|
}
|
|
}
|
|
|
|
if (LOG.isDebugEnabled()) {
|
|
LOG.debug("getVirtualMachine().clearCaches()");
|
|
}
|
|
getVirtualMachineProxy().clearCaches();
|
|
afterMethodInvocation(suspendContext);
|
|
|
|
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];
|
|
DebugProcessImpl.this.getManagerThread().startLongProcessAndFork(new Runnable() {
|
|
public void run() {
|
|
ThreadReferenceProxyImpl thread = context.getThread();
|
|
try {
|
|
if (LOG.isDebugEnabled()) {
|
|
getVirtualMachineProxy().logThreads();
|
|
LOG.debug("Invoke in " + thread.name());
|
|
LOG.assertTrue(thread.isSuspended(), thread.toString());
|
|
LOG.assertTrue(context.isEvaluating());
|
|
}
|
|
result[0] = invokeMethod(invokePolicy);
|
|
if(result[0] instanceof ObjectReference) {
|
|
context.keep(((ObjectReference)result[0]));
|
|
}
|
|
}
|
|
catch (Exception e) {
|
|
exception[0] = e;
|
|
}
|
|
finally{
|
|
LOG.assertTrue(thread.isSuspended(), thread.toString());
|
|
LOG.assertTrue(context.isEvaluating());
|
|
}
|
|
}
|
|
});
|
|
|
|
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];
|
|
}
|
|
}
|
|
|
|
public Value invokeMethod(final EvaluationContext evaluationContext, final ObjectReference objRef,
|
|
final Method method,
|
|
final List args) throws EvaluateException {
|
|
|
|
final ThreadReference thread = getEvaluationThread(evaluationContext);
|
|
InvokeCommand<Value> invokeCommand = new InvokeCommand<Value>() {
|
|
protected Value invokeMethod(int invokePolicy) throws InvocationException,
|
|
ClassNotLoadedException,
|
|
IncompatibleThreadStateException,
|
|
InvalidTypeException {
|
|
if (LOG.isDebugEnabled()) {
|
|
LOG.debug("Invoke " + method.name());
|
|
}
|
|
return objRef.invokeMethod(thread, method, args, invokePolicy);
|
|
}
|
|
};
|
|
return invokeCommand.start((EvaluationContextImpl)evaluationContext, method);
|
|
}
|
|
|
|
private ThreadReference getEvaluationThread(final EvaluationContext evaluationContext) throws EvaluateException {
|
|
ThreadReferenceProxy evaluationThread = evaluationContext.getSuspendContext().getThread();
|
|
if(evaluationThread == null) throw EvaluateExceptionUtil.NULL_STACK_FRAME;
|
|
return evaluationThread.getThreadReference();
|
|
}
|
|
|
|
public Value invokeMethod(final EvaluationContext evaluationContext, final ClassType classType,
|
|
final Method method,
|
|
final List args) throws EvaluateException {
|
|
|
|
final ThreadReference thread = getEvaluationThread(evaluationContext);
|
|
InvokeCommand<Value> invokeCommand = new InvokeCommand<Value>() {
|
|
protected Value invokeMethod(int invokePolicy) throws InvocationException,
|
|
ClassNotLoadedException,
|
|
IncompatibleThreadStateException,
|
|
InvalidTypeException {
|
|
if (LOG.isDebugEnabled()) {
|
|
LOG.debug("Invoke " + method.name());
|
|
}
|
|
return classType.invokeMethod(thread, method, args, invokePolicy);
|
|
}
|
|
};
|
|
return invokeCommand.start((EvaluationContextImpl)evaluationContext, method);
|
|
}
|
|
|
|
public ArrayReference newInstance(final ArrayType arrayType,
|
|
final int dimension)
|
|
throws EvaluateException {
|
|
return arrayType.newInstance(dimension);
|
|
}
|
|
|
|
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>() {
|
|
protected ObjectReference invokeMethod(int invokePolicy) 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);
|
|
}
|
|
|
|
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) {
|
|
if (LOG.isDebugEnabled()) {
|
|
LOG.debug(
|
|
"before invocation in thread " + suspendContext.getThread().name() + " method " + (method == null ? "null" : method.name()));
|
|
}
|
|
|
|
if (method != null) {
|
|
pushStatisText("Evaluating " + DebuggerUtilsEx.methodName(method));
|
|
}
|
|
else {
|
|
pushStatisText("Evaluating ...");
|
|
}
|
|
}
|
|
|
|
private void afterMethodInvocation(SuspendContextImpl suspendContext) {
|
|
if (LOG.isDebugEnabled()) {
|
|
LOG.debug("after invocation in thread " + suspendContext.getThread().name());
|
|
}
|
|
popStatisText();
|
|
}
|
|
|
|
public ReferenceType findClass(EvaluationContext evaluationContext, String className,
|
|
ClassLoaderReference classLoader) throws EvaluateException {
|
|
try {
|
|
DebuggerManagerThreadImpl.assertIsManagerThread();
|
|
ReferenceType result = null;
|
|
final VirtualMachineProxyImpl vmProxy = getVirtualMachineProxy();
|
|
if (vmProxy == null) {
|
|
throw new VMDisconnectedException();
|
|
}
|
|
List list = vmProxy.classesByName(className);
|
|
for (Iterator it = list.iterator(); it.hasNext();) {
|
|
ReferenceType refType = (ReferenceType)it.next();
|
|
if (refType.isPrepared() && Comparing.equal(refType.classLoader(), classLoader)) {
|
|
result = refType;
|
|
break;
|
|
}
|
|
}
|
|
if (result == null) {
|
|
return loadClass((EvaluationContextImpl)evaluationContext, className, classLoader);
|
|
}
|
|
else {
|
|
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 String reformatArrayName(String className) {
|
|
if (className.indexOf('[') == -1) return className;
|
|
|
|
int dims = 0;
|
|
while (className.endsWith("[]")) {
|
|
className = className.substring(0, className.length() - 2);
|
|
dims++;
|
|
}
|
|
|
|
StringBuffer buffer = new StringBuffer();
|
|
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();
|
|
}
|
|
|
|
public ReferenceType loadClass(EvaluationContextImpl evaluationContext, String qName,
|
|
ClassLoaderReference classLoader) throws InvocationException,
|
|
ClassNotLoadedException,
|
|
IncompatibleThreadStateException,
|
|
InvalidTypeException,
|
|
EvaluateException {
|
|
DebuggerManagerThreadImpl.assertIsManagerThread();
|
|
qName = reformatArrayName(qName);
|
|
ReferenceType refType = null;
|
|
VirtualMachine virtualMachine = getVirtualMachineProxy().getVirtualMachine();
|
|
final List classClasses = virtualMachine.classesByName("java.lang.Class");
|
|
if (classClasses.size() > 0) {
|
|
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;");
|
|
}
|
|
else {
|
|
forNameMethod = classClassType.concreteMethodByName("forName", "(Ljava/lang/String;)Ljava/lang/Class;");
|
|
}
|
|
final List args = new ArrayList(); // do not use unmodifiable lists because the list is modified by JPDA
|
|
final StringReference qNameMirror = virtualMachine.mirrorOf(qName);
|
|
qNameMirror.disableCollection();
|
|
args.add(qNameMirror);
|
|
if (classLoader != null) {
|
|
args.add(virtualMachine.mirrorOf(true));
|
|
args.add(classLoader);
|
|
}
|
|
try {
|
|
final Value value = invokeMethod(evaluationContext, classClassType, forNameMethod, args);
|
|
if (value instanceof ClassObjectReference) {
|
|
refType = ((ClassObjectReference)value).reflectedType();
|
|
}
|
|
}
|
|
finally {
|
|
qNameMirror.enableCollection();
|
|
}
|
|
}
|
|
return refType;
|
|
}
|
|
|
|
public int getSuspendPolicy() {
|
|
return mySuspendPolicy;
|
|
}
|
|
|
|
public void setSuspendPolicy(boolean suspendAll) {
|
|
mySuspendPolicy = suspendAll ? EventRequest.SUSPEND_ALL : EventRequest.SUSPEND_EVENT_THREAD;
|
|
DebuggerSettings.getInstance().setSuspendPolicy(suspendAll);
|
|
}
|
|
|
|
public void setSuspendPolicy(int policy) {
|
|
mySuspendPolicy = policy;
|
|
DebuggerSettings.getInstance().setSuspendPolicy(policy == EventRequest.SUSPEND_ALL);
|
|
}
|
|
|
|
public void logThreads() {
|
|
if (LOG.isDebugEnabled()) {
|
|
try {
|
|
Collection<ThreadReferenceProxyImpl> allThreads = getVirtualMachineProxy().allThreads();
|
|
for (Iterator<ThreadReferenceProxyImpl> iterator = allThreads.iterator(); iterator.hasNext();) {
|
|
ThreadReferenceProxyImpl threadReferenceProxy = iterator.next();
|
|
LOG.debug("Thread name=" + threadReferenceProxy.name() + " suspendCount()=" + threadReferenceProxy.suspendCount());
|
|
}
|
|
}
|
|
catch (Exception e) {
|
|
LOG.debug(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
public SuspendManager getSuspendManager() {
|
|
return mySuspendManager;
|
|
}
|
|
|
|
public CompoundPositionManager getPositionManager() {
|
|
return myPositionManager;
|
|
}
|
|
//ManagerCommands
|
|
|
|
public void stop(boolean forceTerminate) {
|
|
this.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;
|
|
}
|
|
|
|
protected void action() throws Exception {
|
|
if (isAttached()) {
|
|
if (myIsTerminateTargetVM) {
|
|
getVirtualMachineProxy().exit(-1);
|
|
}
|
|
else {
|
|
getVirtualMachineProxy().dispose();
|
|
}
|
|
}
|
|
else {
|
|
stopConnecting();
|
|
}
|
|
}
|
|
}
|
|
|
|
private class StepOutCommand extends ResumeCommand {
|
|
public StepOutCommand(SuspendContextImpl suspendContext) {
|
|
super(suspendContext);
|
|
}
|
|
|
|
public void contextAction() {
|
|
showStatusText("Stepping out");
|
|
if (doStep(getSuspendContext(), StepRequest.STEP_OUT, null)) {
|
|
super.contextAction();
|
|
}
|
|
}
|
|
}
|
|
|
|
private class StepIntoCommand extends ResumeCommand {
|
|
private final boolean myIgnoreFilters;
|
|
|
|
public StepIntoCommand(SuspendContextImpl suspendContext, boolean ignoreFilters) {
|
|
super(suspendContext);
|
|
myIgnoreFilters = ignoreFilters;
|
|
}
|
|
|
|
public void contextAction() {
|
|
showStatusText("Stepping into");
|
|
RequestHint hint = new RequestHint(getSuspendContext(), StepRequest.STEP_INTO);
|
|
hint.setIgnoreFilters(myIgnoreFilters);
|
|
if (doStep(getSuspendContext(), StepRequest.STEP_INTO, hint)) {
|
|
super.contextAction();
|
|
}
|
|
}
|
|
}
|
|
|
|
private class StepOverCommand extends ResumeCommand {
|
|
private boolean myIsIgnoreBreakpoints;
|
|
|
|
public StepOverCommand(SuspendContextImpl suspendContext, boolean ignoreBreakpoints) {
|
|
super(suspendContext);
|
|
myIsIgnoreBreakpoints = ignoreBreakpoints;
|
|
}
|
|
|
|
public void contextAction() {
|
|
showStatusText("Stepping over");
|
|
RequestHint hint = new RequestHint(getSuspendContext(), StepRequest.STEP_OVER);
|
|
hint.setRestoreBreakpoints(myIsIgnoreBreakpoints);
|
|
|
|
if (doStep(getSuspendContext(), StepRequest.STEP_OVER, hint)) {
|
|
if (myIsIgnoreBreakpoints) {
|
|
DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager().disableBreakpoints(DebugProcessImpl.this);
|
|
}
|
|
super.contextAction();
|
|
}
|
|
}
|
|
}
|
|
|
|
private class RunToCursorCommand extends ResumeCommand {
|
|
private final LineBreakpoint myRunToCursorBreakpoint;
|
|
|
|
private RunToCursorCommand(SuspendContextImpl suspendContext, Document document, int lineIndex) {
|
|
super(suspendContext);
|
|
myRunToCursorBreakpoint = DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager().addRunToCursorBreakpoint(document, lineIndex);
|
|
}
|
|
|
|
public void contextAction() {
|
|
showStatusText("Run to cursor");
|
|
cancelRunToCursorBreakpoint();
|
|
if (myRunToCursorBreakpoint == null) {
|
|
return;
|
|
}
|
|
myRunToCursorBreakpoint.SUSPEND_POLICY = DebuggerSettings.SUSPEND_ALL;
|
|
myRunToCursorBreakpoint.LOG_ENABLED = false;
|
|
getRequestsManager().createRequest(myRunToCursorBreakpoint);
|
|
DebugProcessImpl.this.myRunToCursorBreakpoint = myRunToCursorBreakpoint;
|
|
super.contextAction();
|
|
}
|
|
}
|
|
|
|
private class ResumeCommand extends SuspendContextCommandImpl {
|
|
|
|
public ResumeCommand(SuspendContextImpl suspendContext) {
|
|
super(suspendContext);
|
|
}
|
|
|
|
public void contextAction() {
|
|
showStatusText("Process resumed");
|
|
getSuspendManager().resume(getSuspendContext());
|
|
myDebugProcessDispatcher.getMulticaster().resumed(getSuspendContext());
|
|
}
|
|
}
|
|
|
|
private class PauseCommand extends DebuggerCommandImpl {
|
|
public PauseCommand() {
|
|
}
|
|
|
|
public void action() {
|
|
if (!isAttached()) return;
|
|
logThreads();
|
|
getVirtualMachineProxy().suspend();
|
|
logThreads();
|
|
SuspendContextImpl suspendContext = mySuspendManager.pushSuspendContext(EventRequest.SUSPEND_ALL, 0);
|
|
mySuspendManager.notifyPaused(suspendContext);
|
|
}
|
|
}
|
|
|
|
private class ResumeThreadCommand extends SuspendContextCommandImpl {
|
|
private final ThreadReferenceProxyImpl myThread;
|
|
|
|
public ResumeThreadCommand(SuspendContextImpl suspendContext, ThreadReferenceProxyImpl thread) {
|
|
super(suspendContext);
|
|
myThread = thread;
|
|
}
|
|
|
|
public void contextAction() {
|
|
if (getSuspendManager().isFrozen(myThread)) {
|
|
getSuspendManager().unfreezeThread(myThread);
|
|
}
|
|
|
|
if (getSuspendContext().getThread() == myThread) {
|
|
DebugProcessImpl.this.getManagerThread().invoke(createResumeCommand(getSuspendContext()));
|
|
}
|
|
else {
|
|
Set<SuspendContextImpl> suspendingContexts = SuspendManagerUtil.getSuspendingContexts(getSuspendManager(), myThread);
|
|
for (Iterator<SuspendContextImpl> iterator = suspendingContexts.iterator(); iterator.hasNext();) {
|
|
SuspendContextImpl suspendContext = iterator.next();
|
|
getSuspendManager().resumeThread(suspendContext, myThread);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private class FreezeThreadCommand extends DebuggerCommandImpl {
|
|
private final ThreadReferenceProxyImpl myThread;
|
|
|
|
public FreezeThreadCommand(ThreadReferenceProxyImpl thread) {
|
|
myThread = thread;
|
|
}
|
|
|
|
protected void action() throws Exception {
|
|
SuspendManager suspendManager = getSuspendManager();
|
|
if (!suspendManager.isFrozen(myThread)) {
|
|
suspendManager.freezeThread(myThread);
|
|
}
|
|
}
|
|
}
|
|
|
|
private class PopFrameCommand extends DebuggerContextCommandImpl {
|
|
private final StackFrameProxyImpl myStackFrame;
|
|
|
|
public PopFrameCommand(DebuggerContextImpl context, StackFrameProxyImpl frameProxy) {
|
|
super(context);
|
|
myStackFrame = frameProxy;
|
|
}
|
|
|
|
public void threadAction() {
|
|
ThreadReferenceProxyImpl thread = myStackFrame.threadProxy();
|
|
|
|
if (myStackFrame.isBottom()) {
|
|
DebuggerInvocationUtil.invokeLater(myProject, new Runnable() {
|
|
public void run() {
|
|
Messages.showMessageDialog(myProject, "Cannot pop bottom frame", "Action not Perfomed", Messages.getErrorIcon());
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
|
|
try {
|
|
thread.popFrames(myStackFrame);
|
|
}
|
|
catch (EvaluateException e) {
|
|
LOG.error(e);
|
|
}
|
|
getSuspendManager().popFrame(getSuspendContext());
|
|
}
|
|
}
|
|
|
|
|
|
public ExecutionResult attachVirtualMachine(final RunProfileState state, final RemoteConnection remoteConnection, boolean pollConnection)
|
|
throws ExecutionException {
|
|
myWaitFor.down();
|
|
|
|
LOG.assertTrue(SwingUtilities.isEventDispatchThread());
|
|
LOG.assertTrue(myState == STATE_INITIAL);
|
|
|
|
myConnection = remoteConnection;
|
|
|
|
createVirtualMachine(pollConnection);
|
|
|
|
try {
|
|
synchronized (myProcessListeners) {
|
|
myExecution = state.execute();
|
|
|
|
for (Iterator<ProcessListener> iterator = myProcessListeners.iterator(); iterator.hasNext();) {
|
|
ProcessListener processListener = iterator.next();
|
|
myExecution.getProcessHandler().addProcessListener(processListener);
|
|
}
|
|
myProcessListeners.clear();
|
|
}
|
|
}
|
|
catch (ExecutionException e) {
|
|
stop(false);
|
|
throw e;
|
|
}
|
|
|
|
if (ApplicationManager.getApplication().isUnitTestMode()) {
|
|
return myExecution;
|
|
}
|
|
|
|
final Alarm debugPortTimeout = new Alarm(Alarm.ThreadToUse.SHARED_THREAD);
|
|
|
|
myExecution.getProcessHandler().addProcessListener(new ProcessAdapter() {
|
|
public void processTerminated(ProcessEvent event) {
|
|
debugPortTimeout.cancelAllRequests();
|
|
}
|
|
|
|
public void startNotified(ProcessEvent event) {
|
|
debugPortTimeout.addRequest(new Runnable() {
|
|
public void run() {
|
|
if(myState == STATE_INITIAL) {
|
|
ApplicationManager.getApplication().invokeLater(new Runnable() {
|
|
public void run() {
|
|
String message = createConnectionStatusMessage(
|
|
"Check your run/debug configuration. Failed to establish connection to the target VM",
|
|
remoteConnection
|
|
);
|
|
Messages.showErrorDialog(myProject, message, "Cannot Debug Application");
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}, LOCAL_START_TIMEOUT);
|
|
}
|
|
});
|
|
|
|
return myExecution;
|
|
}
|
|
|
|
private void createVirtualMachine(final boolean pollConnection) throws ExecutionException {
|
|
final Semaphore semaphore = new Semaphore();
|
|
final ExecutionException[] exception = new ExecutionException[1];
|
|
|
|
myDebugProcessDispatcher.addListener(new DebugProcessAdapter() {
|
|
public void connectorIsReady() {
|
|
semaphore.up();
|
|
myDebugProcessDispatcher.removeListener(this);
|
|
}
|
|
});
|
|
|
|
final long time = System.currentTimeMillis();
|
|
|
|
this.getManagerThread().invokeLater(new DebuggerCommandImpl() {
|
|
protected void action() {
|
|
VirtualMachine vm = null;
|
|
|
|
try {
|
|
while (System.currentTimeMillis() - time < LOCAL_START_TIMEOUT) {
|
|
try {
|
|
vm = createVirtualMachineInt();
|
|
break;
|
|
}
|
|
catch (ExecutionException e) {
|
|
if (pollConnection && !myConnection.isServerMode() && e.getCause() instanceof IOException) {
|
|
synchronized (this) {
|
|
try {
|
|
wait(500);
|
|
}
|
|
catch (InterruptedException ie) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
exception[0] = e;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
finally {
|
|
semaphore.up();
|
|
}
|
|
|
|
if(vm != null) {
|
|
final VirtualMachine vm1 = vm;
|
|
afterProcessStarted(new Runnable() {
|
|
public void run() {
|
|
getManagerThread().invokeLater(new DebuggerCommandImpl() {
|
|
protected void action() throws Exception {
|
|
commitVM(vm1);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
semaphore.down();
|
|
semaphore.waitFor();
|
|
|
|
if (exception[0] != null) throw exception[0];
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
public void startNotified(ProcessEvent event) {
|
|
run();
|
|
}
|
|
}
|
|
MyProcessAdapter processListener = new MyProcessAdapter();
|
|
addProcessListener(processListener);
|
|
if(myExecution != null) {
|
|
if(myExecution.getProcessHandler().isStartNotified()) {
|
|
processListener.run();
|
|
}
|
|
}
|
|
}
|
|
|
|
public DebuggerCommandImpl createPauseCommand() {
|
|
return new PauseCommand();
|
|
}
|
|
|
|
public SuspendContextCommandImpl createResumeCommand(SuspendContextImpl suspendContext) {
|
|
return new ResumeCommand(suspendContext);
|
|
}
|
|
|
|
public SuspendContextCommandImpl createStepOverCommand(SuspendContextImpl suspendContext, boolean ignoreBreakpoints) {
|
|
return new StepOverCommand(suspendContext, ignoreBreakpoints);
|
|
}
|
|
|
|
public SuspendContextCommandImpl createStepOutCommand(SuspendContextImpl suspendContext) {
|
|
return new StepOutCommand(suspendContext);
|
|
}
|
|
|
|
public SuspendContextCommandImpl createStepIntoCommand(SuspendContextImpl suspendContext, boolean ignoreFilters) {
|
|
return new StepIntoCommand(suspendContext, ignoreFilters);
|
|
}
|
|
|
|
public SuspendContextCommandImpl createRunToCursorCommand(SuspendContextImpl suspendContext, Document document, int lineIndex)
|
|
throws EvaluateException {
|
|
RunToCursorCommand runToCursorCommand = new RunToCursorCommand(suspendContext, document, lineIndex);
|
|
if(runToCursorCommand.myRunToCursorBreakpoint == null) {
|
|
PsiFile psiFile = PsiDocumentManager.getInstance(getProject()).getPsiFile(document);
|
|
throw new EvaluateException("There is no executable code at " + psiFile.getName() + ":" + 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) {
|
|
return new PopFrameCommand(context, stackFrame);
|
|
}
|
|
}
|
|
|