all repos — mgba @ 45c6299b3b18ef85aacc00c0d5af3f65e4324100

mGBA Game Boy Advance Emulator

src/debugger/cli-debugger.c (view raw)

  1#include "cli-debugger.h"
  2#include "decoder.h"
  3
  4#include <signal.h>
  5#include <stdarg.h>
  6#include <stdio.h>
  7#include <stdlib.h>
  8#include <string.h>
  9#include <unistd.h>
 10
 11#ifdef USE_PTHREADS
 12#include <pthread.h>
 13#endif
 14
 15struct DebugVector {
 16	struct DebugVector* next;
 17	enum DVType {
 18		ERROR_TYPE,
 19		INT_TYPE,
 20		CHAR_TYPE
 21	} type;
 22	union {
 23		int32_t intValue;
 24		const char* charValue;
 25	};
 26};
 27
 28static const char* ERROR_MISSING_ARGS = "Arguments missing";
 29
 30static struct CLIDebugger* _activeDebugger;
 31
 32typedef void (DebuggerCommand)(struct CLIDebugger*, struct DebugVector*);
 33
 34static void _breakInto(struct CLIDebugger*, struct DebugVector*);
 35static void _continue(struct CLIDebugger*, struct DebugVector*);
 36static void _next(struct CLIDebugger*, struct DebugVector*);
 37static void _print(struct CLIDebugger*, struct DebugVector*);
 38static void _printHex(struct CLIDebugger*, struct DebugVector*);
 39static void _printStatus(struct CLIDebugger*, struct DebugVector*);
 40static void _quit(struct CLIDebugger*, struct DebugVector*);
 41static void _readByte(struct CLIDebugger*, struct DebugVector*);
 42static void _readHalfword(struct CLIDebugger*, struct DebugVector*);
 43static void _readWord(struct CLIDebugger*, struct DebugVector*);
 44static void _setBreakpoint(struct CLIDebugger*, struct DebugVector*);
 45static void _clearBreakpoint(struct CLIDebugger*, struct DebugVector*);
 46static void _setWatchpoint(struct CLIDebugger*, struct DebugVector*);
 47
 48static void _breakIntoDefault(int signal);
 49
 50static struct {
 51	const char* name;
 52	DebuggerCommand* command;
 53} _debuggerCommands[] = {
 54	{ "b", _setBreakpoint },
 55	{ "break", _setBreakpoint },
 56	{ "c", _continue },
 57	{ "continue", _continue },
 58	{ "d", _clearBreakpoint },
 59	{ "delete", _clearBreakpoint },
 60	{ "i", _printStatus },
 61	{ "info", _printStatus },
 62	{ "n", _next },
 63	{ "next", _next },
 64	{ "p", _print },
 65	{ "p/x", _printHex },
 66	{ "print", _print },
 67	{ "print/x", _printHex },
 68	{ "q", _quit },
 69	{ "quit", _quit },
 70	{ "rb", _readByte },
 71	{ "rh", _readHalfword },
 72	{ "rw", _readWord },
 73	{ "status", _printStatus },
 74	{ "w", _setWatchpoint },
 75	{ "watch", _setWatchpoint },
 76	{ "x", _breakInto },
 77	{ 0, 0 }
 78};
 79
 80static inline void _printPSR(union PSR psr) {
 81	printf("%08X [%c%c%c%c%c%c%c]\n", psr.packed,
 82		psr.n ? 'N' : '-',
 83		psr.z ? 'Z' : '-',
 84		psr.c ? 'C' : '-',
 85		psr.v ? 'V' : '-',
 86		psr.i ? 'I' : '-',
 87		psr.f ? 'F' : '-',
 88		psr.t ? 'T' : '-');
 89}
 90
 91static void _handleDeath(int sig) {
 92	(void)(sig);
 93	printf("No debugger attached!\n");
 94}
 95
 96static void _breakInto(struct CLIDebugger* debugger, struct DebugVector* dv) {
 97	(void)(debugger);
 98	(void)(dv);
 99	struct sigaction sa, osa;
100	sa.sa_handler = _handleDeath;
101	sigemptyset(&sa.sa_mask);
102	sigaddset(&sa.sa_mask, SIGTRAP);
103	sa.sa_flags = SA_RESTART;
104	sigaction(SIGTRAP, &sa, &osa);
105#ifdef USE_PTHREADS
106	pthread_kill(pthread_self(), SIGTRAP);
107#else
108	kill(getpid(), SIGTRAP);
109#endif
110	sigaction(SIGTRAP, &osa, 0);
111}
112
113static void _continue(struct CLIDebugger* debugger, struct DebugVector* dv) {
114	(void)(dv);
115	debugger->d.state = DEBUGGER_RUNNING;
116}
117
118static void _next(struct CLIDebugger* debugger, struct DebugVector* dv) {
119	(void)(dv);
120	ARMRun(debugger->d.cpu);
121	_printStatus(debugger, 0);
122}
123
124static void _print(struct CLIDebugger* debugger, struct DebugVector* dv) {
125	(void)(debugger);
126	for ( ; dv; dv = dv->next) {
127		printf(" %u", dv->intValue);
128	}
129	printf("\n");
130}
131
132static void _printHex(struct CLIDebugger* debugger, struct DebugVector* dv) {
133	(void)(debugger);
134	for ( ; dv; dv = dv->next) {
135		printf(" 0x%08X", dv->intValue);
136	}
137	printf("\n");
138}
139
140static inline void _printLine(struct CLIDebugger* debugger, uint32_t address, enum ExecutionMode mode) {
141	char disassembly[32];
142	if (mode == MODE_ARM) {
143		uint32_t instruction = debugger->d.cpu->memory->load32(debugger->d.cpu->memory, address, 0);
144		printf("%08X\n", instruction);
145	} else {
146		uint16_t instruction = debugger->d.cpu->memory->loadU16(debugger->d.cpu->memory, address, 0);
147		ARMDisassembleThumb(instruction, debugger->d.cpu->gprs[ARM_PC] + WORD_SIZE_THUMB, disassembly, sizeof(disassembly));
148		printf("%04X: %s\n", instruction, disassembly);
149	}
150}
151
152static void _printStatus(struct CLIDebugger* debugger, struct DebugVector* dv) {
153	(void)(dv);
154	int r;
155	for (r = 0; r < 4; ++r) {
156		printf("%08X %08X %08X %08X\n",
157			debugger->d.cpu->gprs[r << 2],
158			debugger->d.cpu->gprs[(r << 2) + 1],
159			debugger->d.cpu->gprs[(r << 2) + 2],
160			debugger->d.cpu->gprs[(r << 2) + 3]);
161	}
162	_printPSR(debugger->d.cpu->cpsr);
163	int instructionLength;
164	enum ExecutionMode mode = debugger->d.cpu->cpsr.t;
165	if (mode == MODE_ARM) {
166		instructionLength = WORD_SIZE_ARM;
167	} else {
168		instructionLength = WORD_SIZE_THUMB;
169	}
170	_printLine(debugger, debugger->d.cpu->gprs[ARM_PC] - instructionLength, mode);
171}
172
173static void _quit(struct CLIDebugger* debugger, struct DebugVector* dv) {
174	(void)(dv);
175	debugger->d.state = DEBUGGER_SHUTDOWN;
176}
177
178static void _readByte(struct CLIDebugger* debugger, struct DebugVector* dv) {
179	if (!dv || dv->type != INT_TYPE) {
180		printf("%s\n", ERROR_MISSING_ARGS);
181		return;
182	}
183	uint32_t address = dv->intValue;
184	uint8_t value = debugger->d.cpu->memory->loadU8(debugger->d.cpu->memory, address, 0);
185	printf(" 0x%02X\n", value);
186}
187
188static void _readHalfword(struct CLIDebugger* debugger, struct DebugVector* dv) {
189	if (!dv || dv->type != INT_TYPE) {
190		printf("%s\n", ERROR_MISSING_ARGS);
191		return;
192	}
193	uint32_t address = dv->intValue;
194	uint16_t value = debugger->d.cpu->memory->loadU16(debugger->d.cpu->memory, address, 0);
195	printf(" 0x%04X\n", value);
196}
197
198static void _readWord(struct CLIDebugger* debugger, struct DebugVector* dv) {
199	if (!dv || dv->type != INT_TYPE) {
200		printf("%s\n", ERROR_MISSING_ARGS);
201		return;
202	}
203	uint32_t address = dv->intValue;
204	uint32_t value = debugger->d.cpu->memory->load32(debugger->d.cpu->memory, address, 0);
205	printf(" 0x%08X\n", value);
206}
207
208static void _setBreakpoint(struct CLIDebugger* debugger, struct DebugVector* dv) {
209	if (!dv || dv->type != INT_TYPE) {
210		printf("%s\n", ERROR_MISSING_ARGS);
211		return;
212	}
213	uint32_t address = dv->intValue;
214	ARMDebuggerSetBreakpoint(&debugger->d, address);
215}
216
217static void _clearBreakpoint(struct CLIDebugger* debugger, struct DebugVector* dv) {
218	if (!dv || dv->type != INT_TYPE) {
219		printf("%s\n", ERROR_MISSING_ARGS);
220		return;
221	}
222	uint32_t address = dv->intValue;
223	ARMDebuggerClearBreakpoint(&debugger->d, address);
224}
225
226static void _setWatchpoint(struct CLIDebugger* debugger, struct DebugVector* dv) {
227	if (!dv || dv->type != INT_TYPE) {
228		printf("%s\n", ERROR_MISSING_ARGS);
229		return;
230	}
231	uint32_t address = dv->intValue;
232	ARMDebuggerSetWatchpoint(&debugger->d, address);
233}
234
235static void _breakIntoDefault(int signal) {
236	(void)(signal);
237	ARMDebuggerEnter(&_activeDebugger->d, DEBUGGER_ENTER_MANUAL);
238}
239
240enum _DVParseState {
241	PARSE_ERROR = -1,
242	PARSE_ROOT = 0,
243	PARSE_EXPECT_REGISTER,
244	PARSE_EXPECT_REGISTER_2,
245	PARSE_EXPECT_LR,
246	PARSE_EXPECT_PC,
247	PARSE_EXPECT_SP,
248	PARSE_EXPECT_DECIMAL,
249	PARSE_EXPECT_HEX,
250	PARSE_EXPECT_PREFIX,
251	PARSE_EXPECT_SUFFIX,
252};
253
254static struct DebugVector* _DVParse(struct CLIDebugger* debugger, const char* string, size_t length) {
255	if (!string || length < 1) {
256		return 0;
257	}
258
259	enum _DVParseState state = PARSE_ROOT;
260	struct DebugVector dvTemp = { .type = INT_TYPE };
261	uint32_t current = 0;
262
263	while (length > 0 && string[0] && string[0] != ' ' && state != PARSE_ERROR) {
264		char token = string[0];
265		++string;
266		--length;
267		switch (state) {
268		case PARSE_ROOT:
269			switch (token) {
270			case 'r':
271				state = PARSE_EXPECT_REGISTER;
272				break;
273			case 'p':
274				state = PARSE_EXPECT_PC;
275				break;
276			case 's':
277				state = PARSE_EXPECT_SP;
278				break;
279			case 'l':
280				state = PARSE_EXPECT_LR;
281				break;
282			case '1':
283			case '2':
284			case '3':
285			case '4':
286			case '5':
287			case '6':
288			case '7':
289			case '8':
290			case '9':
291				state = PARSE_EXPECT_DECIMAL;
292				current = token - '0';
293				break;
294			case '0':
295				state = PARSE_EXPECT_PREFIX;
296				break;
297			case '$':
298				state = PARSE_EXPECT_HEX;
299				current = 0;
300				break;
301			default:
302				state = PARSE_ERROR;
303				break;
304			};
305			break;
306		case PARSE_EXPECT_LR:
307			switch (token) {
308			case 'r':
309				current = debugger->d.cpu->gprs[ARM_LR];
310				state = PARSE_EXPECT_SUFFIX;
311				break;
312			default:
313				state = PARSE_ERROR;
314				break;
315			}
316			break;
317		case PARSE_EXPECT_PC:
318			switch (token) {
319			case 'c':
320				current = debugger->d.cpu->gprs[ARM_PC];
321				state = PARSE_EXPECT_SUFFIX;
322				break;
323			default:
324				state = PARSE_ERROR;
325				break;
326			}
327			break;
328		case PARSE_EXPECT_SP:
329			switch (token) {
330			case 'p':
331				current = debugger->d.cpu->gprs[ARM_SP];
332				state = PARSE_EXPECT_SUFFIX;
333				break;
334			default:
335				state = PARSE_ERROR;
336				break;
337			}
338			break;
339		case PARSE_EXPECT_REGISTER:
340			switch (token) {
341			case '0':
342			case '2':
343			case '3':
344			case '4':
345			case '5':
346			case '6':
347			case '7':
348			case '8':
349			case '9':
350				current = debugger->d.cpu->gprs[token - '0'];
351				state = PARSE_EXPECT_SUFFIX;
352				break;
353			case '1':
354				state = PARSE_EXPECT_REGISTER_2;
355				break;
356			default:
357				state = PARSE_ERROR;
358				break;
359			}
360			break;
361		case PARSE_EXPECT_REGISTER_2:
362			switch (token) {
363			case '0':
364			case '1':
365			case '2':
366			case '3':
367			case '4':
368			case '5':
369				current = debugger->d.cpu->gprs[token - '0' + 10];
370				state = PARSE_EXPECT_SUFFIX;
371				break;
372			default:
373				state = PARSE_ERROR;
374				break;
375			}
376			break;
377		case PARSE_EXPECT_DECIMAL:
378			switch (token) {
379			case '0':
380			case '1':
381			case '2':
382			case '3':
383			case '4':
384			case '5':
385			case '6':
386			case '7':
387			case '8':
388			case '9':
389				// TODO: handle overflow
390				current *= 10;
391				current += token - '0';
392				break;
393			default:
394				state = PARSE_ERROR;
395			}
396			break;
397		case PARSE_EXPECT_HEX:
398			switch (token) {
399			case '0':
400			case '1':
401			case '2':
402			case '3':
403			case '4':
404			case '5':
405			case '6':
406			case '7':
407			case '8':
408			case '9':
409				// TODO: handle overflow
410				current *= 16;
411				current += token - '0';
412				break;
413			case 'A':
414			case 'B':
415			case 'C':
416			case 'D':
417			case 'E':
418			case 'F':
419				// TODO: handle overflow
420				current *= 16;
421				current += token - 'A' + 10;
422				break;
423			case 'a':
424			case 'b':
425			case 'c':
426			case 'd':
427			case 'e':
428			case 'f':
429				// TODO: handle overflow
430				current *= 16;
431				current += token - 'a' + 10;
432				break;
433			default:
434				state = PARSE_ERROR;
435				break;
436			}
437			break;
438		case PARSE_EXPECT_PREFIX:
439			switch (token) {
440			case 'X':
441			case 'x':
442				current = 0;
443				state = PARSE_EXPECT_HEX;
444				break;
445			default:
446				state = PARSE_ERROR;
447				break;
448			}
449			break;
450		case PARSE_EXPECT_SUFFIX:
451			// TODO
452			state = PARSE_ERROR;
453			break;
454		case PARSE_ERROR:
455			// This shouldn't be reached
456			break;
457		}
458	}
459
460	struct DebugVector* dv = malloc(sizeof(struct DebugVector));
461	if (state == PARSE_ERROR) {
462		dv->type = ERROR_TYPE;
463		dv->next = 0;
464	} else {
465		dvTemp.intValue = current;
466		*dv = dvTemp;
467		if (string[0] == ' ') {
468			dv->next = _DVParse(debugger, string + 1, length - 1);
469		}
470	}
471	return dv;
472}
473
474static void _DVFree(struct DebugVector* dv) {
475	struct DebugVector* next;
476	while (dv) {
477		next = dv->next;
478		free(dv);
479		dv = next;
480	}
481}
482
483static int _parse(struct CLIDebugger* debugger, const char* line, size_t count) {
484	const char* firstSpace = strchr(line, ' ');
485	size_t cmdLength;
486	struct DebugVector* dv = 0;
487	if (firstSpace) {
488		cmdLength = firstSpace - line;
489		dv = _DVParse(debugger, firstSpace + 1, count - cmdLength - 1);
490		if (dv && dv->type == ERROR_TYPE) {
491			printf("Parse error\n");
492			_DVFree(dv);
493			return 0;
494		}
495	} else {
496		cmdLength = count;
497	}
498
499	int i;
500	const char* name;
501	for (i = 0; (name = _debuggerCommands[i].name); ++i) {
502		if (strlen(name) != cmdLength) {
503			continue;
504		}
505		if (strncasecmp(name, line, cmdLength) == 0) {
506			_debuggerCommands[i].command(debugger, dv);
507			_DVFree(dv);
508			return 1;
509		}
510	}
511	_DVFree(dv);
512	printf("Command not found\n");
513	return 0;
514}
515
516static char* _prompt(EditLine* el) {
517	(void)(el);
518	return "> ";
519}
520
521static void _commandLine(struct ARMDebugger* debugger) {
522	struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
523	const char* line;
524	_printStatus(cliDebugger, 0);
525	int count = 0;
526	HistEvent ev;
527	while (debugger->state == DEBUGGER_PAUSED) {
528		line = el_gets(cliDebugger->elstate, &count);
529		if (!line) {
530			debugger->state = DEBUGGER_EXITING;
531			return;
532		}
533		if (line[0] == '\n') {
534			if (history(cliDebugger->histate, &ev, H_FIRST) >= 0) {
535				_parse(cliDebugger, ev.str, strlen(ev.str) - 1);
536			}
537		} else {
538			if (_parse(cliDebugger, line, count - 1)) {
539				history(cliDebugger->histate, &ev, H_ENTER, line);
540			}
541		}
542	}
543}
544
545static void _reportEntry(struct ARMDebugger* debugger, enum DebuggerEntryReason reason) {
546	(void) (debugger);
547	switch (reason) {
548	case DEBUGGER_ENTER_MANUAL:
549	case DEBUGGER_ENTER_ATTACHED:
550		break;
551	case DEBUGGER_ENTER_BREAKPOINT:
552		printf("Hit breakpoint\n");
553		break;
554	case DEBUGGER_ENTER_WATCHPOINT:
555		printf("Hit watchpoint\n");
556		break;
557	case DEBUGGER_ENTER_ILLEGAL_OP:
558		printf("Hit illegal opcode\n");
559		break;
560	}
561}
562
563static unsigned char _tabComplete(EditLine* elstate, int ch) {
564	(void)(ch);
565	const LineInfo* li = el_line(elstate);
566	const char* commandPtr;
567	int cmd = 0, len = 0;
568	const char* name = 0;
569	for (commandPtr = li->buffer; commandPtr <= li->cursor; ++commandPtr, ++len) {
570		for (; (name = _debuggerCommands[cmd].name); ++cmd) {
571			int cmp = strncasecmp(name, li->buffer, len);
572			if (cmp > 0) {
573				return CC_ERROR;
574			}
575			if (cmp == 0) {
576				break;
577			}
578		}
579	}
580	if (_debuggerCommands[cmd + 1].name && strncasecmp(_debuggerCommands[cmd + 1].name, li->buffer, len - 1) == 0) {
581		return CC_ERROR;
582	}
583	name += len - 1;
584	el_insertstr(elstate, name);
585	el_insertstr(elstate, " ");
586	return CC_REDISPLAY;
587}
588
589static void _cliDebuggerInit(struct ARMDebugger* debugger) {
590	struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
591	// TODO: get argv[0]
592	cliDebugger->elstate = el_init("gbac", stdin, stdout, stderr);
593	el_set(cliDebugger->elstate, EL_PROMPT, _prompt);
594	el_set(cliDebugger->elstate, EL_EDITOR, "emacs");
595
596	el_set(cliDebugger->elstate, EL_CLIENTDATA, cliDebugger);
597	el_set(cliDebugger->elstate, EL_ADDFN, "tab-complete", "Tab completion", _tabComplete);
598	el_set(cliDebugger->elstate, EL_BIND, "\t", "tab-complete", 0);
599	cliDebugger->histate = history_init();
600	HistEvent ev;
601	history(cliDebugger->histate, &ev, H_SETSIZE, 200);
602	el_set(cliDebugger->elstate, EL_HIST, history, cliDebugger->histate);
603	_activeDebugger = cliDebugger;
604	signal(SIGINT, _breakIntoDefault);
605}
606
607static void _cliDebuggerDeinit(struct ARMDebugger* debugger) {
608	struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
609	history_end(cliDebugger->histate);
610	el_end(cliDebugger->elstate);
611}
612
613void CLIDebuggerCreate(struct CLIDebugger* debugger) {
614	debugger->d.init = _cliDebuggerInit;
615	debugger->d.deinit = _cliDebuggerDeinit;
616	debugger->d.paused = _commandLine;
617	debugger->d.entered = _reportEntry;
618}