FpGdbmiDebugger
Note: FpGdbmiDebugger is in beta Development.
For more status detais Debugger StatusNote: FpGdbmiDebugger does not yet fix any of the following issues:
- Properties can still not be shown
- Watches can not call functions
- Strings can not be distinguished from pchar when using Dwarf 2
- open array are handled, but the extra hidden param (highPARAMNAME) is displayed too.
About
FpGdbmiDebugger is an extension of the GdbmiDebugger used by the IDE. Both debuggers use GDB.
FpGdbmiDebugger is intended to keep GDB as a back-end, so it can later be extended to use gdbserver for remote debugging. (An additional debugger that does not depend on GDB will also be developed, and like FpGdbmiDebugger, it will be based on the FpDebug package)
FpGdbmiDebugger is designed to perform some tasks without needing GDB:
- Reading Line-Info, for displaying the blue gutter dots (working)
- Reading Dwarf to evaluate Locals and watches (partly done)
Reading data from the debuggee is done via gdb memread, but does not need gdb to have knowledge of Pascal values. Except on Windows where data can be read directly. - Reading Stackframes/traces (not yet done)
- Disassemble (not yet done)
FpGdbmiDebugger is not intended to perform execution control directly (this is left to gdb), such as stepping, and running between breakpoints. (As said, there will also be another debugger that does not depend on GDB that will be capable of doing this)
Please read also our blog entry on the future of debuggers in Lazarus: Lazarus Blog - Debuggers
Testing
See progress for which features are implemented. Any feature not mentioned should be assumed not yet to be implemented.
Do NOT report any unimplemented features.
To verify displayed watch values, a menu entry "Display GDB instead of FPDebug values" is available in the run menu (when the package is installed). This will be only while the package is in development.
To test FpGdbmiDebugger, install the package components/lazdebuggers/lazdebuggerfpgdbmi/lazdebbugerfpgdbmi.lpk Then go to Menu: Tools > Options > Debugger. Select "GNU debugger (with fpdebug)".
There is also a testcase in that directory. Please read the comments in the *.sample files.
Progress
FpGdbmiDebugger works with Dwarf 2 (+ dwarfsets) and also with Dwarf 3. To take full advantage of it, Dwarf 3 should be used. Do not use external symbol files, except on macOS, where a dSym bundle must be used.
The following features should work
Code line indicators (blue dots in gutter)
Locals
Improvements over the GDB based version:
- Locals show values for types based on an internal pointer (string, object, dyn array). GDB shows only the address in the internal pointer
Not yet implemented:
- currency type variables are shown as integer e.g 1.370 will be shown as 1370 (see section on currency)
- "self" is shown as "this"
Watches
Watches that are not implemented are handed to the normal GDB based evaluation, and the result will be prefixed with "{GDB:}"
Watches include hint evaluation, and debug-inspector.
Improvements over the GDB based version:
- Expressions across nested procedures do work GDB_Debugger_Tips#Nested_Procedures_.2F_Function
- Windows: Faster
Not yet implemented:
- currency type variables are shown as integer e.g 1.370 will be shown as 1370 (see section on currency)
- With Dwarf2 strings are handled the same as on normal GDB (handled an PChar). s[1] will display same result for string and for pchar. Dwarf 3 will display strings as an array with the proper bounds and indices.
- unitname.identifier
- Any expression, or operator that is not listed in the below section. (May be forwarded to gdb, or give an error)
- Error checking. Expressions that result in an error, may be forwarded to GDB, instead of displaying an error generated by fpdebug.
- overflow checking. See below
- Setting in the watch properties:
- RepeatCount for watches is not implemented
- DisplayFormats (default, pointer, hex, structure, mem dump) are partly implemented
- Information needed for applying Pascal scopes is not part of the debug info. In case of several identifiers matching a name, scoping may be incorrect. This is known to happen for:
- multiple levels of nested procs GDB_Debugger_Tips#Nested_Procedures_.2F_Function
- Using Identifiers cross unit borders (the debugger does not have the info which units, and in which order were used. It will always search *all* other units. (Note: For value identifiers (variables) declared in the current unit (location of select stack frame), the debug info contains a copy of the type declarations. If accessing such type info by name, it will be found before searching other units.
The following expressions can be evaluated (if not listed, then it is not implemented)
- simple terms (just an identifier)
- Includes the value identifier "self" (if in a method), and the constants nil, true, false
- typecasts
- @ and ^ (address of and deref)
- "^TFoo(x)" typecast x, as pointer to TFoo
- () bracketed expression "a*(b+c)"
- [] index for arrays, pointers, and pchar
- . Foo.Bar access of members
- constant numbers in decimal, hex ($), oct (&), bin (%)
- constant string (ansistring)
- +-*/ for int and float
- mod for int
- + for string
- +- for pointer+int
- div for int
- = <> < > <= >= for int, float, string
- not and or xor: for int and boolean
Internal details
- All calculations are currently done without overflow checking. Overflown results will be used without indication of this.
- @ currently returns typed pointers in the debugger. This matters for (@WordVar+2)^ , which increments by 2 words (4 bytes).
- Fields in objects are ordered by base-class last. This means that in Foo:TFoo fields declared in TFoo are shown before fields in TObject (base). As a result field order does not represent memory layout (not sure if it actually is guaranteed with gdb).
Setting in the watch properties:
- "Use instance class" is implemented
Issues due to dwarf 2 encoding
AnsiString
Is encoded as PChar. So the debugger can not differentiate between the two. This works when using Dwarf 3.
Currency
Currency is encoded as * byte signed integer (int64)
GDB does show it correctly, but this is by checking for the hardcoded name "Currency" (on an signed 8 byte). GDB does therefore show the following wrong;
program Project1; type Currency= -1..$7fffffffffffffff; var a : Currency; begin a := 220; end.
a is shown as 0.022 by gdb.
If and when such a workaround may be added is open. Again this may be resolved by using a higher dwarf version.
Implementation notes
FpGdbmiDebugger reads the dwarf info directly from the exe. This means both (gdb and fpdebug) read this info. For large projects this adds a few seconds to the debugger startup time.
FpGdbmiDebugger uses this info to determine the type-info associated with a watch, and its location in memory.
- On Windows, FpGdbmiDebugger can attach as a 2nd debugger to an application that is already being debugged by gdb. Therefore FpGdbmiDebugger can use the Windows API to read memory from the debuggee
- On other platforms, FpGdbmiDebugger issues "read memory" requests to gdb. This still means that gdb does NOT have to evaluate type-info. It only reads raw memory.
In terms of speed: on windows there is a huge speed improvement. On Linux (despite a flood of mem read commands done via gdb) the speed-up is only minimal.
On my PC (may be totally different on others) FpGdbmiDebugger on Windows reads watches (depending on type) in average in less than 10% of the time used by GdbmiDebugger. On Linux FpGdbmiDebugger takes 80% of the time used by GdbmiDebugger.
This is NOT compared to gdb itself. There are other differences contributing to this timings. Such as implementation detail in GdbmiDebugger, which needs avg 2.7 calls to gdb for one watch. And time used due to communication of the 2 processes via pipes (suspected to take some time on win, but not sure).
See also
- TDebuggerIntf - the base class of implementing a debugger as Lazarus-IDE plugin package.