*gdb_design.txt* For Vim version 6.2. Last change: 2004 Mar 28 VIMGDB INTERNALS by Xavier de Gaye VimGDB internals *gdbint* This document describes some aspects of VimGDB internals. 1. File list |gdbint-filelist| 2. Gdb modules |gdbint-modules| 3. Out Of Band functions |gdbint-oob| 4. Vim core |gdbint-vimcore| 5. Known bugs |gdbint-bugs| 6. Changes log |gdbint-changes| ============================================================================== 1. File list *gdbint-filelist* This section lists all files changed or added to Vim source distribution in order to implement VimGDB. gdb.c new gdb.h new gdb_lvl2.c new gdb_lvl3.c new macros/gdb_mappings.vim new and not required by gdb Main changes to Vim core source code: os_unix.c see |gdbint-vimcore| normal.c see |gdbint-vimcore| buffer.c see |gdbint-vimcore| gui.c GUI support: follows os_unix.c model gui_gtk_x11.c GUI support: follows os_unix.c model gui_x11.c GUI support: follows os_unix.c model Enable the window input-line and customize this window for VimGDB with mappings for <Tab>, CTRL-Z and with gdbvim file type: ex_getln.c Syntax: syntax/gdb.vim exhaustive implementation of gdb syntax syntax/gdbvim.vim gdb console syntax, gdb.vim is sourced syntax/gdbvar.vim gdb variables window syntax The following Vim files have been changed or added for housekeeping purpose: o Gdb is a feature: feature.h include |+signs| when FEAT_GDB is defined configure.in config.mk.in Makefile config.h.in version.c eval.c support for the new builtin function gdb() o Gdb adds new code to Vim source code: globals.h structs.h proto.h proto/gdb.pro new main.c gdb_new() and gdb_delete() buffer.c clean up in free_buffer() window.c gdb console not last in only_one_window() screen.c status line gdb label in win_redr_status() option.c options 'gdbprg', 'asm' and 'gdbvariables' option.h options 'gdbprg', 'asm' and 'gdbvariables' ============================================================================== 2. Gdb modules *gdbint-modules* The Gdb modules provide an objet oriented type interface to Vim core source code. gdb.h defines a gdb_T structure that can be viewed as a class. External code (i.e. Vim code) uses an opaque handle to this structure. There is only one instance of this structure for the whole Vim session. The following diagram describes the data flow between the GDB process, VimGDB and user commands entered with the Vim gdb() builtin function (the corresponding C function is named f_gdb()). GDB VimGDB User interface GDB -->------------- parse_output output | process_annotation --> prompt user - | | --- yes <-- IS_OOBACTIVE | | | | O | process_completion --> prompt user - u | | | t | gdb_redraw | | | | o | firstcmd && prompt annotation | f | | | v v v b | | | a | normal_cmd | | [Window input-line] n | | | | d | gdb_winput..................gdb_setwinput -- f_gdb("") | | | d | | | [GDB command] a | | | t | gdb_docmd ----------<------------------- f_gdb("cmd") a | | | | GDB <-gdb_send_cmd- input The link "......." between gdb_winput and gdb_setwinput denotes that by setting the prompt to be inserted later in the window input-line in this->winput_cmd, gdb_setwinput triggers an indirect ulterior invocation of gdb_winput when Vim will return to main_loop. VimGDB modes: ------------ The VimGDB modules: gdb_lvl2.c: only level 2 specific code gdb_lvl3.c: level 3 specific and code common to both levels gdb.c: code non specific to any mode Level 3 mode uses GDB CLI and annotations level 3. This mode uses the GDB "interpreter-exec" command to send GDB/MI commands and fetch the breakpoint table and to manage the variables window. gdb_setup_cli() selects the appropriate mode depending on the "interpreter-exec" command support by GDB. gdb_lvl2_init() and gdb_lvl3_init() assign their own pointer to the oobfunc array in gdb_T and the appropriate function pointers in gdb_T in order to implement the proper mode. These are, among others, a parser function and the function invoked to send GDB commands. A hook is provided in exec_gdb() so that a future pure GDB/MI implementation may plug its own functions and oob array. VimGDB can be compiled for exclusive support of level 2 mode, or exclusive support of level 3 mode, or for both (see appropriate defines in gdb.h). When annotations level 2 becomes obsolete for all existing GDB implementations, undefine GDB_LVL2_SUPPORT in gdb.h. Screen updates: -------------- Screen updates are managed by Vim core. However, VimGDB changes the screen content while in low level functions. Therefore, the screen must be updated by VimGDB in these circumstances. This is done through the gdb.c function gdb_redraw(). One of these cases is when writing GDB output to the gdb console, as is shown in the following call graph: main_loop } main.c normal_cmd } normal.c safe_vgetc } getchar.c vgetc } vgetorpeek } inchar } ui_inchar } ui.c mch_inchar } os_unix.c gdb_process_output } gdb.c parse_output } gdb_redraw } Track gdb_redraw() invocations to get all screen updates done by VimGDB. Data insulation: --------------- Vim internal data is not written to directly by VimGDB except: got_int described in |gdbint-vimcore| RedrawingDisabled to force redrawing emsg_skip to avoid displaying unwanted error msgs cmd_silent msg_didout Memory: ------ Memory leaks can be traced with GNU mtrace. VimGDB memory leaks are traced when Vim is compiled with "-DGDB_MTRACE -DHAVE_MTRACE". Set MALLOC_TRACE environment variable to an mtrace log file and use mtrace on this file to search for memory leaks. There must be none. Except for the gdb_T structure, no memory allocation is done by VimGDB before the first GDB process is started. Virtual memory used by the gdb buffer is limited to 64 K. ============================================================================== 3. Out Of Band functions *gdbint-oob* VimGDB can be extended by implementing an oobfunc_T function and adding it to the oobfunc array. All functions in oobfunc array are successively invoked at each GDB "prompt" annotation in the array order. Some functions might not always need to process something in which case they return NULL on their (first) invocation with OOB_CMD state. The gdb console status line, assembly buffers, breakpoints and GDB/MI variables are implemented using Out Of Band functions. Highlighting breakpoints in assembly code: ----------------------------------------- Setting a breakpoint and highlighting it in assembly code when the corresponding function is not yet disassembled involves the interaction of three Out Of Band functions. The following pseudo code describes how this is done. Note: The matter is complicated by the fact that GDB may send ANO_BP_INVALID when stepping or continuing, even though the table did not change. States: BPS_BP_HIT got ANO_BREAKPOINT and ('enable once' or ! in bpinfo) BPS_INVALID got ANO_BP_INVALID or got ANO_BREAKPOINT and ('enable once' or ! in bpinfo) BPS_FR_INVALID got ANO_FRAME_INVALID BPS_BP_SET a "break" type GDB command is being processed get_lastbp() if asm != 0 // assembly is on && BPS_INVALID // GDB reports bp table changed && BPS_BP_SET // a break GDB command is being processed { set this->asm_add to address of last bp in GDB bp table so that function is disassembled in get_asm() } gdb_get_asm() disassemble this->asm_add reuse an old buffer if needed, in this case, in the reused buffer: remove Vim bp signs and corresponding bps in bpinfo list remove frame sign edit buf containing this->asm_add get_bp() when asm is on, the GDB bp table must be fetched whenever stepping as we may be editing a newly disassembled function and therefore must set again all bps belonging to this buffer: if BPS_INVALID // GDB reports table changed || (asm && BPS_FR_INVALID) // in case stepping in a new asm buf process_record() for all bps in GDB bp table process_record() if record already in bpinfo list update change sign if needed else { if an asm bp (<function+in>) search all buffers in the asm buffer pool whose name starts with function and find the one containing instruction address else get source file and line number if BPS_INVALID || (asm && BPS_FR_INVALID && buf == fr_buf) { edit the file or buffer add bp sign restore the frame sign if needed if editing the frame buffer (fr_buf) && (BPS_BP_HIT || (asm && BPS_FR_INVALID)) { set frame sign on top if needed set the cursor at frame sign } } } ============================================================================== 4. Vim core *gdbint-vimcore* Both following sections describe how is handled GDB output processing when Vim is run in plain terminal (non GUI) mode. This is implemented by changes made in os_unix.c and normal.c. The same processing is done when in GUI mode but the function names and source files are different. GDB output processing: --------------------- At the top level, in normal.c, safe_vgetc() is redirected so that GDB output is processed only when the low level Vim functions mch_inchar() and RealWaitForChar() are invoked from Vim normal mode commands functions. At the low level, mch_inchar() and RealWaitForChar() cooperate so that gdb_process_output() is invoked whenever enabled by the top level, and there is something to read from GDB's pseudo terminal and the timeout is not zero. Gdb window input-line popup: --------------------------- The following pseudo code describes how gdb_process_output() triggers a window input_line popup: main_loop normal_cmd { ... vgetorpeek { ... mch_inchar { gdb_process_output { // when VimGDB decides that the window // input_line must be popped up got_int = TRUE gdb_setwinput { set winput_cmd, the string to be inserted later in the window input-line return TRUE } } return 0 } got_int is TRUE so: flush all input flush stuff and typeahead buffer interrupting all pending mappings and commands } if winput_cmd != NULL gdb_winput // launch the window input-line } Signs: ----- The |+signs| feature must behave differently when |+gdb| is running. These changes are implemented in buffer.c: sign_mark_adjust() sign->lnum stays unchanged, whatever the changes made to the buffer, to stay consistent with GDB's breakpoint line numbers ============================================================================== 5. Known bugs *gdbint-bugs* Bugs are ordered in decreasing priority order. A "hack" item in a bug description means that it is considered to be a GDB bug and VimGDB is temporarily implementing this "hack" to fix it. When GDB fixes the bug, the "hack" will be removed from VimGDB and from this list. 1 - Annotations missing after detach and enabled once breakpoints date: 2004/02/09 bug: a) when a breakpoint is 'enable once', it becomes disabled the next time it is hit. GDB does not send a 'breakpoints-invalid' annotation in this case to annotate the change b) after a detach command, GDB does not send a 'frames-invalid' annotation. hack: a) see gdb.c compile-conditionnal BP_INVALID_ANO_MISSING b) CMD_DETACH is parsed by gdb.c status: bug report sent to bug-gdb 2 - Setting a breakpoint at an invalid instruction address in an assembly buffer. date: 2004/02/19 test case: debuggee stopped in asm buffer (for example usleep) and frame sign highlighted in asm buffer set a bp in usleep at an invalid instruction address continue bug: two asm buffers usleep-asm and usleep-asm1, one containing the frame sign, one containing the bp sign the reason is get_lastbp() sets this->asm_add to invalid address, get_asm() does not find address in existing usleep-asm and therefore disassembles usleep in another identical usleep-asm1 buffer status: bug report sent to bug-gdb on setting bp at invalid address that causes inferior process to get SIGSEGV: considered not a GDB bug pending 3 - Undocumented breakpoint table line format date: 2004/03/06 test case: (gdb) break read Breakpoint 2 at 0x40011a70: file __libc_read, line -1. (gdb) info breakpoints Num Type Disp Enb Address What 1 breakpoint keep y 0x08048604 in main at toto.c:40 breakpoint already hit 1 time 2 breakpoint keep y 0x40011a70 in __libc_write at __libc_read:-1 bug: invalid negative line number: cannot highlight breakpoint in assembly buffer status: this bug does not occur with level 3 and GDB 6.0 CLOSED 4 - GDB command "info symbol ADDR" fails to produce function name date: 2004/03/07 test case: (gdb) break usleep Breakpoint 2 at 0x8048430 (gdb) info symbol 0x8048430 No symbol matches 0x8048430. (gdb) print/a 0x8048430 $2 = 0x8048430 <usleep> (gdb) info breakpoints Num Type Disp Enb Address What 1 breakpoint keep y 0x08048617 in main at toto.c:41 2 breakpoint keep y 0x08048430 <usleep> bug: GDB command "info symbol ADDR" fails to produce function name hack: see gdb_lvl3.c:gdb_get_asmfunc_hack() status: cannot reproduce it on 2004/03/27, keep the hack though CLOSED ============================================================================== 6. Changes log *gdbint-changes* VIM62 patch 6 (cvs vim62-gdb1-6): configure.in, Makefile: add new modules gdb.h, gdb_lvl2.c and gdb_lvl3.c option.c option.h: change 'gdbdisplay' to 'gdbvariables' gdb.c, gdb.h, gdb_lvl2.c and gdb_lvl3.c: implementation of modes level 2 and level 3 FIX: "disable" and "delete": the related sign was updated only after the next time the debuggee stopped gui.c:gui_wait_for_chars(): break from last loop in gui_wait_for_chars() when retval OK FIX: first keystroke after a gdb command is hidden gui_gtk_x11.c: gui_mch_wait_for_chars gui_x11.c: gui_mch_wait_for_chars swap events handling: handle keystroke events before GDB events FIX: when hitting fast the step key: last "step" sometimes occur after 'updatetime' timeout has elapsed gdb.c, os_unix.c, gui.c: HP-UX 11.0 uses poll(), change some compilation defines and correct wtime computation in gdb_process_output VIM62 patch 5 (cvs vim62-gdb1-5): option.c option.h gdb.c:process_display gdb.c: oob function get_display gdb.c: oob function undisplay gdb display window gdb.c:gdb_docmd() give_warning("GDB busy: command discarded, please retry", TRUE); gdb.c:get_asmfunc() gdb.c:get_asmfunc_hack() gdb.c:get_asm() gdb.c:as_frset() when looking for an address in asm buffers, search only those buffers whose name starts with function name VIM62 patch 4 (cvs vim62-gdb1-4): gdb.c:get_lastbp() see |gdbint-oob| gdb.c:get_asm() correct handling of new asm buffer names gdb.c:parse_note() restrict annotation parsing to exact token match, not just the beginning (ANO_BP_END was never parsed) VIM62 patch 3 (cvs vim62-gdb1-3): gdb.c:get_bp(), process_record() and process_annotation() get_bp() is a new out of band oobfunc_T function that fetches the breakpoint info table. Support of breakpoints annotations, providing the following features: 1) breakpoint sign text is the last two digits of the breakpoint number 2) disabled breakpoints; GDB commands "tbreak", "thbreak", "disable", 'enable once' and 'enable delete' are supported. Disabled breakpoints are highlighted with a different color. An 'enable once' bp that is hit becomes disabled but does not generate an ANO_BP_INVALID annotation, therefore we must track the "disposition" and "enable" state of a bp to know when to trigger get_bp() in this case. 3) breakpoints in assembly buffers. If the assembly buffer does not exist yet at the time the breakpoint is set, the "sign" will be "place"d the next time it is disassembled. In order to do that: a) trigger get_bp() whenever program stops at a bp unknown of bpinfo list and asm option is set b) get_asm() is run before get_bp(), thus we must handle the case where a bp sign ends up on top of the frame sign, which may occur in the above case or if the buffer had been previously wiped-out; this is done in process_record() 4) when receiving a frame annotation (ANO_FRAME_BEGIN or ANO_SOURCE), do not edit this buffer if the related breakpoint contains a "commands" with the "continue" statement Since we do not need anymore to parse gdb buffer for breakpoint data, a lot of regexp have been removed as well as the functions: update_output, bp_delete, bp_parse_set, bp_parse_delete gdb.c:process_annotation() Do not parse frame annotations when running a backtrace GDB command and receiving an ANO_FRAME_BEGIN annotation. VIM62 patch 2 (cvs vim62-gdb1-2): gdb.c:fr_lite() Remove previous frame sign: GDB sends ANO_FRAME_INVALID annotations whenever stepping, running, etc... and these annotations invoke fr_unlite() that turns off the previous frame sign. However, when moving along the stack frame with GDB "up", "down", "frame" commands, we don't get annotations from GDB and must turn off the previous frame sign before setting a new frame sign. GUI support:gui.c, gui_gtk_x11.c and gui_x11.c These changes made to support the gdb interface with a GUI, follow the same model as the one used in os_unix.c for a plain terminal. Instead of registering as a listener on select() calls, we register a call back function. This has been tested with GTK. The changes made for GTK have been made for Motif and Athena in gui_x11.c but NOT tested, NOT even compiled. VIM62 initial patch 1 (cvs vim62-gdb1-1): Makefile Run 'make autoconf' before make to rebuild 'auto/configure' and include feature VimGDB. This was not required with Vim61. buffer.c:buf_getsigntype() No changes required anymore. Changes were made in Vim61-gdb to reverse signs display order. buffer.c:buf_addsign() buffer.c:buf_delsign() buffer.c:insert_sign() structs.h - struct signlist Use the changes introduced in Vim62 for FEAT_NETBEANS_INTG to reorder properly signs list (last sign on top). gdb.c:edit_file() Do no set curbuf to 'nobuflisted' when failing to edit an asm buffer after user canceled the command. This bug still exists in Vim61-gdb. gdb.c screen.c:update_debug_sign() has been improved in Vim62: the phantom sign (sign id #2) must be added *before* the frame sign (sign id #1) is deleted, and deleted *after* the frame sign is added. This avoids having the displayed buffer with an empty sign list which would cause a repaint without the sign columns [redraw_buf_later(buf, NOT_VALID); in buffer.c:buf_delsign()] and thus screen flickers. ex_getln.c:ex_window() When setting folds to a plain window, the window input-line gets also empty fold columns. Fix: set option foldcolumn=0 This bug still exists in Vim61-gdb. This problem also shows up in the standard window command-line. VIM61 (cvs vim61-gdb1-1): buffer.c:buf_getsigntype() return the last added sign->typenr, not the first, so that the screen displays a frame highlighting on top of a breakpoint highlighting and is not hidden