23d98a492b9bbb4781d237ebc69c902316d87c59
[idea/community.git] / platform / xdebugger-impl / src / com / intellij / xdebugger / impl / XDebugSessionImpl.java
1 /*
2  * Copyright 2000-2016 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.ExecutionManager;
19 import com.intellij.execution.configurations.RunConfiguration;
20 import com.intellij.execution.configurations.RunProfile;
21 import com.intellij.execution.executors.DefaultDebugExecutor;
22 import com.intellij.execution.filters.HyperlinkInfo;
23 import com.intellij.execution.filters.OpenFileHyperlinkInfo;
24 import com.intellij.execution.process.ProcessAdapter;
25 import com.intellij.execution.process.ProcessEvent;
26 import com.intellij.execution.process.ProcessHandler;
27 import com.intellij.execution.runners.ExecutionEnvironment;
28 import com.intellij.execution.ui.ConsoleView;
29 import com.intellij.execution.ui.ConsoleViewContentType;
30 import com.intellij.execution.ui.RunContentDescriptor;
31 import com.intellij.execution.ui.RunnerLayoutUi;
32 import com.intellij.ide.DataManager;
33 import com.intellij.notification.NotificationGroup;
34 import com.intellij.notification.NotificationListener;
35 import com.intellij.openapi.Disposable;
36 import com.intellij.openapi.actionSystem.AnAction;
37 import com.intellij.openapi.application.ApplicationManager;
38 import com.intellij.openapi.application.ReadAction;
39 import com.intellij.openapi.application.Result;
40 import com.intellij.openapi.diagnostic.Logger;
41 import com.intellij.openapi.editor.markup.GutterIconRenderer;
42 import com.intellij.openapi.project.Project;
43 import com.intellij.openapi.ui.MessageType;
44 import com.intellij.openapi.util.Comparing;
45 import com.intellij.openapi.util.Computable;
46 import com.intellij.openapi.util.Disposer;
47 import com.intellij.openapi.wm.ToolWindowId;
48 import com.intellij.ui.AppUIUtil;
49 import com.intellij.util.EventDispatcher;
50 import com.intellij.util.SmartList;
51 import com.intellij.util.containers.SmartHashSet;
52 import com.intellij.util.ui.UIUtil;
53 import com.intellij.xdebugger.*;
54 import com.intellij.xdebugger.breakpoints.*;
55 import com.intellij.xdebugger.frame.XExecutionStack;
56 import com.intellij.xdebugger.frame.XStackFrame;
57 import com.intellij.xdebugger.frame.XSuspendContext;
58 import com.intellij.xdebugger.frame.XValueMarkerProvider;
59 import com.intellij.xdebugger.impl.breakpoints.*;
60 import com.intellij.xdebugger.impl.evaluate.XDebuggerEditorLinePainter;
61 import com.intellij.xdebugger.impl.evaluate.quick.common.ValueLookupManager;
62 import com.intellij.xdebugger.impl.frame.XValueMarkers;
63 import com.intellij.xdebugger.impl.frame.XWatchesViewImpl;
64 import com.intellij.xdebugger.impl.settings.XDebuggerSettingManagerImpl;
65 import com.intellij.xdebugger.impl.ui.XDebugSessionData;
66 import com.intellij.xdebugger.impl.ui.XDebugSessionTab;
67 import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants;
68 import com.intellij.xdebugger.stepping.XSmartStepIntoHandler;
69 import com.intellij.xdebugger.stepping.XSmartStepIntoVariant;
70 import gnu.trove.THashMap;
71 import gnu.trove.THashSet;
72 import org.jetbrains.annotations.NotNull;
73 import org.jetbrains.annotations.Nullable;
74
75 import javax.swing.*;
76 import javax.swing.event.HyperlinkListener;
77 import java.util.*;
78 import java.util.concurrent.atomic.AtomicBoolean;
79
80 /**
81  * @author nik
82  */
83 public class XDebugSessionImpl implements XDebugSession {
84   private static final Logger LOG = Logger.getInstance("#com.intellij.xdebugger.impl.XDebugSessionImpl");
85   public static final NotificationGroup NOTIFICATION_GROUP = NotificationGroup.toolWindowGroup("Debugger messages", ToolWindowId.DEBUG,
86                                                                                                false);
87   private XDebugProcess myDebugProcess;
88   private final Map<XBreakpoint<?>, CustomizedBreakpointPresentation> myRegisteredBreakpoints =
89     new THashMap<>();
90   private final Set<XBreakpoint<?>> myInactiveSlaveBreakpoints = Collections.synchronizedSet(new SmartHashSet<>());
91   private boolean myBreakpointsDisabled;
92   private final XDebuggerManagerImpl myDebuggerManager;
93   private MyBreakpointListener myBreakpointListener;
94   private XSuspendContext mySuspendContext;
95   private XExecutionStack myCurrentExecutionStack;
96   private XStackFrame myCurrentStackFrame;
97   private boolean myIsTopFrame;
98   private volatile XSourcePosition myTopFramePosition;
99   private final AtomicBoolean myPaused = new AtomicBoolean();
100   private MyDependentBreakpointListener myDependentBreakpointListener;
101   private XValueMarkers<?, ?> myValueMarkers;
102   private final String mySessionName;
103   private @Nullable XDebugSessionTab mySessionTab;
104   private final XDebugSessionData mySessionData;
105   private XBreakpoint<?> myActiveNonLineBreakpoint;
106   private final EventDispatcher<XDebugSessionListener> myDispatcher = EventDispatcher.create(XDebugSessionListener.class);
107   private final Project myProject;
108   private final @Nullable ExecutionEnvironment myEnvironment;
109   private final AtomicBoolean myStopped = new AtomicBoolean();
110   private boolean myPauseActionSupported;
111   private final AtomicBoolean myShowTabOnSuspend;
112   private final List<AnAction> myRestartActions = new SmartList<>();
113   private final List<AnAction> myExtraStopActions = new SmartList<>();
114   private final List<AnAction> myExtraActions = new SmartList<>();
115   private ConsoleView myConsoleView;
116   private final Icon myIcon;
117
118   private volatile boolean breakpointsInitialized;
119
120   public XDebugSessionImpl(@NotNull ExecutionEnvironment environment, @NotNull XDebuggerManagerImpl debuggerManager) {
121     this(environment, debuggerManager, environment.getRunProfile().getName(), environment.getRunProfile().getIcon(), false, null);
122   }
123
124   public XDebugSessionImpl(@Nullable ExecutionEnvironment environment,
125                            @NotNull XDebuggerManagerImpl debuggerManager,
126                            @NotNull String sessionName,
127                            @Nullable Icon icon,
128                            boolean showTabOnSuspend,
129                            @Nullable RunContentDescriptor contentToReuse) {
130     myEnvironment = environment;
131     mySessionName = sessionName;
132     myDebuggerManager = debuggerManager;
133     myShowTabOnSuspend = new AtomicBoolean(showTabOnSuspend);
134     myProject = debuggerManager.getProject();
135     ValueLookupManager.getInstance(myProject).startListening();
136     myIcon = icon;
137
138     XDebugSessionData oldSessionData = null;
139     if (contentToReuse == null) {
140       contentToReuse = environment != null ? environment.getContentToReuse() : null;
141     }
142     if (contentToReuse != null) {
143       JComponent component = contentToReuse.getComponent();
144       if (component != null) {
145         oldSessionData = XDebugSessionData.DATA_KEY.getData(DataManager.getInstance().getDataContext(component));
146       }
147     }
148
149     String currentConfigurationName = getConfigurationName();
150     if (oldSessionData == null || !oldSessionData.getConfigurationName().equals(currentConfigurationName)) {
151       oldSessionData = new XDebugSessionData(getWatchExpressions(), currentConfigurationName);
152     }
153     mySessionData = oldSessionData;
154   }
155
156   @Override
157   @NotNull
158   public String getSessionName() {
159     return mySessionName;
160   }
161
162   @Override
163   @NotNull
164   public RunContentDescriptor getRunContentDescriptor() {
165     assertSessionTabInitialized();
166     //noinspection ConstantConditions
167     return mySessionTab.getRunContentDescriptor();
168   }
169
170   private void assertSessionTabInitialized() {
171     if (myShowTabOnSuspend.get()) {
172       LOG.error("Debug tool window isn't shown yet because debug process isn't suspended");
173     }
174     else {
175       LOG.assertTrue(mySessionTab != null, "Debug tool window not initialized yet!");
176     }
177   }
178
179   @Override
180   public void setPauseActionSupported(final boolean isSupported) {
181     myPauseActionSupported = isSupported;
182   }
183
184   @NotNull
185   public List<AnAction> getRestartActions() {
186     return myRestartActions;
187   }
188
189   public void addRestartActions(AnAction... restartActions) {
190     if (restartActions != null) {
191       Collections.addAll(myRestartActions, restartActions);
192     }
193   }
194
195   @NotNull
196   public List<AnAction> getExtraActions() {
197     return myExtraActions;
198   }
199
200   public void addExtraActions(AnAction... extraActions) {
201     if (extraActions != null) {
202       Collections.addAll(myExtraActions, extraActions);
203     }
204   }
205
206   public List<AnAction> getExtraStopActions() {
207     return myExtraStopActions;
208   }
209
210   public void addExtraStopActions(AnAction... extraStopActions) {
211     if (extraStopActions != null) {
212       Collections.addAll(myExtraStopActions, extraStopActions);
213     }
214   }
215
216   @Override
217   public void rebuildViews() {
218     if (!myShowTabOnSuspend.get() && mySessionTab != null) {
219       mySessionTab.rebuildViews();
220     }
221   }
222
223   @Override
224   @Nullable
225   public RunProfile getRunProfile() {
226     return myEnvironment != null ? myEnvironment.getRunProfile() : null;
227   }
228
229   public boolean isPauseActionSupported() {
230     return myPauseActionSupported;
231   }
232
233   @Override
234   @NotNull
235   public Project getProject() {
236     return myDebuggerManager.getProject();
237   }
238
239   @Override
240   @NotNull
241   public XDebugProcess getDebugProcess() {
242     return myDebugProcess;
243   }
244
245   @Override
246   public boolean isSuspended() {
247     return myPaused.get() && mySuspendContext != null;
248   }
249
250   @Override
251   public boolean isPaused() {
252     return myPaused.get();
253   }
254
255   @Override
256   @Nullable
257   public XStackFrame getCurrentStackFrame() {
258     return myCurrentStackFrame;
259   }
260
261   @Override
262   public XSuspendContext getSuspendContext() {
263     return mySuspendContext;
264   }
265
266   @Override
267   @Nullable
268   public XSourcePosition getCurrentPosition() {
269     return myCurrentStackFrame != null ? myCurrentStackFrame.getSourcePosition() : null;
270   }
271
272   @Nullable
273   @Override
274   public XSourcePosition getTopFramePosition() {
275     return myTopFramePosition;
276   }
277
278   XDebugSessionTab init(@NotNull XDebugProcess process, @Nullable RunContentDescriptor contentToReuse) {
279     LOG.assertTrue(myDebugProcess == null);
280     myDebugProcess = process;
281
282     if (myDebugProcess.checkCanInitBreakpoints()) {
283       initBreakpoints();
284     }
285
286     myDebugProcess.getProcessHandler().addProcessListener(new ProcessAdapter() {
287       @Override
288       public void processTerminated(final ProcessEvent event) {
289         stopImpl();
290         myDebugProcess.getProcessHandler().removeProcessListener(this);
291       }
292     });
293     //todo[nik] make 'createConsole()' method return ConsoleView
294     myConsoleView = (ConsoleView)myDebugProcess.createConsole();
295     if (!myShowTabOnSuspend.get()) {
296       initSessionTab(contentToReuse);
297     }
298
299     return mySessionTab;
300   }
301
302   public void reset() {
303     breakpointsInitialized = false;
304   }
305
306   @Override
307   public void initBreakpoints() {
308     ApplicationManager.getApplication().assertReadAccessAllowed();
309     LOG.assertTrue(!breakpointsInitialized);
310     breakpointsInitialized = true;
311
312     XBreakpointManagerImpl breakpointManager = myDebuggerManager.getBreakpointManager();
313     XDependentBreakpointManager dependentBreakpointManager = breakpointManager.getDependentBreakpointManager();
314     disableSlaveBreakpoints(dependentBreakpointManager);
315     processAllBreakpoints(true, false);
316
317     if (myBreakpointListener == null) {
318       myBreakpointListener = new MyBreakpointListener();
319       breakpointManager.addBreakpointListener(myBreakpointListener);
320     }
321     if (myDependentBreakpointListener == null) {
322       myDependentBreakpointListener = new MyDependentBreakpointListener();
323       dependentBreakpointManager.addListener(myDependentBreakpointListener);
324     }
325   }
326
327   @Override
328   public ConsoleView getConsoleView() {
329     return myConsoleView;
330   }
331
332   @Nullable
333   public XDebugSessionTab getSessionTab() {
334     return mySessionTab;
335   }
336
337   @Override
338   public RunnerLayoutUi getUI() {
339     assertSessionTabInitialized();
340     assert mySessionTab != null;
341     return mySessionTab.getUi();
342   }
343
344   private void initSessionTab(@Nullable RunContentDescriptor contentToReuse) {
345     mySessionTab = XDebugSessionTab.create(this, myIcon, myEnvironment, contentToReuse);
346     myDebugProcess.sessionInitialized();
347   }
348
349   public XDebugSessionData getSessionData() {
350     return mySessionData;
351   }
352
353   private void disableSlaveBreakpoints(final XDependentBreakpointManager dependentBreakpointManager) {
354     Set<XBreakpoint<?>> slaveBreakpoints = dependentBreakpointManager.getAllSlaveBreakpoints();
355     if (slaveBreakpoints.isEmpty()) {
356       return;
357     }
358
359     Set<XBreakpointType<?, ?>> breakpointTypes = new THashSet<>();
360     for (XBreakpointHandler<?> handler : myDebugProcess.getBreakpointHandlers()) {
361       breakpointTypes.add(getBreakpointTypeClass(handler));
362     }
363     for (XBreakpoint<?> slaveBreakpoint : slaveBreakpoints) {
364       if (breakpointTypes.contains(slaveBreakpoint.getType())) {
365         myInactiveSlaveBreakpoints.add(slaveBreakpoint);
366       }
367     }
368   }
369
370   public void showSessionTab() {
371     RunContentDescriptor descriptor = getRunContentDescriptor();
372     ExecutionManager.getInstance(getProject()).getContentManager()
373       .showRunContent(DefaultDebugExecutor.getDebugExecutorInstance(), descriptor);
374   }
375
376   @Nullable
377   public XValueMarkers<?, ?> getValueMarkers() {
378     if (myValueMarkers == null) {
379       XValueMarkerProvider<?, ?> provider = myDebugProcess.createValueMarkerProvider();
380       if (provider != null) {
381         myValueMarkers = XValueMarkers.createValueMarkers(provider);
382       }
383     }
384     return myValueMarkers;
385   }
386
387   @SuppressWarnings("unchecked") //need to compile under 1.8, please do not remove before checking
388   private static XBreakpointType getBreakpointTypeClass(final XBreakpointHandler handler) {
389     return XDebuggerUtil.getInstance().findBreakpointType(handler.getBreakpointTypeClass());
390   }
391
392   private <B extends XBreakpoint<?>> void processBreakpoints(final XBreakpointHandler<B> handler,
393                                                              boolean register,
394                                                              final boolean temporary) {
395     Collection<? extends B> breakpoints = myDebuggerManager.getBreakpointManager().getBreakpoints(handler.getBreakpointTypeClass());
396     for (B b : breakpoints) {
397       handleBreakpoint(handler, b, register, temporary);
398     }
399   }
400
401   private <B extends XBreakpoint<?>> void handleBreakpoint(final XBreakpointHandler<B> handler, final B b, final boolean register,
402                                                            final boolean temporary) {
403     if (register) {
404       boolean active = ApplicationManager.getApplication().runReadAction((Computable<Boolean>)() -> isBreakpointActive(b));
405       if (active) {
406         synchronized (myRegisteredBreakpoints) {
407           myRegisteredBreakpoints.put(b, new CustomizedBreakpointPresentation());
408         }
409         handler.registerBreakpoint(b);
410       }
411     }
412     else {
413       boolean removed;
414       synchronized (myRegisteredBreakpoints) {
415         removed = myRegisteredBreakpoints.remove(b) != null;
416       }
417       if (removed) {
418         handler.unregisterBreakpoint(b, temporary);
419       }
420     }
421   }
422
423   @Nullable
424   public CustomizedBreakpointPresentation getBreakpointPresentation(@NotNull XBreakpoint<?> breakpoint) {
425     synchronized (myRegisteredBreakpoints) {
426       return myRegisteredBreakpoints.get(breakpoint);
427     }
428   }
429
430   private void processAllHandlers(final XBreakpoint<?> breakpoint, final boolean register) {
431     for (XBreakpointHandler<?> handler : myDebugProcess.getBreakpointHandlers()) {
432       processBreakpoint(breakpoint, handler, register);
433     }
434   }
435
436   private <B extends XBreakpoint<?>> void processBreakpoint(final XBreakpoint<?> breakpoint,
437                                                             final XBreakpointHandler<B> handler,
438                                                             boolean register) {
439     XBreakpointType<?, ?> type = breakpoint.getType();
440     if (handler.getBreakpointTypeClass().equals(type.getClass())) {
441       //noinspection unchecked
442       B b = (B)breakpoint;
443       handleBreakpoint(handler, b, register, false);
444     }
445   }
446
447   public boolean isBreakpointActive(@NotNull XBreakpoint<?> b) {
448     ApplicationManager.getApplication().assertReadAccessAllowed();
449     return !areBreakpointsMuted() && b.isEnabled() && !isInactiveSlaveBreakpoint(b) && !((XBreakpointBase)b).isDisposed();
450   }
451
452   @Override
453   public boolean areBreakpointsMuted() {
454     return mySessionData.isBreakpointsMuted();
455   }
456
457   @Override
458   public void addSessionListener(@NotNull XDebugSessionListener listener, @NotNull Disposable parentDisposable) {
459     myDispatcher.addListener(listener, parentDisposable);
460   }
461
462   @Override
463   public void addSessionListener(@NotNull final XDebugSessionListener listener) {
464     myDispatcher.addListener(listener);
465   }
466
467   @Override
468   public void removeSessionListener(@NotNull final XDebugSessionListener listener) {
469     myDispatcher.removeListener(listener);
470   }
471
472   @Override
473   public void setBreakpointMuted(boolean muted) {
474     ApplicationManager.getApplication().assertReadAccessAllowed();
475     if (areBreakpointsMuted() == muted) return;
476     mySessionData.setBreakpointsMuted(muted);
477     processAllBreakpoints(!muted, muted);
478     myDebuggerManager.getBreakpointManager().getLineBreakpointManager().queueAllBreakpointsUpdate();
479   }
480
481   @Override
482   public void stepOver(final boolean ignoreBreakpoints) {
483     if (!myDebugProcess.checkCanPerformCommands()) return;
484
485     if (ignoreBreakpoints) {
486       disableBreakpoints();
487     }
488     myDebugProcess.startStepOver(doResume());
489   }
490
491   @Override
492   public void stepInto() {
493     if (!myDebugProcess.checkCanPerformCommands()) return;
494
495     myDebugProcess.startStepInto(doResume());
496   }
497
498   @Override
499   public void stepOut() {
500     if (!myDebugProcess.checkCanPerformCommands()) return;
501
502     myDebugProcess.startStepOut(doResume());
503   }
504
505   @Override
506   public <V extends XSmartStepIntoVariant> void smartStepInto(XSmartStepIntoHandler<V> handler, V variant) {
507     if (!myDebugProcess.checkCanPerformCommands()) return;
508
509     doResume();
510     handler.startStepInto(variant);
511   }
512
513   @Override
514   public void forceStepInto() {
515     if (!myDebugProcess.checkCanPerformCommands()) return;
516
517     myDebugProcess.startForceStepInto(doResume());
518   }
519
520   @Override
521   public void runToPosition(@NotNull final XSourcePosition position, final boolean ignoreBreakpoints) {
522     if (!myDebugProcess.checkCanPerformCommands()) return;
523
524     if (ignoreBreakpoints) {
525       disableBreakpoints();
526     }
527     myDebugProcess.runToPosition(position, doResume());
528   }
529
530   @Override
531   public void pause() {
532     if (!myDebugProcess.checkCanPerformCommands()) return;
533
534     myDebugProcess.startPausing();
535   }
536
537   private void processAllBreakpoints(final boolean register, final boolean temporary) {
538     for (XBreakpointHandler<?> handler : myDebugProcess.getBreakpointHandlers()) {
539       processBreakpoints(handler, register, temporary);
540     }
541   }
542
543   private void disableBreakpoints() {
544     myBreakpointsDisabled = true;
545     processAllBreakpoints(false, true);
546   }
547
548   @Override
549   public void resume() {
550     if (!myDebugProcess.checkCanPerformCommands()) return;
551
552     myDebugProcess.resume(doResume());
553   }
554
555   @Nullable
556   private XSuspendContext doResume() {
557     if (!myPaused.getAndSet(false)) {
558       return null;
559     }
560
561     myDispatcher.getMulticaster().beforeSessionResume();
562     XSuspendContext context = mySuspendContext;
563     mySuspendContext = null;
564     myCurrentExecutionStack = null;
565     myCurrentStackFrame = null;
566     myTopFramePosition = null;
567     myActiveNonLineBreakpoint = null;
568     updateExecutionPosition();
569     UIUtil.invokeLaterIfNeeded(() -> {
570       if (mySessionTab != null) {
571         mySessionTab.getUi().clearAttractionBy(XDebuggerUIConstants.LAYOUT_VIEW_BREAKPOINT_CONDITION);
572       }
573     });
574     myDispatcher.getMulticaster().sessionResumed();
575     return context;
576   }
577
578   @Override
579   public void updateExecutionPosition() {
580     // allowed only for the active session
581     if (myDebuggerManager.getCurrentSession() == this) {
582       boolean isTopFrame = isTopFrameSelected();
583       myDebuggerManager.updateExecutionPoint(getCurrentPosition(), !isTopFrame, getPositionIconRenderer(isTopFrame));
584     }
585   }
586
587   public boolean isTopFrameSelected() {
588     return myCurrentExecutionStack != null && myIsTopFrame;
589   }
590
591
592   @Override
593   public void showExecutionPoint() {
594     if (mySuspendContext != null) {
595       XExecutionStack executionStack = mySuspendContext.getActiveExecutionStack();
596       if (executionStack != null) {
597         XStackFrame topFrame = executionStack.getTopFrame();
598         if (topFrame != null) {
599           setCurrentStackFrame(executionStack, topFrame, true);
600           myDebuggerManager.showExecutionPosition();
601         }
602       }
603     }
604   }
605
606   @Override
607   public void setCurrentStackFrame(@NotNull XExecutionStack executionStack, @NotNull XStackFrame frame, boolean isTopFrame) {
608     if (mySuspendContext == null) return;
609
610     boolean frameChanged = myCurrentStackFrame != frame;
611     myCurrentExecutionStack = executionStack;
612     myCurrentStackFrame = frame;
613     myIsTopFrame = isTopFrame;
614     activateSession();
615
616     if (frameChanged) {
617       myDispatcher.getMulticaster().stackFrameChanged();
618     }
619   }
620
621   void activateSession() {
622     myDebuggerManager.setCurrentSession(this);
623     updateExecutionPosition();
624   }
625
626   public XBreakpoint<?> getActiveNonLineBreakpoint() {
627     if (myActiveNonLineBreakpoint != null) {
628       XSourcePosition breakpointPosition = myActiveNonLineBreakpoint.getSourcePosition();
629       XSourcePosition position = getTopFramePosition();
630       if (breakpointPosition == null ||
631           (position != null && !(breakpointPosition.getFile().equals(position.getFile()) && breakpointPosition.getLine() == position.getLine()))) {
632         return myActiveNonLineBreakpoint;
633       }
634     }
635     return null;
636   }
637
638   @Nullable
639   private GutterIconRenderer getPositionIconRenderer(boolean isTopFrame) {
640     if (!isTopFrame) {
641       return null;
642     }
643     XBreakpoint<?> activeNonLineBreakpoint = getActiveNonLineBreakpoint();
644     if (activeNonLineBreakpoint != null) {
645       return ((XBreakpointBase<?, ?, ?>)activeNonLineBreakpoint).createGutterIconRenderer();
646     }
647     if (myCurrentExecutionStack != null) {
648       return myCurrentExecutionStack.getExecutionLineIconRenderer();
649     }
650     return null;
651   }
652
653   @Override
654   public void updateBreakpointPresentation(@NotNull final XLineBreakpoint<?> breakpoint,
655                                            @Nullable final Icon icon,
656                                            @Nullable final String errorMessage) {
657     CustomizedBreakpointPresentation presentation;
658     synchronized (myRegisteredBreakpoints) {
659       presentation = myRegisteredBreakpoints.get(breakpoint);
660       if (presentation == null ||
661           (Comparing.equal(presentation.getIcon(), icon) && Comparing.strEqual(presentation.getErrorMessage(), errorMessage))) {
662         return;
663       }
664
665       presentation.setErrorMessage(errorMessage);
666       presentation.setIcon(icon);
667     }
668     myDebuggerManager.getBreakpointManager().getLineBreakpointManager().queueBreakpointUpdate((XLineBreakpointImpl<?>)breakpoint);
669   }
670
671   @Override
672   public boolean breakpointReached(@NotNull final XBreakpoint<?> breakpoint, @NotNull final XSuspendContext suspendContext) {
673     return breakpointReached(breakpoint, null, suspendContext);
674   }
675
676   @Override
677   public boolean breakpointReached(@NotNull final XBreakpoint<?> breakpoint, @Nullable String evaluatedLogExpression,
678                                    @NotNull XSuspendContext suspendContext) {
679     return breakpointReached(breakpoint, evaluatedLogExpression, suspendContext, true);
680   }
681
682   public void breakpointReachedNoProcessing(@NotNull final XBreakpoint<?> breakpoint, @NotNull XSuspendContext suspendContext) {
683     breakpointReached(breakpoint, null, suspendContext, false);
684   }
685
686   private boolean breakpointReached(@NotNull final XBreakpoint<?> breakpoint, @Nullable String evaluatedLogExpression,
687                                    @NotNull XSuspendContext suspendContext, boolean doProcessing) {
688     if (doProcessing) {
689       if (breakpoint.isLogMessage()) {
690         XSourcePosition position = breakpoint.getSourcePosition();
691         OpenFileHyperlinkInfo hyperlinkInfo =
692           position != null ? new OpenFileHyperlinkInfo(myProject, position.getFile(), position.getLine()) : null;
693         printMessage(XDebuggerBundle.message("xbreakpoint.reached.text") + " ", XBreakpointUtil.getShortText(breakpoint), hyperlinkInfo);
694       }
695
696       if (evaluatedLogExpression != null) {
697         printMessage(evaluatedLogExpression, null, null);
698       }
699
700       processDependencies(breakpoint);
701
702       if (breakpoint.getSuspendPolicy() == SuspendPolicy.NONE) {
703         return false;
704       }
705     }
706
707     myActiveNonLineBreakpoint =
708       (!(breakpoint instanceof XLineBreakpoint) || ((XLineBreakpoint)breakpoint).getType().canBeHitInOtherPlaces()) ? breakpoint : null;
709
710     // set this session active on breakpoint, update execution position will be called inside positionReached
711     myDebuggerManager.setCurrentSession(this);
712
713     positionReachedInternal(suspendContext, true);
714
715     if (doProcessing && breakpoint instanceof XLineBreakpoint<?> && ((XLineBreakpoint)breakpoint).isTemporary()) {
716       handleTemporaryBreakpointHit(breakpoint);
717     }
718     return true;
719   }
720
721   private void handleTemporaryBreakpointHit(final XBreakpoint<?> breakpoint) {
722     addSessionListener(new XDebugSessionListener() {
723       private void removeBreakpoint() {
724         XDebuggerUtil.getInstance().removeBreakpoint(myProject, breakpoint);
725         removeSessionListener(this);
726       }
727
728       @Override
729       public void sessionResumed() {
730         removeBreakpoint();
731       }
732
733       @Override
734       public void sessionStopped() {
735         removeBreakpoint();
736       }
737     });
738   }
739
740   public void processDependencies(final XBreakpoint<?> breakpoint) {
741     XDependentBreakpointManager dependentBreakpointManager = myDebuggerManager.getBreakpointManager().getDependentBreakpointManager();
742     if (!dependentBreakpointManager.isMasterOrSlave(breakpoint)) return;
743
744     List<XBreakpoint<?>> breakpoints = dependentBreakpointManager.getSlaveBreakpoints(breakpoint);
745     myInactiveSlaveBreakpoints.removeAll(breakpoints);
746     for (XBreakpoint<?> slaveBreakpoint : breakpoints) {
747       processAllHandlers(slaveBreakpoint, true);
748     }
749
750     if (dependentBreakpointManager.getMasterBreakpoint(breakpoint) != null && !dependentBreakpointManager.isLeaveEnabled(breakpoint)) {
751       boolean added = myInactiveSlaveBreakpoints.add(breakpoint);
752       if (added) {
753         processAllHandlers(breakpoint, false);
754         myDebuggerManager.getBreakpointManager().getLineBreakpointManager().queueBreakpointUpdate(breakpoint);
755       }
756     }
757   }
758
759   private void printMessage(final String message, final String hyperLinkText, @Nullable final HyperlinkInfo info) {
760     AppUIUtil.invokeOnEdt(() -> {
761       myConsoleView.print(message, ConsoleViewContentType.SYSTEM_OUTPUT);
762       if (info != null) {
763         myConsoleView.printHyperlink(hyperLinkText, info);
764       }
765       else if (hyperLinkText != null) {
766         myConsoleView.print(hyperLinkText, ConsoleViewContentType.SYSTEM_OUTPUT);
767       }
768       myConsoleView.print("\n", ConsoleViewContentType.SYSTEM_OUTPUT);
769     });
770   }
771
772   public void unsetPaused() {
773     myPaused.set(false);
774   }
775
776   private void positionReachedInternal(@NotNull final XSuspendContext suspendContext, boolean attract) {
777     enableBreakpoints();
778     mySuspendContext = suspendContext;
779     myCurrentExecutionStack = suspendContext.getActiveExecutionStack();
780     myCurrentStackFrame = myCurrentExecutionStack != null ? myCurrentExecutionStack.getTopFrame() : null;
781     myIsTopFrame = true;
782     myTopFramePosition = myCurrentStackFrame != null ? myCurrentStackFrame.getSourcePosition() : null;
783
784     myPaused.set(true);
785
786     updateExecutionPosition();
787
788     final boolean showOnSuspend = myShowTabOnSuspend.compareAndSet(true, false);
789     if (showOnSuspend || attract) {
790       AppUIUtil.invokeLaterIfProjectAlive(myProject, () -> {
791         if (showOnSuspend) {
792           initSessionTab(null);
793           showSessionTab();
794         }
795
796         // user attractions should only be made if event happens independently (e.g. program paused/suspended)
797         // and should not be made when user steps in the code
798         if (attract) {
799           if (mySessionTab == null) {
800             LOG.debug("Cannot request focus because Session Tab is not initialized yet");
801             return;
802           }
803
804           if (XDebuggerSettingManagerImpl.getInstanceImpl().getGeneralSettings().isShowDebuggerOnBreakpoint()) {
805             mySessionTab.toFront(true, this::updateExecutionPosition);
806           }
807
808           if (myTopFramePosition == null) {
809             // if there is no source position available, we should somehow tell the user that session is stopped.
810             // the best way is to show the stack frames.
811             XDebugSessionTab.showFramesView(this);
812           }
813
814           mySessionTab.getUi().attractBy(XDebuggerUIConstants.LAYOUT_VIEW_BREAKPOINT_CONDITION);
815         }
816       });
817     }
818
819     myDispatcher.getMulticaster().sessionPaused();
820   }
821
822   @Override
823   public void positionReached(@NotNull final XSuspendContext suspendContext) {
824     positionReached(suspendContext, false);
825   }
826
827   public void positionReached(@NotNull XSuspendContext suspendContext, boolean attract) {
828     myActiveNonLineBreakpoint = null;
829     positionReachedInternal(suspendContext, attract);
830   }
831
832   @Override
833   public void sessionResumed() {
834     doResume();
835   }
836
837   private void enableBreakpoints() {
838     if (myBreakpointsDisabled) {
839       myBreakpointsDisabled = false;
840       new ReadAction() {
841         @Override
842         protected void run(@NotNull Result result) {
843           processAllBreakpoints(true, false);
844         }
845       }.execute();
846     }
847   }
848
849   @Override
850   public boolean isStopped() {
851     return myStopped.get();
852   }
853
854   private void stopImpl() {
855     if (!myStopped.compareAndSet(false, true)) {
856       return;
857     }
858
859     try {
860       if (breakpointsInitialized) {
861         XBreakpointManagerImpl breakpointManager = myDebuggerManager.getBreakpointManager();
862         if (myBreakpointListener != null) {
863           breakpointManager.removeBreakpointListener(myBreakpointListener);
864         }
865         if (myDependentBreakpointListener != null) {
866           breakpointManager.getDependentBreakpointManager().removeListener(myDependentBreakpointListener);
867         }
868       }
869     }
870     finally {
871       //noinspection unchecked
872       myDebugProcess.stopAsync().done(aVoid -> {
873         if (!myProject.isDisposed()) {
874           myProject.getMessageBus().syncPublisher(XDebuggerManager.TOPIC).processStopped(myDebugProcess);
875         }
876
877         if (mySessionTab != null) {
878           ((XWatchesViewImpl)mySessionTab.getWatchesView()).updateSessionData();
879           mySessionTab.detachFromSession();
880         }
881         else if (myConsoleView != null) {
882           AppUIUtil.invokeOnEdt(() -> Disposer.dispose(myConsoleView));
883         }
884
885         myTopFramePosition = null;
886         myCurrentExecutionStack = null;
887         myCurrentStackFrame = null;
888         mySuspendContext = null;
889
890         updateExecutionPosition();
891
892         if (myValueMarkers != null) {
893           myValueMarkers.clear();
894         }
895         if (XDebuggerSettingManagerImpl.getInstanceImpl().getGeneralSettings().isUnmuteOnStop()) {
896           mySessionData.setBreakpointsMuted(false);
897         }
898         myDebuggerManager.removeSession(this);
899         myDispatcher.getMulticaster().sessionStopped();
900         myProject.putUserData(XDebuggerEditorLinePainter.CACHE, null);
901
902         synchronized (myRegisteredBreakpoints) {
903           myRegisteredBreakpoints.clear();
904         }
905       });
906     }
907   }
908
909   public boolean isInactiveSlaveBreakpoint(final XBreakpoint<?> breakpoint) {
910     return myInactiveSlaveBreakpoints.contains(breakpoint);
911   }
912
913   @Override
914   public void stop() {
915     ProcessHandler processHandler = myDebugProcess.getProcessHandler();
916     if (processHandler.isProcessTerminated() || processHandler.isProcessTerminating()) return;
917
918     if (processHandler.detachIsDefault()) {
919       processHandler.detachProcess();
920     }
921     else {
922       processHandler.destroyProcess();
923     }
924   }
925
926   @Override
927   public void reportError(@NotNull final String message) {
928     reportMessage(message, MessageType.ERROR);
929   }
930
931   @Override
932   public void reportMessage(@NotNull final String message, @NotNull final MessageType type) {
933     reportMessage(message, type, null);
934   }
935
936   @Override
937   public void reportMessage(@NotNull final String message, @NotNull final MessageType type, @Nullable final HyperlinkListener listener) {
938     NotificationListener notificationListener = listener == null ? null : (notification, event) -> listener.hyperlinkUpdate(event);
939     NOTIFICATION_GROUP.createNotification("", message, type.toNotificationType(), notificationListener).notify(myProject);
940   }
941
942   private class MyBreakpointListener implements XBreakpointListener<XBreakpoint<?>> {
943     @Override
944     public void breakpointAdded(@NotNull final XBreakpoint<?> breakpoint) {
945       if (!myBreakpointsDisabled) {
946         processAllHandlers(breakpoint, true);
947       }
948     }
949
950     @Override
951     public void breakpointRemoved(@NotNull final XBreakpoint<?> breakpoint) {
952       if (getActiveNonLineBreakpoint() == breakpoint) {
953         myActiveNonLineBreakpoint = null;
954       }
955       processAllHandlers(breakpoint, false);
956     }
957
958     @Override
959     public void breakpointChanged(@NotNull final XBreakpoint<?> breakpoint) {
960       breakpointRemoved(breakpoint);
961       breakpointAdded(breakpoint);
962     }
963   }
964
965   private class MyDependentBreakpointListener implements XDependentBreakpointListener {
966     @Override
967     public void dependencySet(final XBreakpoint<?> slave, final XBreakpoint<?> master) {
968       boolean added = myInactiveSlaveBreakpoints.add(slave);
969       if (added) {
970         processAllHandlers(slave, false);
971       }
972     }
973
974     @Override
975     public void dependencyCleared(final XBreakpoint<?> breakpoint) {
976       boolean removed = myInactiveSlaveBreakpoints.remove(breakpoint);
977       if (removed) {
978         processAllHandlers(breakpoint, true);
979       }
980     }
981   }
982
983   @NotNull
984   private String getConfigurationName() {
985     if (myEnvironment != null) {
986       RunProfile profile = myEnvironment.getRunProfile();
987       if (profile instanceof RunConfiguration) {
988         return ((RunConfiguration)profile).getType().getId();
989       }
990     }
991     return getSessionName();
992   }
993
994   public void setWatchExpressions(@NotNull XExpression[] watchExpressions) {
995     mySessionData.setWatchExpressions(watchExpressions);
996     myDebuggerManager.getWatchesManager().setWatches(getConfigurationName(), watchExpressions);
997   }
998
999   XExpression[] getWatchExpressions() {
1000     return myDebuggerManager.getWatchesManager().getWatches(getConfigurationName());
1001   }
1002
1003   @Nullable
1004   public ExecutionEnvironment getExecutionEnvironment() {
1005     return myEnvironment;
1006   }
1007 }