neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

debugger.c (24240B)


      1 /// @file debugger.c
      2 ///
      3 /// Vim script debugger functions
      4 
      5 #include <inttypes.h>
      6 #include <stdbool.h>
      7 #include <stdlib.h>
      8 #include <string.h>
      9 
     10 #include "nvim/ascii_defs.h"
     11 #include "nvim/buffer_defs.h"
     12 #include "nvim/charset.h"
     13 #include "nvim/cmdexpand_defs.h"
     14 #include "nvim/debugger.h"
     15 #include "nvim/drawscreen.h"
     16 #include "nvim/errors.h"
     17 #include "nvim/eval.h"
     18 #include "nvim/eval/typval.h"
     19 #include "nvim/eval/typval_defs.h"
     20 #include "nvim/ex_cmds_defs.h"
     21 #include "nvim/ex_docmd.h"
     22 #include "nvim/ex_getln.h"
     23 #include "nvim/fileio.h"
     24 #include "nvim/garray.h"
     25 #include "nvim/garray_defs.h"
     26 #include "nvim/getchar.h"
     27 #include "nvim/getchar_defs.h"
     28 #include "nvim/gettext_defs.h"
     29 #include "nvim/globals.h"
     30 #include "nvim/keycodes.h"
     31 #include "nvim/macros_defs.h"
     32 #include "nvim/memory.h"
     33 #include "nvim/message.h"
     34 #include "nvim/os/os.h"
     35 #include "nvim/os/os_defs.h"
     36 #include "nvim/path.h"
     37 #include "nvim/pos_defs.h"
     38 #include "nvim/regexp.h"
     39 #include "nvim/runtime.h"
     40 #include "nvim/runtime_defs.h"
     41 #include "nvim/state_defs.h"
     42 #include "nvim/types_defs.h"
     43 #include "nvim/vim_defs.h"
     44 
     45 /// batch mode debugging: don't save and restore typeahead.
     46 static bool debug_greedy = false;
     47 
     48 static char *debug_oldval = NULL;  // old and newval for debug expressions
     49 static char *debug_newval = NULL;
     50 
     51 /// The list of breakpoints: dbg_breakp.
     52 /// This is a grow-array of structs.
     53 struct debuggy {
     54  int dbg_nr;                   ///< breakpoint number
     55  int dbg_type;                 ///< DBG_FUNC or DBG_FILE or DBG_EXPR
     56  char *dbg_name;               ///< function, expression or file name
     57  regprog_T *dbg_prog;          ///< regexp program
     58  linenr_T dbg_lnum;            ///< line number in function or file
     59  int dbg_forceit;              ///< ! used
     60  typval_T *dbg_val;            ///< last result of watchexpression
     61  int dbg_level;                ///< stored nested level for expr
     62 };
     63 
     64 #include "debugger.c.generated.h"
     65 
     66 /// Debug mode. Repeatedly get Ex commands, until told to continue normal
     67 /// execution.
     68 void do_debug(char *cmd)
     69 {
     70  int save_msg_scroll = msg_scroll;
     71  int save_State = State;
     72  int save_did_emsg = did_emsg;
     73  const bool save_cmd_silent = cmd_silent;
     74  int save_msg_silent = msg_silent;
     75  int save_emsg_silent = emsg_silent;
     76  bool save_redir_off = redir_off;
     77  tasave_T typeaheadbuf;
     78  bool typeahead_saved = false;
     79  int save_ignore_script = 0;
     80  char *cmdline = NULL;
     81  char *p;
     82  char *tail = NULL;
     83  static int last_cmd = 0;
     84 #define CMD_CONT        1
     85 #define CMD_NEXT        2
     86 #define CMD_STEP        3
     87 #define CMD_FINISH      4
     88 #define CMD_QUIT        5
     89 #define CMD_INTERRUPT   6
     90 #define CMD_BACKTRACE   7
     91 #define CMD_FRAME       8
     92 #define CMD_UP          9
     93 #define CMD_DOWN        10
     94 
     95  RedrawingDisabled++;          // don't redisplay the window
     96  no_wait_return++;             // don't wait for return
     97  did_emsg = false;             // don't use error from debugged stuff
     98  cmd_silent = false;           // display commands
     99  msg_silent = false;           // display messages
    100  emsg_silent = false;          // display error messages
    101  redir_off = true;             // don't redirect debug commands
    102 
    103  State = MODE_NORMAL;
    104  debug_mode = true;
    105 
    106  if (!debug_did_msg) {
    107    msg(_("Entering Debug mode.  Type \"cont\" to continue."), 0);
    108  }
    109  if (debug_oldval != NULL) {
    110    smsg(0, _("Oldval = \"%s\""), debug_oldval);
    111    XFREE_CLEAR(debug_oldval);
    112  }
    113  if (debug_newval != NULL) {
    114    smsg(0, _("Newval = \"%s\""), debug_newval);
    115    XFREE_CLEAR(debug_newval);
    116  }
    117  char *sname = estack_sfile(ESTACK_NONE);
    118  if (sname != NULL) {
    119    msg(sname, 0);
    120  }
    121  xfree(sname);
    122  if (SOURCING_LNUM != 0) {
    123    smsg(0, _("line %" PRId64 ": %s"), (int64_t)SOURCING_LNUM, cmd);
    124  } else {
    125    smsg(0, _("cmd: %s"), cmd);
    126  }
    127 
    128  // Repeat getting a command and executing it.
    129  while (true) {
    130    msg_scroll = true;
    131    need_wait_return = false;
    132 
    133    // Save the current typeahead buffer and replace it with an empty one.
    134    // This makes sure we get input from the user here and don't interfere
    135    // with the commands being executed.  Reset "ex_normal_busy" to avoid
    136    // the side effects of using ":normal". Save the stuff buffer and make
    137    // it empty. Set ignore_script to avoid reading from script input.
    138    int save_ex_normal_busy = ex_normal_busy;
    139    ex_normal_busy = 0;
    140    if (!debug_greedy) {
    141      save_typeahead(&typeaheadbuf);
    142      typeahead_saved = true;
    143      save_ignore_script = ignore_script;
    144      ignore_script = true;
    145    }
    146 
    147    // don't debug any function call, e.g. from an expression mapping
    148    int n = debug_break_level;
    149    debug_break_level = -1;
    150 
    151    xfree(cmdline);
    152    cmdline = getcmdline_prompt('>', NULL, 0, EXPAND_NOTHING, NULL, CALLBACK_NONE, false, NULL);
    153 
    154    debug_break_level = n;
    155    if (typeahead_saved) {
    156      restore_typeahead(&typeaheadbuf);
    157      ignore_script = save_ignore_script;
    158    }
    159    ex_normal_busy = save_ex_normal_busy;
    160 
    161    cmdline_row = msg_row;
    162    msg_starthere();
    163    if (cmdline != NULL) {
    164      // If this is a debug command, set "last_cmd".
    165      // If not, reset "last_cmd".
    166      // For a blank line use previous command.
    167      p = skipwhite(cmdline);
    168      if (*p != NUL) {
    169        switch (*p) {
    170        case 'c':
    171          last_cmd = CMD_CONT;
    172          tail = "ont";
    173          break;
    174        case 'n':
    175          last_cmd = CMD_NEXT;
    176          tail = "ext";
    177          break;
    178        case 's':
    179          last_cmd = CMD_STEP;
    180          tail = "tep";
    181          break;
    182        case 'f':
    183          last_cmd = 0;
    184          if (p[1] == 'r') {
    185            last_cmd = CMD_FRAME;
    186            tail = "rame";
    187          } else {
    188            last_cmd = CMD_FINISH;
    189            tail = "inish";
    190          }
    191          break;
    192        case 'q':
    193          last_cmd = CMD_QUIT;
    194          tail = "uit";
    195          break;
    196        case 'i':
    197          last_cmd = CMD_INTERRUPT;
    198          tail = "nterrupt";
    199          break;
    200        case 'b':
    201          last_cmd = CMD_BACKTRACE;
    202          if (p[1] == 't') {
    203            tail = "t";
    204          } else {
    205            tail = "acktrace";
    206          }
    207          break;
    208        case 'w':
    209          last_cmd = CMD_BACKTRACE;
    210          tail = "here";
    211          break;
    212        case 'u':
    213          last_cmd = CMD_UP;
    214          tail = "p";
    215          break;
    216        case 'd':
    217          last_cmd = CMD_DOWN;
    218          tail = "own";
    219          break;
    220        default:
    221          last_cmd = 0;
    222        }
    223        if (last_cmd != 0) {
    224          // Check that the tail matches.
    225          p++;
    226          while (*p != NUL && *p == *tail) {
    227            p++;
    228            tail++;
    229          }
    230          if (ASCII_ISALPHA(*p) && last_cmd != CMD_FRAME) {
    231            last_cmd = 0;
    232          }
    233        }
    234      }
    235 
    236      if (last_cmd != 0) {
    237        // Execute debug command: decide where to break next and return.
    238        switch (last_cmd) {
    239        case CMD_CONT:
    240          debug_break_level = -1;
    241          break;
    242        case CMD_NEXT:
    243          debug_break_level = ex_nesting_level;
    244          break;
    245        case CMD_STEP:
    246          debug_break_level = 9999;
    247          break;
    248        case CMD_FINISH:
    249          debug_break_level = ex_nesting_level - 1;
    250          break;
    251        case CMD_QUIT:
    252          got_int = true;
    253          debug_break_level = -1;
    254          break;
    255        case CMD_INTERRUPT:
    256          got_int = true;
    257          debug_break_level = 9999;
    258          // Do not repeat ">interrupt" cmd, continue stepping.
    259          last_cmd = CMD_STEP;
    260          break;
    261        case CMD_BACKTRACE:
    262          do_showbacktrace(cmd);
    263          continue;
    264        case CMD_FRAME:
    265          if (*p == NUL) {
    266            do_showbacktrace(cmd);
    267          } else {
    268            p = skipwhite(p);
    269            do_setdebugtracelevel(p);
    270          }
    271          continue;
    272        case CMD_UP:
    273          debug_backtrace_level++;
    274          do_checkbacktracelevel();
    275          continue;
    276        case CMD_DOWN:
    277          debug_backtrace_level--;
    278          do_checkbacktracelevel();
    279          continue;
    280        }
    281        // Going out reset backtrace_level
    282        debug_backtrace_level = 0;
    283        break;
    284      }
    285 
    286      // don't debug this command
    287      n = debug_break_level;
    288      debug_break_level = -1;
    289      do_cmdline(cmdline, getexline, NULL, DOCMD_VERBOSE|DOCMD_EXCRESET);
    290      debug_break_level = n;
    291    }
    292    lines_left = Rows - 1;
    293  }
    294  xfree(cmdline);
    295 
    296  RedrawingDisabled--;
    297  no_wait_return--;
    298  redraw_all_later(UPD_NOT_VALID);
    299  need_wait_return = false;
    300  msg_scroll = save_msg_scroll;
    301  lines_left = Rows - 1;
    302  State = save_State;
    303  debug_mode = false;
    304  did_emsg = save_did_emsg;
    305  cmd_silent = save_cmd_silent;
    306  msg_silent = save_msg_silent;
    307  emsg_silent = save_emsg_silent;
    308  redir_off = save_redir_off;
    309 
    310  // Only print the message again when typing a command before coming back here.
    311  debug_did_msg = true;
    312 }
    313 
    314 static int get_maxbacktrace_level(char *sname)
    315 {
    316  int maxbacktrace = 0;
    317 
    318  if (sname == NULL) {
    319    return 0;
    320  }
    321 
    322  char *p = sname;
    323  char *q;
    324  while ((q = strstr(p, "..")) != NULL) {
    325    p = q + 2;
    326    maxbacktrace++;
    327  }
    328  return maxbacktrace;
    329 }
    330 
    331 static void do_setdebugtracelevel(char *arg)
    332 {
    333  int level = atoi(arg);
    334  if (*arg == '+' || level < 0) {
    335    debug_backtrace_level += level;
    336  } else {
    337    debug_backtrace_level = level;
    338  }
    339 
    340  do_checkbacktracelevel();
    341 }
    342 
    343 static void do_checkbacktracelevel(void)
    344 {
    345  if (debug_backtrace_level < 0) {
    346    debug_backtrace_level = 0;
    347    msg(_("frame is zero"), 0);
    348  } else {
    349    char *sname = estack_sfile(ESTACK_NONE);
    350    int max = get_maxbacktrace_level(sname);
    351 
    352    if (debug_backtrace_level > max) {
    353      debug_backtrace_level = max;
    354      smsg(0, _("frame at highest level: %d"), max);
    355    }
    356    xfree(sname);
    357  }
    358 }
    359 
    360 static void do_showbacktrace(char *cmd)
    361 {
    362  char *sname = estack_sfile(ESTACK_NONE);
    363  int max = get_maxbacktrace_level(sname);
    364  if (sname != NULL) {
    365    int i = 0;
    366    char *cur = sname;
    367    while (!got_int) {
    368      char *next = strstr(cur, "..");
    369      if (next != NULL) {
    370        *next = NUL;
    371      }
    372      if (i == max - debug_backtrace_level) {
    373        smsg(0, "->%d %s", max - i, cur);
    374      } else {
    375        smsg(0, "  %d %s", max - i, cur);
    376      }
    377      i++;
    378      if (next == NULL) {
    379        break;
    380      }
    381      *next = '.';
    382      cur = next + 2;
    383    }
    384    xfree(sname);
    385  }
    386 
    387  if (SOURCING_LNUM != 0) {
    388    smsg(0, _("line %" PRId64 ": %s"), (int64_t)SOURCING_LNUM, cmd);
    389  } else {
    390    smsg(0, _("cmd: %s"), cmd);
    391  }
    392 }
    393 
    394 /// ":debug".
    395 void ex_debug(exarg_T *eap)
    396 {
    397  int debug_break_level_save = debug_break_level;
    398 
    399  debug_break_level = 9999;
    400  do_cmdline_cmd(eap->arg);
    401  debug_break_level = debug_break_level_save;
    402 }
    403 
    404 static char *debug_breakpoint_name = NULL;
    405 static linenr_T debug_breakpoint_lnum;
    406 
    407 /// When debugging or a breakpoint is set on a skipped command, no debug prompt
    408 /// is shown by do_one_cmd().  This situation is indicated by debug_skipped, and
    409 /// debug_skipped_name is then set to the source name in the breakpoint case. If
    410 /// a skipped command decides itself that a debug prompt should be displayed, it
    411 /// can do so by calling dbg_check_skipped().
    412 static bool debug_skipped;
    413 static char *debug_skipped_name;
    414 
    415 /// Go to debug mode when a breakpoint was encountered or "ex_nesting_level" is
    416 /// at or below the break level.  But only when the line is actually
    417 /// executed.  Return true and set breakpoint_name for skipped commands that
    418 /// decide to execute something themselves.
    419 /// Called from do_one_cmd() before executing a command.
    420 void dbg_check_breakpoint(exarg_T *eap)
    421 {
    422  debug_skipped = false;
    423  if (debug_breakpoint_name != NULL) {
    424    if (!eap->skip) {
    425      char *p;
    426      // replace K_SNR with "<SNR>"
    427      if ((uint8_t)debug_breakpoint_name[0] == K_SPECIAL
    428          && (uint8_t)debug_breakpoint_name[1] == KS_EXTRA
    429          && debug_breakpoint_name[2] == KE_SNR) {
    430        p = "<SNR>";
    431      } else {
    432        p = "";
    433      }
    434      smsg(0, _("Breakpoint in \"%s%s\" line %" PRId64),
    435           p,
    436           debug_breakpoint_name + (*p == NUL ? 0 : 3),
    437           (int64_t)debug_breakpoint_lnum);
    438      debug_breakpoint_name = NULL;
    439      do_debug(eap->cmd);
    440    } else {
    441      debug_skipped = true;
    442      debug_skipped_name = debug_breakpoint_name;
    443      debug_breakpoint_name = NULL;
    444    }
    445  } else if (ex_nesting_level <= debug_break_level) {
    446    if (!eap->skip) {
    447      do_debug(eap->cmd);
    448    } else {
    449      debug_skipped = true;
    450      debug_skipped_name = NULL;
    451    }
    452  }
    453 }
    454 
    455 /// Go to debug mode if skipped by dbg_check_breakpoint() because eap->skip was
    456 /// set.
    457 ///
    458 /// @return true when the debug mode is entered this time.
    459 bool dbg_check_skipped(exarg_T *eap)
    460 {
    461  if (!debug_skipped) {
    462    return false;
    463  }
    464 
    465  // Save the value of got_int and reset it.  We don't want a previous
    466  // interruption cause flushing the input buffer.
    467  bool prev_got_int = got_int;
    468  got_int = false;
    469  debug_breakpoint_name = debug_skipped_name;
    470  // eap->skip is true
    471  eap->skip = false;
    472  dbg_check_breakpoint(eap);
    473  eap->skip = true;
    474  got_int |= prev_got_int;
    475  return true;
    476 }
    477 
    478 static garray_T dbg_breakp = { 0, 0, sizeof(struct debuggy), 4, NULL };
    479 #define BREAKP(idx)             (((struct debuggy *)dbg_breakp.ga_data)[idx])
    480 #define DEBUGGY(gap, idx)       (((struct debuggy *)(gap)->ga_data)[idx])
    481 static int last_breakp = 0;     // nr of last defined breakpoint
    482 static bool has_expr_breakpoint = false;
    483 
    484 // Profiling uses file and func names similar to breakpoints.
    485 static garray_T prof_ga = { 0, 0, sizeof(struct debuggy), 4, NULL };
    486 #define DBG_FUNC        1
    487 #define DBG_FILE        2
    488 #define DBG_EXPR        3
    489 
    490 /// Evaluate the "bp->dbg_name" expression and return the result.
    491 /// Disables error messages.
    492 static typval_T *eval_expr_no_emsg(struct debuggy *const bp)
    493  FUNC_ATTR_NONNULL_ALL
    494 {
    495  // Disable error messages, a bad expression would make Vim unusable.
    496  emsg_off++;
    497  typval_T *const tv = eval_expr(bp->dbg_name, NULL);
    498  emsg_off--;
    499  return tv;
    500 }
    501 
    502 /// Parse the arguments of ":profile", ":breakadd" or ":breakdel" and put them
    503 /// in the entry just after the last one in dbg_breakp.  Note that "dbg_name"
    504 /// is allocated.
    505 /// Returns FAIL for failure.
    506 ///
    507 /// @param arg
    508 /// @param gap  either &dbg_breakp or &prof_ga
    509 static int dbg_parsearg(char *arg, garray_T *gap)
    510 {
    511  char *p = arg;
    512  bool here = false;
    513 
    514  ga_grow(gap, 1);
    515 
    516  struct debuggy *bp = &DEBUGGY(gap, gap->ga_len);
    517 
    518  // Find "func" or "file".
    519  if (strncmp(p, "func", 4) == 0) {
    520    bp->dbg_type = DBG_FUNC;
    521  } else if (strncmp(p, "file", 4) == 0) {
    522    bp->dbg_type = DBG_FILE;
    523  } else if (gap != &prof_ga && strncmp(p, "here", 4) == 0) {
    524    if (curbuf->b_ffname == NULL) {
    525      emsg(_(e_noname));
    526      return FAIL;
    527    }
    528    bp->dbg_type = DBG_FILE;
    529    here = true;
    530  } else if (gap != &prof_ga && strncmp(p, "expr", 4) == 0) {
    531    bp->dbg_type = DBG_EXPR;
    532  } else {
    533    semsg(_(e_invarg2), p);
    534    return FAIL;
    535  }
    536  p = skipwhite(p + 4);
    537 
    538  // Find optional line number.
    539  if (here) {
    540    bp->dbg_lnum = curwin->w_cursor.lnum;
    541  } else if (gap != &prof_ga && ascii_isdigit(*p)) {
    542    bp->dbg_lnum = getdigits_int32(&p, true, 0);
    543    p = skipwhite(p);
    544  } else {
    545    bp->dbg_lnum = 0;
    546  }
    547 
    548  // Find the function or file name.  Don't accept a function name with ().
    549  if ((!here && *p == NUL)
    550      || (here && *p != NUL)
    551      || (bp->dbg_type == DBG_FUNC && strstr(p, "()") != NULL)) {
    552    semsg(_(e_invarg2), arg);
    553    return FAIL;
    554  }
    555 
    556  if (bp->dbg_type == DBG_FUNC) {
    557    bp->dbg_name = xstrdup(strncmp(p, "g:", 2) == 0 ? p + 2 : p);
    558  } else if (here) {
    559    bp->dbg_name = xstrdup(curbuf->b_ffname);
    560  } else if (bp->dbg_type == DBG_EXPR) {
    561    bp->dbg_name = xstrdup(p);
    562    bp->dbg_val = eval_expr_no_emsg(bp);
    563  } else {
    564    // Expand the file name in the same way as do_source().  This means
    565    // doing it twice, so that $DIR/file gets expanded when $DIR is
    566    // "~/dir".
    567    char *q = expand_env_save(p);
    568    if (q == NULL) {
    569      return FAIL;
    570    }
    571    p = expand_env_save(q);
    572    xfree(q);
    573    if (p == NULL) {
    574      return FAIL;
    575    }
    576    if (*p != '*') {
    577      bp->dbg_name = fix_fname(p);
    578      xfree(p);
    579    } else {
    580      bp->dbg_name = p;
    581    }
    582  }
    583 
    584  if (bp->dbg_name == NULL) {
    585    return FAIL;
    586  }
    587  return OK;
    588 }
    589 
    590 /// ":breakadd".  Also used for ":profile".
    591 void ex_breakadd(exarg_T *eap)
    592 {
    593  garray_T *gap = &dbg_breakp;
    594  if (eap->cmdidx == CMD_profile) {
    595    gap = &prof_ga;
    596  }
    597 
    598  if (dbg_parsearg(eap->arg, gap) != OK) {
    599    return;
    600  }
    601 
    602  struct debuggy *bp = &DEBUGGY(gap, gap->ga_len);
    603  bp->dbg_forceit = eap->forceit;
    604 
    605  if (bp->dbg_type != DBG_EXPR) {
    606    char *pat = file_pat_to_reg_pat(bp->dbg_name, NULL, NULL, false);
    607    if (pat != NULL) {
    608      bp->dbg_prog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
    609      xfree(pat);
    610    }
    611    if (pat == NULL || bp->dbg_prog == NULL) {
    612      xfree(bp->dbg_name);
    613    } else {
    614      if (bp->dbg_lnum == 0) {           // default line number is 1
    615        bp->dbg_lnum = 1;
    616      }
    617      if (eap->cmdidx != CMD_profile) {
    618        DEBUGGY(gap, gap->ga_len).dbg_nr = ++last_breakp;
    619        debug_tick++;
    620      }
    621      gap->ga_len++;
    622    }
    623  } else {
    624    // DBG_EXPR
    625    DEBUGGY(gap, gap->ga_len++).dbg_nr = ++last_breakp;
    626    debug_tick++;
    627    if (gap == &dbg_breakp) {
    628      has_expr_breakpoint = true;
    629    }
    630  }
    631 }
    632 
    633 /// ":debuggreedy".
    634 void ex_debuggreedy(exarg_T *eap)
    635 {
    636  if (eap->addr_count == 0 || eap->line2 != 0) {
    637    debug_greedy = true;
    638  } else {
    639    debug_greedy = false;
    640  }
    641 }
    642 
    643 static void update_has_expr_breakpoint(void)
    644 {
    645  has_expr_breakpoint = false;
    646  for (int i = 0; i < dbg_breakp.ga_len; i++) {
    647    if (BREAKP(i).dbg_type == DBG_EXPR) {
    648      has_expr_breakpoint = true;
    649      break;
    650    }
    651  }
    652 }
    653 
    654 /// ":breakdel" and ":profdel".
    655 void ex_breakdel(exarg_T *eap)
    656 {
    657  int todel = -1;
    658  bool del_all = false;
    659  linenr_T best_lnum = 0;
    660  garray_T *gap = &dbg_breakp;
    661 
    662  if (eap->cmdidx == CMD_profdel) {
    663    gap = &prof_ga;
    664  }
    665 
    666  if (ascii_isdigit(*eap->arg)) {
    667    // ":breakdel {nr}"
    668    int nr = atoi(eap->arg);
    669    for (int i = 0; i < gap->ga_len; i++) {
    670      if (DEBUGGY(gap, i).dbg_nr == nr) {
    671        todel = i;
    672        break;
    673      }
    674    }
    675  } else if (*eap->arg == '*') {
    676    todel = 0;
    677    del_all = true;
    678  } else {
    679    // ":breakdel {func|file|expr} [lnum] {name}"
    680    if (dbg_parsearg(eap->arg, gap) == FAIL) {
    681      return;
    682    }
    683    struct debuggy *bp = &DEBUGGY(gap, gap->ga_len);
    684    for (int i = 0; i < gap->ga_len; i++) {
    685      struct debuggy *bpi = &DEBUGGY(gap, i);
    686      if (bp->dbg_type == bpi->dbg_type
    687          && strcmp(bp->dbg_name, bpi->dbg_name) == 0
    688          && (bp->dbg_lnum == bpi->dbg_lnum
    689              || (bp->dbg_lnum == 0
    690                  && (best_lnum == 0
    691                      || bpi->dbg_lnum < best_lnum)))) {
    692        todel = i;
    693        best_lnum = bpi->dbg_lnum;
    694      }
    695    }
    696    xfree(bp->dbg_name);
    697  }
    698 
    699  if (todel < 0) {
    700    semsg(_("E161: Breakpoint not found: %s"), eap->arg);
    701    return;
    702  }
    703 
    704  while (!GA_EMPTY(gap)) {
    705    xfree(DEBUGGY(gap, todel).dbg_name);
    706    if (DEBUGGY(gap, todel).dbg_type == DBG_EXPR
    707        && DEBUGGY(gap, todel).dbg_val != NULL) {
    708      tv_free(DEBUGGY(gap, todel).dbg_val);
    709    }
    710    vim_regfree(DEBUGGY(gap, todel).dbg_prog);
    711    gap->ga_len--;
    712    if (todel < gap->ga_len) {
    713      memmove(&DEBUGGY(gap, todel), &DEBUGGY(gap, todel + 1),
    714              (size_t)(gap->ga_len - todel) * sizeof(struct debuggy));
    715    }
    716    if (eap->cmdidx == CMD_breakdel) {
    717      debug_tick++;
    718    }
    719    if (!del_all) {
    720      break;
    721    }
    722  }
    723 
    724  // If all breakpoints were removed clear the array.
    725  if (GA_EMPTY(gap)) {
    726    ga_clear(gap);
    727  }
    728  if (gap == &dbg_breakp) {
    729    update_has_expr_breakpoint();
    730  }
    731 }
    732 
    733 /// ":breaklist".
    734 void ex_breaklist(exarg_T *eap)
    735 {
    736  if (GA_EMPTY(&dbg_breakp)) {
    737    msg(_("No breakpoints defined"), 0);
    738    return;
    739  }
    740 
    741  for (int i = 0; i < dbg_breakp.ga_len; i++) {
    742    struct debuggy *bp = &BREAKP(i);
    743    if (bp->dbg_type == DBG_FILE) {
    744      home_replace(NULL, bp->dbg_name, NameBuff, MAXPATHL, true);
    745    }
    746    if (bp->dbg_type != DBG_EXPR) {
    747      smsg(0, _("%3d  %s %s  line %" PRId64),
    748           bp->dbg_nr,
    749           bp->dbg_type == DBG_FUNC ? "func" : "file",
    750           bp->dbg_type == DBG_FUNC ? bp->dbg_name : NameBuff,
    751           (int64_t)bp->dbg_lnum);
    752    } else {
    753      smsg(0, _("%3d  expr %s"), bp->dbg_nr, bp->dbg_name);
    754    }
    755  }
    756 }
    757 
    758 /// Find a breakpoint for a function or sourced file.
    759 /// Returns line number at which to break; zero when no matching breakpoint.
    760 ///
    761 /// @param file  true for a file, false for a function
    762 /// @param fname  file or function name
    763 /// @param after  after this line number
    764 linenr_T dbg_find_breakpoint(bool file, char *fname, linenr_T after)
    765 {
    766  return debuggy_find(file, fname, after, &dbg_breakp, NULL);
    767 }
    768 
    769 /// @param file     true for a file, false for a function
    770 /// @param fname    file or function name
    771 /// @param fp[out]  forceit
    772 ///
    773 /// @returns true if profiling is on for a function or sourced file.
    774 bool has_profiling(bool file, char *fname, bool *fp)
    775 {
    776  return debuggy_find(file, fname, 0, &prof_ga, fp)
    777         != 0;
    778 }
    779 
    780 /// Common code for dbg_find_breakpoint() and has_profiling().
    781 ///
    782 /// @param file  true for a file, false for a function
    783 /// @param fname  file or function name
    784 /// @param after  after this line number
    785 /// @param gap  either &dbg_breakp or &prof_ga
    786 /// @param fp  if not NULL: return forceit
    787 static linenr_T debuggy_find(bool file, char *fname, linenr_T after, garray_T *gap, bool *fp)
    788 {
    789  struct debuggy *bp;
    790  linenr_T lnum = 0;
    791  char *name = fname;
    792 
    793  // Return quickly when there are no breakpoints.
    794  if (GA_EMPTY(gap)) {
    795    return 0;
    796  }
    797 
    798  // Replace K_SNR in function name with "<SNR>".
    799  if (!file && (uint8_t)fname[0] == K_SPECIAL) {
    800    name = xmalloc(strlen(fname) + 3);
    801    STRCPY(name, "<SNR>");
    802    STRCPY(name + 5, fname + 3);
    803  }
    804 
    805  for (int i = 0; i < gap->ga_len; i++) {
    806    // Skip entries that are not useful or are for a line that is beyond
    807    // an already found breakpoint.
    808    bp = &DEBUGGY(gap, i);
    809    if ((bp->dbg_type == DBG_FILE) == file
    810        && bp->dbg_type != DBG_EXPR
    811        && (gap == &prof_ga
    812            || (bp->dbg_lnum > after && (lnum == 0 || bp->dbg_lnum < lnum)))) {
    813      // Save the value of got_int and reset it.  We don't want a
    814      // previous interruption cancel matching, only hitting CTRL-C
    815      // while matching should abort it.
    816      bool prev_got_int = got_int;
    817      got_int = false;
    818      if (vim_regexec_prog(&bp->dbg_prog, false, name, 0)) {
    819        lnum = bp->dbg_lnum;
    820        if (fp != NULL) {
    821          *fp = bp->dbg_forceit;
    822        }
    823      }
    824      got_int |= prev_got_int;
    825    } else if (bp->dbg_type == DBG_EXPR) {
    826      bool line = false;
    827 
    828      typval_T *const tv = eval_expr_no_emsg(bp);
    829      if (tv != NULL) {
    830        if (bp->dbg_val == NULL) {
    831          xfree(debug_oldval);
    832          debug_oldval = typval_tostring(NULL, true);
    833          bp->dbg_val = tv;
    834          xfree(debug_newval);
    835          debug_newval = typval_tostring(bp->dbg_val, true);
    836          line = true;
    837        } else {
    838          if (typval_compare(tv, bp->dbg_val, EXPR_IS, false) == OK
    839              && tv->vval.v_number == false) {
    840            line = true;
    841            xfree(debug_oldval);
    842            debug_oldval = typval_tostring(bp->dbg_val, true);
    843            // Need to evaluate again, typval_compare() overwrites "tv".
    844            typval_T *const v = eval_expr_no_emsg(bp);
    845            xfree(debug_newval);
    846            debug_newval = typval_tostring(v, true);
    847            tv_free(bp->dbg_val);
    848            bp->dbg_val = v;
    849          }
    850          tv_free(tv);
    851        }
    852      } else if (bp->dbg_val != NULL) {
    853        xfree(debug_oldval);
    854        debug_oldval = typval_tostring(bp->dbg_val, true);
    855        xfree(debug_newval);
    856        debug_newval = typval_tostring(NULL, true);
    857        tv_free(bp->dbg_val);
    858        bp->dbg_val = NULL;
    859        line = true;
    860      }
    861 
    862      if (line) {
    863        lnum = after > 0 ? after : 1;
    864        break;
    865      }
    866    }
    867  }
    868  if (name != fname) {
    869    xfree(name);
    870  }
    871 
    872  return lnum;
    873 }
    874 
    875 /// Called when a breakpoint was encountered.
    876 void dbg_breakpoint(char *name, linenr_T lnum)
    877 {
    878  // We need to check if this line is actually executed in do_one_cmd()
    879  debug_breakpoint_name = name;
    880  debug_breakpoint_lnum = lnum;
    881 }