2 * Copyright 2000-2009 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
.intellij
.xdebugger
.impl
;
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
.util
.Disposer
;
32 import com
.intellij
.openapi
.util
.text
.StringUtil
;
33 import com
.intellij
.util
.EventDispatcher
;
34 import com
.intellij
.xdebugger
.*;
35 import com
.intellij
.xdebugger
.stepping
.XSmartStepIntoVariant
;
36 import com
.intellij
.xdebugger
.stepping
.XSmartStepIntoHandler
;
37 import com
.intellij
.xdebugger
.breakpoints
.*;
38 import com
.intellij
.xdebugger
.evaluation
.XDebuggerEvaluator
;
39 import com
.intellij
.xdebugger
.frame
.XExecutionStack
;
40 import com
.intellij
.xdebugger
.frame
.XStackFrame
;
41 import com
.intellij
.xdebugger
.frame
.XSuspendContext
;
42 import com
.intellij
.xdebugger
.impl
.breakpoints
.*;
43 import com
.intellij
.xdebugger
.impl
.ui
.DebuggerUIUtil
;
44 import com
.intellij
.xdebugger
.impl
.ui
.XDebugSessionData
;
45 import com
.intellij
.xdebugger
.impl
.ui
.XDebugSessionTab
;
46 import org
.jetbrains
.annotations
.NotNull
;
47 import org
.jetbrains
.annotations
.Nullable
;
55 public class XDebugSessionImpl
implements XDebugSession
{
56 private static final Logger LOG
= Logger
.getInstance("#com.intellij.xdebugger.impl.XDebugSessionImpl");
57 private XDebugProcess myDebugProcess
;
58 private final Map
<XBreakpoint
<?
>, CustomizedBreakpointPresentation
> myRegisteredBreakpoints
= new HashMap
<XBreakpoint
<?
>, CustomizedBreakpointPresentation
>();
59 private final Set
<XBreakpoint
<?
>> myDisabledSlaveBreakpoints
= new HashSet
<XBreakpoint
<?
>>();
60 private boolean myBreakpointsMuted
;
61 private boolean myBreakpointsDisabled
;
62 private final XDebuggerManagerImpl myDebuggerManager
;
63 private MyBreakpointListener myBreakpointListener
;
64 private XSuspendContext mySuspendContext
;
65 private XStackFrame myCurrentStackFrame
;
66 private XSourcePosition myCurrentPosition
;
67 private boolean myPaused
;
68 private MyDependentBreakpointListener myDependentBreakpointListener
;
69 private String mySessionName
;
70 private XDebugSessionTab mySessionTab
;
71 private final EventDispatcher
<XDebugSessionListener
> myDispatcher
= EventDispatcher
.create(XDebugSessionListener
.class);
72 private Project myProject
;
73 private @Nullable ExecutionEnvironment myEnvironment
;
74 private ProgramRunner myRunner
;
75 private boolean myStopped
;
76 private boolean myPauseActionSupported
;
78 public XDebugSessionImpl(final @NotNull ExecutionEnvironment env
, final @NotNull ProgramRunner runner
, XDebuggerManagerImpl debuggerManager
) {
79 this(env
, runner
, debuggerManager
, env
.getRunProfile().getName());
82 public XDebugSessionImpl(final @Nullable ExecutionEnvironment env
, final @Nullable ProgramRunner runner
, XDebuggerManagerImpl debuggerManager
,
83 final @NotNull String sessionName
) {
86 mySessionName
= sessionName
;
87 myDebuggerManager
= debuggerManager
;
88 myProject
= debuggerManager
.getProject();
92 public String
getSessionName() {
97 public RunContentDescriptor
getRunContentDescriptor() {
98 LOG
.assertTrue(mySessionTab
!= null, "Call init() first!");
99 return mySessionTab
.getRunContentDescriptor();
102 public void setPauseActionSupported(final boolean isSupported
) {
103 myPauseActionSupported
= isSupported
;
106 public void rebuildViews() {
107 mySessionTab
.rebuildViews();
111 public RunProfile
getRunProfile() {
112 return myEnvironment
!= null ? myEnvironment
.getRunProfile() : null;
115 public boolean isPauseActionSupported() {
116 return myPauseActionSupported
;
120 public Project
getProject() {
121 return myDebuggerManager
.getProject();
125 public XDebugProcess
getDebugProcess() {
126 return myDebugProcess
;
129 public boolean isSuspended() {
130 return myPaused
&& mySuspendContext
!= null;
133 public boolean isPaused() {
138 public XStackFrame
getCurrentStackFrame() {
139 return myCurrentStackFrame
;
142 public XSuspendContext
getSuspendContext() {
143 return mySuspendContext
;
147 public XSourcePosition
getCurrentPosition() {
148 return myCurrentPosition
;
151 public XDebugSessionTab
init(final XDebugProcess process
, @NotNull final XDebugSessionData sessionData
) {
152 LOG
.assertTrue(myDebugProcess
== null);
153 myDebugProcess
= process
;
155 XBreakpointManagerImpl breakpointManager
= myDebuggerManager
.getBreakpointManager();
156 XDependentBreakpointManager dependentBreakpointManager
= breakpointManager
.getDependentBreakpointManager();
157 disableSlaveBreakpoints(dependentBreakpointManager
);
158 processAllBreakpoints(true, false);
160 myBreakpointListener
= new MyBreakpointListener();
161 breakpointManager
.addBreakpointListener(myBreakpointListener
);
162 myDependentBreakpointListener
= new MyDependentBreakpointListener();
163 dependentBreakpointManager
.addListener(myDependentBreakpointListener
);
165 initSessionTab(sessionData
);
166 process
.sessionInitialized();
171 public XDebugSessionTab
getSessionTab() {
175 private void initSessionTab(@NotNull XDebugSessionData sessionData
) {
176 mySessionTab
= new XDebugSessionTab(myProject
, mySessionName
);
177 if (myEnvironment
!= null) {
178 mySessionTab
.setEnvironment(myEnvironment
);
180 Disposer
.register(myProject
, mySessionTab
);
181 mySessionTab
.attachToSession(this, myRunner
, myEnvironment
, sessionData
);
184 private void disableSlaveBreakpoints(final XDependentBreakpointManager dependentBreakpointManager
) {
185 Set
<XBreakpoint
<?
>> slaveBreakpoints
= dependentBreakpointManager
.getAllSlaveBreakpoints();
186 Set
<XBreakpointType
<?
,?
>> breakpointTypes
= new HashSet
<XBreakpointType
<?
,?
>>();
187 for (XBreakpointHandler
<?
> handler
: myDebugProcess
.getBreakpointHandlers()) {
188 breakpointTypes
.add(getBreakpointTypeClass(handler
));
190 for (XBreakpoint
<?
> slaveBreakpoint
: slaveBreakpoints
) {
191 if (breakpointTypes
.contains(slaveBreakpoint
.getType())) {
192 myDisabledSlaveBreakpoints
.add(slaveBreakpoint
);
197 private static <B
extends XBreakpoint
<?
>> XBreakpointType
<?
, ?
> getBreakpointTypeClass(final XBreakpointHandler
<B
> handler
) {
198 return XDebuggerUtil
.getInstance().findBreakpointType(handler
.getBreakpointTypeClass());
201 private <B
extends XBreakpoint
<?
>> void processBreakpoints(final XBreakpointHandler
<B
> handler
, boolean register
, final boolean temporary
) {
202 XBreakpointType
<B
,?
> type
= XDebuggerUtil
.getInstance().findBreakpointType(handler
.getBreakpointTypeClass());
203 Collection
<?
extends B
> breakpoints
= myDebuggerManager
.getBreakpointManager().getBreakpoints(type
);
204 for (B b
: breakpoints
) {
205 handleBreakpoint(handler
, b
, register
, temporary
);
209 private <B
extends XBreakpoint
<?
>> void handleBreakpoint(final XBreakpointHandler
<B
> handler
, final B b
, final boolean register
,
210 final boolean temporary
) {
211 if (register
&& isBreakpointActive(b
)) {
212 synchronized (myRegisteredBreakpoints
) {
213 myRegisteredBreakpoints
.put(b
, new CustomizedBreakpointPresentation());
215 handler
.registerBreakpoint(b
);
218 boolean removed
= false;
219 synchronized (myRegisteredBreakpoints
) {
220 if (myRegisteredBreakpoints
.containsKey(b
)) {
221 myRegisteredBreakpoints
.remove(b
);
226 handler
.unregisterBreakpoint(b
, temporary
);
232 public CustomizedBreakpointPresentation
getBreakpointPresentation(@NotNull XLineBreakpoint
<?
> breakpoint
) {
233 synchronized (myRegisteredBreakpoints
) {
234 return myRegisteredBreakpoints
.get(breakpoint
);
238 private void processAllHandlers(final XBreakpoint
<?
> breakpoint
, final boolean register
) {
239 for (XBreakpointHandler
<?
> handler
: myDebugProcess
.getBreakpointHandlers()) {
240 processBreakpoint(breakpoint
, handler
, register
);
244 private <B
extends XBreakpoint
<?
>> void processBreakpoint(final XBreakpoint
<?
> breakpoint
, final XBreakpointHandler
<B
> handler
, boolean register
) {
245 XBreakpointType
<?
, ?
> type
= breakpoint
.getType();
246 if (handler
.getBreakpointTypeClass().equals(type
.getClass())) {
247 //noinspection unchecked
249 handleBreakpoint(handler
, b
, register
, false);
253 private boolean isBreakpointActive(final XBreakpoint
<?
> b
) {
254 return !myBreakpointsMuted
&& b
.isEnabled() && !myDisabledSlaveBreakpoints
.contains(b
);
257 public boolean areBreakpointsMuted() {
258 return myBreakpointsMuted
;
261 public void addSessionListener(@NotNull final XDebugSessionListener listener
) {
262 myDispatcher
.addListener(listener
);
265 public void removeSessionListener(@NotNull final XDebugSessionListener listener
) {
266 myDispatcher
.removeListener(listener
);
269 public void setBreakpointMuted(boolean muted
) {
270 if (myBreakpointsMuted
== muted
) return;
271 myBreakpointsMuted
= muted
;
272 processAllBreakpoints(!muted
, muted
);
273 myDebuggerManager
.getBreakpointManager().getLineBreakpointManager().queueAllBreakpointsUpdate();
276 public void stepOver(final boolean ignoreBreakpoints
) {
277 if (ignoreBreakpoints
) {
278 disableBreakpoints();
281 myDebugProcess
.startStepOver();
284 public void stepInto() {
286 myDebugProcess
.startStepInto();
289 public void stepOut() {
291 myDebugProcess
.startStepOut();
294 public <V
extends XSmartStepIntoVariant
> void smartStepInto(XSmartStepIntoHandler
<V
> handler
, V variant
) {
296 handler
.startStepInto(variant
);
299 public void forceStepInto() {
303 public void runToPosition(@NotNull final XSourcePosition position
, final boolean ignoreBreakpoints
) {
304 if (ignoreBreakpoints
) {
305 disableBreakpoints();
308 myDebugProcess
.runToPosition(position
);
311 public void pause() {
312 myDebugProcess
.startPausing();
315 private void processAllBreakpoints(final boolean register
, final boolean temporary
) {
316 for (XBreakpointHandler
<?
> handler
: myDebugProcess
.getBreakpointHandlers()) {
317 processBreakpoints(handler
, register
, temporary
);
321 private void disableBreakpoints() {
322 myBreakpointsDisabled
= true;
323 processAllBreakpoints(false, true);
326 public void resume() {
328 myDebugProcess
.resume();
331 private void doResume() {
332 myDispatcher
.getMulticaster().beforeSessionResume();
333 myDebuggerManager
.updateExecutionPosition(this, null, false);
334 mySuspendContext
= null;
335 myCurrentStackFrame
= null;
336 myCurrentPosition
= null;
338 myDispatcher
.getMulticaster().sessionResumed();
341 public void showExecutionPoint() {
342 if (mySuspendContext
!= null) {
343 XExecutionStack executionStack
= mySuspendContext
.getActiveExecutionStack();
344 if (executionStack
!= null) {
345 XStackFrame topFrame
= executionStack
.getTopFrame();
346 if (topFrame
!= null) {
347 setCurrentStackFrame(topFrame
);
348 myDebuggerManager
.showExecutionPosition();
354 public void setCurrentStackFrame(@NotNull final XStackFrame frame
) {
355 if (mySuspendContext
== null) return;
357 boolean frameChanged
= myCurrentStackFrame
!= frame
;
358 myCurrentStackFrame
= frame
;
359 XSourcePosition position
= frame
.getSourcePosition();
360 if (position
!= null) {
361 XExecutionStack activeExecutionStack
= mySuspendContext
.getActiveExecutionStack();
362 boolean isTopFrame
= activeExecutionStack
!= null && activeExecutionStack
.getTopFrame() == frame
;
363 myDebuggerManager
.updateExecutionPosition(this, position
, !isTopFrame
);
367 myDispatcher
.getMulticaster().stackFrameChanged();
371 public void updateBreakpointPresentation(@NotNull final XLineBreakpoint
<?
> breakpoint
, @Nullable final Icon icon
, @Nullable final String errorMessage
) {
372 CustomizedBreakpointPresentation presentation
;
373 synchronized (myRegisteredBreakpoints
) {
374 presentation
= myRegisteredBreakpoints
.get(breakpoint
);
375 if (presentation
!= null) {
376 presentation
.setErrorMessage(errorMessage
);
377 presentation
.setIcon(icon
);
380 if (presentation
!= null) {
381 myDebuggerManager
.getBreakpointManager().getLineBreakpointManager().queueBreakpointUpdate((XLineBreakpointImpl
<?
>)breakpoint
);
385 public boolean breakpointReached(@NotNull final XBreakpoint
<?
> breakpoint
, @NotNull final XSuspendContext suspendContext
) {
386 return breakpointReached(breakpoint
, null, suspendContext
);
389 public boolean breakpointReached(@NotNull XBreakpoint
<?
> breakpoint
, @Nullable String evaluatedLogExpression
,
390 @NotNull XSuspendContext suspendContext
) {
391 XDebuggerEvaluator evaluator
= XDebuggerUtilImpl
.getEvaluator(suspendContext
);
392 String condition
= breakpoint
.getCondition();
393 if (condition
!= null && evaluator
!= null) {
394 LOG
.debug("evaluating condition: " + condition
);
395 boolean result
= evaluator
.evaluateCondition(condition
);
396 LOG
.debug("condition evaluates to " + result
);
402 if (breakpoint
.isLogMessage()) {
403 String text
= StringUtil
.decapitalize(XBreakpointUtil
.getDisplayText(breakpoint
));
404 final XSourcePosition position
= breakpoint
.getSourcePosition();
405 final OpenFileHyperlinkInfo hyperlinkInfo
= position
!= null ?
new OpenFileHyperlinkInfo(myProject
, position
.getFile(), position
.getLine()) : null;
406 printMessage(XDebuggerBundle
.message("xbreakpoint.reached.text") + " ", text
, hyperlinkInfo
);
409 if (evaluatedLogExpression
!= null) {
410 printMessage(evaluatedLogExpression
, null, null);
413 String expression
= breakpoint
.getLogExpression();
414 if (expression
!= null && evaluator
!= null) {
415 LOG
.debug("evaluating log expression: " + expression
);
416 final String message
= evaluator
.evaluateMessage(expression
);
417 if (message
!= null) {
418 printMessage(message
, null, null);
423 processDependencies(breakpoint
);
425 if (breakpoint
.getSuspendPolicy() == SuspendPolicy
.NONE
) {
429 positionReached(suspendContext
);
433 private void processDependencies(final XBreakpoint
<?
> breakpoint
) {
434 XDependentBreakpointManager dependentBreakpointManager
= myDebuggerManager
.getBreakpointManager().getDependentBreakpointManager();
435 if (!dependentBreakpointManager
.isMasterOrSlave(breakpoint
)) return;
437 List
<XBreakpoint
<?
>> breakpoints
= dependentBreakpointManager
.getSlaveBreakpoints(breakpoint
);
438 myDisabledSlaveBreakpoints
.removeAll(breakpoints
);
439 for (XBreakpoint
<?
> slaveBreakpoint
: breakpoints
) {
440 processAllHandlers(slaveBreakpoint
, true);
443 if (dependentBreakpointManager
.getMasterBreakpoint(breakpoint
) != null && !dependentBreakpointManager
.isLeaveEnabled(breakpoint
)) {
444 boolean added
= myDisabledSlaveBreakpoints
.add(breakpoint
);
446 processAllHandlers(breakpoint
, false);
447 myDebuggerManager
.getBreakpointManager().getLineBreakpointManager().queueBreakpointUpdate(breakpoint
);
452 private void printMessage(final String message
, final String hyperLinkText
, @Nullable final HyperlinkInfo info
) {
453 DebuggerUIUtil
.invokeOnEventDispatch(new Runnable() {
455 final ConsoleView consoleView
= getConsoleView();
456 consoleView
.print(message
, ConsoleViewContentType
.SYSTEM_OUTPUT
);
458 consoleView
.printHyperlink(hyperLinkText
, info
);
460 else if (hyperLinkText
!= null) {
461 consoleView
.print(hyperLinkText
, ConsoleViewContentType
.SYSTEM_OUTPUT
);
463 consoleView
.print("\n", ConsoleViewContentType
.SYSTEM_OUTPUT
);
468 private ConsoleView
getConsoleView() {
469 return (ConsoleView
)mySessionTab
.getConsole();
472 public void positionReached(@NotNull final XSuspendContext suspendContext
) {
474 mySuspendContext
= suspendContext
;
475 XExecutionStack executionStack
= suspendContext
.getActiveExecutionStack();
476 myCurrentStackFrame
= executionStack
!= null ? executionStack
.getTopFrame() : null;
477 myCurrentPosition
= myCurrentStackFrame
!= null ? myCurrentStackFrame
.getSourcePosition() : null;
480 if (myCurrentPosition
!= null) {
481 myDebuggerManager
.updateExecutionPosition(this, myCurrentPosition
, false);
483 mySessionTab
.toFront();
484 myDispatcher
.getMulticaster().sessionPaused();
487 private void enableBreakpoints() {
488 if (myBreakpointsDisabled
) {
489 myBreakpointsDisabled
= false;
491 protected void run(final Result result
) {
492 processAllBreakpoints(true, false);
498 public boolean isStopped() {
502 public void stopImpl() {
503 if (myStopped
) return;
505 myDebugProcess
.stop();
506 myCurrentPosition
= null;
507 myCurrentStackFrame
= null;
508 mySuspendContext
= null;
509 myDebuggerManager
.updateExecutionPosition(this, null, false);
510 XBreakpointManagerImpl breakpointManager
= myDebuggerManager
.getBreakpointManager();
511 breakpointManager
.removeBreakpointListener(myBreakpointListener
);
512 breakpointManager
.getDependentBreakpointManager().removeListener(myDependentBreakpointListener
);
514 myDebuggerManager
.removeSession(this);
515 myDispatcher
.getMulticaster().sessionStopped();
518 public boolean isDisabledSlaveBreakpoint(final XBreakpoint
<?
> breakpoint
) {
519 return myDisabledSlaveBreakpoints
.contains(breakpoint
);
523 ProcessHandler processHandler
= myDebugProcess
.getProcessHandler();
524 if (processHandler
.isProcessTerminated() || processHandler
.isProcessTerminating()) return;
526 if (processHandler
.detachIsDefault()) {
527 processHandler
.detachProcess();
530 processHandler
.destroyProcess();
534 private class MyBreakpointListener
implements XBreakpointListener
<XBreakpoint
<?
>> {
535 public void breakpointAdded(@NotNull final XBreakpoint
<?
> breakpoint
) {
536 if (!myBreakpointsDisabled
) {
537 processAllHandlers(breakpoint
, true);
541 public void breakpointRemoved(@NotNull final XBreakpoint
<?
> breakpoint
) {
542 processAllHandlers(breakpoint
, false);
545 public void breakpointChanged(@NotNull final XBreakpoint
<?
> breakpoint
) {
546 breakpointRemoved(breakpoint
);
547 breakpointAdded(breakpoint
);
551 private class MyDependentBreakpointListener
implements XDependentBreakpointListener
{
552 public void dependencySet(final XBreakpoint
<?
> slave
, final XBreakpoint
<?
> master
) {
553 boolean added
= myDisabledSlaveBreakpoints
.add(slave
);
555 processAllHandlers(slave
, false);
559 public void dependencyCleared(final XBreakpoint
<?
> breakpoint
) {
560 boolean removed
= myDisabledSlaveBreakpoints
.remove(breakpoint
);
562 processAllHandlers(breakpoint
, true);