FeaturesPluginsDocs & SupportCommunityPartners

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. 
Plug-in architecture
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.
UI separated
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.
  • Debugger Core UI module:
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, ...
  • ViewModel module:
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.
  • Debugger JPDA module:
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:
Debugger APIs

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 statesee 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).
Requirements:
  • 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 (...)
Threads View

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

   
Call Stack 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:

  1. Breakpoint.Type.getCathegoryDisplayName ()
  2. Breakpoint.Type.getTypeDisplayName ()
  3. Breakpoint.Presenter.getCustomizer ()
  4. Breakpoint.Action.getCustomizer ()
  5. Breakpoint.Action.getCustomizer ()

Example how the New Breakpoint Dialog can looks like:

New Breakpoint Dialog

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


V. How to extend default debugger model / UI

  1. Extend debugger primitives - define new UI (Node) for it.
  2. Redefine content of some Debugger View
  3. Add some Execution Control Action
  4. 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]



Companion
Projects:
MySQL Database Server   Open JDK: an Open SourceJDK   GlassFish Community: an Open Source Application Server    Mobile & Embedded Community    Open Solaris   java.net - The Source for Java Technology Collaboration   Virtual Box - full virtualizer  Open ESB - The Open Enterprise Service Bus Powered by