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

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

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