Sign in Join
Wiki copytemporary

TDebuggerIntf: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: TDebuggerIntf

OldNewLine
1+**TDebuggerIntf** is the base class of implementing a debugger as Lazarus-IDE plugin package. The package must have DebuggerIntf package as it's requirements. Any debugger plug-in must implement the class and register via **RegisterDebugger** (called at **Register** of registering unit).
2+
3+Implementation notes:
4+
5+- the class is designed to act as asynchronous object. The interaction is implemented in form of commands sent to the class.
6+- the command should be put (or not put) into an execution queue. The information about acceptance or denial of the command should be returned as soon as possible.
7+
8+
9+Command request is sent from the Main Thread. Thus any actual debugging job should be implemented in a separate thread NOT to impact the user experience.
10+
11+- once a command has been handled from the queue, the corresponding event should be called and/or debugger state changed
12+
13+## Debugger Workflow
14+
15+### Initialization
16+
17+IDE does following steps via TDebuggerIntf methods:
18+
19+- allocate (external debugger executable name is provided) (the state is expected **dsNone**)
20+- allocate debug objects (Breakpoints, watches) within TDebuggerIntf
21+- assign debugger properties
22+- .Init() (as a result of Init method, the debugger should be in **dsIdle** state)
23+- assigning environment
24+- assigning debugged process information (WorkingDir, FileName, Arguments, etc...)
25+
26+### Breakpoints
27+
28+In order to set a break-point there should be a new sub-class of **TGDBBreakPoint** provided.
29+
30+The list of break-points is stored and managed at TDBGBreakPoints class. The class constructor requires to specify the class of **TGDBBreakPoint** to be created (which should be the new sub-class) TDBGBreakPoints is created via protected **CreateBreakPoints** method. The method needs to be overridden and the code to allocate TDBGBreakPoints with the proper sub-class used.
31+
32+#### Validation
33+
34+IDE allows to create a breakpoint on any address and or source line. However, the source line where the breakpoint is created might not contain any code or debugging information. In order to indicate if the breakpoint could be stopped at **TGDBBreakPoint** class provides **Valid** property of enumeration **TValidState** type. The property is a public read-only property, however, it could be changed via the protected method **SetValid()**.
35+
36+- vsUnknown - this is the initial value of Valid property. Once the debugger verifies, if the requested break point is a valid point, it should change the value to either Valid or Invalid.
37+- vsValid - the breakpoint is a valid breakpoint, and the execution could stop on it.
38+- vsInvalid - the breakpoint requested is at the invalid location or debugging information doesn't exist.
39+
40+#### Hit
41+
42+Whenever breakpoint is hit, **TGDBBreakPoint.Hit()** method should be called.
43+
44+### Execution Address
45+
46+The execution address or execution line should be shown whenever the debugger is stopped or paused. This could occur whenever the debugger stops on a breakpoint, paused or an error occurs in the debugged process.
47+
48+The debugger should call (protected) method **DoCurrent()**, to specify where location of the instructions be executed. The method accepts one parameter, where address and source line of code is specified. At least address field should be populated. It's understood that source line information might not be available, if so Line should be set to 0.
49+
50+### Threads Information
51+
52+The information about threads and each thread call stack is an additional information about debugged process. **TDebuggerIntf** should either populate it whenever the information is available or whenever the information is explicitly requested.
53+
54+In order to populate the information the debugger should update **Threads** property (type **TThreadsSupplier**). The property is a list of threads. The list could be cleaned every time before population.
55+
56+The list should be populated using **Add** method. **TThreadEntity** class could be either allocated explicitly, or **CreateEntity** method of **Threads** could be used. It is important to remember that **Add** methods allocates a copy of **TThreadEntity**, thus right after the call to **Add** the added object could be freed (and must be freed eventually).
57+
58+Once the list of threads is added to **Threads**, it's necessary to validate the data by calling **SetValidity** method, passing **ddsValid** as a parameter.
59+
60+Finally, whenever a debugged process is stopped, it's desired to identify the "current" event thread by assinging ID of the thread to **CurrentThreadId** property of **Threads** object.
61+
62+**TThreadsSupplier** should be sub-classed in order to get the notification when thread information is needed. (The sub-class should be created in **CreateThreads** of TDebuggerIntf sub-class) The sub-class must override and implement **RequestMasterData** method. The method is called whenever IDE needs the up-to-date information about threads. Similar to **RequestCommand** the method is expected to behave asynchronously and doesn't have to update threads information immediately.
63+
64+## Minimal Implementation
65+
66+The following methods must be implemented for TDebbugerIntf
67+
68+- **GetSupportedCommands** (or **GetCommands**) - returns the set of formats that a debugger can perform
69+- **RequestCommand** - the method actually performing commands. Only commands returned by **GetSupportedCommands** would be passed to this method
70+- **Caption** - the method return user-friendly name of the debugger
71+
72+```delphi
73+type
74+ TMiniDebugger = class(TDebuggerIntf)
75+ protected
76+ function RequestCommand(const ACommand: TDBGCommand; const AParams: array of const): Boolean; override;
77+ function GetSupportedCommands: TDBGCommands; override;
78+ public
79+ class function Caption: String; override;
80+ end;
81+
82+procedure Register;
83+
84+implementation
85+
86+procedure Register;
87+begin
88+ RegisterDebugger(TMiniDebugger);
89+end;
90+
91+class function TMiniDebugger.Caption: String;
92+begin
93+ Result:='Initial Debugger Plug-in';
94+end;
95+
96+function TMiniDebugger.GetSupportedCommands: TDBGCommands;
97+begin
98+ Result := [dcRun, dcStop];
99+end;
100+
101+function TMiniDebugger.RequestCommand(const ACommand: TDBGCommand;
102+ const AParams: array of const): Boolean;
103+begin
104+ case ACommand of
105+ dcRun: begin // the debugger was requested to run
106+ Result:=true; // typically it should set the state into dsIdle (to get more data from IDE)
107+ SetState(dsRun); // the debugger replies as if it started immediately
108+ end;
109+ dcStop: begin // the debugger was requested to stop
110+ Result:=true;
111+ SetState(dsStop); // the debugger replied as if it stopped immediately
112+ end;
113+ else
114+ Result:=false;
115+ end;
116+end;
117+```
118+
119+## Reference
120+
121+### RequestCommand
122+
123+The method should return **false**, if the command cannot be executed.
124+
125+### property Environment
126+
127+```delphi
128+ property Environment: TStrings read write
129+```
130+
131+Environment variables that should be passed (or updated) in a debugged project. Assigning values to Environment triggers dcEnvironment command.
132+
133+## Commands
134+
135+The following commands are called via **RequestCommand** method.
136+
137+<table>
138+<thead>
139+<tr>
140+<th><p>Command</p></th>
141+<th><p>Explanation</p></th>
142+<th><p>Parameters</p></th>
143+</tr>
144+</thead>
145+<tbody>
146+<tr>
147+<td><p>dcRun</p></td>
148+<td><p>Starts (or Restarts) or Resumes the execution of a debugged process.</p>
149+<p>The debugged process executable and arguments to be used are available through <strong>FileName</strong> and <strong>Arguments</strong> properties of TDebuggerIntf object.</p></td>
150+<td><p>no parameters</p></td>
151+</tr>
152+<tr>
153+<td><p>dcPause</p></td>
154+<td></td>
155+<td><p>no parameters</p></td>
156+</tr>
157+<tr>
158+<td><p>dcStop</p></td>
159+<td></td>
160+<td><p>no parameters</p></td>
161+</tr>
162+<tr>
163+<td><p>dcStepOver</p></td>
164+<td></td>
165+<td><p>no parameters</p></td>
166+</tr>
167+<tr>
168+<td><p>dcStepInto</p></td>
169+<td></td>
170+<td><p>no parameters</p></td>
171+</tr>
172+<tr>
173+<td><p>dcStepOut</p></td>
174+<td></td>
175+<td><p>no parameters</p></td>
176+</tr>
177+<tr>
178+<td><p>dcRunTo</p></td>
179+<td></td>
180+<td><p>0 - AnsiString - file name of the source code to run to</p>
181+<p>1 - Integer - Line number to run to</p></td>
182+</tr>
183+<tr>
184+<td><p>dcJumpto</p></td>
185+<td></td>
186+<td><p>0 - AnsiString - file name of the source code to jump to</p>
187+<p>1 - Integer - Line number to run to</p></td>
188+</tr>
189+<tr>
190+<td><p>dcAttach</p></td>
191+<td></td>
192+<td><p>0 - AnsiString - process ID to attach-to.</p></td>
193+</tr>
194+<tr>
195+<td><p>dcDetach</p></td>
196+<td></td>
197+<td><p>no parameters</p></td>
198+</tr>
199+<tr>
200+<td><p>dcBreak</p></td>
201+<td></td>
202+<td></td>
203+</tr>
204+<tr>
205+<td><p>dcWatch</p></td>
206+<td></td>
207+<td></td>
208+</tr>
209+<tr>
210+<td><p>dcLocal</p></td>
211+<td></td>
212+<td></td>
213+</tr>
214+<tr>
215+<td><div id=#dcEvaluate>
216+</div>
217+<p>dcEvaluate</p></td>
218+<td></td>
219+<td><p>0 - AnsiString - expression to be evaluated</p>
220+<p>1 - pointer to AnsiString - the string where the result should be saved into</p>
221+<p>2 - pointer to TDBGType - the reference where the information about the result type should be saved to</p>
222+<p>3 - Integer - TDBGEvaluateFlags casted into Integer</p></td>
223+</tr>
224+<tr>
225+<td><p>dcModify</p></td>
226+<td><p>Modifies the given expression / Assigns a value to a variable/field/property</p></td>
227+<td><p>0 - AnsiString - identified expression</p>
228+<p>1 - AnsiString - value to be assigned</p></td>
229+</tr>
230+<tr>
231+<td><div id=dcEnvironment>
232+</div>
233+<p>dcEnvironment</p></td>
234+<td><p>Prepares/updates an environmental variable for a debugged process.</p></td>
235+<td><p>0 - AnsiString - environment variable in %name%=%value% format</p>
236+<p>1 - Boolean - the flag, if "true" then the variable needs to be set, "false" if needs to be removed</p></td>
237+</tr>
238+<tr>
239+<td><p>dcSetStackFrame</p></td>
240+<td></td>
241+<td></td>
242+</tr>
243+<tr>
244+<td><p>dcDisassemble</p></td>
245+<td></td>
246+<td></td>
247+</tr>
248+<tr>
249+<td><p>dcStepOverInstr</p></td>
250+<td></td>
251+<td></td>
252+</tr>
253+<tr>
254+<td><p>dcStepIntoInstr</p></td>
255+<td></td>
256+<td></td>
257+</tr>
258+<tr>
259+<td><p>dcSendConsoleInput</p></td>
260+<td></td>
261+<td></td>
262+</tr>
263+</tbody>
264+</table>
265+
266+## Development Hints
267+
268+- it might be helpful to recompile DebuggerIntf package, adding the following Custom Options to the compiler options of the package
269+
270+
271+
272+-dDBG_STATE
273+
274+-dDBG_EVENTS
275+
276+-dDBG_VERBOSE
277+
278+-dDBG_WARNINGS
279+
280+-dDBG_DATA_MONITORS
281+
282+-dDBG_DISASSEMBLER
283+
284+these defines provide more logging output of TDebuggerIntf
285+
286+- it might be helpful to rebuild IDE adding the following defines into build Options:
287+
288+
289+
290+-dVerboseDebugger
291+
292+**VerboseDebugger** logs messages debugging calls from IDE itself
293+
294+## See Also
295+
296+- [[FpGdbmiDebugger]] - debugger plug-in, that works over the external GDB debugger, but has a better understanding of Pascal expressions and types
297+
298+---
299+*From the Free Pascal wiki, page "TDebuggerIntf", as it stood on 2017-10-30 after a change by Skalogryz, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
300+Category: Debugging

Back to history