NetBeans Debugger - Programmers Guide
Abstract:
This document describes NetBeans Debugger modules architecture,
APIs, and how to write debugger plug-ins.
Author:
Jan Jancura <jan.jancura@sun.com>
History:
[05/21 2003] First draft of document is prepared for Debugger
Summit.
[11/04 2004] Second version for NetBeans 4.0.
$Revision: 1.2 $
Content:
I. Overview
Debugger
Modules Overview
Debugger
APIs Overview
II. Debugger Core
API
1. DebuggerManager
2.
Session
3. DebuggerEngine
4. Watches
5. Breakpoints
6. Exension of
basic model - lookups, Meta-inf/services
7. Debugger Start Process
III. Standard
Debugger API extensions - Debugger SPI
6. Threads
7. Call Stacks and Local Variables
IV. Debugger UI [PENDING]
1. Actions
2. Debugger Window
a. Sessions View
b. Threads View
c. Call Stack View
d. Local Variables View
e. Watches View
f. Breakpoints View
3. Output Window
4. New Breakpoint Dialog
5. Integration with Editor
V. Declarative part
of Debugger APIs
VI. How to
extend default debugger model / UI
VII. How to
implement new Debugger Plug-in
VIII. Debugging of
mixed-languages environments
IX. Debugging of
enterprise
applications
Appendix A: List of
definitions
I. Overview
NetBeans Debugger is full featured multisession, multithreaded
Java debugger based on
JPDA.
Its extensible by plug-in technology, so it supports integration of
other debugger implementations (for different programming languages
like JSP, C++, ANT, ...) to the NetBeans IDE.
Debugger Modules Overview
DAR1 &
DAR3 requirement leads to spliting
debugger to at least two separate modules:
- Debugger Core: language
neutral part of debugger implementation, UI and APIs. Defines interface
for debugger plug-ins, mediates communication among different plug-ins
and rest of NetBeans.
- Debugger JPDA: this
debugger plug-in implements Java debugger based on JDI/JPDA
interface.

UI of NB Debugger should be separated from implementation (see
DAR10). It means that
Debugger Core UI should be separated
from
Debugger Core Implementation,
and
Debugger JPDA UI should be
separated from
Debugger
JPDA Implementation.

It leads to following debugger modules architecture first introduced in
NB4.0:
- Debugger Core API module:
Contains Debugger Core API, SPI and
Debugger Core implementation.
Defines interface to debugger plug-ins and mediates communication among
them.
Contains Debugger Core UI and SPI.
Provides basic shared debugger ui components like Debugger Views
(Session View, Breakpoints View, ...), Actions, Attach Dialog, New
Breakpoint Dialog, ...
Common
support for sharing of Tree Table / Tree View among a number of
different modules.
- Debugger JPDA API module:
Contains Debugger JPDA API / SPI.
Defines interface for denugger JPDA plug-ins.
Contains Debugger JPDA implementation.
- Debugger JPDA UI module:
contains Debugger JPDA UI.
- Debugger JPDA ANT module:
intergates Debugger JPDA to the NetBeans IDE. Contains bridge to Java
Hierarchy, NetBeans Editor, Projects module and ANT tasks.
Debugger APIs Overview
3) NB Debugger implementation should be splitted to several modules.
And some external modules are based on NB Debugger modules. It means
that we should define some interface for communication between them. We
need some APIs (SPIs) for communication between:
- Debugger Core and Debugger Plug-ins
(like Java Debugger Plug-in and DBX
Debugger Plug-in).
- Debugger Core and Debugger Core UI
- Debugger Core UI and debugger plug-in
UI.
- Java Debugger Implementation and JSP
Debugger and J2EE Debugger
Based on discussion of requirements I have decided to define three
sets of APIs for NB Debugger:
- Debugger Core API / SPI:
The main purpose for Debugger Core API / SPIs is to create simple
interface
which allows different debugger implementations to coexist in one IDE.
Main goal is to share some common UI components. Debugger Core API /
SPI defines communication between:
- Debugger Core and Debugger Plug-ins
- Debugger Core and Debugger UI (Debugger Core UI and Plug-ins UI)
- Debugger JPDA API / SPI:
Is extension of Debugger Core API / SPI. It defines communication
between:
- NetBeans Java Debugger and Java Debugger UI
- NetBeans Java Debugger and ather modules like JSP
Debugger, J2EE Debugger
- Debugger UI SPI: Defines
how to plug-in UI extensions to shared debugger UI defined in Debugger
Core UI module.
Debugger API dependences:
II. Debugger
Core API
Overview
Debugger Core API defines some basic tree of debugger primitives:
DebuggerManager, Session, Breakpoint, DebuggerEngine (language),
Breakpoint and Watch. DebuggerManager
is root interface of Debugger Core API. It manages list of currently
running Sessions. Each session
should represent one program / application running in debug mode
(application can contain more than one process). Session can be
debugged in one or more languages. So you can debug the same
application in JSP, Java or bytecode, for example. Each language is
represented by one DebuggerEngine.
All Breakpoints and Watches are shared among all running
Sessions and languages.
DebuggerManager
|
|-- Breakpoints
|
|-- Watches
|
|-- Sessions
<language name -> DebuggerEngine>
<language name - DebuggerEngine>
...
This basic model can be extended. Debugger Core API does not use
standard Java class extension mechanism, but it is based on lookup
pattern. Registration of different
services (specified in SPI) is used in place of standard Java class
extension mechanism.
Root of this tree (
DebuggerManager)
manages list of
Breakpoints,
Watches and running debugger
Sessions. User of API can add or remove
watches and breakpoints. All breakpoints and watches are shared for all
running sessions.
A new sessions are created during a prodcess of
debugger start. Session
typically represents some process or application running in debug mode.
Each session has a map from a name of programming language to a
instance of
DebuggerEngine.
DebuggerEngine represents some implementation of debugger (for some
particular programming language).
This very simple and plain model of debugger can (and should) be
extended using a lookup pattern.
1.
DebuggerManager
DebuggerManager reopresents root of
Debugger Core API / SPIs. There
is always one instance of DebuggerManager
only. It can be obtained from: DebuggerManager.getDebuggerManager
().
DebuggerManager features:
- sessions management:
- debugger starts and finishes sessions: DebuggerManager.startDebugger
(...), DebuggerManager.finishDebugger ()
- manage list of running debugger sessions: DebuggerManager.getSessions
()
- manage current session property: DebuggerManager.getCurrentSession
(), setCurentSession (...)
- breakpoints management:
- provides list of breakpoints: DebuggerManager.getBreakpoints
()
- add / remove breakpoint: DebuggerManager.addBreakpoint
(Breakpoint b), DebuggerManager.removeBreakpoint
(Breakpoint b)
- listening on breakpoints: DebuggerManagerListener.
breakpointAdded (Breakpoint b), DebuggerManagerListener.breakpointRemoved (Breakpoint b)
- watches management:
- create a new instances of watch: DebuggerManager.createWatch
(...)
- provides list of watches: DebuggerManager.getWatches ()
2. Session
Session (in sense of this APIs) visually
represents one process or
application.
Session
features:
- languages management:
- Session manages list of supported programming languages: Session.
getSupportedLanguages()
- On of the languages is current: Session.getCurrentLanguage (),
Session.setCurrentLanguage ()
- Each language corresponds to one DebuggerEngine: Session.getEngineForLanguage (),
Session.getCurrentEngine ()
- additional services registration:
- Session is final class. The standard method how to extend its
functionality is using lookup methods: Session.lookup (Class c), Session.lookupFirst (Class c)
- properties:
- name (ro) - name of session
- location name (ro) - name of computer this session is running on
- type ID (ro) - identifies type of session, its used for
registration of services
One of the sessions is always marked as
Current
Session. Current session can be changed by user. Debugger State and
state of all debugger actions should be defined by
Current Session. Current Session can be changed by debugger plug-in in
some special cases (like when some breakpoint is reached).
3.
DebuggerEngine
Debugger Engine represents
implementation of one debugger (Java
Debugger, CPP Debugger). It can support debugging of one or more Sessions,
in one or more languages. It manages debugger actions.
DebuggerEngine features:
Session (in sense of this APIs) represents some engine capable of:
- execution control: Session should implement start and
finish session actions. Optionally it can implement trace into / over /
out, run to cursor, go, fix pop and pause actions.
- managing watches: add / remove / get all watches
- managing session state: see
Session State definition
Session need not be mapped 1:1 to
Session Node (Session Node is an item
visible in
Session
View).
From the API point of view there is no constraints if session should
represent one or more debugger engines, one or more languages or how to
represent client-server applications (one Session Node or two?). This
should be defined in some UI Guidelines document.
Session State
definition:
State instance defines:
- enabled / disabled state of debugger actions (like Trace Into
Action)
- global state of session's engine:
- not running: debugger session has not been started or it has
finished (in this case it will be removed from Sessions
View)
- starting: [PENDING]
- running: [PENDING]
- stopped: [PENDING]
One of the sessions is always marked as
Current
Session. Current session can be changed by user. Debugger State and
state of all Debugger Execution control Actions should be defined by
Current Session. Current Session can be changed by debugger plug-in in
some special cases (like when some breakpoint is reached).
Session lifecycle
<<picture>>
4. Watch
Watch definition:
Watch in NetBeans debugger is defined by some text expression
(String). Format of this text is plug-in dependent. It should contain
some name of variable or some expression. Watch for this expression
returns some value (current value of expression in text format), type
or
error message. Watch is always evaluated it the context defined by Current
Session.
Watch can produce some hierarchy of
fields for some Debugger
Plug-ins. Debugger Core API / SPI contains some basic support for
fields, variables
and locals. This support is lightweight and optional.
Variable
definition:
Variable in NetBeans debugger is defined by:
- variableName: Contains name of variable, field or local. Its
read only property.
- value: Contains text representation of current value of this
Variable. Its read write property.
- type: Contains text representation of current type of this
Variable. Its read only property.
Variable instance can produce some hierarchy or fields and
subfields.
Requirements:
- There is one list of watches shared for all debugger plug-ins.
- List of watches is projects dependent.
- User can create watch even when no session is running.
- Watch can produce some hierarchy of fields.
Implementation:
Debugger.getDebugger () instance
keeps list of shared
watches. If some Session is set up as a current, Debugger creates
special Watch for it (Session.createWatch ()). Instance of watch
from global list than delegates all functionality to this plug-in
specific Watch implementation.
In order to represent fields of current watch value, watch
implementation should implement VariablesProducer interface.
And
return set of fields represented by Variable instances.
Variable instance itself can implement VariableProducer interface too.
<<Picture>>
5.
Breakpoint
From the philosophicall point of view the
breakpoint is something
like listener on some special event in debugged program. This APIs
distinguishs between Breakpoint Type definition, Breakpoint
definition, and some definition what happens when breakpoint is reached
(deinition of Breakpoint Action).
Each debugger plug-in can add some new
set of Breakpoint Types, but
one breakpoint type can be shared (supported) by two different debugger
plug-ins.
Instance of breakpoint is typically
created by user. Debugger Core
module supports two ways how the user can create a new breakpoint:
Toggle Breakpoint Action (in menu, toolbar or editor gutter), and New Breakpoint Dialog.
Debugger engine (debugger plug-in) can create breakpoints too. Such
breakpoints should be hidden for users. New breakpoint instance is
always created for some concrete Breakpoint Type.
Breakpoint
Type
definition:
represent unique identification of some type of breakpoints, and
defines:
- cathegory display name:
Breakpoint types can be presented in some cathegories (for example in New Breakpoint Dialog). Cathegory
display name can be mapped to some programming language. This should be
exactly defined in some UI Guidelines document.
- display name:
Should be unique description under some breakpoint typecathegory.
For example: cathegory - Java, type - Line breakpoint.
Breakpoint
definition:
Debugger Core API / SPIs divides definition of breakpoint to some
pieces
(logical parts):
- hidden state: Breakpoint can be hidden, not displayed in
UI.
- valid state: Breakpoint can be created even when no
brakpoint session is created, and breakpoint data can not be validated
(for example if breakpoint on line is set on someline containing
some debuggable source code). So, this property of breakpoint
definition shows of breakpoint has been validated.
- enabled state: Breakpoint can be temporarily disabled.
- representation in Breakpoints
View: Breakpoint should define how the breakpoint will be
represented in Breakpoints View (like Node). It means:
- icon
- display name
- set of properties
- representation in New
Breakpoint Dialog: Breakpoint should define how the breakpoint
will be represented in New Breakpoints Dialog (like some JPanel).
Breakpoint
Action
definition:
Action defines what happen when the breakpoint is reached, and
defines:
- representation in Breakpoints
View: Breakpoint Action should define how the action will
be represented in Breakpoints View as a part of Breakpoints Node. It
should define some set of additional properties, which will be
added to set of properties defined by breakpoint.
- representation in New
Breakpoint Dialog: Breakpoint Action should define how the
action will be represented in New Breakpoints Dialog (like some
JPanel).
- Debugger should allow to share one breakpoint definition (like
line breakpoint in Java file) for more debugger plug-ins.
- User can create breakpoint even when no session is running.
- There is one list of breakpoints shared for all debugger plug-ins.
- List of breakpoints is project dependent.
- There should be special type of breakpoint for
org.openide.text.Line supported.
- There should be possible to creaate all other types of
breakpoints from New Breakpoint
Dialog (shared debugger UI).
- There should be possibility how to validate concrete breakpoint
for concrete Debugger Plug-in and Session.
Implementation:
Breakpoint Type is implemented by Breakpoint.Type
interface. List of all registered breakpoint types is stored in layers.
Breakpoint implementation is divided to three classes:
- Breakpoint: Defines some abstract body of breakpoint. Is
should not be subclassed by Debugger Plug-ins. This class only
implements "logic" of breakpoint like:
- hidden, valid and enabled properties
- deleting of breakpoints
- grouping of breakpoints (see BreakpointGroup class)
- list of Breakpoint Actions
- link on project instance of this breakpoint
- link on Breakpoint.Presenter
- it defines comuncation between Breakpoint.Presenter,
Breakpoint.Impl and Breakpoint.Action
- Breakpoint.Presenter: This class represents defininition
of concrete instance of breakpoint (all information needed to create
one
breakpoint), and it defines UI representation of it in Explorer -
Breakpoints View (as Breakpoint Node) and in New
Breakpoint Dialog.
- Breakpoint.Impl: This class represents plug-in dependent
implementation of breakpoint. One breakpoint can have assigned ore than
one Breakpoint.Impls - one for each Debugger Plug-in which supports
this type of breakpoint.
Breakpoint Action is represented by Breakpoint.Action
interface.
Debugger instance keeps list of all registered breakpoint types (Breakpoint.Type).
When some new breakpoint is created in New Breakpoint Dialog or by
ToggleBreakpointAction, a Breakpoint.Presenter is registered to
Debugger. Debugger keeps list of all shared breakpoint presenters. When
some new Session is started, Debugger calls its Debugger Plug-in
method DebuggerPlugIn.createImpl
(Breakpoint.Presenter p, Session s) to create
Breakpoint.Impl instance for each Breakpoint.Presenter.
<<Picture>>
6. Exension of
basic model - lookups, Meta-inf/services
7.
Debugger Start Process
III. Standard
Debugger API extensions - Debugger SPI
1. Threads
This part of APIs is optional, Debugger Plug-in do not have to
produce any threads hierarchy.
Thread definition:
Definition of thread in Debugger Core API / SPIs is very
lightweight. Thread
is some object with name. The name of thread can be changed during
time. Each thread can produce some subthreads.
Implementation:
The Session managing some hierarchy of threads have to implement
ThreadsRoot interface. ThreadsRoot defines:
- set of threads: Set of threads produced by Session is
represented by ThreadsProducer interface.
- current thread property: One of threads produced by
Session can be marked as current
Threads are represented by AbstractThread class.
AbstractThread class defines the only one property: thread name. In
order to represent some subthreads, AbstractThread can implement ThreadsProducer
interface.
<<Picture>>
2. Call Stacks and Local Variables
Each thread can optionally produce some call stack.
Call Stack definition:
Call Stack is some ordered set of Call Stack Frames. One of the
frames can be marked current.
Call Stack Frame
definition:
Call Stack Frame is simple object with two properties:
- line: represents Line object of this Call Stack Frame
- call stack frame name: some text description of Call
Stack Frame
Each Thread and Call Stack Frame can optionally produce some set of
local variables.
Implementation:
A thread producing some call stack should implement CallStackProducer
interface. CallStackProducer defines:
- list of call stack frames
- current call stack frame
Each Call StackFrame is represented by some instance of CallStackFrame
interface. Call Stack Frame instance can implement VariablesProducer
interface to represent local variables. Local varaibles are than
reresented by Variable class.
<<Picture>>
III. Debugger UI
1. Actions
Action -> debugger -> session
DebuggerState
How to add new actions
2. Debugger Window
Debugger Window "listens" on debugger hierarchy and updates its state.
Registration of nodes.
a. Sessions View
Sessions View shows list of currently running sessions (registered in
debugger engine). Sessions are displayed in a table. One of the
sessions
is always
current session. Sessions View allows user to:
- change current session
- do some operations on session like Finish; debugger-plug-in can
redefine set of provided actions on its sessions
- change set of visible columns; debugger-plug-in can redefine set
of provided columns for this view
How does it work:
Session View listens (PropertyChangeListener) on Debugger instance on
Debugger.PROP_SESSIONS. When this property is fired SV reads current
list of sessions from
Debugger.getDefault ().getSessions (),
creates nodes for them and updates view content.
Implementation Details:
Sessions View is implemented in
org.netbeans.modules.debugger.support.nodes.SessionsView.
Root Node of Sessions View is:
org.netbeans.modules.debugger.support.nodes.SessionRootNode
Its registered in layers in folder Services/Debugger/Nodes
b. Threads View
Threads View shows list or tree of threads of current session (selected
in Sessions View). Threads are displayed in a tree-table. One of the
threads can be
current thread.
Threads View allows user to:
- change current thread
- do some operations on thread; debugger plug-in can redefine set
of provided actions on its threads
- change set of visible columns; debugger plug-in can redefine set
of provided columns for this view
How does it work:
Threads View listens (PropertyChangeListener) on Debugger instance on
Debugger.PROP_CURRENT_SESSION. When this property is fired TV gets
current session:
Session
currentSession = Debugger.getDefault ().getCurrentSession ()
If the current session implements
ThreadsRoot interface, it
gets
root of threads and displays all threads returned by getThreads ()
method.
if (currentSession implements ThreadsRoot)
AbstractThread[] threadsToDisplay = ((ThreadsRoot) currentSession).getThreadsRoot ().getThreads ()
Threads View listens on root of threads too (ThreadsListener), to
listen changes of set of threads.
((ThreadsRoot) currentSession).getThreadsRoot ().addThreadsListener (...)
Implementation Details:
Threads View is implemented in
org.netbeans.modules.debugger.support.nodes.ThreadsView.
Root Node of Threads View is:
org.netbeans.modules.debugger.support.nodes.ThreadsRootNode
Its registered in layers in folder Services/Debugger/Nodes
c. Call Stack View
Call Stack View shows list or tree of call stack frames of current
thread (selected in Threads View). Call stack frames are displayed in a
tree-table. One of the call stack frames is always
current call stack frame.
Call Stack View allows user to:
- change current call stack frame
- do some operations on call stack frames; debugger plug-in can
redefine set of provided actions on its call stack frames
- change set of visible columns; debugger plug-in can redefine set
of provided columns for this view
How does it work:
Call Stack View listens (PropertyChangeListener) on:
- Debugger instance on current session property (Debugger.PROP_CURRENT_SESSION)
- current session instance on current thread property (ThreadsRoot.PROP_CURRENT_THREAD)
- current thread instance on call stack property (CallStackProducer.PROP_CALLSTACK)
Call Stack View shows nodes for callStack obtained this way:
Session
currentSession = Debugger.getDefault ().getCurrentSession ();
if (currentSession implements ThreadsRoot) {
AbstractThread currentThread = ((ThreadsRoot)
currentSession).getCurrentThread () ;
if (currentThread implements CallStackProducer) {
Location[] callStack = ((CallStackProducer)
currentThread).getCallStack ();
// show nodes for callStack
return;
}
}
// show empty view
Implementation Details:
Call Stack View is implemented in
org.netbeans.modules.debugger.support.nodes.CallStackView.
Root Node of Call Stack View is:
org.netbeans.modules.debugger.support.nodes.CallStackRootNode
Its registered in layers in folder Services/Debugger/Nodes
d. Local Variables View
[PENDING]
e. Watches View
[PENDING]
f. Breakpoints View
[PENDING]
3. Output Window
[PENDING]
Debugger Console
Process Output
4. New Breakpoint Dialog
NewBreakpoint Dialog offers all installed Breakpoint Types to user.
User can select some Debugger Type, customize its properties and define
and customize some set of acions for it.
Requirements:
- New Breakpoint Dialog offers all installed Breakpoint Types from
all installed Debugger Plug-ins.
- Breakpoint Types are organized in two levels hierarchy:
breakpoint cathegory display name
and breakpoint type display name.
- There should be some support for validation of breakpoint before
its registered to Debugger instance.
New Breakpoint Dialog is created from:
- Breakpoint.Type.getCathegoryDisplayName ()
- Breakpoint.Type.getTypeDisplayName ()
- Breakpoint.Presenter.getCustomizer ()
- Breakpoint.Action.getCustomizer ()
- Breakpoint.Action.getCustomizer ()
Example how the New Breakpoint Dialog can looks like:

Implementation details:
JComponent instances returned from
Breakpoint.Presenter.getCustomizer () or
Breakpoint.Action.getCustomizer () calls can implement Controller
interface. In this case this instances will be informed when Ok and
Cancel buttons are pressed (Controller.ok () and Controller.cancel
()) and they can refuse such events. They can define if Ok
button should be enabled too (Controller.isValid ()).
5. Integration with Editor
[PENDING]
DebuggerAnnotation
BreakpointCookie
IV. Declarative
part of Debugger APIs
[PENDING]
Services/Debugger/BreakpointTypes
Services/Debugger/ProfileTypes
Services/Debugger/PlugIns
Services/Debugger/Nodes
Services/Debugger/Views
Services/Hidden/debugger.instance
- Extend debugger primitives - define new UI (Node) for it.
- Redefine content of some Debugger View
- Add some Execution Control Action
- Add / Hide / Redefine some Debugger View
VI. How to
implement new Debugger Plug-in
[PENDING]
VII. Debugging of
mixed-languages environments
[PENDING]
One session - more languages
How to install a new language support for existing debugger engine
VIII. Debugging of
enterprise applications
[PENDING]