|
|
1.1 root 1: /*
2: * Hatari - history.c
3: *
1.1.1.4 root 4: * Copyright (C) 2011-2014 by Eero Tamminen
1.1 root 5: *
1.1.1.2 root 6: * This file is distributed under the GNU General Public License, version 2
7: * or at your option any later version. Read the file gpl.txt for details.
1.1 root 8: *
9: * history.c - functions for debugger entry & breakpoint history
10: */
11: const char History_fileid[] = "Hatari history.c : " __DATE__ " " __TIME__;
12:
13: #include <assert.h>
1.1.1.4 root 14: #include <errno.h>
1.1 root 15: #include "main.h"
16: #include "debugui.h"
17: #include "debug_priv.h"
18: #include "dsp.h"
19: #include "dsp_core.h"
20: #include "evaluate.h"
1.1.1.4 root 21: #include "file.h"
1.1 root 22: #include "history.h"
23: #include "m68000.h"
24: #include "68kDisass.h"
25:
1.1.1.3 root 26: #define HISTORY_ITEMS_MIN 64
1.1.1.2 root 27:
28: history_type_t HistoryTracking;
1.1 root 29:
30: typedef struct {
31: bool shown:1;
32: bool valid:1;
33: bool for_dsp:1;
34: /* reason for debugger entry/breakpoint hit */
35: debug_reason_t reason:8;
36: union {
37: Uint16 dsp;
38: Uint32 cpu;
39: } pc;
40: } hist_item_t;
41:
42: static struct {
1.1.1.3 root 43: unsigned idx; /* index to current history item */
44: unsigned count; /* how many items of history are collected */
45: unsigned limit; /* ring-buffer size */
46: hist_item_t *item; /* ring-buffer */
1.1 root 47: } History;
48:
49:
50: /**
51: * Convert debugger entry/breakpoint entry reason to a string
52: */
1.1.1.2 root 53: static const char* History_ReasonStr(debug_reason_t reason)
1.1 root 54: {
55: switch(reason) {
56: case REASON_CPU_EXCEPTION:
57: return "CPU exception";
58: case REASON_CPU_BREAKPOINT:
59: return "CPU breakpoint";
60: case REASON_DSP_BREAKPOINT:
61: return "DSP breakpoint";
62: case REASON_CPU_STEPS:
63: return "CPU steps";
64: case REASON_DSP_STEPS:
65: return "DSP steps";
1.1.1.5 ! root 66: case REASON_PROGRAM:
! 67: return "Program break";
1.1 root 68: case REASON_USER:
69: return "User break";
70: default:
71: return "Unknown reason";
72: }
73: }
74:
75:
76: /**
1.1.1.2 root 77: * Set what kind of history is collected.
78: * Clear history if tracking type changes as rest of
79: * data wouldn't then be anymore valid.
1.1 root 80: */
1.1.1.3 root 81: static void History_Enable(history_type_t track, unsigned limit)
1.1 root 82: {
1.1.1.2 root 83: const char *msg;
1.1.1.3 root 84: if (track != HistoryTracking || limit != History.limit) {
85: fprintf(stderr, "Re-allocating & zeroing history due to type/limit change.\n");
86: if (History.item) {
87: free(History.item);
88: }
1.1 root 89: memset(&History, 0, sizeof(History));
1.1.1.3 root 90: History.item = calloc(limit, sizeof(History.item[0]));
91: History.limit = limit;
1.1 root 92: }
1.1.1.2 root 93: switch (track) {
94: case HISTORY_TRACK_NONE:
95: msg = "disabled";
96: break;
97: case HISTORY_TRACK_CPU:
98: msg = "enabled for CPU";
99: break;
100: case HISTORY_TRACK_DSP:
101: msg = "enabled for DSP";
102: break;
103: case HISTORY_TRACK_ALL:
104: msg = "enabled for CPU & DSP";
105: break;
106: default:
107: msg = "error";
108: }
109: HistoryTracking = track;
1.1.1.3 root 110: fprintf(stderr, "History tracking %s (max. %d instructions).\n", msg, limit);
1.1 root 111: }
112:
113: /**
114: * Advance & initialize next history item in ring buffer
115: */
116: static void History_Advance(void)
117: {
118: History.idx++;
1.1.1.3 root 119: History.idx %= History.limit;
1.1 root 120: History.item[History.idx].valid = true;
121: History.item[History.idx].shown = false;
122: History.item[History.idx].reason = REASON_NONE;
123: History.count++;
124: }
125:
126: /**
127: * Add CPU PC to history
128: */
129: void History_AddCpu(void)
130: {
131: Uint32 pc = M68000_GetPC();
132:
133: History_Advance();
134: History.item[History.idx].for_dsp = false;
135: History.item[History.idx].pc.cpu = pc;
136: }
137:
138: /**
139: * Add DSP PC to history
140: */
141: void History_AddDsp(void)
142: {
143: Uint16 pc = DSP_GetPC();
144:
145: History_Advance();
146: History.item[History.idx].for_dsp = true;
147: History.item[History.idx].pc.dsp = pc;
148: }
149:
150: /**
151: * Flag last history entry as debugger entry point, with given reason
152: */
153: void History_Mark(debug_reason_t reason)
154: {
1.1.1.3 root 155: if (History.item) {
156: History.item[History.idx].reason = reason;
157: }
1.1 root 158: }
159:
160: /**
1.1.1.4 root 161: * Output collected CPU/DSP debugger/breakpoint history
1.1 root 162: */
1.1.1.4 root 163: static Uint32 History_Output(Uint32 count, FILE *fp)
1.1 root 164: {
165: bool show_all;
1.1.1.4 root 166: Uint32 retval;
1.1 root 167: int i;
168:
1.1.1.3 root 169: if (History.count > History.limit) {
170: History.count = History.limit;
1.1 root 171: }
172: if (count > History.count) {
173: count = History.count;
174: } else {
175: if (!count) {
176: /* default to all */
177: count = History.count;
178: }
179: }
180: if (count <= 0) {
1.1.1.4 root 181: fprintf(stderr, "No history items to show.\n");
182: return 0;
1.1 root 183: }
1.1.1.4 root 184: retval = count;
1.1 root 185:
186: i = History.idx;
187: show_all = false;
188: if (History.item[i].shown) {
189: /* even last item already shown, show all again */
190: show_all = true;
191: }
1.1.1.3 root 192: i = (i + History.limit - count) % History.limit;
1.1 root 193:
194: while (count-- > 0) {
195: i++;
1.1.1.3 root 196: i %= History.limit;
1.1 root 197: if (!History.item[i].valid) {
1.1.1.4 root 198: fprintf(fp, "ERROR: invalid history item %d!", count);
1.1 root 199: }
200: if (History.item[i].shown && !show_all) {
201: continue;
202: }
203: History.item[i].shown = true;
204:
205: if (History.item[i].for_dsp) {
206: Uint16 pc = History.item[i].pc.dsp;
1.1.1.4 root 207: DSP_DisasmAddress(fp, pc, pc);
1.1 root 208: } else {
209: Uint32 dummy;
1.1.1.4 root 210: Disasm(fp, History.item[i].pc.cpu, &dummy, 1);
1.1 root 211: }
212: if (History.item[i].reason != REASON_NONE) {
1.1.1.4 root 213: fprintf(fp, "Debugger: *%s*\n", History_ReasonStr(History.item[i].reason));
1.1 root 214: }
215: }
1.1.1.4 root 216: return retval;
217: }
218:
219: /* History_Output() helper for "info" & "lock" commands */
220: void History_Show(FILE *fp, Uint32 count)
221: {
222: History_Output(count, fp);
223: }
224:
225: /*
226: * save all history to given file
227: */
228: static void History_Save(const char *name)
229: {
230: Uint32 count;
231: FILE *fp;
232:
233: if (File_Exists(name)) {
234: fprintf(stderr, "ERROR: file '%s' already exists!\n", name);
235:
236: } else if ((fp = fopen(name, "w"))) {
237: count = History_Output(0, fp);
238: fprintf(stderr, "%d history items saved to '%s'.\n", count, name);
239: fclose(fp);
240: } else {
241: fprintf(stderr, "ERROR: opening '%s' failed (%d).\n", name, errno);
242: }
1.1 root 243: }
244:
1.1.1.3 root 245: /*
246: * Readline callback
247: */
248: char *History_Match(const char *text, int state)
249: {
1.1.1.4 root 250: static const char* cmds[] = { "cpu", "dsp", "off", "save" };
1.1.1.5 ! root 251: return DebugUI_MatchHelper(cmds, ARRAY_SIZE(cmds), text, state);
1.1.1.3 root 252: }
253:
1.1 root 254: /**
255: * Command: Show collected CPU/DSP debugger/breakpoint history
256: */
257: int History_Parse(int nArgc, char *psArgs[])
258: {
1.1.1.3 root 259: int count, limit = 0;
1.1 root 260:
1.1.1.3 root 261: if (nArgc < 2) {
262: return DebugUI_PrintCmdHelp(psArgs[0]);
263: }
264: if (nArgc > 2) {
265: limit = atoi(psArgs[2]);
266: }
267: /* make sure value is valid & positive */
268: if (!limit) {
269: limit = History.limit;
270: }
271: if (limit < HISTORY_ITEMS_MIN) {
272: limit = HISTORY_ITEMS_MIN;
1.1 root 273: }
274: count = atoi(psArgs[1]);
1.1.1.3 root 275:
276: if (count <= 0) {
1.1 root 277: /* no count -> enable or disable? */
278: if (strcmp(psArgs[1], "on") == 0) {
1.1.1.3 root 279: History_Enable(HISTORY_TRACK_ALL, limit);
1.1 root 280: return DEBUGGER_CMDDONE;
281: }
282: if (strcmp(psArgs[1], "off") == 0) {
1.1.1.3 root 283: History_Enable(HISTORY_TRACK_NONE, limit);
1.1.1.2 root 284: return DEBUGGER_CMDDONE;
285: }
286: if (strcmp(psArgs[1], "cpu") == 0) {
1.1.1.3 root 287: History_Enable(HISTORY_TRACK_CPU, limit);
1.1.1.2 root 288: return DEBUGGER_CMDDONE;
289: }
290: if (strcmp(psArgs[1], "dsp") == 0) {
1.1.1.3 root 291: History_Enable(HISTORY_TRACK_DSP, limit);
1.1 root 292: return DEBUGGER_CMDDONE;
293: }
1.1.1.4 root 294: if (nArgc == 3 && strcmp(psArgs[1], "save") == 0) {
295: History_Save(psArgs[2]);
296: return DEBUGGER_CMDDONE;
297: }
1.1.1.3 root 298: fprintf(stderr, "History range is 1-<limit>\n");
299: return DebugUI_PrintCmdHelp(psArgs[0]);
1.1 root 300: }
301:
1.1.1.4 root 302: History_Show(stderr, count);
1.1 root 303: return DEBUGGER_CMDDONE;
304: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.