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

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

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