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

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

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