Diff for /freem/src/mdebug.c between versions 1.4 and 1.13

version 1.4, 2025/04/28 19:38:55 version 1.13, 2025/05/20 18:07:41
Line 24 Line 24
  *   along with FreeM.  If not, see <https://www.gnu.org/licenses/>.   *   along with FreeM.  If not, see <https://www.gnu.org/licenses/>.
  *   *
  *   $Log$   *   $Log$
    *   Revision 1.13  2025/05/20 18:07:41  snw
    *   Add completion to debugger
    *
    *   Revision 1.12  2025/05/01 21:02:31  snw
    *   Documentation updates
    *
    *   Revision 1.11  2025/05/01 17:02:30  snw
    *   Further debugging improvements
    *
    *   Revision 1.10  2025/05/01 04:45:15  snw
    *   Improve backtraces
    *
    *   Revision 1.9  2025/05/01 03:56:29  snw
    *   -m
    *
    *   Revision 1.8  2025/04/30 20:03:09  snw
    *   Work on entryref parser
    *
    *   Revision 1.7  2025/04/30 17:19:16  snw
    *   Improve backtraces in debugger
    *
    *   Revision 1.6  2025/04/30 14:41:03  snw
    *   Further debugger work
    *
    *   Revision 1.5  2025/04/29 18:46:17  snw
    *   Begin work on interactive debugger
    *
  *   Revision 1.4  2025/04/28 19:38:55  snw   *   Revision 1.4  2025/04/28 19:38:55  snw
  *   Add trace mode   *   Add trace mode
  *   *
Line 38 Line 65
 #include <stdio.h>  #include <stdio.h>
 #include <stdlib.h>  #include <stdlib.h>
 #include <string.h>  #include <string.h>
   #include <unistd.h>
   
   #include "config.h"
   
   #ifdef HAVE_LIBREADLINE
   #  if defined(HAVE_READLINE_READLINE_H)
   #    include <readline/readline.h>
   #  elif defined(HAVE_READLINE_H)
   #    include <readline.h>
   #  else /* !defined(HAVE_READLINE_H) */
   extern char *readline ();
   #  endif /* !defined(HAVE_READLINE_H) */
   /*char *cmdline = NULL;*/
   #else /* !defined(HAVE_READLINE_READLINE_H) */
     /* no readline */
   #endif /* HAVE_LIBREADLINE */
   
   #ifdef HAVE_READLINE_HISTORY
   #  if defined(HAVE_READLINE_HISTORY_H)
   #    include <readline/history.h>
   #  elif defined(HAVE_HISTORY_H)
   #    include <history.h>
   #  else /* !defined(HAVE_HISTORY_H) */
   extern void add_history ();
   extern int write_history ();
   extern int read_history ();
   #  endif /* defined(HAVE_READLINE_HISTORY_H) */
     /* no history */
   #endif /* HAVE_READLINE_HISTORY */
   
   
 #include "mpsdef.h"  #include "mpsdef.h"
 #include "mdebug.h"  #include "mdebug.h"
 #include "freem.h"  #include "freem.h"
 #include "mref.h"  #include "mref.h"
   
   #ifdef HAVE_LIBREADLINE
   char *dbg_commands[] = {
       "backtrace",
       "continue",
       "examine",
       "exit",
       "halt",
       "next",
       "quit",
       "step",
       "trace",
       "watch",
       NULL
   };
   
   char **dbg_completion(const char *, int, int);
   char *dbg_generator(const char *, int);
   
   char **dbg_completion(const char *text, int start, int end)
   {
       if (start > 0) return NULL;
       rl_attempted_completion_over = 1;
       return rl_completion_matches (text, dbg_generator);
   }
   
   char *dbg_generator(const char *text, int state)
   {
       static int list_index, len;
       char *name;
   
       if (!state) {
           list_index = 0;
           len = strlen(text);
       }
   
       while ((name = dbg_commands[list_index++])) {
           if (strncmp (name, text, len) == 0) {
               return strdup (name);
           }
       }
   
       return NULL;
   }
   #endif
   
 dbg_watch dbg_watchlist[MAXWATCH];    /* list of watchpoints */  dbg_watch dbg_watchlist[MAXWATCH];    /* list of watchpoints */
 short dbg_enable_watch;               /* 0 = watches disabled, 1 = watches enabled */  short dbg_enable_watch;               /* 0 = watches disabled, 1 = watches enabled */
 int dbg_pending_watches;  int dbg_pending_watches;
Line 65  void dbg_init (void) Line 167  void dbg_init (void)
   
 }  }
   
   int debugger (int entry_mode, char *entryref)
   {
       
   #if defined(HAVE_LIBREADLINE)    
       static int first_entry = TRUE;
       static int stepmode = STEPMODE_NONE;
       
       char *dbg_buf;
       char dbg_prompt[256];
       int dbg_argc;
       HIST_ENTRY **dbg_hist;
       int dbg_hist_idx;
       HIST_ENTRY *dbg_hist_ent;
       char rouname_buf[256];
       char *savptr;
       char *dbg_cmd;
       char *dbarg;
       int do_prompt = FALSE;
       char cur_code[512];
       char tbuf[512];
       char tbuf2[512];
       register int i;
       register int j;
   
       rl_attempted_completion_function = dbg_completion;
       set_io (UNIX);
       
       if (first_entry) {
           first_entry = FALSE;
           
           printf ("\nEntering debug mode; M commands unavailable.\n");
           printf ("Type 'exit' to return to direct mode; 'help' for help.\n\n");
   
           do_prompt = TRUE;
       }
       else {
           switch (entry_mode) {
               
               case DEBENTRY_CMD:
                   if (stepmode == STEPMODE_CMD) {
                       do_prompt = TRUE;
                   }
                   else {
                       do_prompt = FALSE;
                   }
                       
                   break;
                       
               case DEBENTRY_LINE:
                   if (stepmode == STEPMODE_CMD || stepmode == STEPMODE_LINE) {
                       do_prompt = TRUE;
                   }
                   else {
                       do_prompt = FALSE;
                   }
                   
                   break;
                   
               case DEBENTRY_BREAKPOINT:
                   do_prompt = TRUE;
                   break;
                   
               case DEBENTRY_SIGINT:
                   break;
           }
       }
   
   
       if (!do_prompt) return TRUE;
   
       while (1) {
           
           if (nstx == 0) {
               stcpy (rouname_buf, rou_name);
           }
           else {
               getraddress (tbuf, nstx);
               stcpy (rouname_buf, &(tbuf[3]));
           }
           stcnv_m2c (rouname_buf);
           
           if (stepmode != STEPMODE_NONE) {
               getraddress (tbuf, nstx);
               stcnv_m2c (tbuf);
               strcpy (tbuf2, &(tbuf[3]));
               routine_get_line (tbuf2, cur_code);
   
               printf ("%s\n", cur_code);
           }
           
           snprintf (dbg_prompt, sizeof (dbg_prompt) - 1, "%s $STACK=%d [DEBUG]> ", rouname_buf, nstx);
           if (dbg_enable_watch && dbg_pending_watches) dbg_dump_watchlist ();
           dbg_buf = readline (dbg_prompt);
           
           if (!dbg_buf) continue;
           if (strlen (dbg_buf) < 1) continue;
   
           dbg_argc = 1;
           for (i = 0; i < strlen (dbg_buf); i++) {
               if (dbg_buf[i] == ' ') dbg_argc++;
           }
           dbarg = dbg_buf;
           savptr = dbg_buf;
           dbg_cmd = strtok_r (dbg_buf, " ", &savptr);
   
           dbarg += strlen (dbg_cmd) + 1;
   
           if ((strcmp (dbg_cmd, "exit") == 0) || (strcmp (dbg_cmd, "quit") == 0)) {
               first_entry = TRUE;
               stepmode = STEPMODE_NONE;
               printf ("\n\nExiting debug mode.\n\n");
               set_io (MUMPS);
               return FALSE;
           }
           else if ((strcmp (dbg_cmd, "examine") == 0) || (strcmp (dbg_cmd, "e") == 0)) {
               char *glvn;
               char key[STRLEN];
               char res[STRLEN];
               
               if (dbg_argc < 2) {
                   printf ("debug:  syntax error\n");
               }
               else {
                   glvn = dbarg;
                   name_to_key (key, glvn, STRLEN - 1);
                   if (key[0] != '^') {
                       symtab (get_sym, key, res);
                   }
                   else {
                       if (key[1] != '$') {
                           global (get_sym, key, res);
                       }
                       else {
                           ssvn (get_sym, key, res);
                       }
                   }
   
                   if (merr () != OK) {
                       if (merr () == M6) {
                           printf ("examine:  local variable %s is not defined\n", glvn);
                           merr_clear ();
                       }
                       else if (merr () == M7) {
                           printf ("examine:  global variable %s is not defined\n", glvn);
                           merr_clear ();
                       }
                       else {
                           printf ("examine:  error retrieving %s\n", glvn);
                           merr_clear ();
                       }
                   }                
                   else {
                       stcnv_m2c (res);
                       printf ("%s=\"%s\"\n", glvn, res);
                   }
   
               }
           }
           else if ((strcmp (dbg_cmd, "trace") == 0) || (strcmp (dbg_cmd, "t") == 0)) {
               if (trace_mode == 0) {                
                   trace_mode = 1;
                   printf ("debug:  trace on\n");
               }
               else {
                   trace_mode = 0;
                   printf ("debug:  trace off\n");
               }
               ssvn_job_update ();
           }
           else if ((strcmp (dbg_cmd, "step") == 0) || (strcmp (dbg_cmd, "s") == 0)) {
               stepmode = STEPMODE_CMD;
               return TRUE;
           }
           else if ((strcmp (dbg_cmd, "next") == 0) || (strcmp (dbg_cmd, "n") == 0)) {
               stepmode = STEPMODE_LINE;
               return TRUE;
           }
           else if ((strcmp (dbg_cmd, "continue") == 0) || (strcmp (dbg_cmd, "cont") == 0) || (strcmp(dbg_cmd, "c") == 0)) {
               stepmode = STEPMODE_CONT;
               return TRUE;
           }
           else if ((strcmp (dbg_cmd, "backtrace") == 0) || (strcmp (dbg_cmd, "bt") == 0)) {
               char tmpbuf[1024];
               char ecbuf[256];
               char m_cmd[10];
               char lref[1024];
               char bt_mcode[1024];
               
               printf ("%-10s%-10s%s\n", "$STACK", "COMMAND", "PLACE");
               printf ("%-10s%-10s%s\n", "======", "=======", "=====");
               
               
               for (i = 1; i <= nstx; i++) getraddress (callerr[i], i);
               for (i = nstx; i > 0; i--) {
                   
                   stcpy (tmpbuf, callerr[i]);
                   stcnv_m2c (tmpbuf);
                   strcpy (ecbuf, &(tmpbuf[1]));
   
                   for (j = 0; j < strlen (ecbuf); j++) {
                       if (ecbuf[j] == ')') {
                           ecbuf[j] = '\0';
                           break;
                       }
                   }
   
                   switch (ecbuf[0]) {
                       case 'F':
                           sprintf (m_cmd, "FOR");
                           break;
                       case 'D':
                           sprintf (m_cmd, "DO");
                           break;
                   }
                               
                   
                   printf ("%-10d%-10s%s\n", i, m_cmd, &(tmpbuf[3]));
                   stcpy (lref, &(tmpbuf[3]));
                   stcnv_m2c (lref);
                   if (routine_get_line (lref, bt_mcode) != NULL) {
                       stcnv_m2c (bt_mcode);
                       printf ("%-20s%s\n", " ", bt_mcode);
                   }
               }
               stcpy (ecbuf, etrap);
               stcnv_m2c (ecbuf);
               printf ("ETRAP:\t%s\n", ecbuf);
               stcpy (ecbuf, ztrap[nstx]);
               stcnv_m2c (ecbuf);
               printf ("ZTRAP:\t%s\n", ecbuf);
               
           }
           else if ((strcmp (dbg_cmd, "halt") == 0) || (strcmp (dbg_cmd, "h") == 0)) {
               printf ("debug:  forcing halt\n");          
               exit (0);
           }
           else if ((strcmp (dbg_cmd, "watch") == 0) || (strcmp (dbg_cmd, "w") == 0)) {
               char watchmode;
               char *glvn;
               
               if (dbg_argc == 1) {
                   switch (dbg_enable_watch) {
                       case 0:
                           dbg_enable_watch = 1;
                           printf ("debug:  watchpoints enabled\n");
                           break;
                       case 1:
                           dbg_enable_watch = 0;
                           printf ("debug:  watchpoints disabled\n");
                           break;
                   }
               }
               else {
                   watchmode = dbarg[0];
                   glvn = &(dbarg[1]);
                   
                   switch (watchmode) {
                       case '+':
                           dbg_add_watch (glvn);
                           break;
                       case '-':
                           dbg_remove_watch (glvn);
                           break;
                       case '?':
                           dbg_dump_watch (glvn);
                           break;
                       default:
                           printf ("debug:  watch mode must be +, -, or ?\n");
                           break;
                   }
   
                   set_io (UNIX);
               }               
           }
           else {
               printf ("DEBUG:  invalid command\r\n");
           }
           
       }
   
       return TRUE;
   #else
       return FALSE;
   #endif
   
   }
   
 dbg_watch *dbg_add_watch (char *varnam)  dbg_watch *dbg_add_watch (char *varnam)
 {  {
     register int i;      register int i;
Line 95  dbg_watch *dbg_add_watch (char *varnam) Line 484  dbg_watch *dbg_add_watch (char *varnam)
         return NULL;          return NULL;
     }      }
   
     if ((dbg_watchlist[index].varnam = (char *) malloc (256 * sizeof (char))) == NULL) {      if ((dbg_watchlist[index].varnam = (char *) calloc (256, sizeof (char))) == NULL) {
         set_io (UNIX);          set_io (UNIX);
         fprintf (stderr, "Could not allocate memory for the new watchlist entry.\n");          fprintf (stderr, "Could not allocate memory for the new watchlist entry.\n");
         set_io (MUMPS);          set_io (MUMPS);
Line 107  dbg_watch *dbg_add_watch (char *varnam) Line 496  dbg_watch *dbg_add_watch (char *varnam)
     dbg_watchlist[index].chgct = 0;      dbg_watchlist[index].chgct = 0;
   
     set_io (UNIX);      set_io (UNIX);
     fprintf (stderr, "Added '%s' to the watchlist.\n", dbg_get_watch_name (varnam));      fprintf (stderr, "Added '%s' to the watchlist.\n", varnam);
     set_io (MUMPS);      set_io (MUMPS);
   
     return NULL;      return NULL;
Line 119  void dbg_dump_watchlist (void) Line 508  void dbg_dump_watchlist (void)
         register int i;          register int i;
   
         for (i = 0; i < MAXWATCH; i++) {          for (i = 0; i < MAXWATCH; i++) {
                 if (dbg_watchlist[i].firect) {              if (dbg_watchlist[i].firect) {
                         dbg_dump_watch (dbg_watchlist[i].varnam);                  dbg_dump_watch (dbg_watchlist[i].varnam);
                 }              }
         }          }
   
         dbg_pending_watches = 0;          dbg_pending_watches = 0;
Line 232  char *dbg_get_watch_name (char *varnam) Line 621  char *dbg_get_watch_name (char *varnam)
   
 void dbg_fire_watch (char *varnam) {  void dbg_fire_watch (char *varnam) {
   
         dbg_watch *w;      dbg_watch *w;
       
         if ((w = dbg_find_watch (varnam)) == NULL) {      if ((w = dbg_find_watch (varnam)) == NULL) {
                 return;          return;
         }      }
       
         w->chgct++;      w->chgct++;
         w->firect++;      w->firect++;
         dbg_pending_watches++;      dbg_pending_watches++;
               
 }  }

Removed from v.1.4  
changed lines
  Added in v.1.13


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