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

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

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