|
|
| version 1.3, 2025/03/09 19:50:47 | version 1.11, 2025/05/01 17:02:30 |
|---|---|
| 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.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 | |
| * Add trace mode | |
| * | |
| * Revision 1.3 2025/03/09 19:50:47 snw | * Revision 1.3 2025/03/09 19:50:47 snw |
| * Second phase of REUSE compliance and header reformat | * Second phase of REUSE compliance and header reformat |
| * | * |
| Line 35 | Line 59 |
| #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" |
| Line 62 void dbg_init (void) | Line 116 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; | |
| 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); | |
| 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 { | |
| 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 116 void dbg_dump_watchlist (void) | Line 414 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 229 char *dbg_get_watch_name (char *varnam) | Line 527 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++; |
| } | } |