all repos — mgba @ aefa5f0ab8511dd2f72d3ded479327268072e536

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