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