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