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>