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

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

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