2 * Copyright 2000-2016 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.jetbrains.python.debugger;
18 import com.google.common.base.Strings;
19 import com.google.common.collect.Lists;
20 import com.google.common.collect.Maps;
21 import com.intellij.execution.process.ProcessEvent;
22 import com.intellij.execution.process.ProcessHandler;
23 import com.intellij.execution.process.ProcessListener;
24 import com.intellij.execution.ui.ConsoleView;
25 import com.intellij.execution.ui.ConsoleViewContentType;
26 import com.intellij.execution.ui.ExecutionConsole;
27 import com.intellij.openapi.actionSystem.AnActionEvent;
28 import com.intellij.openapi.actionSystem.DefaultActionGroup;
29 import com.intellij.openapi.actionSystem.Presentation;
30 import com.intellij.openapi.actionSystem.ToggleAction;
31 import com.intellij.openapi.application.AccessToken;
32 import com.intellij.openapi.application.ApplicationInfo;
33 import com.intellij.openapi.application.ApplicationManager;
34 import com.intellij.openapi.diagnostic.Logger;
35 import com.intellij.openapi.editor.Document;
36 import com.intellij.openapi.extensions.Extensions;
37 import com.intellij.openapi.fileEditor.FileDocumentManager;
38 import com.intellij.openapi.module.Module;
39 import com.intellij.openapi.module.ModuleUtilCore;
40 import com.intellij.openapi.progress.ProgressIndicator;
41 import com.intellij.openapi.progress.ProgressManager;
42 import com.intellij.openapi.progress.Task;
43 import com.intellij.openapi.project.Project;
44 import com.intellij.openapi.ui.Messages;
45 import com.intellij.openapi.util.Key;
46 import com.intellij.openapi.util.Ref;
47 import com.intellij.openapi.vfs.VirtualFile;
48 import com.intellij.psi.PsiElement;
49 import com.intellij.psi.PsiFile;
50 import com.intellij.psi.PsiManager;
51 import com.intellij.psi.ResolveState;
52 import com.intellij.psi.scope.PsiScopeProcessor;
53 import com.intellij.psi.util.PsiTreeUtil;
54 import com.intellij.remote.RemoteProcessControl;
55 import com.intellij.util.ui.UIUtil;
56 import com.intellij.xdebugger.*;
57 import com.intellij.xdebugger.breakpoints.*;
58 import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider;
59 import com.intellij.xdebugger.frame.XExecutionStack;
60 import com.intellij.xdebugger.frame.XStackFrame;
61 import com.intellij.xdebugger.frame.XSuspendContext;
62 import com.intellij.xdebugger.frame.XValueChildrenList;
63 import com.intellij.xdebugger.stepping.XSmartStepIntoHandler;
64 import com.jetbrains.python.PythonFileType;
65 import com.jetbrains.python.console.PythonConsoleView;
66 import com.jetbrains.python.console.PythonDebugLanguageConsoleView;
67 import com.jetbrains.python.console.pydev.PydevCompletionVariant;
68 import com.jetbrains.python.debugger.pydev.*;
69 import com.jetbrains.python.debugger.settings.PyDebuggerSettings;
70 import com.jetbrains.python.psi.*;
71 import com.jetbrains.python.psi.resolve.PyResolveContext;
72 import com.jetbrains.python.psi.resolve.PyResolveUtil;
73 import com.jetbrains.python.psi.resolve.RatedResolveResult;
74 import com.jetbrains.python.psi.types.PyClassType;
75 import com.jetbrains.python.psi.types.PyModuleType;
76 import com.jetbrains.python.psi.types.PyType;
77 import com.jetbrains.python.psi.types.PyTypeParser;
78 import org.jetbrains.annotations.NotNull;
79 import org.jetbrains.annotations.Nullable;
81 import java.io.IOException;
82 import java.net.ServerSocket;
84 import java.util.concurrent.ConcurrentHashMap;
86 import static javax.swing.SwingUtilities.invokeLater;
91 // todo: bundle messages
92 // todo: pydevd supports module reloading - look for a way to use the feature
93 public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, ProcessListener {
95 private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.debugger.PyDebugProcess");
96 private static final int CONNECTION_TIMEOUT = 60000;
98 private final ProcessDebugger myDebugger;
99 private final XBreakpointHandler[] myBreakpointHandlers;
100 private final PyDebuggerEditorsProvider myEditorsProvider;
101 private final ProcessHandler myProcessHandler;
102 private final ExecutionConsole myExecutionConsole;
103 private final Map<PySourcePosition, XLineBreakpoint> myRegisteredBreakpoints = new ConcurrentHashMap<>();
104 private final Map<String, XBreakpoint<? extends ExceptionBreakpointProperties>> myRegisteredExceptionBreakpoints =
105 new ConcurrentHashMap<>();
107 private final List<PyThreadInfo> mySuspendedThreads = Collections.synchronizedList(Lists.<PyThreadInfo>newArrayList());
108 private final Map<String, XValueChildrenList> myStackFrameCache = Maps.newHashMap();
109 private final Map<String, PyDebugValue> myNewVariableValue = Maps.newHashMap();
110 private boolean myDownloadSources = false;
112 private boolean myClosing = false;
114 private PyPositionConverter myPositionConverter;
115 private final XSmartStepIntoHandler<?> mySmartStepIntoHandler;
116 private boolean myWaitingForConnection = false;
117 private PyStackFrame myConsoleContextFrame = null;
118 private PyReferrersLoader myReferrersProvider;
120 public PyDebugProcess(@NotNull XDebugSession session,
121 @NotNull ServerSocket serverSocket,
122 @NotNull ExecutionConsole executionConsole,
123 @Nullable ProcessHandler processHandler, boolean multiProcess) {
124 this(session, multiProcess ? process -> process.createMultiprocessDebugger(serverSocket)
125 : process -> new RemoteDebugger(process, serverSocket, process.getConnectTimeout()),
126 executionConsole, processHandler);
129 public PyDebugProcess(final @NotNull XDebugSession session,
130 @NotNull final ExecutionConsole executionConsole,
131 @Nullable final ProcessHandler processHandler,
132 @NotNull String serverHost, int serverPort) {
133 this(session, process -> new ClientModeMultiProcessDebugger(process, serverHost, serverPort), executionConsole, processHandler);
136 private PyDebugProcess(@NotNull XDebugSession session,
137 @NotNull DebuggerFactory debuggerFactory,
138 @NotNull ExecutionConsole executionConsole,
139 @Nullable ProcessHandler processHandler) {
142 session.setPauseActionSupported(true);
144 myDebugger = debuggerFactory.createDebugger(this);
146 List<XBreakpointHandler> breakpointHandlers = new ArrayList<>();
147 breakpointHandlers.add(new PyLineBreakpointHandler(this));
148 breakpointHandlers.add(new PyExceptionBreakpointHandler(this));
149 for (PyBreakpointHandlerFactory factory : Extensions.getExtensions(PyBreakpointHandlerFactory.EP_NAME)) {
150 breakpointHandlers.add(factory.createBreakpointHandler(this));
152 myBreakpointHandlers = breakpointHandlers.toArray(new XBreakpointHandler[breakpointHandlers.size()]);
154 myEditorsProvider = new PyDebuggerEditorsProvider();
155 mySmartStepIntoHandler = new PySmartStepIntoHandler(this);
156 myProcessHandler = processHandler;
157 myExecutionConsole = executionConsole;
158 if (myProcessHandler != null) {
159 myProcessHandler.addProcessListener(this);
161 if (processHandler instanceof PositionConverterProvider) {
162 myPositionConverter = ((PositionConverterProvider)processHandler).createPositionConverter(this);
165 myPositionConverter = new PyLocalPositionConverter();
167 myDebugger.addCloseListener(new RemoteDebuggerCloseListener() {
169 public void closed() {
174 public void communicationError() {
175 detachDebuggedProcess();
179 public void detached() {
180 detachDebuggedProcess();
184 session.addSessionListener(new XDebugSessionListener() {
186 public void stackFrameChanged() {
187 String currentFrameThreadId = null;
188 final XStackFrame currentFrame = session.getCurrentStackFrame();
189 if (currentFrame instanceof PyStackFrame) {
190 currentFrameThreadId = ((PyStackFrame)currentFrame).getThreadId();
192 final XExecutionStack activeStack = session.getSuspendContext().getActiveExecutionStack();
193 if ((activeStack == null) || (currentFrameThreadId == null)) {
196 final XStackFrame frameFromSuspendContext = activeStack.getTopFrame();
197 String activeStackThreadId = null;
198 if (frameFromSuspendContext instanceof PyStackFrame) {
199 activeStackThreadId = ((PyStackFrame)frameFromSuspendContext).getThreadId();
201 if (!currentFrameThreadId.equals(activeStackThreadId)) {
202 // another thread was selected, we should update suspendContext
203 PyThreadInfo threadInfo = null;
204 for (PyThreadInfo info : mySuspendedThreads) {
205 if (info.getId().equals(currentFrameThreadId)) {
210 if (threadInfo != null) {
211 getSession().positionReached(createSuspendContext(threadInfo));
218 private MultiProcessDebugger createMultiprocessDebugger(ServerSocket serverSocket) {
219 MultiProcessDebugger debugger = new MultiProcessDebugger(this, serverSocket, 10000);
220 debugger.addOtherDebuggerCloseListener(new MultiProcessDebugger.DebuggerProcessListener() {
222 public void threadsClosed(Set<String> threadIds) {
223 for (PyThreadInfo t : mySuspendedThreads) {
224 if (threadIds.contains(t.getId())) {
225 if (getSession().isSuspended()) {
226 getSession().resume();
236 protected void detachDebuggedProcess() {
237 handleStop(); //in case of normal debug we stop the session
240 protected void handleStop() {
244 public void setPositionConverter(PyPositionConverter positionConverter) {
245 myPositionConverter = positionConverter;
250 public PyPositionConverter getPositionConverter() {
251 return myPositionConverter;
256 public XBreakpointHandler<?>[] getBreakpointHandlers() {
257 return myBreakpointHandlers;
262 public XDebuggerEditorsProvider getEditorsProvider() {
263 return myEditorsProvider;
268 protected ProcessHandler doGetProcessHandler() {
269 return myProcessHandler;
274 public ExecutionConsole createConsole() {
275 return myExecutionConsole;
279 public XSmartStepIntoHandler<?> getSmartStepIntoHandler() {
280 return mySmartStepIntoHandler;
284 public void sessionInitialized() {
285 waitForConnection(getConnectionMessage(), getConnectionTitle());
288 protected void waitForConnection(final String connectionMessage, String connectionTitle) {
289 ProgressManager.getInstance().run(new Task.Backgroundable(getSession().getProject(), connectionTitle, false) {
291 public void run(@NotNull final ProgressIndicator indicator) {
292 indicator.setText(connectionMessage);
295 myWaitingForConnection = true;
296 myDebugger.waitForConnect();
297 myWaitingForConnection = false;
304 catch (final Exception e) {
305 myWaitingForConnection = false;
306 if (myProcessHandler != null) {
307 myProcessHandler.destroyProcess();
311 () -> Messages.showErrorDialog("Unable to establish connection with debugger:\n" + e.getMessage(), getConnectionTitle()));
320 getSession().rebuildViews();
321 registerBreakpoints();
322 setShowReturnValues(PyDebuggerSettings.getInstance().isWatchReturnValues());
326 public int handleDebugPort(int localPort) throws IOException {
327 if (myProcessHandler instanceof RemoteProcessControl) {
328 return getRemoteTunneledPort(localPort, (RemoteProcessControl)myProcessHandler);
335 protected static int getRemoteTunneledPort(int localPort, @NotNull RemoteProcessControl handler) throws IOException {
337 return handler.getRemoteSocket(localPort).getSecond();
339 catch (Exception e) {
340 throw new IOException(e);
345 public void recordSignature(PySignature signature) {
346 PySignatureCacheManager.getInstance(getSession().getProject()).recordSignature(myPositionConverter.convertSignature(signature));
350 public void recordLogEvent(PyConcurrencyEvent event) {
351 PyConcurrencyService.getInstance(getSession().getProject()).recordEvent(getSession(), event, event.isAsyncio());
355 public void showConsole(PyThreadInfo thread) {
356 myConsoleContextFrame = new PyExecutionStack(this, thread).getTopFrame();
357 if (myExecutionConsole instanceof PythonDebugLanguageConsoleView) {
358 PythonDebugLanguageConsoleView consoleView = (PythonDebugLanguageConsoleView)myExecutionConsole;
359 UIUtil.invokeLaterIfNeeded(() -> {
360 consoleView.enableConsole(false);
361 consoleView.getPydevConsoleView().setConsoleEnabled(true);
367 public void consoleInputRequested(boolean isStarted) {
368 if (myExecutionConsole instanceof PythonDebugLanguageConsoleView) {
369 PythonConsoleView consoleView = ((PythonDebugLanguageConsoleView)myExecutionConsole).getPydevConsoleView();
371 consoleView.inputRequested();
374 consoleView.inputReceived();
379 protected void afterConnect() {
382 protected void beforeConnect() {
385 protected String getConnectionMessage() {
386 return "Waiting for connection...";
389 protected String getConnectionTitle() {
390 return "Connecting To Debugger";
393 private void handshake() throws PyDebuggerException {
394 String remoteVersion = myDebugger.handshake();
395 String currentBuild = ApplicationInfo.getInstance().getBuild().asStringWithoutProductCode();
396 if ("@@BUILD_NUMBER@@".equals(remoteVersion)) {
397 remoteVersion = currentBuild;
399 else if (remoteVersion.startsWith("PY-")) {
400 remoteVersion = remoteVersion.substring(3);
403 remoteVersion = null;
405 printToConsole("Connected to pydev debugger (build " + remoteVersion + ")\n", ConsoleViewContentType.SYSTEM_OUTPUT);
407 if (remoteVersion != null) {
408 if (!(remoteVersion.equals(currentBuild) || remoteVersion.startsWith(currentBuild))) {
409 LOG.warn(String.format("Wrong debugger version. Remote version: %s Current build: %s", remoteVersion, currentBuild));
410 printToConsole("Warning: wrong debugger version. Use pycharm-debugger.egg from PyCharm installation folder.\n",
411 ConsoleViewContentType.ERROR_OUTPUT);
417 public void printToConsole(String text, ConsoleViewContentType contentType) {
418 ((ConsoleView)myExecutionConsole).print(text, contentType);
421 private void registerBreakpoints() {
422 registerLineBreakpoints();
423 registerExceptionBreakpoints();
426 private void registerExceptionBreakpoints() {
427 for (XBreakpoint<? extends ExceptionBreakpointProperties> bp : myRegisteredExceptionBreakpoints.values()) {
428 addExceptionBreakpoint(bp);
432 public void registerLineBreakpoints() {
433 for (Map.Entry<PySourcePosition, XLineBreakpoint> entry : myRegisteredBreakpoints.entrySet()) {
434 addBreakpoint(entry.getKey(), entry.getValue());
439 public void registerAdditionalActions(@NotNull DefaultActionGroup leftToolbar,
440 @NotNull DefaultActionGroup topToolbar,
441 @NotNull DefaultActionGroup settings) {
442 super.registerAdditionalActions(leftToolbar, topToolbar, settings);
443 settings.add(new WatchReturnValuesAction(this));
444 settings.add(new SimplifiedView(this));
447 private static class WatchReturnValuesAction extends ToggleAction {
448 private volatile boolean myWatchesReturnValues;
449 private final PyDebugProcess myProcess;
450 private final String myText;
452 public WatchReturnValuesAction(@NotNull PyDebugProcess debugProcess) {
453 super("", "Enables watching executed functions return values", null);
454 myWatchesReturnValues = PyDebuggerSettings.getInstance().isWatchReturnValues();
455 myProcess = debugProcess;
456 myText = "Show Return Values";
460 public void update(@NotNull final AnActionEvent e) {
462 final Presentation presentation = e.getPresentation();
463 presentation.setEnabled(true);
464 presentation.setText(myText);
468 public boolean isSelected(AnActionEvent e) {
469 return myWatchesReturnValues;
473 public void setSelected(AnActionEvent e, boolean watch) {
474 myWatchesReturnValues = watch;
475 PyDebuggerSettings.getInstance().setWatchReturnValues(watch);
476 final Project project = e.getProject();
477 if (project != null) {
478 myProcess.setShowReturnValues(myWatchesReturnValues);
479 myProcess.getSession().rebuildViews();
484 private static class SimplifiedView extends ToggleAction {
485 private volatile boolean mySimplifiedView;
486 private final PyDebugProcess myProcess;
487 private final String myText;
489 public SimplifiedView(@NotNull PyDebugProcess debugProcess) {
490 super("", "Disables watching classes, functions and modules objects", null);
491 mySimplifiedView = PyDebuggerSettings.getInstance().isSimplifiedView();
492 myProcess = debugProcess;
493 myText = "Simplified Variables View";
497 public void update(@NotNull final AnActionEvent e) {
499 final Presentation presentation = e.getPresentation();
500 presentation.setEnabled(true);
501 presentation.setText(myText);
505 public boolean isSelected(AnActionEvent e) {
506 return mySimplifiedView;
510 public void setSelected(AnActionEvent e, boolean hide) {
511 mySimplifiedView = hide;
512 PyDebuggerSettings.getInstance().setSimplifiedView(hide);
513 myProcess.getSession().rebuildViews();
517 public void setShowReturnValues(boolean showReturnValues) {
518 myDebugger.setShowReturnValues(showReturnValues);
522 public void startStepOver(@Nullable XSuspendContext context) {
523 passToCurrentThread(context, ResumeOrStepCommand.Mode.STEP_OVER);
527 public void startStepInto(@Nullable XSuspendContext context) {
528 passToCurrentThread(context, ResumeOrStepCommand.Mode.STEP_INTO);
531 public void startStepIntoMyCode(@Nullable XSuspendContext context) {
532 if (!checkCanPerformCommands()) return;
533 getSession().sessionResumed();
534 passToCurrentThread(context, ResumeOrStepCommand.Mode.STEP_INTO_MY_CODE);
538 public void startStepOut(@Nullable XSuspendContext context) {
539 passToCurrentThread(context, ResumeOrStepCommand.Mode.STEP_OUT);
542 public void startSmartStepInto(String functionName) {
545 for (PyThreadInfo suspendedThread : mySuspendedThreads) {
546 myDebugger.smartStepInto(suspendedThread.getId(), functionName);
557 public void resume(@Nullable XSuspendContext context) {
558 passToAllThreads(ResumeOrStepCommand.Mode.RESUME);
562 public void startPausing() {
564 myDebugger.suspendAllThreads();
568 public void suspendAllOtherThreads(PyThreadInfo thread) {
569 myDebugger.suspendOtherThreads(thread);
573 * Check if there is the thread suspended on the breakpoint with "Suspend all" policy
575 * @return true if this thread exists
578 public boolean isSuspendedOnAllThreadsPolicy() {
579 if (getSession().isSuspended()) {
580 for (PyThreadInfo threadInfo : getThreads()) {
581 final List<PyStackFrameInfo> frames = threadInfo.getFrames();
582 if ((threadInfo.getState() == PyThreadInfo.State.SUSPENDED) && (frames != null)) {
583 XBreakpoint<?> breakpoint = null;
584 if (threadInfo.isStopOnBreakpoint()) {
585 final PySourcePosition position = frames.get(0).getPosition();
586 breakpoint = myRegisteredBreakpoints.get(position);
588 else if (threadInfo.isExceptionBreak()) {
589 String exceptionName = threadInfo.getMessage();
590 if (exceptionName != null) {
591 breakpoint = myRegisteredExceptionBreakpoints.get(exceptionName);
594 if ((breakpoint != null) && (breakpoint.getType().isSuspendThreadSupported()) &&
595 (breakpoint.getSuspendPolicy() == SuspendPolicy.ALL)) {
604 private void passToAllThreads(final ResumeOrStepCommand.Mode mode) {
607 for (PyThreadInfo thread : myDebugger.getThreads()) {
608 myDebugger.resumeOrStep(thread.getId(), mode);
613 private void passToCurrentThread(@Nullable XSuspendContext context, final ResumeOrStepCommand.Mode mode) {
616 String threadId = threadIdBeforeResumeOrStep(context);
618 for (PyThreadInfo suspendedThread : mySuspendedThreads) {
619 if (threadId == null || threadId.equals(suspendedThread.getId())) {
620 myDebugger.resumeOrStep(suspendedThread.getId(), mode);
628 private static String threadIdBeforeResumeOrStep(@Nullable XSuspendContext context) {
629 if (context instanceof PySuspendContext) {
630 return ((PySuspendContext)context).getActiveExecutionStack().getThreadId();
637 protected boolean isConnected() {
638 return myDebugger.isConnected();
641 protected void disconnect() {
642 myDebugger.disconnect();
646 public boolean isDownloadSources() {
647 return myDownloadSources;
650 public void setDownloadSources(boolean downloadSources) {
651 myDownloadSources = downloadSources;
654 protected void cleanUp() {
655 mySuspendedThreads.clear();
656 myDownloadSources = false;
660 public void runToPosition(@NotNull final XSourcePosition position, @Nullable XSuspendContext context) {
662 if (isConnected() && !mySuspendedThreads.isEmpty()) {
663 final PySourcePosition pyPosition = myPositionConverter.convertToPython(position);
664 String type = PyLineBreakpointType.ID;
665 AccessToken lock = ApplicationManager.getApplication().acquireReadActionLock();
667 final Document document = FileDocumentManager.getInstance().getDocument(position.getFile());
668 if (document != null) {
669 for (XBreakpointType breakpointType : Extensions.getExtensions(XBreakpointType.EXTENSION_POINT_NAME)) {
670 if (breakpointType instanceof PyBreakpointType &&
671 ((PyBreakpointType)breakpointType).canPutInDocument(getSession().getProject(), document)) {
672 type = breakpointType.getId();
681 myDebugger.setTempBreakpoint(type, pyPosition.getFile(), pyPosition.getLine());
683 passToCurrentThread(context, ResumeOrStepCommand.Mode.RESUME);
688 public PyDebugValue evaluate(final String expression, final boolean execute, boolean doTrunc) throws PyDebuggerException {
690 final PyStackFrame frame = currentFrame();
691 return evaluate(expression, execute, frame, doTrunc);
694 private PyDebugValue evaluate(String expression, boolean execute, PyStackFrame frame, boolean trimResult) throws PyDebuggerException {
695 return myDebugger.evaluate(frame.getThreadId(), frame.getFrameId(), expression, execute, trimResult);
698 public void consoleExec(String command, PyDebugCallback<String> callback) {
701 final PyStackFrame frame = currentFrame();
702 myDebugger.consoleExec(frame.getThreadId(), frame.getFrameId(), command, callback);
704 catch (PyDebuggerException e) {
711 public XValueChildrenList loadFrame() throws PyDebuggerException {
712 final PyStackFrame frame = currentFrame();
713 //do not reload frame every time it is needed, because due to bug in pdb, reloading frame clears all variable changes
714 if (!myStackFrameCache.containsKey(frame.getThreadFrameId())) {
715 XValueChildrenList values = myDebugger.loadFrame(frame.getThreadId(), frame.getFrameId());
716 myStackFrameCache.put(frame.getThreadFrameId(), values);
718 return applyNewValue(myStackFrameCache.get(frame.getThreadFrameId()), frame.getThreadFrameId());
721 private XValueChildrenList applyNewValue(XValueChildrenList pyDebugValues, String threadFrameId) {
722 if (myNewVariableValue.containsKey(threadFrameId)) {
723 PyDebugValue newValue = myNewVariableValue.get(threadFrameId);
724 XValueChildrenList res = new XValueChildrenList();
725 for (int i = 0; i < pyDebugValues.size(); i++) {
726 final String name = pyDebugValues.getName(i);
727 if (name.equals(newValue.getName())) {
728 res.add(name, newValue);
731 res.add(name, pyDebugValues.getValue(i));
737 return pyDebugValues;
742 public XValueChildrenList loadVariable(final PyDebugValue var) throws PyDebuggerException {
743 final PyStackFrame frame = currentFrame();
744 PyDebugValue debugValue = var.setName(var.getFullName());
745 return myDebugger.loadVariable(frame.getThreadId(), frame.getFrameId(), debugValue);
749 public void loadReferrers(PyReferringObjectsValue var, PyDebugCallback<XValueChildrenList> callback) {
751 final PyStackFrame frame = currentFrame();
752 myDebugger.loadReferrers(frame.getThreadId(), frame.getFrameId(), var, callback);
754 catch (PyDebuggerException e) {
760 public void changeVariable(final PyDebugValue var, final String value) throws PyDebuggerException {
761 final PyStackFrame frame = currentFrame();
762 PyDebugValue newValue = myDebugger.changeVariable(frame.getThreadId(), frame.getFrameId(), var, value);
763 myNewVariableValue.put(frame.getThreadFrameId(), newValue);
768 public PyReferrersLoader getReferrersLoader() {
769 if (myReferrersProvider == null) {
770 myReferrersProvider = new PyReferrersLoader(this);
772 return myReferrersProvider;
776 public ArrayChunk getArrayItems(PyDebugValue var, int rowOffset, int colOffset, int rows, int cols, String format)
777 throws PyDebuggerException {
778 final PyStackFrame frame = currentFrame();
779 return myDebugger.loadArrayItems(frame.getThreadId(), frame.getFrameId(), var, rowOffset, colOffset, rows, cols, format);
783 public String loadSource(String path) {
784 return myDebugger.loadSource(path);
788 public boolean canSaveToTemp(String name) {
789 final Project project = getSession().getProject();
790 return PyDebugSupportUtils.canSaveToTemp(project, name);
794 private PyStackFrame currentFrame() throws PyDebuggerException {
795 if (!isConnected()) {
796 throw new PyDebuggerException("Disconnected");
799 final PyStackFrame frame = (PyStackFrame)getSession().getCurrentStackFrame();
801 if (frame == null && myConsoleContextFrame != null) {
802 return myConsoleContextFrame;
806 throw new PyDebuggerException("Process is running");
813 private String getFunctionName(final XLineBreakpoint breakpoint) {
814 if (breakpoint.getSourcePosition() == null) {
817 final VirtualFile file = breakpoint.getSourcePosition().getFile();
818 AccessToken lock = ApplicationManager.getApplication().acquireReadActionLock();
820 final Document document = FileDocumentManager.getInstance().getDocument(file);
821 final Project project = getSession().getProject();
822 if (document != null) {
823 if (file.getFileType() == PythonFileType.INSTANCE) {
824 PsiElement psiElement = XDebuggerUtil.getInstance().
825 findContextElement(file, document.getLineStartOffset(breakpoint.getSourcePosition().getLine()), project, false);
826 PyFunction function = PsiTreeUtil.getParentOfType(psiElement, PyFunction.class);
827 if (function != null) {
828 return function.getName();
839 public void addBreakpoint(final PySourcePosition position, final XLineBreakpoint breakpoint) {
840 myRegisteredBreakpoints.put(position, breakpoint);
842 final String conditionExpression = breakpoint.getConditionExpression() == null
844 : breakpoint.getConditionExpression().getExpression();
845 final String logExpression = breakpoint.getLogExpressionObject() == null
847 : breakpoint.getLogExpressionObject().getExpression();
848 SuspendPolicy policy = breakpoint.getType().isSuspendThreadSupported()? breakpoint.getSuspendPolicy(): SuspendPolicy.NONE;
849 myDebugger.setBreakpoint(breakpoint.getType().getId(),
854 getFunctionName(breakpoint),
860 public void addTemporaryBreakpoint(String typeId, String file, int line) {
862 myDebugger.setTempBreakpoint(typeId, file, line);
866 public void removeBreakpoint(final PySourcePosition position) {
867 XLineBreakpoint breakpoint = myRegisteredBreakpoints.get(position);
868 if (breakpoint != null) {
869 myRegisteredBreakpoints.remove(position);
871 myDebugger.removeBreakpoint(breakpoint.getType().getId(), position.getFile(), position.getLine());
876 public void addExceptionBreakpoint(XBreakpoint<? extends ExceptionBreakpointProperties> breakpoint) {
877 myRegisteredExceptionBreakpoints.put(breakpoint.getProperties().getException(), breakpoint);
879 myDebugger.addExceptionBreakpoint(breakpoint.getProperties());
883 public void removeExceptionBreakpoint(XBreakpoint<? extends ExceptionBreakpointProperties> breakpoint) {
884 myRegisteredExceptionBreakpoints.remove(breakpoint.getProperties().getException());
886 myDebugger.removeExceptionBreakpoint(breakpoint.getProperties());
890 public Collection<PyThreadInfo> getThreads() {
891 return myDebugger.getThreads();
895 public void threadSuspended(final PyThreadInfo threadInfo, boolean updateSourcePosition) {
896 if (!mySuspendedThreads.contains(threadInfo)) {
897 mySuspendedThreads.add(threadInfo);
899 final List<PyStackFrameInfo> frames = threadInfo.getFrames();
900 if (frames != null) {
901 final PySuspendContext suspendContext = createSuspendContext(threadInfo);
903 XBreakpoint<?> breakpoint = null;
904 if (threadInfo.isStopOnBreakpoint()) {
905 final PySourcePosition position = frames.get(0).getPosition();
906 breakpoint = myRegisteredBreakpoints.get(position);
907 if (breakpoint == null) {
908 myDebugger.removeTempBreakpoint(position.getFile(), position.getLine());
911 else if (threadInfo.isExceptionBreak()) {
912 String exceptionName = threadInfo.getMessage();
913 threadInfo.setMessage(null);
914 if (exceptionName != null) {
915 breakpoint = myRegisteredExceptionBreakpoints.get(exceptionName);
918 if (breakpoint != null) {
919 if ((breakpoint.getType().isSuspendThreadSupported()) && (breakpoint.getSuspendPolicy() == SuspendPolicy.ALL)) {
920 suspendAllOtherThreads(threadInfo);
924 if (updateSourcePosition) {
925 if (breakpoint != null) {
926 if (!getSession().breakpointReached(breakpoint, threadInfo.getMessage(), suspendContext)) {
927 resume(suspendContext);
931 getSession().positionReached(suspendContext);
939 protected PySuspendContext createSuspendContext(PyThreadInfo threadInfo) {
940 return new PySuspendContext(this, threadInfo);
944 public void threadResumed(final PyThreadInfo threadInfo) {
945 mySuspendedThreads.remove(threadInfo);
948 private void dropFrameCaches() {
949 myStackFrameCache.clear();
950 myNewVariableValue.clear();
954 public List<PydevCompletionVariant> getCompletions(String prefix) throws Exception {
957 final PyStackFrame frame = currentFrame();
958 return myDebugger.getCompletions(frame.getThreadId(), frame.getFrameId(), prefix);
960 return Lists.newArrayList();
964 public String getDescription(String prefix) throws Exception {
967 final PyStackFrame frame = currentFrame();
968 return myDebugger.getDescription(frame.getThreadId(), frame.getFrameId(), prefix);
975 public void startNotified(ProcessEvent event) {
979 public void processTerminated(ProcessEvent event) {
984 public void processWillTerminate(ProcessEvent event, boolean willBeDestroyed) {
989 public void onTextAvailable(ProcessEvent event, Key outputType) {
992 public PyStackFrame createStackFrame(PyStackFrameInfo frameInfo) {
993 return new PyStackFrame(getSession().getProject(), this, frameInfo,
994 getPositionConverter().convertFromPython(frameInfo.getPosition()));
998 public String getCurrentStateMessage() {
999 if (getSession().isStopped()) {
1000 return XDebuggerBundle.message("debugger.state.message.disconnected");
1002 else if (isConnected()) {
1003 return XDebuggerBundle.message("debugger.state.message.connected");
1006 return getConnectionMessage();
1010 public void addProcessListener(ProcessListener listener) {
1011 ProcessHandler handler = doGetProcessHandler();
1012 if (handler != null) {
1013 handler.addProcessListener(listener);
1017 public boolean isWaitingForConnection() {
1018 return myWaitingForConnection;
1021 public void setWaitingForConnection(boolean waitingForConnection) {
1022 myWaitingForConnection = waitingForConnection;
1025 public int getConnectTimeout() {
1026 return CONNECTION_TIMEOUT;
1031 private XSourcePosition getCurrentFrameSourcePosition() {
1033 PyStackFrame frame = currentFrame();
1035 return frame.getSourcePosition();
1037 catch (PyDebuggerException e) {
1042 public Project getProject() {
1043 return getSession().getProject();
1048 public XSourcePosition getSourcePositionForName(String name, String parentType) {
1049 if (name == null) return null;
1050 XSourcePosition currentPosition = getCurrentFrameSourcePosition();
1052 final PsiFile file = getPsiFile(currentPosition);
1054 if (file == null) return null;
1056 if (Strings.isNullOrEmpty(parentType)) {
1057 final Ref<PsiElement> elementRef = resolveInCurrentFrame(name, currentPosition, file);
1058 return elementRef.isNull() ? null : XDebuggerUtil.getInstance().createPositionByElement(elementRef.get());
1061 final PyType parentDef = resolveTypeFromString(parentType, file);
1062 if (parentDef == null) {
1065 List<? extends RatedResolveResult> results =
1066 parentDef.resolveMember(name, null, AccessDirection.READ, PyResolveContext.noImplicits());
1067 if (results != null && !results.isEmpty()) {
1068 return XDebuggerUtil.getInstance().createPositionByElement(results.get(0).getElement());
1071 return typeToPosition(parentDef); // at least try to return parent
1078 private static Ref<PsiElement> resolveInCurrentFrame(final String name, XSourcePosition currentPosition, PsiFile file) {
1079 final Ref<PsiElement> elementRef = Ref.create();
1080 PsiElement currentElement = file.findElementAt(currentPosition.getOffset());
1082 if (currentElement == null) {
1087 PyResolveUtil.scopeCrawlUp(new PsiScopeProcessor() {
1089 public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) {
1090 if ((element instanceof PyImportElement)) {
1091 PyImportElement importElement = (PyImportElement)element;
1092 if (name.equals(importElement.getVisibleName())) {
1093 if (elementRef.isNull()) {
1094 elementRef.set(element);
1101 if (elementRef.isNull()) {
1102 elementRef.set(element);
1110 public <T> T getHint(@NotNull Key<T> hintKey) {
1115 public void handleEvent(@NotNull Event event, @Nullable Object associated) {
1118 }, currentElement, name, null);
1123 private PsiFile getPsiFile(XSourcePosition currentPosition) {
1124 if (currentPosition == null) {
1128 return PsiManager.getInstance(getProject()).findFile(currentPosition.getFile());
1134 public XSourcePosition getSourcePositionForType(String typeName) {
1135 XSourcePosition currentPosition = getCurrentFrameSourcePosition();
1137 final PsiFile file = getPsiFile(currentPosition);
1139 if (file == null || typeName == null || !(file instanceof PyFile)) return null;
1142 final PyType pyType = resolveTypeFromString(typeName, file);
1143 return pyType == null ? null : typeToPosition(pyType);
1147 private static XSourcePosition typeToPosition(PyType pyType) {
1148 final PyClassType classType = PyUtil.as(pyType, PyClassType.class);
1150 if (classType != null) {
1151 return XDebuggerUtil.getInstance().createPositionByElement(classType.getPyClass());
1154 final PyModuleType moduleType = PyUtil.as(pyType, PyModuleType.class);
1155 if (moduleType != null) {
1156 return XDebuggerUtil.getInstance().createPositionByElement(moduleType.getModule());
1161 private PyType resolveTypeFromString(String typeName, PsiFile file) {
1162 typeName = typeName.replace("__builtin__.", "");
1163 PyType pyType = null;
1164 if (!typeName.contains(".")) {
1166 pyType = PyTypeParser.getTypeByName(file, typeName);
1168 if (pyType == null) {
1169 PyElementGenerator generator = PyElementGenerator.getInstance(getProject());
1170 PyPsiFacade psiFacade = PyPsiFacade.getInstance(getProject());
1171 PsiFile dummyFile = generator.createDummyFile(((PyFile)file).getLanguageLevel(), "");
1172 Module moduleForFile = ModuleUtilCore.findModuleForPsiElement(file);
1173 dummyFile.putUserData(ModuleUtilCore.KEY_MODULE, moduleForFile);
1175 pyType = psiFacade.parseTypeAnnotation(typeName, dummyFile);
1180 private interface DebuggerFactory {
1181 @NotNull ProcessDebugger createDebugger(@NotNull PyDebugProcess process);