Add support for Debug Adapter Protocol

If -debug_dap flag is given, xsystem35 speaks the Debug Adapter Protocol
(https://microsoft.github.io/debug-adapter-protocol/) via stdin/stdout.
This enables xsystem35 to work as a backend of VS Code's Debugger (and
possibly other debuggers that support DAP).
This commit is contained in:
kichikuou
2021-10-17 21:05:24 +09:00
parent 4bea33d852
commit 089520ec55
12 changed files with 556 additions and 26 deletions
+1 -1
View File
@@ -15,7 +15,7 @@ jobs:
- name: Install Deps
run: |
sudo apt update
sudo apt install libgtk-3-dev libsdl2-dev libsdl2-ttf-dev libsdl2-mixer-dev libportmidi-dev
sudo apt install libgtk-3-dev libsdl2-dev libsdl2-ttf-dev libsdl2-mixer-dev libportmidi-dev libcjson-dev
- name: Build
run: |
+1 -1
View File
@@ -33,7 +33,7 @@ jobs:
echo PATH: $PATH
- name: Install Deps
run: pacman -S --noconfirm ${{ matrix.package }}-SDL2 ${{ matrix.package }}-SDL2_ttf ${{ matrix.package }}-SDL2_mixer ${{ matrix.package }}-libjpeg-turbo ${{ matrix.package }}-nsis ${{ matrix.package }}-ntldd-git
run: pacman -S --noconfirm ${{ matrix.package }}-SDL2 ${{ matrix.package }}-SDL2_ttf ${{ matrix.package }}-SDL2_mixer ${{ matrix.package }}-libjpeg-turbo ${{ matrix.package }}-cjson ${{ matrix.package }}-nsis ${{ matrix.package }}-ntldd-git
- name: Checkout
uses: actions/checkout@v2
+10
View File
@@ -49,6 +49,16 @@ else()
optional_pkg_check_modules(SDL2MIXER IMPORTED_TARGET SDL2_mixer)
option(ENABLE_DEBUGGER "Enable built-in debugger" ON)
if (ENABLE_DEBUGGER)
pkg_check_modules(cJSON IMPORTED_TARGET libcjson)
if (NOT cJSON_FOUND)
# libcjson-dev of Debian buster / Ubuntu 20.04 does not install pkgconfig files.
find_library(cJSON cjson)
if (NOT cJSON)
message(FATAL_ERROR "libcjson is required but not found.")
endif()
endif()
endif()
include(FindJPEG)
if (JPEG_FOUND)
+3 -3
View File
@@ -8,7 +8,7 @@ Prebuilt binaries for Windows and Android can be downloaded from the [Releases](
## Build
### Linux (Debian / Ubuntu)
$ sudo apt install build-essential cmake libgtk-3-dev libsdl2-dev libsdl2-ttf-dev libsdl2-mixer-dev
$ sudo apt install build-essential cmake libgtk-3-dev libsdl2-dev libsdl2-ttf-dev libsdl2-mixer-dev libcjson-dev
$ mkdir -p out/debug
$ cd out/debug
$ cmake -DCMAKE_BUILD_TYPE=Debug ../../
@@ -18,7 +18,7 @@ Prebuilt binaries for Windows and Android can be downloaded from the [Releases](
[Homebrew](https://brew.sh/index_ja) is needed.
$ brew install cmake pkg-config sdl2 sdl2_mixer sdl2_ttf libjpeg
$ brew install cmake pkg-config sdl2 sdl2_mixer sdl2_ttf libjpeg cjson
$ mkdir -p out/debug
$ cd out/debug
$ cmake -DCMAKE_BUILD_TYPE=Debug ../../
@@ -28,7 +28,7 @@ Prebuilt binaries for Windows and Android can be downloaded from the [Releases](
[MSYS2](https://www.msys2.org) is needed.
$ pacman -S cmake mingw-w64-x86_64-cmake mingw-w64-x86_64-SDL2 mingw-w64-x86_64-SDL2_ttf mingw-w64-x86_64-SDL2_mixer mingw-w64-x86_64-libjpeg-turbo
$ pacman -S cmake mingw-w64-x86_64-cmake mingw-w64-x86_64-SDL2 mingw-w64-x86_64-SDL2_ttf mingw-w64-x86_64-SDL2_mixer mingw-w64-x86_64-libjpeg-turbo mingw-w64-x86_64-cjson
$ mkdir -p out/debug
$ cd out/debug
$ cmake -G"MSYS Makefiles" -DCMAKE_BUILD_TYPE=Debug ../../
+8 -1
View File
@@ -71,7 +71,7 @@ target_sources(xsystem35 PRIVATE
# Debugger
if (ENABLE_DEBUGGER)
target_sources(xsystem35 PRIVATE
debug_symbol.c debugger.c debugger_cui.c)
debug_symbol.c debugger.c debugger_cui.c debugger_dap.c)
endif()
if (WIN32)
@@ -116,6 +116,13 @@ else() # non-emscripten, non-android
if (SDL2MIXER_FOUND)
target_link_libraries(xsystem35 PRIVATE PkgConfig::SDL2MIXER)
endif()
if (ENABLE_DEBUGGER)
if (cJSON_FOUND)
target_link_libraries(xsystem35 PRIVATE PkgConfig::cJSON)
else()
target_link_libraries(xsystem35 PRIVATE ${cJSON})
endif()
endif()
install(TARGETS xsystem35 RUNTIME DESTINATION bin)
+34 -3
View File
@@ -31,6 +31,7 @@
#define INTERNAL_BREAKPOINT_NO -1
DebuggerState dbg_state = DBG_RUNNING;
DebuggerImpl *dbg_impl;
struct debug_symbols *symbols;
static Breakpoint *breakpoints = NULL;
@@ -43,12 +44,17 @@ static struct {
int line;
} step_exec_state;
void dbg_init(const char *symbols_path) {
dbg_cui_init();
void dbg_init(const char *symbols_path, boolean use_dap) {
dbg_impl = use_dap ? &dbg_dap_impl : &dbg_cui_impl;
dbg_impl->init();
dbg_state = DBG_STOPPED_ENTRY;
symbols = dsym_load(symbols_path);
}
void dbg_quit() {
dbg_impl->quit();
}
int dbg_lookup_var(const char *name) {
if (symbols)
return dsym_lookup_variable(symbols, name);
@@ -113,6 +119,26 @@ boolean dbg_delete_breakpoint(int no) {
return false;
}
void dbg_delete_breakpoints_in_page(int page) {
Breakpoint *prev = NULL;
for (Breakpoint *bp = breakpoints; bp;) {
if (bp->page == page) {
assert(bp->dfile->data[bp->addr] == BREAKPOINT);
bp->dfile->data[bp->addr] = bp->restore_op;
ald_freedata(bp->dfile);
if (prev)
prev->next = bp->next;
else
breakpoints = bp->next;
bp = bp->next;
free(bp);
} else {
prev = bp;
bp = bp->next;
}
}
}
BYTE dbg_handle_breakpoint(int page, int addr) {
Breakpoint *bp = dbg_find_breakpoint(page, addr);
if (!bp)
@@ -265,5 +291,10 @@ void dbg_main(void) {
default:
break;
}
dbg_cui_repl();
dbg_impl->repl();
}
void dbg_onsleep(void) {
if (dbg_impl)
dbg_impl->onsleep();
}
+6 -4
View File
@@ -39,17 +39,19 @@ extern DebuggerState dbg_state;
#ifdef ENABLE_DEBUGGER
#define dbg_trapped() (dbg_state != DBG_RUNNING)
#define dbg_interrupted() (dbg_state == DBG_STOPPED_INTERRUPT)
void dbg_init(const char *symbols_path);
void dbg_init(const char *symbols_path, boolean use_dap);
void dbg_quit();
void dbg_main(void);
void dbg_onsleep(void);
BYTE dbg_handle_breakpoint(int page, int addr);
#else // ENABLE_DEBUGGER
#define dbg_trapped() false
#define dbg_interrupted() false
#define dbg_init(symbols_path)
#define dbg_init(symbols_path, use_dap)
#define dbg_quit()
#define dbg_main()
#define dbg_onsleep()
#define dbg_handle_breakpoint(page, addr) BREAKPOINT
#endif // ENABLE_DEBUGGER
+17 -2
View File
@@ -315,7 +315,7 @@ static void sigint_handler(int sig_num) {
dbg_state = DBG_STOPPED_INTERRUPT;
}
void dbg_cui_init(void) {
static void dbg_cui_init(void) {
#ifdef HAVE_SIGACTION
sys_set_signalhandler(SIGINT, sigint_handler);
#endif
@@ -325,6 +325,9 @@ void dbg_cui_init(void) {
#endif
}
static void dbg_cui_quit(void) {
}
typedef struct {
const char *name;
const char *alias;
@@ -379,7 +382,7 @@ static CommandResult cmd_help(void) {
return CONTINUE_REPL;
}
void dbg_cui_repl(void) {
static void dbg_cui_repl(void) {
if (dbg_state == DBG_STOPPED_BREAKPOINT) {
Breakpoint *bp = dbg_find_breakpoint(nact->current_page, nact->current_addr);
if (bp)
@@ -408,3 +411,15 @@ void dbg_cui_repl(void) {
}
}
}
static void dbg_cui_onsleep(void) {
if (dbg_state == DBG_STOPPED_INTERRUPT)
dbg_main();
}
DebuggerImpl dbg_cui_impl = {
.init = dbg_cui_init,
.quit = dbg_cui_quit,
.repl = dbg_cui_repl,
.onsleep = dbg_cui_onsleep,
};
+450
View File
@@ -0,0 +1,450 @@
/*
* Copyright (C) 2021 kichikuou <KichikuouChrome@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <SDL_mutex.h>
#include <SDL_thread.h>
#ifdef _WIN32
#include <fcntl.h>
#include <io.h>
#endif
#include "cjson/cJSON.h"
#include "debugger.h"
#include "debugger_private.h"
#include "debug_symbol.h"
#include "system.h"
#include "variable.h"
#define THREAD_ID 1
static boolean initialized = false;
static char *src_dir;
cJSON *create_source(const char *name) {
cJSON *source = cJSON_CreateObject();
cJSON_AddStringToObject(source, "name", name);
if (src_dir) {
char *buf = alloca(strlen(src_dir) + strlen(name) + 2);
sprintf(buf, "%s/%s", src_dir, name);
cJSON_AddStringToObject(source, "path", buf);
}
cJSON_AddNumberToObject(source, "sourceReference", 0);
return source;
}
static void send_json(cJSON *json) {
static int seq = 0;
cJSON_AddNumberToObject(json, "seq", seq);
char *str = cJSON_PrintUnformatted(json);
printf("Content-Length: %zu\r\n\r\n%s", strlen(str), str);
fflush(stdout);
free(str);
cJSON_free(json);
}
static void emit_initialized_event() {
cJSON *event = cJSON_CreateObject();
cJSON_AddStringToObject(event, "type", "event");
cJSON_AddStringToObject(event, "event", "initialized");
send_json(event);
}
static void emit_terminated_event() {
cJSON *event = cJSON_CreateObject();
cJSON_AddStringToObject(event, "type", "event");
cJSON_AddStringToObject(event, "event", "terminated");
send_json(event);
}
static void emit_stop_event() {
const char *reason;
switch (dbg_state) {
case DBG_STOPPED_ENTRY: reason = "entry"; break;
case DBG_STOPPED_STEP: reason = "step"; break;
case DBG_STOPPED_NEXT: reason = "step"; break;
case DBG_STOPPED_BREAKPOINT: reason = "breakpoint"; break;
case DBG_STOPPED_INTERRUPT: reason = "pause"; break;
default: reason = "unknown"; break;
}
cJSON *event = cJSON_CreateObject(), *body;
cJSON_AddStringToObject(event, "type", "event");
cJSON_AddStringToObject(event, "event", "stopped");
cJSON_AddItemToObjectCS(event, "body", body = cJSON_CreateObject());
cJSON_AddStringToObject(body, "reason", reason);
cJSON_AddNumberToObject(body, "threadId", THREAD_ID); // needed?
send_json(event);
}
static void cmd_initialize(cJSON *args, cJSON *resp) {
cJSON *body;
cJSON_AddBoolToObject(resp, "success", true);
cJSON_AddItemToObjectCS(resp, "body", body = cJSON_CreateObject());
cJSON_AddBoolToObject(body, "supportsConfigurationDoneRequest", true);
cJSON_AddBoolToObject(body, "supportsEvaluateForHovers", true);
}
static void cmd_launch(cJSON *args, cJSON *resp) {
cJSON *srcDir = cJSON_GetObjectItemCaseSensitive(args, "srcDir");
if (cJSON_IsString(srcDir))
src_dir = strdup(srcDir->valuestring);
cJSON_AddBoolToObject(resp, "success", true);
emit_initialized_event();
initialized = true;
}
static void cmd_configurationDone(cJSON *args, cJSON *resp) {
cJSON_AddBoolToObject(resp, "success", true);
}
static void cmd_stackTrace(cJSON *args, cJSON *resp) {
StackTrace *trace = dbg_stack_trace();
cJSON *body, *stackFrames;
cJSON_AddBoolToObject(resp, "success", true);
cJSON_AddItemToObjectCS(resp, "body", body = cJSON_CreateObject());
cJSON_AddItemToObjectCS(body, "stackFrames", stackFrames = cJSON_CreateArray());
for (int i = 0; i < trace->nr_frame; i++) {
StackFrame *frame = &trace->frames[i];
cJSON *item = cJSON_CreateObject();
cJSON_AddItemToArray(stackFrames, item);
cJSON_AddNumberToObject(item, "id", i);
cJSON_AddStringToObject(item, "name", frame->name);
cJSON_AddItemToObjectCS(item, "source", create_source(frame->src));
cJSON_AddNumberToObject(item, "line", frame->line);
cJSON_AddNumberToObject(item, "column", 0);
}
cJSON_AddNumberToObject(body, "totalFrames", trace->nr_frame);
free(trace);
}
static void cmd_setBreakpoints(cJSON *args, cJSON *resp) {
cJSON *source = cJSON_GetObjectItemCaseSensitive(args, "source");
cJSON *source_name = cJSON_GetObjectItemCaseSensitive(source, "name");
cJSON *lines = cJSON_GetObjectItemCaseSensitive(args, "lines");
if (!cJSON_IsString(source_name) || !cJSON_IsArray(lines)) {
cJSON_AddBoolToObject(resp, "success", false);
// TODO: add message
return;
}
const char *filename = source_name->valuestring;
int page = dsym_src2page(symbols, filename);
dbg_delete_breakpoints_in_page(page);
cJSON *body, *breakpoints;
cJSON_AddBoolToObject(resp, "success", true);
cJSON_AddItemToObjectCS(resp, "body", body = cJSON_CreateObject());
cJSON_AddItemToObjectCS(body, "breakpoints", breakpoints = cJSON_CreateArray());
cJSON *line;
cJSON_ArrayForEach(line, lines) {
cJSON *item = cJSON_CreateObject();
cJSON_AddItemToArray(breakpoints, item);
int line_no = line->valueint;
int addr = dsym_line2addr(symbols, page, line_no);
if (page < 0) {
fprintf(stderr, "No source file named %s.\n", filename);
cJSON_AddBoolToObject(item, "verified", false);
continue;
}
if (addr < 0) {
fprintf(stderr, "No line %d in file %s.\n", line_no, filename);
cJSON_AddBoolToObject(item, "verified", false);
continue;
}
Breakpoint *bp = dbg_set_breakpoint(page, addr, false);
if (!bp) {
fprintf(stderr, "Failed to set breakpoint at %d:0x%x\n", page, addr);
cJSON_AddBoolToObject(item, "verified", false);
continue;
}
line_no = dsym_addr2line(symbols, page, addr);
cJSON_AddNumberToObject(item, "id", bp->no);
cJSON_AddBoolToObject(item, "verified", true);
cJSON_AddItemToObjectCS(item, "source", create_source(filename));
cJSON_AddNumberToObject(item, "line", line_no);
}
}
static void cmd_evaluate(cJSON *args, cJSON *resp) {
cJSON *expression = cJSON_GetObjectItemCaseSensitive(args, "expression");
if (!cJSON_IsString(expression)) {
cJSON_AddBoolToObject(resp, "success", false);
// TODO: add message
return;
}
const char *varname = expression->valuestring;
int var = dbg_lookup_var(varname);
if (var < 0) {
fprintf(stderr, "Unrecognized variable name \"%s\".\n", varname);
// TODO: add message
cJSON_AddBoolToObject(resp, "success", false);
return;
}
char result[20];
sprintf(result, "%d", sysVar[var]);
cJSON *body;
cJSON_AddBoolToObject(resp, "success", true);
cJSON_AddItemToObjectCS(resp, "body", body = cJSON_CreateObject());
cJSON_AddStringToObject(body, "result", result);
cJSON_AddNumberToObject(body, "variablesReference", 0);
}
static void cmd_continue(cJSON *args, cJSON *resp) {
cJSON_AddBoolToObject(resp, "success", true);
}
static void cmd_pause(cJSON *args, cJSON *resp) {
dbg_state = DBG_STOPPED_INTERRUPT;
cJSON_AddBoolToObject(resp, "success", true);
}
static void cmd_stepIn(cJSON *args, cJSON *resp) {
dbg_step();
cJSON_AddBoolToObject(resp, "success", true);
}
static void cmd_next(cJSON *args, cJSON *resp) {
dbg_next();
cJSON_AddBoolToObject(resp, "success", true);
}
static void cmd_threads(cJSON *args, cJSON *resp) {
cJSON *body, *threads, *thread;
cJSON_AddBoolToObject(resp, "success", true);
cJSON_AddItemToObjectCS(resp, "body", body = cJSON_CreateObject());
cJSON_AddItemToObjectCS(body, "threads", threads = cJSON_CreateArray());
cJSON_AddItemToArray(threads, thread = cJSON_CreateObject());
cJSON_AddNumberToObject(thread, "id", THREAD_ID);
cJSON_AddStringToObject(thread, "name", "main thread");
}
static void cmd_scopes(cJSON *args, cJSON *resp) {
cJSON *body;
cJSON_AddBoolToObject(resp, "success", true);
cJSON_AddItemToObjectCS(resp, "body", body = cJSON_CreateObject());
cJSON_AddItemToObjectCS(body, "scopes", cJSON_CreateArray());
}
static void cmd_disconnect(cJSON *args, cJSON *resp) {
cJSON_AddBoolToObject(resp, "success", true);
send_json(resp);
sys_exit(0);
}
static boolean handle_request(cJSON *request) {
boolean continue_repl = true;
cJSON *resp = cJSON_CreateObject();
cJSON_AddStringToObject(resp, "type", "response");
cJSON *request_seq = cJSON_DetachItemFromObjectCaseSensitive(request, "seq");
cJSON_AddItemToObjectCS(resp, "request_seq", request_seq);
cJSON *command = cJSON_DetachItemFromObjectCaseSensitive(request, "command");
cJSON_AddItemToObjectCS(resp, "command", command);
cJSON *args = cJSON_GetObjectItemCaseSensitive(request, "arguments");
if (!cJSON_IsString(command)) {
fprintf(stderr, "protocol error: command is not a string\n");
// FIXME: return an error response
cJSON_free(resp);
return continue_repl;
}
if (!strcmp(command->valuestring, "initialize")) {
cmd_initialize(args, resp);
} else if (!strcmp(command->valuestring, "launch")) {
cmd_launch(args, resp);
} else if (!strcmp(command->valuestring, "configurationDone")) {
cmd_configurationDone(args, resp);
} else if (!strcmp(command->valuestring, "continue")) {
cmd_continue(args, resp);
continue_repl = false;
} else if (!strcmp(command->valuestring, "stackTrace")) {
cmd_stackTrace(args, resp);
} else if (!strcmp(command->valuestring, "stepIn")) {
cmd_stepIn(args, resp);
continue_repl = false;
} else if (!strcmp(command->valuestring, "next")) {
cmd_next(args, resp);
continue_repl = false;
} else if (!strcmp(command->valuestring, "pause")) {
cmd_pause(args, resp);
} else if (!strcmp(command->valuestring, "evaluate")) {
cmd_evaluate(args, resp);
} else if (!strcmp(command->valuestring, "setBreakpoints")) {
cmd_setBreakpoints(args, resp);
} else if (!strcmp(command->valuestring, "threads")) {
cmd_threads(args, resp);
} else if (!strcmp(command->valuestring, "scopes")) {
cmd_scopes(args, resp);
} else if (!strcmp(command->valuestring, "disconnect")) {
cmd_disconnect(args, resp);
} else {
fprintf(stderr, "unknown command \"%s\"\n", command->valuestring);
}
send_json(resp);
return continue_repl;
}
static boolean handle_message(char *msg) {
cJSON *json = cJSON_Parse(msg);
cJSON *type = cJSON_GetObjectItemCaseSensitive(json, "type");
boolean continue_repl = true;
if (cJSON_IsString(type) && !strcmp(type->valuestring, "request"))
continue_repl = handle_request(json);
cJSON_free(json);
free(msg);
return continue_repl;
}
struct cmdq_elem {
char *cmd;
struct cmdq_elem *next;
};
struct {
SDL_mutex *mutex;
SDL_cond *cond_nonempty;
struct cmdq_elem *head;
struct cmdq_elem *last;
} cmd_queue;
static boolean cmdq_isempty(void) {
return !cmd_queue.head;
}
static void cmdq_enqueue(char *cmd) {
struct cmdq_elem *e = malloc(sizeof(struct cmdq_elem));
e->cmd = cmd;
e->next = NULL;
SDL_LockMutex(cmd_queue.mutex);
if (!cmd_queue.head) {
cmd_queue.head = cmd_queue.last = e;
} else {
cmd_queue.last->next = e;
cmd_queue.last = e;
}
SDL_UnlockMutex(cmd_queue.mutex);
SDL_CondSignal(cmd_queue.cond_nonempty);
}
static char *cmdq_dequeue(void) {
SDL_LockMutex(cmd_queue.mutex);
while (!cmd_queue.head)
SDL_CondWait(cmd_queue.cond_nonempty, cmd_queue.mutex);
struct cmdq_elem *e = cmd_queue.head;
cmd_queue.head = e->next;
if (!e->next)
cmd_queue.last = NULL;
SDL_UnlockMutex(cmd_queue.mutex);
char *cmd = e->cmd;
free(e);
return cmd;
}
static int read_command_thread(void *data) {
int content_length = -1;
char header[512];
while (fgets(header, sizeof(header), stdin)) {
if (sscanf(header, "Content-Length: %d", &content_length) == 1) {
continue;
} else if ((header[0] == '\r' && header[1] == '\n') || header[0] == '\n') {
if (content_length < 0) {
fprintf(stderr, "Debug Adapter Protocol error: no Content-Length header\n");
continue;
}
char *buf = malloc(content_length);
fread(buf, content_length, 1, stdin);
cmdq_enqueue(buf);
content_length = -1;
} else {
fprintf(stderr, "Unknown Debug Adapter Protocol header: %s", header);
}
}
cmdq_enqueue(NULL); // EOF
return 0;
}
static void dbg_dap_init(void) {
cmd_queue.mutex = SDL_CreateMutex();
cmd_queue.cond_nonempty = SDL_CreateCond();
#ifdef _WIN32
_setmode(_fileno(stdin), _O_BINARY);
_setmode(_fileno(stdout), _O_BINARY);
#endif
SDL_CreateThread(read_command_thread, "Debugger", NULL);
while (!initialized) {
char *msg = cmdq_dequeue();
if (!msg)
break;
handle_message(msg);
}
}
static void dbg_dap_quit(void) {
emit_terminated_event();
}
static void dbg_dap_repl(void) {
emit_stop_event();
dbg_state = DBG_RUNNING;
boolean continue_repl = true;
while (continue_repl) {
char *msg = cmdq_dequeue();
if (!msg)
break;
continue_repl = handle_message(msg);
}
}
static void dbg_dap_onsleep(void) {
while (!cmdq_isempty()) {
char *msg = cmdq_dequeue();
if (!msg)
break;
handle_message(msg);
}
if (dbg_state == DBG_STOPPED_INTERRUPT)
dbg_main();
}
DebuggerImpl dbg_dap_impl = {
.init = dbg_dap_init,
.quit = dbg_dap_quit,
.repl = dbg_dap_repl,
.onsleep = dbg_dap_onsleep,
};
+12 -2
View File
@@ -47,14 +47,24 @@ typedef struct {
StackFrame frames[];
} StackTrace;
typedef struct {
void (*init)(void);
void (*quit)(void);
void (*repl)(void);
void (*onsleep)(void);
} DebuggerImpl;
extern DebuggerImpl dbg_cui_impl;
extern DebuggerImpl dbg_dap_impl;
extern DebuggerImpl *dbg_impl;
Breakpoint *dbg_find_breakpoint(int page, int addr);
Breakpoint *dbg_set_breakpoint(int page, int addr, boolean is_internal);
boolean dbg_delete_breakpoint(int no);
void dbg_delete_breakpoints_in_page(int page);
void dbg_step(void);
void dbg_next(void);
int dbg_lookup_var(const char *name);
StackTrace *dbg_stack_trace(void);
void dbg_cui_init(void);
void dbg_cui_repl(void);
#endif // __DEBUGGER_PRIVATE_H__
+2 -4
View File
@@ -76,8 +76,7 @@ void sdl_updateScreen(void) {
void sdl_sleep(int msec) {
sdl_updateScreen();
if (dbg_interrupted())
dbg_main();
dbg_onsleep();
#ifdef __EMSCRIPTEN__
emscripten_sleep(msec);
#else
@@ -97,8 +96,7 @@ EM_JS(void, wait_vsync, (void), {
void sdl_wait_vsync() {
sdl_updateScreen();
if (dbg_interrupted())
dbg_main();
dbg_onsleep();
#ifdef __EMSCRIPTEN__
wait_vsync();
#else
+12 -5
View File
@@ -88,7 +88,11 @@ static void check_profile();
/* for debugging */
static FILE *fpdebuglog;
static int debuglv = DEBUGLEVEL;
static boolean debugger_enabled;
enum {
DEBUGGER_DISABLED,
DEBUGGER_CUI,
DEBUGGER_DAP,
} debugger_mode = DEBUGGER_DISABLED;
static int audio_buffer_size = 0;
@@ -246,6 +250,7 @@ static void sys35_init() {
}
static void sys35_remove() {
dbg_quit();
mus_exit();
ags_remove();
#ifdef ENABLE_GTK
@@ -293,7 +298,9 @@ static void sys35_ParseOption(int *argc, char **argv) {
fclose(fp);
gameResourceFile = argv[i + 1];
} else if (0 == strcmp(argv[i], "-debug")) {
debugger_enabled = true;
debugger_mode = DEBUGGER_CUI;
} else if (0 == strcmp(argv[i], "-debug_dap")) {
debugger_mode = DEBUGGER_DAP;
} else if (0 == strcmp(argv[i], "-devcd")) {
if (argv[i + 1] != NULL) {
cd_set_devicename(argv[i + 1]);
@@ -455,7 +462,7 @@ static void registerGameFiles(void) {
SYSERROR("No Scenario data available\n");
for (int type = 0; type < DRIFILETYPEMAX; type++) {
boolean use_mmap = true;
if (debugger_enabled && type == DRIFILE_SCO) {
if (debugger_mode != DEBUGGER_DISABLED && type == DRIFILE_SCO) {
// Do not mmap scenario files so that BREAKPOINT instructions can be inserted.
use_mmap = false;
}
@@ -525,10 +532,10 @@ int main(int argc, char **argv) {
#endif
menu_init();
if (debugger_enabled) {
if (debugger_mode != DEBUGGER_DISABLED) {
char symbols_path[500];
snprintf(symbols_path, sizeof(symbols_path), "%s.symbols", nact->files.game_fname[DRIFILE_SCO][0]);
dbg_init(symbols_path);
dbg_init(symbols_path, debugger_mode == DEBUGGER_DAP);
}
nact_main();