all repos — mgba @ 9435226c58e998555e3908251b2fae32db783bd5

mGBA Game Boy Advance Emulator

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

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