Annotation of freem/src/mdebug.c, revision 1.8

1.1       snw         1: /*
1.8     ! snw         2:  *   $Id: mdebug.c,v 1.7 2025/04/30 17:19:16 snw Exp $
1.1       snw         3:  *    debugger enhancements
                      4:  *
                      5:  *  
1.2       snw         6:  *   Author: Serena Willis <snw@coherent-logic.com>
1.1       snw         7:  *    Copyright (C) 1998 MUG Deutschland
1.3       snw         8:  *    Copyright (C) 2020, 2025 Coherent Logic Development LLC
1.1       snw         9:  *
                     10:  *
                     11:  *   This file is part of FreeM.
                     12:  *
                     13:  *   FreeM is free software: you can redistribute it and/or modify
                     14:  *   it under the terms of the GNU Affero Public License as published by
                     15:  *   the Free Software Foundation, either version 3 of the License, or
                     16:  *   (at your option) any later version.
                     17:  *
                     18:  *   FreeM is distributed in the hope that it will be useful,
                     19:  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
                     20:  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     21:  *   GNU Affero Public License for more details.
                     22:  *
                     23:  *   You should have received a copy of the GNU Affero Public License
                     24:  *   along with FreeM.  If not, see <https://www.gnu.org/licenses/>.
                     25:  *
1.4       snw        26:  *   $Log: mdebug.c,v $
1.8     ! snw        27:  *   Revision 1.7  2025/04/30 17:19:16  snw
        !            28:  *   Improve backtraces in debugger
        !            29:  *
1.7       snw        30:  *   Revision 1.6  2025/04/30 14:41:03  snw
                     31:  *   Further debugger work
                     32:  *
1.6       snw        33:  *   Revision 1.5  2025/04/29 18:46:17  snw
                     34:  *   Begin work on interactive debugger
                     35:  *
1.5       snw        36:  *   Revision 1.4  2025/04/28 19:38:55  snw
                     37:  *   Add trace mode
                     38:  *
1.4       snw        39:  *   Revision 1.3  2025/03/09 19:50:47  snw
                     40:  *   Second phase of REUSE compliance and header reformat
                     41:  *
1.3       snw        42:  *
                     43:  * SPDX-FileCopyrightText:  (C) 2025 Coherent Logic Development LLC
                     44:  * SPDX-License-Identifier: AGPL-3.0-or-later
1.1       snw        45:  **/
                     46: 
                     47: #include <stdio.h>
                     48: #include <stdlib.h>
                     49: #include <string.h>
1.5       snw        50: #include <unistd.h>
                     51: 
                     52: #include "config.h"
                     53: 
                     54: #ifdef HAVE_LIBREADLINE
                     55: #  if defined(HAVE_READLINE_READLINE_H)
                     56: #    include <readline/readline.h>
                     57: #  elif defined(HAVE_READLINE_H)
                     58: #    include <readline.h>
                     59: #  else /* !defined(HAVE_READLINE_H) */
                     60: extern char *readline ();
                     61: #  endif /* !defined(HAVE_READLINE_H) */
                     62: /*char *cmdline = NULL;*/
                     63: #else /* !defined(HAVE_READLINE_READLINE_H) */
                     64:   /* no readline */
                     65: #endif /* HAVE_LIBREADLINE */
                     66: 
                     67: #ifdef HAVE_READLINE_HISTORY
                     68: #  if defined(HAVE_READLINE_HISTORY_H)
                     69: #    include <readline/history.h>
                     70: #  elif defined(HAVE_HISTORY_H)
                     71: #    include <history.h>
                     72: #  else /* !defined(HAVE_HISTORY_H) */
                     73: extern void add_history ();
                     74: extern int write_history ();
                     75: extern int read_history ();
                     76: #  endif /* defined(HAVE_READLINE_HISTORY_H) */
                     77:   /* no history */
                     78: #endif /* HAVE_READLINE_HISTORY */
                     79: 
1.1       snw        80: 
                     81: #include "mpsdef.h"
                     82: #include "mdebug.h"
                     83: #include "freem.h"
                     84: #include "mref.h"
                     85: 
                     86: dbg_watch dbg_watchlist[MAXWATCH];    /* list of watchpoints */
                     87: short dbg_enable_watch;               /* 0 = watches disabled, 1 = watches enabled */
                     88: int dbg_pending_watches;
                     89: 
                     90: 
                     91: void dbg_init (void)
                     92: {
                     93:     register int i;
                     94: 
                     95:     dbg_enable_watch = 0;
                     96:     dbg_pending_watches = 0;
                     97: 
                     98:     for (i = 0; i < MAXWATCH; i++) {
                     99: 
                    100:         dbg_watchlist[i].varnam = NULL;
                    101:         dbg_watchlist[i].chgct = 0;
                    102: 
                    103:     }
                    104: 
                    105: }
                    106: 
1.6       snw       107: int debugger (int entry_mode, char *curcode)
1.5       snw       108: {
                    109:     
                    110: #if defined(HAVE_LIBREADLINE)    
                    111:     static int first_entry = TRUE;
1.6       snw       112:     static int stepmode = STEPMODE_NONE;
                    113:     
1.5       snw       114:     char *dbg_buf;
                    115:     char dbg_prompt[256];
                    116:     HIST_ENTRY **dbg_hist;
                    117:     int dbg_hist_idx;
                    118:     HIST_ENTRY *dbg_hist_ent;
                    119:     char rouname_buf[256];
1.6       snw       120:     char *savptr;
                    121:     char *dbg_cmd;
                    122:     int do_prompt = FALSE;
                    123:     register int i;
1.5       snw       124:     
                    125:     set_io (UNIX);
                    126:     
                    127:     if (first_entry) {
                    128:         first_entry = FALSE;
                    129:         
                    130:         printf ("\nEntering debug mode; M commands unavailable.\n");
                    131:         printf ("Type 'exit' to return to direct mode; 'help' for help.\n\n");
1.6       snw       132: 
                    133:         do_prompt = TRUE;
1.5       snw       134:     }
1.6       snw       135:     else {
                    136:         switch (entry_mode) {
                    137:             
                    138:             case DEBENTRY_CMD:
                    139:                 if (stepmode == STEPMODE_CMD) {
                    140:                     do_prompt = TRUE;
                    141:                 }
                    142:                 else {
                    143:                     do_prompt = FALSE;
                    144:                 }
                    145:                     
                    146:                 break;
                    147:                     
                    148:             case DEBENTRY_LINE:
                    149:                 if (stepmode == STEPMODE_CMD || stepmode == STEPMODE_LINE) {
                    150:                     do_prompt = TRUE;
                    151:                 }
                    152:                 else {
                    153:                     do_prompt = FALSE;
                    154:                 }
                    155:                 
                    156:                 break;
                    157:                 
                    158:             case DEBENTRY_BREAKPOINT:
                    159:                 do_prompt = TRUE;
                    160:                 break;
                    161:                 
                    162:             case DEBENTRY_SIGINT:
                    163:                 break;
                    164:         }
                    165:     }
                    166: 
                    167: 
                    168:     if (!do_prompt) return TRUE;
1.5       snw       169: 
                    170:     while (1) {
1.6       snw       171:         
1.7       snw       172:         if (nstx == 0) {
                    173:             stcpy (rouname_buf, rou_name);
                    174:         }
                    175:         else {
                    176:             char tbuf[256];
                    177:             
                    178:             getraddress (tbuf, nstx);
                    179:             stcpy (rouname_buf, &(tbuf[3]));
                    180:         }
1.5       snw       181:         stcnv_m2c (rouname_buf);
                    182:         
                    183:         snprintf (dbg_prompt, sizeof (dbg_prompt) - 1, "%s $STACK=%d [DEBUG]> ", rouname_buf, nstx);
                    184: 
1.6       snw       185:         if ((stepmode == STEPMODE_LINE) || (stepmode == STEPMODE_CMD)) {
                    186:             char codbuf[512];
                    187:             stcpy (codbuf, curcode);
                    188:             stcnv_m2c (codbuf);
                    189:             
                    190:             printf ("%s\n", codbuf);
                    191:         }
1.5       snw       192:         dbg_buf = readline (dbg_prompt);
                    193: 
                    194:         if (!dbg_buf) continue;
                    195: 
1.6       snw       196:         savptr = dbg_buf;
                    197:         dbg_cmd = strtok_r (dbg_buf, " ", &savptr);
                    198:         
                    199:         if ((strcmp (dbg_cmd, "exit") == 0) || (strcmp (dbg_cmd, "quit") == 0)) {
1.5       snw       200:             first_entry = TRUE;
1.6       snw       201:             stepmode = STEPMODE_NONE;
1.5       snw       202:             printf ("\n\nExiting debug mode.\n\n");
                    203:             set_io (MUMPS);
                    204:             return FALSE;
                    205:         }
1.6       snw       206:         else if ((strcmp (dbg_cmd, "step") == 0) || (strcmp (dbg_cmd, "s") == 0)) {
                    207:             stepmode = STEPMODE_CMD;
                    208:             return TRUE;
                    209:         }
                    210:         else if ((strcmp (dbg_cmd, "next") == 0) || (strcmp (dbg_cmd, "n") == 0)) {
                    211:             stepmode = STEPMODE_LINE;
                    212:             return TRUE;
                    213:         }
                    214:         else if ((strcmp (dbg_cmd, "continue") == 0) || (strcmp (dbg_cmd, "cont") == 0) || (strcmp(dbg_cmd, "c") == 0)) {
                    215:             stepmode = STEPMODE_CONT;
                    216:             return TRUE;
                    217:         }
                    218:         else if ((strcmp (dbg_cmd, "backtrace") == 0) || (strcmp (dbg_cmd, "bt") == 0)) {
                    219:             char tmpbuf[256];
1.7       snw       220:             char ecbuf[256];
                    221:             char lref[256];
                    222:             char bt_mcode[256];
                    223:             
                    224:             printf ("%-10s%s\n", "$STACK", "ENTRY");
                    225:             printf ("%-10s%s\n", "======", "=====");
1.6       snw       226:             
                    227:             
                    228:             for (i = 1; i <= nstx; i++) getraddress (callerr[i], i);
                    229:             for (i = nstx; i > 0; i--) {
1.7       snw       230:                 
1.6       snw       231:                 stcpy (tmpbuf, callerr[i]);
                    232:                 stcnv_m2c (tmpbuf);
1.7       snw       233:                 
                    234:                 printf ("%-10d%s\n", i, &(tmpbuf[3]));
                    235:                 stcpy (lref, &(tmpbuf[3]));
                    236:                 stcnv_m2c (lref);
1.8     ! snw       237:                 if (routine_get_line (lref, bt_mcode) != NULL) {
1.7       snw       238:                     stcnv_m2c (bt_mcode);
                    239:                     printf ("\t%s\n", bt_mcode);
                    240:                 }
1.6       snw       241:             }
                    242:         }
1.5       snw       243:         else {
                    244:             printf ("DEBUG:  invalid command\r\n");
                    245:         }
                    246:         
                    247:     }
                    248: 
                    249:     return TRUE;
                    250: #else
                    251:     return FALSE;
                    252: #endif
                    253: 
                    254: }
                    255: 
1.1       snw       256: dbg_watch *dbg_add_watch (char *varnam)
                    257: {
                    258:     register int i;
                    259:     int index = -1;
                    260:     short found = 0;
                    261:     dbg_watch *w;
                    262: 
                    263:     if ((w = dbg_find_watch (varnam)) != NULL) {
                    264:         set_io (UNIX);
                    265:         fprintf (stderr, "You are already watching '%s' (changed %d times).\n", dbg_get_watch_name (w->varnam), w->chgct);
                    266:         set_io (MUMPS);
                    267:         return NULL;
                    268:     }
                    269: 
                    270:     for (i = 0; i < MAXWATCH; i++) {
                    271:         if (dbg_watchlist[i].varnam == NULL) {
                    272:             found++;
                    273:             index = i;
                    274:             break;
                    275:         }
                    276:     }
                    277: 
                    278:     if (!found) {
                    279:         set_io (UNIX);
                    280:         fprintf (stderr, "No free watchlist entries available. Try removing an existing watchpoint first.\n");
                    281:         set_io (MUMPS);
                    282: 
                    283:         return NULL;
                    284:     }
                    285: 
                    286:     if ((dbg_watchlist[index].varnam = (char *) malloc (256 * sizeof (char))) == NULL) {
                    287:         set_io (UNIX);
                    288:         fprintf (stderr, "Could not allocate memory for the new watchlist entry.\n");
                    289:         set_io (MUMPS);
                    290: 
                    291:         return NULL;
                    292:     }
                    293: 
                    294:     strcpy (dbg_watchlist[index].varnam, varnam);
                    295:     dbg_watchlist[index].chgct = 0;
                    296: 
                    297:     set_io (UNIX);
                    298:     fprintf (stderr, "Added '%s' to the watchlist.\n", dbg_get_watch_name (varnam));
                    299:     set_io (MUMPS);
                    300: 
                    301:     return NULL;
                    302:     
                    303: }
                    304: 
                    305: void dbg_dump_watchlist (void) 
                    306: {
                    307:        register int i;
                    308: 
                    309:        for (i = 0; i < MAXWATCH; i++) {
                    310:                if (dbg_watchlist[i].firect) {
                    311:                        dbg_dump_watch (dbg_watchlist[i].varnam);
                    312:                }
                    313:        }
                    314: 
                    315:        dbg_pending_watches = 0;
                    316: }
                    317: 
                    318: 
                    319: void dbg_remove_watch (char *varnam)
                    320: {
                    321:     dbg_watch *w;
                    322: 
                    323:     if ((w = dbg_find_watch (varnam)) == NULL) {
                    324:         set_io (UNIX);
                    325:         fprintf (stderr, "'%s' is not being watched.\n", dbg_get_watch_name (varnam));
                    326:         set_io (MUMPS);
                    327: 
                    328:         return;
                    329:     }
                    330: 
                    331:     free (w->varnam);
                    332:     
                    333:     w->chgct = 0;
                    334:     w->firect = 0;
                    335: 
                    336:     set_io (UNIX);
                    337:     printf ("Removed '%s' from the watchlist.\n", dbg_get_watch_name (varnam));
                    338:     set_io (MUMPS);
                    339:     
                    340:     return;
                    341: }
                    342: 
                    343: void dbg_dump_watch (char *varnam)
                    344: {
                    345:     char *ddwbuf;
                    346:     dbg_watch *w;
                    347: 
                    348:     ddwbuf = (char *) malloc (STRLEN * sizeof (char));
                    349:     NULLPTRCHK(ddwbuf,"dbg_dump_watch");
                    350: 
                    351:     if ((w = dbg_find_watch (varnam)) == NULL) {
                    352:         set_io (UNIX);
                    353:         fprintf (stderr, "'%s' is not being watched.\n", dbg_get_watch_name (varnam));
                    354:         set_io (MUMPS);
                    355: 
                    356:         return;
                    357:     }
                    358: 
                    359:     w->firect = 0;
                    360: 
                    361:     if (varnam[0] != '^') {
                    362:        symtab (get_sym, varnam, ddwbuf);
                    363:     }
                    364:     else {
                    365:        if (varnam[1] == '$') {
                    366:                ssvn (get_sym, varnam, ddwbuf);
                    367:        }
                    368:        else {
                    369:                global (get_sym, varnam, ddwbuf);
                    370:        }
                    371:     }
                    372: 
                    373:     stcnv_m2c (ddwbuf);
                    374: 
                    375:     set_io (UNIX);
                    376:     printf (">> WATCHPOINT:  %s => '%s' (changed %d times)\n", dbg_get_watch_name (varnam), ddwbuf, w->chgct);
                    377:     set_io (MUMPS);
                    378: 
                    379:     free (ddwbuf);
                    380: 
                    381: }
                    382: 
                    383: dbg_watch *dbg_find_watch (char *varnam)
                    384: {
                    385:     register int i;
                    386:     
                    387: 
                    388:     for (i = 0; i < MAXWATCH; i++) {
                    389:         if (dbg_watchlist[i].varnam != NULL) {
                    390:             if (strcmp (varnam, dbg_watchlist[i].varnam) == 0) {
                    391:                 return &(dbg_watchlist[i]);
                    392:             }
                    393: 
                    394:         }
                    395:     }
                    396: 
                    397:     return NULL;
                    398: }
                    399: 
                    400: char *dbg_get_watch_name (char *varnam)
                    401: {
                    402:     freem_ref_t *r;
                    403:     char *s;
                    404:     
                    405:     r = (freem_ref_t *) malloc (sizeof (freem_ref_t));
                    406:     NULLPTRCHK(r,"dbg_get_watch_name");
                    407:     
                    408:     s = (char *) malloc (STRLEN * sizeof (char));
                    409:     NULLPTRCHK(s,"dbg_get_watch_name");
                    410:     
                    411:     mref_init (r, MREF_RT_LOCAL, "");
                    412:     internal_to_mref (r, varnam);
                    413:     mref_to_external (r, s);
                    414:         
                    415:     free (r);
                    416:         
                    417:     return s;
                    418:         
                    419: }
                    420: 
                    421: void dbg_fire_watch (char *varnam) {
                    422: 
                    423:        dbg_watch *w;
                    424: 
                    425:        if ((w = dbg_find_watch (varnam)) == NULL) {
                    426:                return;
                    427:        }
                    428: 
                    429:        w->chgct++;
                    430:        w->firect++;
                    431:        dbg_pending_watches++;
1.4       snw       432:         
1.1       snw       433: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>