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