all repos — mgba @ 4638e4a0170795799e9adc8cf97184d969ba417b

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 _next(struct CLIDebugger*, struct DebugVector*);
 34static void _print(struct CLIDebugger*, struct DebugVector*);
 35static void _printHex(struct CLIDebugger*, struct DebugVector*);
 36static void _printStatus(struct CLIDebugger*, struct DebugVector*);
 37static void _quit(struct CLIDebugger*, struct DebugVector*);
 38static void _readByte(struct CLIDebugger*, struct DebugVector*);
 39static void _readHalfword(struct CLIDebugger*, struct DebugVector*);
 40static void _readWord(struct CLIDebugger*, struct DebugVector*);
 41static void _setBreakpoint(struct CLIDebugger*, struct DebugVector*);
 42static void _clearBreakpoint(struct CLIDebugger*, struct DebugVector*);
 43static void _setWatchpoint(struct CLIDebugger*, struct DebugVector*);
 44
 45static void _breakIntoDefault(int signal);
 46static void _printLine(struct CLIDebugger* debugger, uint32_t address, enum ExecutionMode mode);
 47
 48static struct {
 49	const char* name;
 50	DebuggerCommand* command;
 51} _debuggerCommands[] = {
 52	{ "b", _setBreakpoint },
 53	{ "break", _setBreakpoint },
 54	{ "c", _continue },
 55	{ "continue", _continue },
 56	{ "d", _clearBreakpoint },
 57	{ "delete", _clearBreakpoint },
 58	{ "dis", _disassemble },
 59	{ "disasm", _disassemble },
 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	UNUSED(sig);
 93	printf("No debugger attached!\n");
 94}
 95
 96static void _breakInto(struct CLIDebugger* debugger, struct DebugVector* dv) {
 97	UNUSED(debugger);
 98	UNUSED(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	UNUSED(dv);
115	debugger->d.state = DEBUGGER_RUNNING;
116}
117
118static void _next(struct CLIDebugger* debugger, struct DebugVector* dv) {
119	UNUSED(dv);
120	ARMRun(debugger->d.cpu);
121	_printStatus(debugger, 0);
122}
123
124static void _disassemble(struct CLIDebugger* debugger, struct DebugVector* dv) {
125	uint32_t address;
126	int size;
127	int wordSize;
128	enum ExecutionMode mode = debugger->d.cpu->executionMode;
129
130	if (mode == MODE_ARM) {
131		wordSize = WORD_SIZE_ARM;
132	} else {
133		wordSize = WORD_SIZE_THUMB;
134	}
135
136	if (!dv || dv->type != DV_INT_TYPE) {
137		address = debugger->d.cpu->gprs[ARM_PC] - wordSize;
138	} else {
139		address = dv->intValue;
140		dv = dv->next;
141	}
142
143	if (!dv || dv->type != DV_INT_TYPE) {
144		size = 1;
145	} else {
146		size = dv->intValue;
147		dv = dv->next; // TODO: Check for excess args
148	}
149
150	int i;
151	for (i = 0; i < size; ++i) {
152		_printLine(debugger, address, mode);
153		address += wordSize;
154	}
155}
156
157static void _print(struct CLIDebugger* debugger, struct DebugVector* dv) {
158	UNUSED(debugger);
159	for ( ; dv; dv = dv->next) {
160		printf(" %u", dv->intValue);
161	}
162	printf("\n");
163}
164
165static void _printHex(struct CLIDebugger* debugger, struct DebugVector* dv) {
166	UNUSED(debugger);
167	for ( ; dv; dv = dv->next) {
168		printf(" 0x%08X", dv->intValue);
169	}
170	printf("\n");
171}
172
173static inline void _printLine(struct CLIDebugger* debugger, uint32_t address, enum ExecutionMode mode) {
174	char disassembly[48];
175	struct ARMInstructionInfo info;
176	if (mode == MODE_ARM) {
177		uint32_t instruction = debugger->d.cpu->memory.load32(debugger->d.cpu, address, 0);
178		ARMDecodeARM(instruction, &info);
179		ARMDisassemble(&info, address + WORD_SIZE_ARM * 2, disassembly, sizeof(disassembly));
180		printf("%08X: %s\n", instruction, disassembly);
181	} else {
182		uint16_t instruction = debugger->d.cpu->memory.loadU16(debugger->d.cpu, address, 0);
183		ARMDecodeThumb(instruction, &info);
184		ARMDisassemble(&info, address + WORD_SIZE_THUMB * 2, disassembly, sizeof(disassembly));
185		printf("%04X: %s\n", instruction, disassembly);
186	}
187}
188
189static void _printStatus(struct CLIDebugger* debugger, struct DebugVector* dv) {
190	UNUSED(dv);
191	int r;
192	for (r = 0; r < 4; ++r) {
193		printf("%08X %08X %08X %08X\n",
194			debugger->d.cpu->gprs[r << 2],
195			debugger->d.cpu->gprs[(r << 2) + 1],
196			debugger->d.cpu->gprs[(r << 2) + 2],
197			debugger->d.cpu->gprs[(r << 2) + 3]);
198	}
199	_printPSR(debugger->d.cpu->cpsr);
200	int instructionLength;
201	enum ExecutionMode mode = debugger->d.cpu->cpsr.t;
202	if (mode == MODE_ARM) {
203		instructionLength = WORD_SIZE_ARM;
204	} else {
205		instructionLength = WORD_SIZE_THUMB;
206	}
207	_printLine(debugger, debugger->d.cpu->gprs[ARM_PC] - instructionLength, mode);
208}
209
210static void _quit(struct CLIDebugger* debugger, struct DebugVector* dv) {
211	UNUSED(dv);
212	debugger->d.state = DEBUGGER_SHUTDOWN;
213}
214
215static void _readByte(struct CLIDebugger* debugger, struct DebugVector* dv) {
216	if (!dv || dv->type != DV_INT_TYPE) {
217		printf("%s\n", ERROR_MISSING_ARGS);
218		return;
219	}
220	uint32_t address = dv->intValue;
221	uint8_t value = debugger->d.cpu->memory.loadU8(debugger->d.cpu, address, 0);
222	printf(" 0x%02X\n", value);
223}
224
225static void _readHalfword(struct CLIDebugger* debugger, struct DebugVector* dv) {
226	if (!dv || dv->type != DV_INT_TYPE) {
227		printf("%s\n", ERROR_MISSING_ARGS);
228		return;
229	}
230	uint32_t address = dv->intValue;
231	uint16_t value = debugger->d.cpu->memory.loadU16(debugger->d.cpu, address, 0);
232	printf(" 0x%04X\n", value);
233}
234
235static void _readWord(struct CLIDebugger* debugger, struct DebugVector* dv) {
236	if (!dv || dv->type != DV_INT_TYPE) {
237		printf("%s\n", ERROR_MISSING_ARGS);
238		return;
239	}
240	uint32_t address = dv->intValue;
241	uint32_t value = debugger->d.cpu->memory.load32(debugger->d.cpu, address, 0);
242	printf(" 0x%08X\n", value);
243}
244
245static void _setBreakpoint(struct CLIDebugger* debugger, struct DebugVector* dv) {
246	if (!dv || dv->type != DV_INT_TYPE) {
247		printf("%s\n", ERROR_MISSING_ARGS);
248		return;
249	}
250	uint32_t address = dv->intValue;
251	ARMDebuggerSetBreakpoint(&debugger->d, address);
252}
253
254static void _clearBreakpoint(struct CLIDebugger* debugger, struct DebugVector* dv) {
255	if (!dv || dv->type != DV_INT_TYPE) {
256		printf("%s\n", ERROR_MISSING_ARGS);
257		return;
258	}
259	uint32_t address = dv->intValue;
260	ARMDebuggerClearBreakpoint(&debugger->d, address);
261}
262
263static void _setWatchpoint(struct CLIDebugger* debugger, struct DebugVector* dv) {
264	if (!dv || dv->type != DV_INT_TYPE) {
265		printf("%s\n", ERROR_MISSING_ARGS);
266		return;
267	}
268	uint32_t address = dv->intValue;
269	ARMDebuggerSetWatchpoint(&debugger->d, address);
270}
271
272static void _breakIntoDefault(int signal) {
273	UNUSED(signal);
274	ARMDebuggerEnter(&_activeDebugger->d, DEBUGGER_ENTER_MANUAL);
275}
276
277static uint32_t _performOperation(enum Operation operation, uint32_t current, uint32_t next, struct DebugVector* dv) {
278	switch (operation) {
279	case OP_ASSIGN:
280		current = next;
281		break;
282	case OP_ADD:
283		current += next;
284		break;
285	case OP_SUBTRACT:
286		current -= next;
287		break;
288	case OP_MULTIPLY:
289		current *= next;
290		break;
291	case OP_DIVIDE:
292		if (next != 0) {
293			current /= next;
294		} else {
295			dv->type = DV_ERROR_TYPE;
296			return 0;
297		}
298		break;
299	}
300	return current;
301}
302
303static uint32_t _lookupIdentifier(struct ARMDebugger* debugger, const char* name, struct DebugVector* dv) {
304	if (strcmp(name, "sp") == 0) {
305		return debugger->cpu->gprs[ARM_SP];
306	}
307	if (strcmp(name, "lr") == 0) {
308		return debugger->cpu->gprs[ARM_LR];
309	}
310	if (strcmp(name, "pc") == 0) {
311		return debugger->cpu->gprs[ARM_PC];
312	}
313	if (strcmp(name, "cpsr") == 0) {
314		return debugger->cpu->cpsr.packed;
315	}
316	// TODO: test if mode has SPSR
317	if (strcmp(name, "spsr") == 0) {
318		return debugger->cpu->spsr.packed;
319	}
320	if (name[0] == 'r' && name[1] >= '0' && name[1] <= '9') {
321		int reg = atoi(&name[1]);
322		if (reg < 16) {
323			return debugger->cpu->gprs[reg];
324		}
325	}
326	dv->type = DV_ERROR_TYPE;
327	return 0;
328}
329
330static uint32_t _evaluateParseTree(struct ARMDebugger* debugger, struct ParseTree* tree, struct DebugVector* dv) {
331	switch (tree->token.type) {
332	case TOKEN_UINT_TYPE:
333		return tree->token.uintValue;
334	case TOKEN_OPERATOR_TYPE:
335		return _performOperation(tree->token.operatorValue, _evaluateParseTree(debugger, tree->lhs, dv), _evaluateParseTree(debugger, tree->rhs, dv), dv);
336	case TOKEN_IDENTIFIER_TYPE:
337		return _lookupIdentifier(debugger, tree->token.identifierValue, dv);
338	case TOKEN_ERROR_TYPE:
339	default:
340		dv->type = DV_ERROR_TYPE;
341	}
342	return 0;
343}
344
345static struct DebugVector* _DVParse(struct CLIDebugger* debugger, const char* string, size_t length) {
346	if (!string || length < 1) {
347		return 0;
348	}
349
350	struct DebugVector dvTemp = { .type = DV_INT_TYPE };
351
352	struct LexVector lv = { .next = 0 };
353	size_t adjusted = lexExpression(&lv, string, length);
354	if (adjusted > length) {
355		dvTemp.type = DV_ERROR_TYPE;
356		lexFree(lv.next);
357	}
358
359	struct ParseTree tree;
360	parseLexedExpression(&tree, &lv);
361	if (tree.token.type == TOKEN_ERROR_TYPE) {
362		dvTemp.type = DV_ERROR_TYPE;
363	} else {
364		dvTemp.intValue = _evaluateParseTree(&debugger->d, &tree, &dvTemp);
365	}
366
367	parseFree(tree.lhs);
368	parseFree(tree.rhs);
369
370	length -= adjusted;
371	string += adjusted;
372
373	struct DebugVector* dv = malloc(sizeof(struct DebugVector));
374	if (dvTemp.type == DV_ERROR_TYPE) {
375		dv->type = DV_ERROR_TYPE;
376		dv->next = 0;
377	} else {
378		*dv = dvTemp;
379		if (string[0] == ' ') {
380			dv->next = _DVParse(debugger, string + 1, length - 1);
381			if (dv->next && dv->next->type == DV_ERROR_TYPE) {
382				dv->type = DV_ERROR_TYPE;
383			}
384		}
385	}
386	return dv;
387}
388
389static void _DVFree(struct DebugVector* dv) {
390	struct DebugVector* next;
391	while (dv) {
392		next = dv->next;
393		free(dv);
394		dv = next;
395	}
396}
397
398static int _parse(struct CLIDebugger* debugger, const char* line, size_t count) {
399	const char* firstSpace = strchr(line, ' ');
400	size_t cmdLength;
401	struct DebugVector* dv = 0;
402	if (firstSpace) {
403		cmdLength = firstSpace - line;
404		dv = _DVParse(debugger, firstSpace + 1, count - cmdLength - 1);
405		if (dv && dv->type == DV_ERROR_TYPE) {
406			printf("Parse error\n");
407			_DVFree(dv);
408			return 0;
409		}
410	} else {
411		cmdLength = count;
412	}
413
414	int i;
415	const char* name;
416	for (i = 0; (name = _debuggerCommands[i].name); ++i) {
417		if (strlen(name) != cmdLength) {
418			continue;
419		}
420		if (strncasecmp(name, line, cmdLength) == 0) {
421			_debuggerCommands[i].command(debugger, dv);
422			_DVFree(dv);
423			return 1;
424		}
425	}
426	_DVFree(dv);
427	printf("Command not found\n");
428	return 0;
429}
430
431static char* _prompt(EditLine* el) {
432	UNUSED(el);
433	return "> ";
434}
435
436static void _commandLine(struct ARMDebugger* debugger) {
437	struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
438	const char* line;
439	_printStatus(cliDebugger, 0);
440	int count = 0;
441	HistEvent ev;
442	while (debugger->state == DEBUGGER_PAUSED) {
443		line = el_gets(cliDebugger->elstate, &count);
444		if (!line) {
445			debugger->state = DEBUGGER_EXITING;
446			return;
447		}
448		if (line[0] == '\n') {
449			if (history(cliDebugger->histate, &ev, H_FIRST) >= 0) {
450				_parse(cliDebugger, ev.str, strlen(ev.str) - 1);
451			}
452		} else {
453			if (_parse(cliDebugger, line, count - 1)) {
454				history(cliDebugger->histate, &ev, H_ENTER, line);
455			}
456		}
457	}
458}
459
460static void _reportEntry(struct ARMDebugger* debugger, enum DebuggerEntryReason reason) {
461	UNUSED(debugger);
462	switch (reason) {
463	case DEBUGGER_ENTER_MANUAL:
464	case DEBUGGER_ENTER_ATTACHED:
465		break;
466	case DEBUGGER_ENTER_BREAKPOINT:
467		printf("Hit breakpoint\n");
468		break;
469	case DEBUGGER_ENTER_WATCHPOINT:
470		printf("Hit watchpoint\n");
471		break;
472	case DEBUGGER_ENTER_ILLEGAL_OP:
473		printf("Hit illegal opcode\n");
474		break;
475	}
476}
477
478static unsigned char _tabComplete(EditLine* elstate, int ch) {
479	UNUSED(ch);
480	const LineInfo* li = el_line(elstate);
481	const char* commandPtr;
482	int cmd = 0, len = 0;
483	const char* name = 0;
484	for (commandPtr = li->buffer; commandPtr <= li->cursor; ++commandPtr, ++len) {
485		for (; (name = _debuggerCommands[cmd].name); ++cmd) {
486			int cmp = strncasecmp(name, li->buffer, len);
487			if (cmp > 0) {
488				return CC_ERROR;
489			}
490			if (cmp == 0) {
491				break;
492			}
493		}
494	}
495	if (_debuggerCommands[cmd + 1].name && strncasecmp(_debuggerCommands[cmd + 1].name, li->buffer, len - 1) == 0) {
496		return CC_ERROR;
497	}
498	name += len - 1;
499	el_insertstr(elstate, name);
500	el_insertstr(elstate, " ");
501	return CC_REDISPLAY;
502}
503
504static void _cliDebuggerInit(struct ARMDebugger* debugger) {
505	struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
506	// TODO: get argv[0]
507	cliDebugger->elstate = el_init(BINARY_NAME, stdin, stdout, stderr);
508	el_set(cliDebugger->elstate, EL_PROMPT, _prompt);
509	el_set(cliDebugger->elstate, EL_EDITOR, "emacs");
510
511	el_set(cliDebugger->elstate, EL_CLIENTDATA, cliDebugger);
512	el_set(cliDebugger->elstate, EL_ADDFN, "tab-complete", "Tab completion", _tabComplete);
513	el_set(cliDebugger->elstate, EL_BIND, "\t", "tab-complete", 0);
514	cliDebugger->histate = history_init();
515	HistEvent ev;
516	history(cliDebugger->histate, &ev, H_SETSIZE, 200);
517	el_set(cliDebugger->elstate, EL_HIST, history, cliDebugger->histate);
518	_activeDebugger = cliDebugger;
519	signal(SIGINT, _breakIntoDefault);
520}
521
522static void _cliDebuggerDeinit(struct ARMDebugger* debugger) {
523	struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
524	history_end(cliDebugger->histate);
525	el_end(cliDebugger->elstate);
526}
527
528void CLIDebuggerCreate(struct CLIDebugger* debugger) {
529	ARMDebuggerCreate(&debugger->d);
530	debugger->d.init = _cliDebuggerInit;
531	debugger->d.deinit = _cliDebuggerDeinit;
532	debugger->d.paused = _commandLine;
533	debugger->d.entered = _reportEntry;
534}