some fixes in editor showing for remote debug files
[idea/community.git] / platform / xdebugger-impl / src / com / intellij / xdebugger / impl / XDebugSessionImpl.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.intellij.xdebugger.impl;
17
18 import com.intellij.execution.configurations.RunProfile;
19 import com.intellij.execution.filters.HyperlinkInfo;
20 import com.intellij.execution.filters.OpenFileHyperlinkInfo;
21 import com.intellij.execution.process.ProcessHandler;
22 import com.intellij.execution.runners.ExecutionEnvironment;
23 import com.intellij.execution.runners.ProgramRunner;
24 import com.intellij.execution.ui.ConsoleView;
25 import com.intellij.execution.ui.ConsoleViewContentType;
26 import com.intellij.execution.ui.RunContentDescriptor;
27 import com.intellij.openapi.application.ReadAction;
28 import com.intellij.openapi.application.Result;
29 import com.intellij.openapi.diagnostic.Logger;
30 import com.intellij.openapi.project.Project;
31 import com.intellij.openapi.ui.MessageType;
32 import com.intellij.openapi.util.Disposer;
33 import com.intellij.openapi.util.text.StringUtil;
34 import com.intellij.openapi.wm.ToolWindowId;
35 import com.intellij.openapi.wm.ToolWindowManager;
36 import com.intellij.util.EventDispatcher;
37 import com.intellij.util.ui.UIUtil;
38 import com.intellij.xdebugger.*;
39 import com.intellij.xdebugger.breakpoints.*;
40 import com.intellij.xdebugger.evaluation.XDebuggerEvaluator;
41 import com.intellij.xdebugger.frame.XExecutionStack;
42 import com.intellij.xdebugger.frame.XStackFrame;
43 import com.intellij.xdebugger.frame.XSuspendContext;
44 import com.intellij.xdebugger.impl.breakpoints.*;
45 import com.intellij.xdebugger.impl.evaluate.quick.common.ValueLookupManager;
46 import com.intellij.xdebugger.impl.ui.DebuggerUIUtil;
47 import com.intellij.xdebugger.impl.ui.XDebugSessionData;
48 import com.intellij.xdebugger.impl.ui.XDebugSessionTab;
49 import com.intellij.xdebugger.stepping.XSmartStepIntoHandler;
50 import com.intellij.xdebugger.stepping.XSmartStepIntoVariant;
51 import org.jetbrains.annotations.NotNull;
52 import org.jetbrains.annotations.Nullable;
53
54 import javax.swing.*;
55 import java.util.*;
56
57 /**
58  * @author nik
59  */
60 public class XDebugSessionImpl implements XDebugSession {
61   private static final Logger LOG = Logger.getInstance("#com.intellij.xdebugger.impl.XDebugSessionImpl");
62   private XDebugProcess myDebugProcess;
63   private final Map<XBreakpoint<?>, CustomizedBreakpointPresentation> myRegisteredBreakpoints = new HashMap<XBreakpoint<?>, CustomizedBreakpointPresentation>();
64   private final Set<XBreakpoint<?>> myDisabledSlaveBreakpoints = new HashSet<XBreakpoint<?>>();
65   private boolean myBreakpointsMuted;
66   private boolean myBreakpointsDisabled;
67   private final XDebuggerManagerImpl myDebuggerManager;
68   private MyBreakpointListener myBreakpointListener;
69   private XSuspendContext mySuspendContext;
70   private XStackFrame myCurrentStackFrame;
71   private XSourcePosition myCurrentPosition;
72   private boolean myPaused;
73   private MyDependentBreakpointListener myDependentBreakpointListener;
74   private String mySessionName;
75   private XDebugSessionTab mySessionTab;
76   private final EventDispatcher<XDebugSessionListener> myDispatcher = EventDispatcher.create(XDebugSessionListener.class);
77   private Project myProject;
78   private @Nullable ExecutionEnvironment myEnvironment;
79   private ProgramRunner myRunner;
80   private boolean myStopped;
81   private boolean myPauseActionSupported;
82
83   public XDebugSessionImpl(final @NotNull ExecutionEnvironment env, final @NotNull ProgramRunner runner, XDebuggerManagerImpl debuggerManager) {
84     this(env, runner, debuggerManager, env.getRunProfile().getName());
85   }
86
87   public XDebugSessionImpl(final @Nullable ExecutionEnvironment env, final @Nullable ProgramRunner runner, XDebuggerManagerImpl debuggerManager,
88                            final @NotNull String sessionName) {
89     myEnvironment = env;
90     myRunner = runner;
91     mySessionName = sessionName;
92     myDebuggerManager = debuggerManager;
93     myProject = debuggerManager.getProject();
94     ValueLookupManager.getInstance(myProject).startListening();
95   }
96
97   @NotNull
98   public String getSessionName() {
99     return mySessionName;
100   }
101
102   @NotNull
103   public RunContentDescriptor getRunContentDescriptor() {
104     LOG.assertTrue(mySessionTab != null, "Call init() first!");
105     return mySessionTab.getRunContentDescriptor();
106   }
107
108   public void setPauseActionSupported(final boolean isSupported) {
109     myPauseActionSupported = isSupported;
110   }
111
112   public void rebuildViews() {
113     mySessionTab.rebuildViews();
114   }
115
116   @Nullable
117   public RunProfile getRunProfile() {
118     return myEnvironment != null ? myEnvironment.getRunProfile() : null;
119   }
120
121   public boolean isPauseActionSupported() {
122     return myPauseActionSupported;
123   }
124
125   @NotNull
126   public Project getProject() {
127     return myDebuggerManager.getProject();
128   }
129
130   @NotNull
131   public XDebugProcess getDebugProcess() {
132     return myDebugProcess;
133   }
134
135   public boolean isSuspended() {
136     return myPaused && mySuspendContext != null;
137   }
138
139   public boolean isPaused() {
140     return myPaused;
141   }
142
143   @Nullable
144   public XStackFrame getCurrentStackFrame() {
145     return myCurrentStackFrame;
146   }
147
148   public XSuspendContext getSuspendContext() {
149     return mySuspendContext;
150   }
151
152   @Nullable
153   public XSourcePosition getCurrentPosition() {
154     return myCurrentPosition;
155   }
156
157   public XDebugSessionTab init(final XDebugProcess process, @NotNull final XDebugSessionData sessionData) {
158     LOG.assertTrue(myDebugProcess == null);
159     myDebugProcess = process;
160
161     XBreakpointManagerImpl breakpointManager = myDebuggerManager.getBreakpointManager();
162     XDependentBreakpointManager dependentBreakpointManager = breakpointManager.getDependentBreakpointManager();
163     disableSlaveBreakpoints(dependentBreakpointManager);
164     processAllBreakpoints(true, false);
165
166     myBreakpointListener = new MyBreakpointListener();
167     breakpointManager.addBreakpointListener(myBreakpointListener);
168     myDependentBreakpointListener = new MyDependentBreakpointListener();
169     dependentBreakpointManager.addListener(myDependentBreakpointListener);
170
171     initSessionTab(sessionData);
172     process.sessionInitialized();
173
174     return mySessionTab;
175   }
176
177   public XDebugSessionTab getSessionTab() {
178     return mySessionTab;
179   }
180
181   private void initSessionTab(@NotNull XDebugSessionData sessionData) {
182     mySessionTab = new XDebugSessionTab(myProject, mySessionName);
183     if (myEnvironment != null) {
184       mySessionTab.setEnvironment(myEnvironment);
185     }
186     Disposer.register(myProject, mySessionTab);
187     mySessionTab.attachToSession(this, myRunner, myEnvironment, sessionData);
188   }
189
190   private void disableSlaveBreakpoints(final XDependentBreakpointManager dependentBreakpointManager) {
191     Set<XBreakpoint<?>> slaveBreakpoints = dependentBreakpointManager.getAllSlaveBreakpoints();
192     Set<XBreakpointType<?,?>> breakpointTypes = new HashSet<XBreakpointType<?,?>>();
193     for (XBreakpointHandler<?> handler : myDebugProcess.getBreakpointHandlers()) {
194       breakpointTypes.add(getBreakpointTypeClass(handler));
195     }
196     for (XBreakpoint<?> slaveBreakpoint : slaveBreakpoints) {
197       if (breakpointTypes.contains(slaveBreakpoint.getType())) {
198         myDisabledSlaveBreakpoints.add(slaveBreakpoint);
199       }
200     }
201   }
202
203   private static <B extends XBreakpoint<?>> XBreakpointType<?, ?> getBreakpointTypeClass(final XBreakpointHandler<B> handler) {
204     return XDebuggerUtil.getInstance().findBreakpointType(handler.getBreakpointTypeClass());
205   }
206
207   private <B extends XBreakpoint<?>> void processBreakpoints(final XBreakpointHandler<B> handler, boolean register, final boolean temporary) {
208     XBreakpointType<B,?> type = XDebuggerUtil.getInstance().findBreakpointType(handler.getBreakpointTypeClass());
209     Collection<? extends B> breakpoints = myDebuggerManager.getBreakpointManager().getBreakpoints(type);
210     for (B b : breakpoints) {
211       handleBreakpoint(handler, b, register, temporary);
212     }
213   }
214
215   private <B extends XBreakpoint<?>> void handleBreakpoint(final XBreakpointHandler<B> handler, final B b, final boolean register,
216                                                            final boolean temporary) {
217     if (register && isBreakpointActive(b)) {
218       synchronized (myRegisteredBreakpoints) {
219         myRegisteredBreakpoints.put(b, new CustomizedBreakpointPresentation());
220       }
221       handler.registerBreakpoint(b);
222     }
223     if (!register) {
224       boolean removed = false;
225       synchronized (myRegisteredBreakpoints) {
226         if (myRegisteredBreakpoints.containsKey(b)) {
227           myRegisteredBreakpoints.remove(b);
228           removed = true;
229         }
230       }
231       if (removed) {
232         handler.unregisterBreakpoint(b, temporary);
233       }
234     }
235   }
236
237   @Nullable
238   public CustomizedBreakpointPresentation getBreakpointPresentation(@NotNull XLineBreakpoint<?> breakpoint) {
239     synchronized (myRegisteredBreakpoints) {
240       return myRegisteredBreakpoints.get(breakpoint);
241     }
242   }
243
244   private void processAllHandlers(final XBreakpoint<?> breakpoint, final boolean register) {
245     for (XBreakpointHandler<?> handler : myDebugProcess.getBreakpointHandlers()) {
246       processBreakpoint(breakpoint, handler, register);
247     }
248   }
249
250   private <B extends XBreakpoint<?>> void processBreakpoint(final XBreakpoint<?> breakpoint, final XBreakpointHandler<B> handler, boolean register) {
251     XBreakpointType<?, ?> type = breakpoint.getType();
252     if (handler.getBreakpointTypeClass().equals(type.getClass())) {
253       //noinspection unchecked
254       B b = (B)breakpoint;
255       handleBreakpoint(handler, b, register, false);
256     }
257   }
258
259   private boolean isBreakpointActive(final XBreakpoint<?> b) {
260     return !myBreakpointsMuted && b.isEnabled() && !myDisabledSlaveBreakpoints.contains(b);
261   }
262
263   public boolean areBreakpointsMuted() {
264     return myBreakpointsMuted;
265   }
266
267   public void addSessionListener(@NotNull final XDebugSessionListener listener) {
268     myDispatcher.addListener(listener);
269   }
270
271   public void removeSessionListener(@NotNull final XDebugSessionListener listener) {
272     myDispatcher.removeListener(listener);
273   }
274
275   public void setBreakpointMuted(boolean muted) {
276     if (myBreakpointsMuted == muted) return;
277     myBreakpointsMuted = muted;
278     processAllBreakpoints(!muted, muted);
279     myDebuggerManager.getBreakpointManager().getLineBreakpointManager().queueAllBreakpointsUpdate();
280   }
281
282   public void stepOver(final boolean ignoreBreakpoints) {
283     if (ignoreBreakpoints) {
284       disableBreakpoints();
285     }
286     doResume();
287     myDebugProcess.startStepOver();
288   }
289
290   public void stepInto() {
291     doResume();
292     myDebugProcess.startStepInto();
293   }
294
295   public void stepOut() {
296     doResume();
297     myDebugProcess.startStepOut();
298   }
299
300   public <V extends XSmartStepIntoVariant> void smartStepInto(XSmartStepIntoHandler<V> handler, V variant) {
301     doResume();
302     handler.startStepInto(variant);
303   }
304
305   public void forceStepInto() {
306     stepInto();
307   }
308
309   public void runToPosition(@NotNull final XSourcePosition position, final boolean ignoreBreakpoints) {
310     if (ignoreBreakpoints) {
311       disableBreakpoints();
312     }
313     doResume();
314     myDebugProcess.runToPosition(position);
315   }
316
317   public void pause() {
318     myDebugProcess.startPausing();
319   }
320
321   private void processAllBreakpoints(final boolean register, final boolean temporary) {
322     for (XBreakpointHandler<?> handler : myDebugProcess.getBreakpointHandlers()) {
323       processBreakpoints(handler, register, temporary);
324     }
325   }
326
327   private void disableBreakpoints() {
328     myBreakpointsDisabled = true;
329     processAllBreakpoints(false, true);
330   }
331
332   public void resume() {
333     doResume();
334     myDebugProcess.resume();
335   }
336
337   private void doResume() {
338     myDispatcher.getMulticaster().beforeSessionResume();
339     myDebuggerManager.setActiveSession(this, null, false);
340     mySuspendContext = null;
341     myCurrentStackFrame = null;
342     myCurrentPosition = null;
343     myPaused = false;
344     myDispatcher.getMulticaster().sessionResumed();
345   }
346
347   public void showExecutionPoint(XSourcePosition sourcePosition) {
348     XExecutionStack activeExecutionStack = mySuspendContext.getActiveExecutionStack();
349     boolean isTopFrame = activeExecutionStack != null && activeExecutionStack.getTopFrame() == myCurrentStackFrame;
350     myDebuggerManager.setActiveSession(this, sourcePosition, !isTopFrame);
351   }
352
353   public void showExecutionPoint() {
354     if (mySuspendContext != null) {
355       XExecutionStack executionStack = mySuspendContext.getActiveExecutionStack();
356       if (executionStack != null) {
357         XStackFrame topFrame = executionStack.getTopFrame();
358         if (topFrame != null) {
359           setCurrentStackFrame(topFrame);
360           myDebuggerManager.showExecutionPosition();
361         }
362       }
363     }
364   }
365
366   public void setCurrentStackFrame(@NotNull final XStackFrame frame) {
367     if (mySuspendContext == null) return;
368
369     boolean frameChanged = myCurrentStackFrame != frame;
370     myCurrentStackFrame = frame;
371     activateSession();
372
373     if (frameChanged) {
374       myDispatcher.getMulticaster().stackFrameChanged();
375     }
376   }
377
378   public void activateSession() {
379     XSourcePosition position = myCurrentStackFrame != null ? myCurrentStackFrame.getSourcePosition() : null;
380     if (position != null) {
381       XExecutionStack activeExecutionStack = mySuspendContext.getActiveExecutionStack();
382       boolean isTopFrame = activeExecutionStack != null && activeExecutionStack.getTopFrame() == myCurrentStackFrame;
383       myDebuggerManager.setActiveSession(this, position, !isTopFrame);
384     }
385     else {
386       myDebuggerManager.setActiveSession(this, null, false);
387     }
388   }
389
390   public void updateBreakpointPresentation(@NotNull final XLineBreakpoint<?> breakpoint, @Nullable final Icon icon, @Nullable final String errorMessage) {
391     CustomizedBreakpointPresentation presentation;
392     synchronized (myRegisteredBreakpoints) {
393       presentation = myRegisteredBreakpoints.get(breakpoint);
394       if (presentation != null) {
395         presentation.setErrorMessage(errorMessage);
396         presentation.setIcon(icon);
397       }
398     }
399     if (presentation != null) {
400       myDebuggerManager.getBreakpointManager().getLineBreakpointManager().queueBreakpointUpdate((XLineBreakpointImpl<?>)breakpoint);
401     }
402   }
403
404   public boolean breakpointReached(@NotNull final XBreakpoint<?> breakpoint, @NotNull final XSuspendContext suspendContext) {
405     return breakpointReached(breakpoint, null, suspendContext);
406   }
407
408   public boolean breakpointReached(@NotNull XBreakpoint<?> breakpoint, @Nullable String evaluatedLogExpression,
409                                    @NotNull XSuspendContext suspendContext) {
410     XDebuggerEvaluator evaluator = XDebuggerUtilImpl.getEvaluator(suspendContext);
411     String condition = breakpoint.getCondition();
412     if (condition != null && evaluator != null) {
413       LOG.debug("evaluating condition: " + condition);
414       boolean result = evaluator.evaluateCondition(condition);        
415       LOG.debug("condition evaluates to " + result);                 
416       if (!result) {
417         return false;
418       }
419     }
420
421     if (breakpoint.isLogMessage()) {
422       String text = StringUtil.decapitalize(XBreakpointUtil.getDisplayText(breakpoint));
423       final XSourcePosition position = breakpoint.getSourcePosition();
424       final OpenFileHyperlinkInfo hyperlinkInfo = position != null ? new OpenFileHyperlinkInfo(myProject, position.getFile(), position.getLine()) : null;
425       printMessage(XDebuggerBundle.message("xbreakpoint.reached.text") + " ", text, hyperlinkInfo);
426     }
427
428     if (evaluatedLogExpression != null) {
429       printMessage(evaluatedLogExpression, null, null);
430     }
431     else {
432       String expression = breakpoint.getLogExpression();
433       if (expression != null && evaluator != null) {
434         LOG.debug("evaluating log expression: " + expression);
435         final String message = evaluator.evaluateMessage(expression);
436         if (message != null) {
437           printMessage(message, null, null);
438         }
439       }
440     }
441
442     processDependencies(breakpoint);
443
444     if (breakpoint.getSuspendPolicy() == SuspendPolicy.NONE) {
445       return false;
446     }
447
448     positionReached(suspendContext);
449     return true;
450   }
451
452   private void processDependencies(final XBreakpoint<?> breakpoint) {
453     XDependentBreakpointManager dependentBreakpointManager = myDebuggerManager.getBreakpointManager().getDependentBreakpointManager();
454     if (!dependentBreakpointManager.isMasterOrSlave(breakpoint)) return;
455
456     List<XBreakpoint<?>> breakpoints = dependentBreakpointManager.getSlaveBreakpoints(breakpoint);
457     myDisabledSlaveBreakpoints.removeAll(breakpoints);
458     for (XBreakpoint<?> slaveBreakpoint : breakpoints) {
459       processAllHandlers(slaveBreakpoint, true);
460     }
461
462     if (dependentBreakpointManager.getMasterBreakpoint(breakpoint) != null && !dependentBreakpointManager.isLeaveEnabled(breakpoint)) {
463       boolean added = myDisabledSlaveBreakpoints.add(breakpoint);
464       if (added) {
465         processAllHandlers(breakpoint, false);
466         myDebuggerManager.getBreakpointManager().getLineBreakpointManager().queueBreakpointUpdate(breakpoint);
467       }
468     }
469   }
470
471   private void printMessage(final String message, final String hyperLinkText, @Nullable final HyperlinkInfo info) {
472     DebuggerUIUtil.invokeOnEventDispatch(new Runnable() {
473       public void run() {
474         final ConsoleView consoleView = getConsoleView();
475         consoleView.print(message, ConsoleViewContentType.SYSTEM_OUTPUT);
476         if (info != null) {
477           consoleView.printHyperlink(hyperLinkText, info);
478         }
479         else if (hyperLinkText != null) {
480           consoleView.print(hyperLinkText, ConsoleViewContentType.SYSTEM_OUTPUT);
481         }
482         consoleView.print("\n", ConsoleViewContentType.SYSTEM_OUTPUT);
483       }
484     });
485   }
486
487   private ConsoleView getConsoleView() {
488     return (ConsoleView)mySessionTab.getConsole();
489   }
490
491   public void positionReached(@NotNull final XSuspendContext suspendContext) {
492     enableBreakpoints();
493     mySuspendContext = suspendContext;
494     XExecutionStack executionStack = suspendContext.getActiveExecutionStack();
495     myCurrentStackFrame = executionStack != null ? executionStack.getTopFrame() : null;
496     myCurrentPosition = myCurrentStackFrame != null ? myCurrentStackFrame.getSourcePosition() : null;
497
498     myPaused = true;
499     if (myCurrentPosition != null) {
500       myDebuggerManager.setActiveSession(this, myCurrentPosition, false);
501     }
502     UIUtil.invokeLaterIfNeeded(new Runnable() {
503       public void run() {
504         mySessionTab.toFront();
505       }
506     });
507     myDispatcher.getMulticaster().sessionPaused();
508   }
509
510   private void enableBreakpoints() {
511     if (myBreakpointsDisabled) {
512       myBreakpointsDisabled = false;
513       new ReadAction() {
514         protected void run(final Result result) {
515           processAllBreakpoints(true, false);
516         }
517       }.execute();
518     }
519   }
520
521   public boolean isStopped() {
522     return myStopped;
523   }
524
525   public void stopImpl() {
526     if (myStopped) return;
527
528     myDebugProcess.stop();
529     myCurrentPosition = null;
530     myCurrentStackFrame = null;
531     mySuspendContext = null;
532     myDebuggerManager.setActiveSession(this, null, false);
533     XBreakpointManagerImpl breakpointManager = myDebuggerManager.getBreakpointManager();
534     breakpointManager.removeBreakpointListener(myBreakpointListener);
535     breakpointManager.getDependentBreakpointManager().removeListener(myDependentBreakpointListener);
536     myStopped = true;
537     myDebuggerManager.removeSession(this);
538     myDispatcher.getMulticaster().sessionStopped();
539   }
540
541   public boolean isDisabledSlaveBreakpoint(final XBreakpoint<?> breakpoint) {
542     return myDisabledSlaveBreakpoints.contains(breakpoint);
543   }
544
545   public void stop() {
546     ProcessHandler processHandler = myDebugProcess.getProcessHandler();
547     if (processHandler.isProcessTerminated() || processHandler.isProcessTerminating()) return; 
548
549     if (processHandler.detachIsDefault()) {
550       processHandler.detachProcess();
551     }
552     else {
553       processHandler.destroyProcess();
554     }
555   }
556
557   @Override
558   public void reportError(final String message) {
559     UIUtil.invokeLaterIfNeeded(new Runnable() {
560       @Override
561       public void run() {
562         ToolWindowManager.getInstance(myProject).notifyByBalloon(ToolWindowId.DEBUG, MessageType.ERROR, message, null, null);
563       }
564     });
565   }
566
567   private class MyBreakpointListener implements XBreakpointListener<XBreakpoint<?>> {
568     public void breakpointAdded(@NotNull final XBreakpoint<?> breakpoint) {
569       if (!myBreakpointsDisabled) {
570         processAllHandlers(breakpoint, true);
571       }
572     }
573
574     public void breakpointRemoved(@NotNull final XBreakpoint<?> breakpoint) {
575       processAllHandlers(breakpoint, false);
576     }
577
578     public void breakpointChanged(@NotNull final XBreakpoint<?> breakpoint) {
579       breakpointRemoved(breakpoint);
580       breakpointAdded(breakpoint);
581     }
582   }
583
584   private class MyDependentBreakpointListener implements XDependentBreakpointListener {
585     public void dependencySet(final XBreakpoint<?> slave, final XBreakpoint<?> master) {
586       boolean added = myDisabledSlaveBreakpoints.add(slave);
587       if (added) {
588         processAllHandlers(slave, false);
589       }
590     }
591
592     public void dependencyCleared(final XBreakpoint<?> breakpoint) {
593       boolean removed = myDisabledSlaveBreakpoints.remove(breakpoint);
594       if (removed) {
595         processAllHandlers(breakpoint, true);
596       }
597     }
598   }
599 }