all repos — mgba @ 232ae47a6c9356ce8bec916bf284997bc42b331e

mGBA Game Boy Advance Emulator

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

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