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