src/debugger/cli-debugger.c (view raw)
1/* Copyright (c) 2013-2014 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include <mgba/internal/debugger/cli-debugger.h>
7
8#include <mgba/internal/debugger/symbols.h>
9
10#include <mgba/core/core.h>
11#include <mgba/core/version.h>
12#include <mgba/internal/debugger/parser.h>
13#include <mgba-util/string.h>
14
15#if ENABLE_SCRIPTING
16#include <mgba/core/scripting.h>
17#endif
18
19#if !defined(NDEBUG) && !defined(_WIN32)
20#include <signal.h>
21#endif
22
23#ifdef USE_PTHREADS
24#include <pthread.h>
25#endif
26
27const char* ERROR_MISSING_ARGS = "Arguments missing"; // TODO: share
28const char* ERROR_OVERFLOW = "Arguments overflow";
29const char* ERROR_INVALID_ARGS = "Invalid arguments";
30
31#if !defined(NDEBUG) && !defined(_WIN32)
32static void _breakInto(struct CLIDebugger*, struct CLIDebugVector*);
33#endif
34static void _continue(struct CLIDebugger*, struct CLIDebugVector*);
35static void _disassemble(struct CLIDebugger*, struct CLIDebugVector*);
36static void _next(struct CLIDebugger*, struct CLIDebugVector*);
37static void _print(struct CLIDebugger*, struct CLIDebugVector*);
38static void _printBin(struct CLIDebugger*, struct CLIDebugVector*);
39static void _printHex(struct CLIDebugger*, struct CLIDebugVector*);
40static void _printStatus(struct CLIDebugger*, struct CLIDebugVector*);
41static void _printHelp(struct CLIDebugger*, struct CLIDebugVector*);
42static void _quit(struct CLIDebugger*, struct CLIDebugVector*);
43static void _readByte(struct CLIDebugger*, struct CLIDebugVector*);
44static void _reset(struct CLIDebugger*, struct CLIDebugVector*);
45static void _readHalfword(struct CLIDebugger*, struct CLIDebugVector*);
46static void _readWord(struct CLIDebugger*, struct CLIDebugVector*);
47static void _setBreakpoint(struct CLIDebugger*, struct CLIDebugVector*);
48static void _clearBreakpoint(struct CLIDebugger*, struct CLIDebugVector*);
49static void _listBreakpoints(struct CLIDebugger*, struct CLIDebugVector*);
50static void _setReadWriteWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
51static void _setReadWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
52static void _setWriteWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
53static void _setWriteChangedWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
54static void _listWatchpoints(struct CLIDebugger*, struct CLIDebugVector*);
55static void _trace(struct CLIDebugger*, struct CLIDebugVector*);
56static void _writeByte(struct CLIDebugger*, struct CLIDebugVector*);
57static void _writeHalfword(struct CLIDebugger*, struct CLIDebugVector*);
58static void _writeRegister(struct CLIDebugger*, struct CLIDebugVector*);
59static void _writeWord(struct CLIDebugger*, struct CLIDebugVector*);
60static void _dumpByte(struct CLIDebugger*, struct CLIDebugVector*);
61static void _dumpHalfword(struct CLIDebugger*, struct CLIDebugVector*);
62static void _dumpWord(struct CLIDebugger*, struct CLIDebugVector*);
63#ifdef ENABLE_SCRIPTING
64static void _source(struct CLIDebugger*, struct CLIDebugVector*);
65#endif
66
67static struct CLIDebuggerCommandSummary _debuggerCommands[] = {
68 { "b", _setBreakpoint, "Is", "Set a breakpoint" },
69 { "break", _setBreakpoint, "Is", "Set a breakpoint" },
70 { "c", _continue, "", "Continue execution" },
71 { "continue", _continue, "", "Continue execution" },
72 { "d", _clearBreakpoint, "I", "Delete a breakpoint or watchpoint" },
73 { "delete", _clearBreakpoint, "I", "Delete a breakpoint or watchpoint" },
74 { "dis", _disassemble, "Ii", "Disassemble instructions" },
75 { "disasm", _disassemble, "Ii", "Disassemble instructions" },
76 { "disassemble", _disassemble, "Ii", "Disassemble instructions" },
77 { "h", _printHelp, "S", "Print help" },
78 { "help", _printHelp, "S", "Print help" },
79 { "i", _printStatus, "", "Print the current status" },
80 { "info", _printStatus, "", "Print the current status" },
81 { "lb", _listBreakpoints, "", "List breakpoints" },
82 { "listb", _listBreakpoints, "", "List breakpoints" },
83 { "lw", _listWatchpoints, "", "List watchpoints" },
84 { "listw", _listWatchpoints, "", "List watchpoints" },
85 { "n", _next, "", "Execute next instruction" },
86 { "next", _next, "", "Execute next instruction" },
87 { "p", _print, "I", "Print a value" },
88 { "p/t", _printBin, "I", "Print a value as binary" },
89 { "p/x", _printHex, "I", "Print a value as hexadecimal" },
90 { "print", _print, "I", "Print a value" },
91 { "print/t", _printBin, "I", "Print a value as binary" },
92 { "print/x", _printHex, "I", "Print a value as hexadecimal" },
93 { "q", _quit, "", "Quit the emulator" },
94 { "quit", _quit, "", "Quit the emulator" },
95 { "reset", _reset, "", "Reset the emulation" },
96 { "r/1", _readByte, "I", "Read a byte from a specified offset" },
97 { "r/2", _readHalfword, "I", "Read a halfword from a specified offset" },
98 { "r/4", _readWord, "I", "Read a word from a specified offset" },
99 { "status", _printStatus, "", "Print the current status" },
100 { "trace", _trace, "I", "Trace a fixed number of instructions" },
101 { "w", _setReadWriteWatchpoint, "Is", "Set a watchpoint" },
102 { "w/1", _writeByte, "II", "Write a byte at a specified offset" },
103 { "w/2", _writeHalfword, "II", "Write a halfword at a specified offset" },
104 { "w/r", _writeRegister, "SI", "Write a register" },
105 { "w/4", _writeWord, "II", "Write a word at a specified offset" },
106 { "watch", _setReadWriteWatchpoint, "Is", "Set a watchpoint" },
107 { "watch/c", _setWriteChangedWatchpoint, "Is", "Set a change watchpoint" },
108 { "watch/r", _setReadWatchpoint, "Is", "Set a read watchpoint" },
109 { "watch/w", _setWriteWatchpoint, "Is", "Set a write watchpoint" },
110 { "x/1", _dumpByte, "Ii", "Examine bytes at a specified offset" },
111 { "x/2", _dumpHalfword, "Ii", "Examine halfwords at a specified offset" },
112 { "x/4", _dumpWord, "Ii", "Examine words at a specified offset" },
113#ifdef ENABLE_SCRIPTING
114 { "source", _source, "S", "Load a script" },
115#endif
116#if !defined(NDEBUG) && !defined(_WIN32)
117 { "!", _breakInto, "", "Break into attached debugger (for developers)" },
118#endif
119 { 0, 0, 0, 0 }
120};
121
122#if !defined(NDEBUG) && !defined(_WIN32)
123static void _handleDeath(int sig) {
124 UNUSED(sig);
125 printf("No debugger attached!\n");
126}
127
128static void _breakInto(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
129 UNUSED(debugger);
130 UNUSED(dv);
131 struct sigaction sa, osa;
132 sa.sa_handler = _handleDeath;
133 sigemptyset(&sa.sa_mask);
134 sigaddset(&sa.sa_mask, SIGTRAP);
135 sa.sa_flags = SA_RESTART;
136 sigaction(SIGTRAP, &sa, &osa);
137#ifdef USE_PTHREADS
138 pthread_kill(pthread_self(), SIGTRAP);
139#else
140 kill(getpid(), SIGTRAP);
141#endif
142 sigaction(SIGTRAP, &osa, 0);
143}
144#endif
145
146static void _continue(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
147 UNUSED(dv);
148 debugger->d.state = DEBUGGER_RUNNING;
149}
150
151static void _next(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
152 UNUSED(dv);
153 debugger->d.core->step(debugger->d.core);
154 _printStatus(debugger, 0);
155}
156
157static void _disassemble(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
158 debugger->system->disassemble(debugger->system, dv);
159}
160
161static void _print(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
162 for (; dv; dv = dv->next) {
163 if (dv->segmentValue >= 0) {
164 debugger->backend->printf(debugger->backend, " $%02X:%04X", dv->segmentValue, dv->intValue);
165 continue;
166 }
167 debugger->backend->printf(debugger->backend, " %u", dv->intValue);
168 }
169 debugger->backend->printf(debugger->backend, "\n");
170}
171
172static void _printBin(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
173 for (; dv; dv = dv->next) {
174 debugger->backend->printf(debugger->backend, " 0b");
175 int i = 32;
176 while (i--) {
177 debugger->backend->printf(debugger->backend, "%u", (dv->intValue >> i) & 1);
178 }
179 }
180 debugger->backend->printf(debugger->backend, "\n");
181}
182
183static void _printHex(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
184 for (; dv; dv = dv->next) {
185 debugger->backend->printf(debugger->backend, " 0x%08X", dv->intValue);
186 }
187 debugger->backend->printf(debugger->backend, "\n");
188}
189
190static void _printHelp(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
191 UNUSED(dv);
192 if (!dv) {
193 debugger->backend->printf(debugger->backend, "Generic commands:\n");
194 int i;
195 for (i = 0; _debuggerCommands[i].name; ++i) {
196 debugger->backend->printf(debugger->backend, "%-10s %s\n", _debuggerCommands[i].name, _debuggerCommands[i].summary);
197 }
198 if (debugger->system) {
199 debugger->backend->printf(debugger->backend, "%s commands:\n", debugger->system->platformName);
200 for (i = 0; debugger->system->platformCommands[i].name; ++i) {
201 debugger->backend->printf(debugger->backend, "%-10s %s\n", debugger->system->platformCommands[i].name, debugger->system->platformCommands[i].summary);
202 }
203 debugger->backend->printf(debugger->backend, "%s commands:\n", debugger->system->name);
204 for (i = 0; debugger->system->commands[i].name; ++i) {
205 debugger->backend->printf(debugger->backend, "%-10s %s\n", debugger->system->commands[i].name, debugger->system->commands[i].summary);
206 }
207 }
208 } else {
209 int i;
210 for (i = 0; _debuggerCommands[i].name; ++i) {
211 if (strcmp(_debuggerCommands[i].name, dv->charValue) == 0) {
212 debugger->backend->printf(debugger->backend, " %s\n", _debuggerCommands[i].summary);
213 }
214 }
215 if (debugger->system) {
216 for (i = 0; debugger->system->platformCommands[i].name; ++i) {
217 if (strcmp(debugger->system->platformCommands[i].name, dv->charValue) == 0) {
218 debugger->backend->printf(debugger->backend, " %s\n", debugger->system->platformCommands[i].summary);
219 }
220 }
221 for (i = 0; debugger->system->commands[i].name; ++i) {
222 if (strcmp(debugger->system->commands[i].name, dv->charValue) == 0) {
223 debugger->backend->printf(debugger->backend, " %s\n", debugger->system->commands[i].summary);
224 }
225 }
226 }
227 }
228}
229
230static void _quit(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
231 UNUSED(dv);
232 debugger->d.state = DEBUGGER_SHUTDOWN;
233}
234
235static void _readByte(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
236 if (!dv || dv->type != CLIDV_INT_TYPE) {
237 debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
238 return;
239 }
240 uint32_t address = dv->intValue;
241 uint8_t value;
242 if (dv->segmentValue >= 0) {
243 value = debugger->d.core->rawRead8(debugger->d.core, address, dv->segmentValue);
244 } else {
245 value = debugger->d.core->busRead8(debugger->d.core, address);
246 }
247 debugger->backend->printf(debugger->backend, " 0x%02X\n", value);
248}
249
250static void _reset(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
251 UNUSED(dv);
252 debugger->d.core->reset(debugger->d.core);
253 _printStatus(debugger, 0);
254}
255
256static void _readHalfword(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
257 if (!dv || dv->type != CLIDV_INT_TYPE) {
258 debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
259 return;
260 }
261 uint32_t address = dv->intValue;
262 uint16_t value;
263 if (dv->segmentValue >= 0) {
264 value = debugger->d.core->rawRead16(debugger->d.core, address & -1, dv->segmentValue);
265 } else {
266 value = debugger->d.core->busRead16(debugger->d.core, address & ~1);
267 }
268 debugger->backend->printf(debugger->backend, " 0x%04X\n", value);
269}
270
271static void _readWord(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
272 if (!dv || dv->type != CLIDV_INT_TYPE) {
273 debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
274 return;
275 }
276 uint32_t address = dv->intValue;
277 uint32_t value;
278 if (dv->segmentValue >= 0) {
279 value = debugger->d.core->rawRead32(debugger->d.core, address & -3, dv->segmentValue);
280 } else {
281 value = debugger->d.core->busRead32(debugger->d.core, address & ~3);
282 }
283 debugger->backend->printf(debugger->backend, " 0x%08X\n", value);
284}
285
286static void _writeByte(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
287 if (!dv || !dv->next) {
288 debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
289 return;
290 }
291 if (dv->type != CLIDV_INT_TYPE || dv->next->type != CLIDV_INT_TYPE) {
292 debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS);
293 return;
294 }
295 uint32_t address = dv->intValue;
296 uint32_t value = dv->next->intValue;
297 if (value > 0xFF) {
298 debugger->backend->printf(debugger->backend, "%s\n", ERROR_OVERFLOW);
299 return;
300 }
301 if (dv->segmentValue >= 0) {
302 debugger->d.core->rawWrite8(debugger->d.core, address, value, dv->segmentValue);
303 } else {
304 debugger->d.core->busWrite8(debugger->d.core, address, value);
305 }
306}
307
308static void _writeHalfword(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
309 if (!dv || !dv->next) {
310 debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
311 return;
312 }
313 if (dv->type != CLIDV_INT_TYPE || dv->next->type != CLIDV_INT_TYPE) {
314 debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS);
315 return;
316 }
317 uint32_t address = dv->intValue;
318 uint32_t value = dv->next->intValue;
319 if (value > 0xFFFF) {
320 debugger->backend->printf(debugger->backend, "%s\n", ERROR_OVERFLOW);
321 return;
322 }
323 if (dv->segmentValue >= 0) {
324 debugger->d.core->rawWrite16(debugger->d.core, address, value, dv->segmentValue);
325 } else {
326 debugger->d.core->busWrite16(debugger->d.core, address, value);
327 }
328}
329
330static void _writeRegister(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
331 if (!dv || !dv->next) {
332 debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
333 return;
334 }
335 if (dv->type != CLIDV_CHAR_TYPE || dv->next->type != CLIDV_INT_TYPE) {
336 debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS);
337 return;
338 }
339 if (!debugger->d.platform->setRegister(debugger->d.platform, dv->charValue, dv->next->intValue)) {
340 debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS);
341 }
342}
343
344static void _writeWord(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
345 if (!dv || !dv->next) {
346 debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
347 return;
348 }
349 if (dv->type != CLIDV_INT_TYPE || dv->next->type != CLIDV_INT_TYPE) {
350 debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS);
351 return;
352 }
353 uint32_t address = dv->intValue;
354 uint32_t value = dv->next->intValue;
355 if (dv->segmentValue >= 0) {
356 debugger->d.core->rawWrite32(debugger->d.core, address, value, dv->segmentValue);
357 } else {
358 debugger->d.core->busWrite32(debugger->d.core, address, value);
359 }
360}
361
362static void _dumpByte(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
363 if (!dv || dv->type != CLIDV_INT_TYPE) {
364 debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
365 return;
366 }
367 uint32_t address = dv->intValue;
368 uint32_t words = 16;
369 if (dv->next && dv->next->type == CLIDV_INT_TYPE) {
370 words = dv->next->intValue;
371 }
372 while (words) {
373 uint32_t line = 16;
374 if (line > words) {
375 line = words;
376 }
377 debugger->backend->printf(debugger->backend, "0x%08X:", address);
378 for (; line > 0; --line, ++address, --words) {
379 uint32_t value;
380 if (dv->segmentValue >= 0) {
381 value = debugger->d.core->rawRead8(debugger->d.core, address, dv->segmentValue);
382 } else {
383 value = debugger->d.core->busRead8(debugger->d.core, address);
384 }
385 debugger->backend->printf(debugger->backend, " %02X", value);
386 }
387 debugger->backend->printf(debugger->backend, "\n");
388 }
389}
390
391static void _dumpHalfword(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
392 if (!dv || dv->type != CLIDV_INT_TYPE) {
393 debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
394 return;
395 }
396 uint32_t address = dv->intValue;
397 uint32_t words = 8;
398 if (dv->next && dv->next->type == CLIDV_INT_TYPE) {
399 words = dv->next->intValue;
400 }
401 while (words) {
402 uint32_t line = 8;
403 if (line > words) {
404 line = words;
405 }
406 debugger->backend->printf(debugger->backend, "0x%08X:", address);
407 for (; line > 0; --line, address += 2, --words) {
408 uint32_t value;
409 if (dv->segmentValue >= 0) {
410 value = debugger->d.core->rawRead16(debugger->d.core, address, dv->segmentValue);
411 } else {
412 value = debugger->d.core->busRead16(debugger->d.core, address);
413 }
414 debugger->backend->printf(debugger->backend, " %04X", value);
415 }
416 debugger->backend->printf(debugger->backend, "\n");
417 }
418}
419
420static void _dumpWord(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
421 if (!dv || dv->type != CLIDV_INT_TYPE) {
422 debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
423 return;
424 }
425 uint32_t address = dv->intValue;
426 uint32_t words = 4;
427 if (dv->next && dv->next->type == CLIDV_INT_TYPE) {
428 words = dv->next->intValue;
429 }
430 while (words) {
431 uint32_t line = 4;
432 if (line > words) {
433 line = words;
434 }
435 debugger->backend->printf(debugger->backend, "0x%08X:", address);
436 for (; line > 0; --line, address += 4, --words) {
437 uint32_t value;
438 if (dv->segmentValue >= 0) {
439 value = debugger->d.core->rawRead32(debugger->d.core, address, dv->segmentValue);
440 } else {
441 value = debugger->d.core->busRead32(debugger->d.core, address);
442 }
443 debugger->backend->printf(debugger->backend, " %08X", value);
444 }
445 debugger->backend->printf(debugger->backend, "\n");
446 }
447}
448
449#ifdef ENABLE_SCRIPTING
450static void _source(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
451 if (!dv) {
452 debugger->backend->printf(debugger->backend, "Needs a filename\n");
453 return;
454 }
455 if (debugger->d.bridge && mScriptBridgeLoadScript(debugger->d.bridge, dv->charValue)) {
456 mScriptBridgeRun(debugger->d.bridge);
457 } else {
458 debugger->backend->printf(debugger->backend, "Failed to load script\n");
459 }
460}
461#endif
462
463static struct ParseTree* _parseTree(const char* string) {
464 struct LexVector lv;
465 bool error = false;
466 LexVectorInit(&lv, 0);
467 size_t length = strlen(string);
468 size_t adjusted = lexExpression(&lv, string, length, NULL);
469 struct ParseTree* tree = malloc(sizeof(*tree));
470 if (!adjusted) {
471 error = true;
472 } else {
473 parseLexedExpression(tree, &lv);
474
475 if (adjusted > length) {
476 error = true;
477 } else {
478 length -= adjusted;
479 string += adjusted;
480 }
481 }
482 lexFree(&lv);
483 LexVectorClear(&lv);
484 LexVectorDeinit(&lv);
485 if (error) {
486 parseFree(tree);
487 free(tree);
488 return NULL;
489 } else {
490 return tree;
491 }
492}
493
494static void _setBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
495 if (!dv || dv->type != CLIDV_INT_TYPE) {
496 debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
497 return;
498 }
499 struct mBreakpoint breakpoint = {
500 .address = dv->intValue,
501 .segment = dv->segmentValue,
502 .type = BREAKPOINT_HARDWARE
503 };
504 if (dv->next && dv->next->type == CLIDV_CHAR_TYPE) {
505 struct ParseTree* tree = _parseTree(dv->next->charValue);
506 if (tree) {
507 breakpoint.condition = tree;
508 } else {
509 debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS);
510 return;
511 }
512 }
513 debugger->d.platform->setBreakpoint(debugger->d.platform, &breakpoint);
514}
515
516static void _setWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv, enum mWatchpointType type) {
517 if (!dv || dv->type != CLIDV_INT_TYPE) {
518 debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
519 return;
520 }
521 if (!debugger->d.platform->setWatchpoint) {
522 debugger->backend->printf(debugger->backend, "Watchpoints are not supported by this platform.\n");
523 return;
524 }
525 struct mWatchpoint watchpoint = {
526 .address = dv->intValue,
527 .segment = dv->segmentValue,
528 .type = type
529 };
530 if (dv->next && dv->next->type == CLIDV_CHAR_TYPE) {
531 struct ParseTree* tree = _parseTree(dv->next->charValue);
532 if (tree) {
533 watchpoint.condition = tree;
534 } else {
535 debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS);
536 return;
537 }
538 }
539 debugger->d.platform->setWatchpoint(debugger->d.platform, &watchpoint);
540}
541
542static void _setReadWriteWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
543 _setWatchpoint(debugger, dv, WATCHPOINT_RW);
544}
545
546static void _setReadWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
547 _setWatchpoint(debugger, dv, WATCHPOINT_READ);
548}
549
550static void _setWriteWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
551 _setWatchpoint(debugger, dv, WATCHPOINT_WRITE);
552}
553
554static void _setWriteChangedWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
555 _setWatchpoint(debugger, dv, WATCHPOINT_WRITE_CHANGE);
556}
557
558static void _clearBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
559 if (!dv || dv->type != CLIDV_INT_TYPE) {
560 debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
561 return;
562 }
563 uint64_t id = dv->intValue;
564 debugger->d.platform->clearBreakpoint(debugger->d.platform, id);
565}
566
567static void _listBreakpoints(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
568 UNUSED(dv);
569 struct mBreakpointList breakpoints;
570 mBreakpointListInit(&breakpoints, 0);
571 debugger->d.platform->listBreakpoints(debugger->d.platform, &breakpoints);
572 size_t i;
573 for (i = 0; i < mBreakpointListSize(&breakpoints); ++i) {
574 struct mBreakpoint* breakpoint = mBreakpointListGetPointer(&breakpoints, i);
575 if (breakpoint->segment >= 0) {
576 debugger->backend->printf(debugger->backend, "%" PRIz "i: %02X:%X\n", breakpoint->id, breakpoint->segment, breakpoint->address);
577 } else {
578 debugger->backend->printf(debugger->backend, "%" PRIz "i: 0x%X\n", breakpoint->id, breakpoint->address);
579 }
580 }
581 mBreakpointListDeinit(&breakpoints);
582}
583
584static void _listWatchpoints(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
585 UNUSED(dv);
586 struct mWatchpointList watchpoints;
587 mWatchpointListInit(&watchpoints, 0);
588 debugger->d.platform->listWatchpoints(debugger->d.platform, &watchpoints);
589 size_t i;
590 for (i = 0; i < mWatchpointListSize(&watchpoints); ++i) {
591 struct mWatchpoint* watchpoint = mWatchpointListGetPointer(&watchpoints, i);
592 if (watchpoint->segment >= 0) {
593 debugger->backend->printf(debugger->backend, "%" PRIz "i: %02X:%X\n", watchpoint->id, watchpoint->segment, watchpoint->address);
594 } else {
595 debugger->backend->printf(debugger->backend, "%" PRIz "i: 0x%X\n", watchpoint->id, watchpoint->address);
596 }
597 }
598 mWatchpointListDeinit(&watchpoints);
599}
600
601static void _trace(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
602 if (!dv || dv->type != CLIDV_INT_TYPE) {
603 debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
604 return;
605 }
606
607 char trace[1024];
608 trace[sizeof(trace) - 1] = '\0';
609
610 int i;
611 for (i = 0; i < dv->intValue; ++i) {
612 debugger->d.core->step(debugger->d.core);
613 size_t traceSize = sizeof(trace) - 1;
614 debugger->d.platform->trace(debugger->d.platform, trace, &traceSize);
615 if (traceSize + 1 < sizeof(trace)) {
616 trace[traceSize + 1] = '\0';
617 }
618 debugger->backend->printf(debugger->backend, "%s\n", trace);
619 }
620}
621
622static void _printStatus(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
623 UNUSED(dv);
624 debugger->system->printStatus(debugger->system);
625}
626
627struct CLIDebugVector* CLIDVParse(struct CLIDebugger* debugger, const char* string, size_t length) {
628 if (!string || length < 1) {
629 return 0;
630 }
631
632 struct CLIDebugVector dvTemp = { .type = CLIDV_INT_TYPE, .segmentValue = -1 };
633
634 struct LexVector lv;
635 LexVectorInit(&lv, 0);
636 size_t adjusted = lexExpression(&lv, string, length, " ");
637 if (adjusted > length) {
638 dvTemp.type = CLIDV_ERROR_TYPE;
639 }
640
641 struct ParseTree tree;
642 parseLexedExpression(&tree, &lv);
643 if (tree.token.type == TOKEN_ERROR_TYPE) {
644 dvTemp.type = CLIDV_ERROR_TYPE;
645 } else {
646 if (!mDebuggerEvaluateParseTree(&debugger->d, &tree, &dvTemp.intValue, &dvTemp.segmentValue)) {
647 dvTemp.type = CLIDV_ERROR_TYPE;
648 }
649 }
650
651 parseFree(&tree);
652
653 lexFree(&lv);
654 LexVectorDeinit(&lv);
655
656 struct CLIDebugVector* dv = malloc(sizeof(struct CLIDebugVector));
657 if (dvTemp.type == CLIDV_ERROR_TYPE) {
658 dv->type = CLIDV_ERROR_TYPE;
659 dv->next = 0;
660 } else {
661 *dv = dvTemp;
662 }
663 return dv;
664}
665
666struct CLIDebugVector* CLIDVStringParse(struct CLIDebugger* debugger, const char* string, size_t length) {
667 UNUSED(debugger);
668 if (!string || length < 1) {
669 return 0;
670 }
671
672 struct CLIDebugVector dvTemp = { .type = CLIDV_CHAR_TYPE };
673
674 dvTemp.charValue = strndup(string, length);
675
676 struct CLIDebugVector* dv = malloc(sizeof(struct CLIDebugVector));
677 *dv = dvTemp;
678 return dv;
679}
680
681static void _DVFree(struct CLIDebugVector* dv) {
682 struct CLIDebugVector* next;
683 while (dv) {
684 next = dv->next;
685 if (dv->type == CLIDV_CHAR_TYPE) {
686 free(dv->charValue);
687 }
688 free(dv);
689 dv = next;
690 }
691}
692
693static struct CLIDebugVector* _parseArg(struct CLIDebugger* debugger, const char* args, size_t argsLen, char type) {
694 struct CLIDebugVector* dv = NULL;
695 switch (type) {
696 case 'I':
697 case 'i':
698 return CLIDVParse(debugger, args, argsLen);
699 case 'S':
700 case 's':
701 return CLIDVStringParse(debugger, args, argsLen);
702 case '*':
703 dv = _parseArg(debugger, args, argsLen, 'I');
704 if (!dv) {
705 dv = _parseArg(debugger, args, argsLen, 'S');
706 }
707 break;
708 }
709 return dv;
710}
711
712static int _tryCommands(struct CLIDebugger* debugger, struct CLIDebuggerCommandSummary* commands, const char* command, size_t commandLen, const char* args, size_t argsLen) {
713 struct CLIDebugVector* dv = NULL;
714 struct CLIDebugVector* dvLast = NULL;
715 int i;
716 const char* name;
717 for (i = 0; (name = commands[i].name); ++i) {
718 if (strlen(name) != commandLen) {
719 continue;
720 }
721 if (strncasecmp(name, command, commandLen) == 0) {
722 if (commands[i].format && args) {
723 char lastArg = '\0';
724 int arg;
725 for (arg = 0; commands[i].format[arg] && argsLen; ++arg) {
726 while (isspace(args[0]) && argsLen) {
727 ++args;
728 --argsLen;
729 }
730 if (!args[0] || !argsLen) {
731 debugger->backend->printf(debugger->backend, "Wrong number of arguments\n");
732 _DVFree(dv);
733 return 0;
734 }
735
736 size_t adjusted;
737 const char* next = strchr(args, ' ');
738 if (next) {
739 adjusted = next - args;
740 } else {
741 adjusted = argsLen;
742 }
743
744 struct CLIDebugVector* dvNext = NULL;
745 bool nextArgMandatory = false;
746
747 if (commands[i].format[arg] == '+') {
748 dvNext = _parseArg(debugger, args, adjusted, lastArg);
749 --args;
750 } else {
751 nextArgMandatory = isupper(commands[i].format[arg]) || (commands[i].format[arg] == '*');
752 dvNext = _parseArg(debugger, args, adjusted, commands[i].format[arg]);
753 }
754
755 args += adjusted;
756 argsLen -= adjusted;
757
758 if (!dvNext) {
759 if (!nextArgMandatory) {
760 args = NULL;
761 }
762 break;
763 }
764 if (dvNext->type == CLIDV_ERROR_TYPE) {
765 debugger->backend->printf(debugger->backend, "Parse error\n");
766 _DVFree(dv);
767 return 0;
768 }
769
770 if (dvLast) {
771 dvLast->next = dvNext;
772 dvLast = dvNext;
773 } else {
774 dv = dvNext;
775 dvLast = dv;
776 }
777 }
778 }
779
780 if (args) {
781 while (isspace(args[0]) && argsLen) {
782 ++args;
783 --argsLen;
784 }
785 }
786 if (args && argsLen) {
787 debugger->backend->printf(debugger->backend, "Wrong number of arguments\n");
788 _DVFree(dv);
789 return 0;
790 }
791 commands[i].command(debugger, dv);
792 _DVFree(dv);
793 return 1;
794 }
795 }
796 return -1;
797}
798
799static bool _parse(struct CLIDebugger* debugger, const char* line, size_t count) {
800 const char* firstSpace = strchr(line, ' ');
801 size_t cmdLength;
802 if (firstSpace) {
803 cmdLength = firstSpace - line;
804 } else {
805 cmdLength = count;
806 }
807
808 const char* args = 0;
809 if (firstSpace) {
810 args = firstSpace + 1;
811 }
812 int result = _tryCommands(debugger, _debuggerCommands, line, cmdLength, args, count - cmdLength - 1);
813 if (result < 0 && debugger->system) {
814 result = _tryCommands(debugger, debugger->system->commands, line, cmdLength, args, count - cmdLength - 1);
815 if (result < 0) {
816 result = _tryCommands(debugger, debugger->system->platformCommands, line, cmdLength, args, count - cmdLength - 1);
817 }
818 }
819 if (result < 0) {
820 debugger->backend->printf(debugger->backend, "Command not found\n");
821 }
822 return false;
823}
824
825static void _commandLine(struct mDebugger* debugger) {
826 struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
827 const char* line;
828 size_t len;
829 _printStatus(cliDebugger, 0);
830 while (debugger->state == DEBUGGER_PAUSED) {
831 line = cliDebugger->backend->readline(cliDebugger->backend, &len);
832 if (!line || len == 0) {
833 debugger->state = DEBUGGER_SHUTDOWN;
834 return;
835 }
836 if (line[0] == '\n') {
837 line = cliDebugger->backend->historyLast(cliDebugger->backend, &len);
838 if (line && len) {
839 _parse(cliDebugger, line, len);
840 }
841 } else {
842 _parse(cliDebugger, line, len);
843 cliDebugger->backend->historyAppend(cliDebugger->backend, line);
844 }
845 }
846}
847
848static void _reportEntry(struct mDebugger* debugger, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) {
849 struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
850 switch (reason) {
851 case DEBUGGER_ENTER_MANUAL:
852 case DEBUGGER_ENTER_ATTACHED:
853 break;
854 case DEBUGGER_ENTER_BREAKPOINT:
855 if (info) {
856 cliDebugger->backend->printf(cliDebugger->backend, "Hit breakpoint at 0x%08X\n", info->address);
857 } else {
858 cliDebugger->backend->printf(cliDebugger->backend, "Hit breakpoint\n");
859 }
860 break;
861 case DEBUGGER_ENTER_WATCHPOINT:
862 if (info) {
863 if (info->type.wp.accessType & WATCHPOINT_WRITE) {
864 cliDebugger->backend->printf(cliDebugger->backend, "Hit watchpoint at 0x%08X: (new value = 0x%08X, old value = 0x%08X)\n", info->address, info->type.wp.newValue, info->type.wp.oldValue);
865 } else {
866 cliDebugger->backend->printf(cliDebugger->backend, "Hit watchpoint at 0x%08X: (value = 0x%08X)\n", info->address, info->type.wp.oldValue);
867 }
868 } else {
869 cliDebugger->backend->printf(cliDebugger->backend, "Hit watchpoint\n");
870 }
871 break;
872 case DEBUGGER_ENTER_ILLEGAL_OP:
873 if (info) {
874 cliDebugger->backend->printf(cliDebugger->backend, "Hit illegal opcode at 0x%08X: 0x%08X\n", info->address, info->type.bp.opcode);
875 } else {
876 cliDebugger->backend->printf(cliDebugger->backend, "Hit illegal opcode\n");
877 }
878 break;
879 }
880}
881
882static void _cliDebuggerInit(struct mDebugger* debugger) {
883 struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
884 cliDebugger->backend->init(cliDebugger->backend);
885}
886
887static void _cliDebuggerDeinit(struct mDebugger* debugger) {
888 struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
889 if (cliDebugger->system) {
890 if (cliDebugger->system->deinit) {
891 cliDebugger->system->deinit(cliDebugger->system);
892 }
893 free(cliDebugger->system);
894 cliDebugger->system = NULL;
895 }
896 if (cliDebugger->backend && cliDebugger->backend->deinit) {
897 cliDebugger->backend->deinit(cliDebugger->backend);
898 cliDebugger->backend = NULL;
899 }
900}
901
902static void _cliDebuggerCustom(struct mDebugger* debugger) {
903 struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
904 bool retain = false;
905 if (cliDebugger->system) {
906 retain = cliDebugger->system->custom(cliDebugger->system);
907 }
908 if (!retain && debugger->state == DEBUGGER_CUSTOM) {
909 debugger->state = DEBUGGER_RUNNING;
910 }
911}
912
913void CLIDebuggerCreate(struct CLIDebugger* debugger) {
914 debugger->d.init = _cliDebuggerInit;
915 debugger->d.deinit = _cliDebuggerDeinit;
916 debugger->d.custom = _cliDebuggerCustom;
917 debugger->d.paused = _commandLine;
918 debugger->d.entered = _reportEntry;
919 debugger->d.type = DEBUGGER_CLI;
920
921 debugger->system = NULL;
922 debugger->backend = NULL;
923}
924
925void CLIDebuggerAttachSystem(struct CLIDebugger* debugger, struct CLIDebuggerSystem* system) {
926 if (debugger->system) {
927 if (debugger->system->deinit) {
928 debugger->system->deinit(debugger->system);
929 }
930 free(debugger->system);
931 }
932
933 debugger->system = system;
934 system->p = debugger;
935}
936
937void CLIDebuggerAttachBackend(struct CLIDebugger* debugger, struct CLIDebuggerBackend* backend) {
938 if (debugger->backend == backend) {
939 return;
940 }
941 if (debugger->backend && debugger->backend->deinit) {
942 debugger->backend->deinit(debugger->backend);
943 }
944
945 debugger->backend = backend;
946 backend->p = debugger;
947}
948
949bool CLIDebuggerTabComplete(struct CLIDebugger* debugger, const char* token, bool initial, size_t tokenLen) {
950 size_t cmd = 0;
951 size_t len;
952 const char* name = 0;
953 for (len = 1; len <= tokenLen; ++len) {
954 for (; (name = _debuggerCommands[cmd].name); ++cmd) {
955 int cmp = strncasecmp(name, token, len);
956 if (cmp > 0) {
957 return false;
958 }
959 if (cmp == 0) {
960 break;
961 }
962 }
963 }
964 if (!name) {
965 return false;
966 }
967 if (_debuggerCommands[cmd + 1].name && strlen(_debuggerCommands[cmd + 1].name) >= len && name[len - 1] == _debuggerCommands[cmd + 1].name[len - 1]) {
968 --len;
969 const char* next = 0;
970 int i;
971 for (i = cmd + 1; _debuggerCommands[i].name; ++i) {
972 if (strncasecmp(name, _debuggerCommands[i].name, len)) {
973 break;
974 }
975 next = _debuggerCommands[i].name;
976 }
977 if (!next) {
978 return false;
979 }
980
981 for (; name[len]; ++len) {
982 if (name[len] != next[len]) {
983 break;
984 }
985 char out[2] = { name[len], '\0' };
986 debugger->backend->lineAppend(debugger->backend, out);
987 }
988 return true;
989 }
990 name += len - 1;
991 debugger->backend->lineAppend(debugger->backend, name);
992 debugger->backend->lineAppend(debugger->backend, " ");
993 return true;
994}