mirror of
https://github.com/nunuhara/xsystem4.git
synced 2026-10-02 20:08:03 +03:00
Merge pull request #231 from kichikuou/debugger-dap
debugger_dap: Source-level debugging
This commit is contained in:
@@ -81,6 +81,7 @@ void dbg_cmd_repl(void);
|
||||
void dbg_cmd_add_module(const char *name, unsigned nr_commands, struct dbg_cmd *commands);
|
||||
void dbg_handle_breakpoint(void);
|
||||
bool dbg_clear_breakpoint(uint32_t addr, void(*free_data)(void*));
|
||||
void dbg_foreach_breakpoint(void (*fun)(int addr, struct breakpoint*, void *data), void *data);
|
||||
bool dbg_set_function_breakpoint(const char *_name, void(*cb)(struct breakpoint*), void *data);
|
||||
bool dbg_set_address_breakpoint(uint32_t address, void(*cb)(struct breakpoint*), void *data);
|
||||
bool dbg_set_step_over_breakpoint(void);
|
||||
|
||||
@@ -71,6 +71,7 @@ void log_message(const char *log, const char *fmt, ...);
|
||||
#define UNIMPLEMENTED(fmt, ...) \
|
||||
sys_warning("unimplemented: %s" fmt "\n", __func__, ##__VA_ARGS__)
|
||||
|
||||
bool is_absolute_path(const char *path);
|
||||
char *unix_path(const char *path);
|
||||
char *gamedir_path(const char *path);
|
||||
char *gamedir_path_icase(const char *path);
|
||||
|
||||
+21
@@ -175,6 +175,27 @@ bool dbg_clear_breakpoint(uint32_t addr, void(*free_data)(void*))
|
||||
return true;
|
||||
}
|
||||
|
||||
struct dbg_foreach_data {
|
||||
void (*fun)(int addr, struct breakpoint*, void *data);
|
||||
void *data;
|
||||
};
|
||||
|
||||
void dbg_foreach_breakpoint_cb(struct ht_slot *slot, void *d_)
|
||||
{
|
||||
if (!slot->value)
|
||||
return;
|
||||
struct dbg_foreach_data *d = d_;
|
||||
d->fun(slot->ikey, slot->value, d->data);
|
||||
}
|
||||
|
||||
void dbg_foreach_breakpoint(void (*fun)(int addr, struct breakpoint*, void *data), void *data)
|
||||
{
|
||||
if (!bp_table)
|
||||
return;
|
||||
struct dbg_foreach_data d = { fun, data };
|
||||
ht_foreach(bp_table, dbg_foreach_breakpoint_cb, &d);
|
||||
}
|
||||
|
||||
bool dbg_set_function_breakpoint(const char *_name, void(*cb)(struct breakpoint*), void *data)
|
||||
{
|
||||
char *name = utf2sjis(_name, 0);
|
||||
|
||||
+22
-3
@@ -150,9 +150,28 @@ const char *dbg_info_source_line(const struct dbg_info *info, int file, int line
|
||||
|
||||
int dbg_info_find_file(const struct dbg_info *info, const char *filename)
|
||||
{
|
||||
for (int i = 0; i < info->nr_sources; i++) {
|
||||
if (!strcmp(info->sources[i], filename) || !strcmp(path_basename(info->sources[i]), filename))
|
||||
return i;
|
||||
if (is_absolute_path(filename)) {
|
||||
char *target = realpath_utf8(filename); // Convert '\\' to '/' on Windows
|
||||
char *prefix = realpath_utf8(info->src_root);
|
||||
for (int i = 0; i < info->nr_sources; i++) {
|
||||
char *path = path_join(prefix, info->sources[i]);
|
||||
if (!strcasecmp(path, target)) {
|
||||
free(path);
|
||||
free(prefix);
|
||||
free(target);
|
||||
return i;
|
||||
}
|
||||
free(path);
|
||||
}
|
||||
free(prefix);
|
||||
free(target);
|
||||
} else {
|
||||
for (int i = 0; i < info->nr_sources; i++) {
|
||||
if (!strcasecmp(info->sources[i], filename) ||
|
||||
!strcasecmp(path_basename(info->sources[i]), filename)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
+145
-6
@@ -56,6 +56,15 @@ static enum {
|
||||
DAP_STOPPED
|
||||
} dap_state = DAP_UNINITIALIZED;
|
||||
|
||||
static enum {
|
||||
NOT_STEPPING = 0,
|
||||
STEPPING_INTO,
|
||||
STEPPING_OVER,
|
||||
STEPPING_FINISH
|
||||
} stepping = NOT_STEPPING;
|
||||
static int stepping_file = 0;
|
||||
static int stepping_line = 0;
|
||||
|
||||
static struct msgq *queue;
|
||||
|
||||
static void json_add_sjis_to_object(cJSON *obj, const char *name, const char *sjis)
|
||||
@@ -153,6 +162,7 @@ static void cmd_initialize(cJSON *args, cJSON *resp)
|
||||
//cJSON_AddBoolToObject(body, "supportsSetExpression", true);
|
||||
//cJSON_AddBoolToObject(body, "supportsTerminateRequest", true);
|
||||
cJSON_AddBoolToObject(body, "supportsInstructionBreakpoints", true);
|
||||
cJSON_AddBoolToObject(body, "supportsSteppingGranularity", true);
|
||||
send_response(resp, true);
|
||||
|
||||
emit_initialized_event();
|
||||
@@ -194,13 +204,24 @@ static void cmd_stackTrace(cJSON *args, cJSON *resp)
|
||||
json_add_sjis_to_object(frame, "name", ain->functions[fno].name);
|
||||
}
|
||||
char ip[9];
|
||||
if (i == call_stack_ptr - 1) {
|
||||
snprintf(ip, 9, "%x", (unsigned)instr_ptr);
|
||||
} else {
|
||||
snprintf(ip, 9, "%x", call_stack[i+1].call_address);
|
||||
}
|
||||
int addr = (i == call_stack_ptr - 1) ? instr_ptr : call_stack[i+1].call_address;
|
||||
snprintf(ip, 9, "%x", (unsigned)addr);
|
||||
cJSON_AddStringToObject(frame, "instructionPointerReference", ip);
|
||||
cJSON_AddNumberToObject(frame, "line", 0);
|
||||
int file, line;
|
||||
if (dbg_info && dbg_info_addr2line(dbg_info, addr, &file, &line)) {
|
||||
cJSON_AddNumberToObject(frame, "line", line);
|
||||
|
||||
const char *fname = dbg_info_source_name(dbg_info, file);
|
||||
cJSON *source = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(source, "name", fname);
|
||||
char *path = dbg_info_source_path(dbg_info, file);
|
||||
cJSON_AddStringToObject(source, "path", path);
|
||||
free(path);
|
||||
cJSON_AddNumberToObject(source, "sourceReference", 0);
|
||||
cJSON_AddItemToObjectCS(frame, "source", source);
|
||||
} else {
|
||||
cJSON_AddNumberToObject(frame, "line", 0);
|
||||
}
|
||||
cJSON_AddNumberToObject(frame, "column", 0);
|
||||
}
|
||||
send_response(resp, true);
|
||||
@@ -452,6 +473,76 @@ static void cmd_variables(cJSON *args, cJSON *resp)
|
||||
send_response(resp, true);
|
||||
}
|
||||
|
||||
void delete_breakpoints_in_file(int addr, struct breakpoint *bp, void *file_)
|
||||
{
|
||||
int file = *(int *)file_;
|
||||
int f;
|
||||
if (dbg_info_addr2line(dbg_info, addr, &f, NULL) && f == file) {
|
||||
dbg_clear_breakpoint(addr, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void cmd_setBreakpoints(cJSON *args, cJSON *resp)
|
||||
{
|
||||
if (!dbg_info) {
|
||||
cJSON_AddStringToObject(resp, "message", "no debug info available");
|
||||
send_response(resp, false);
|
||||
return;
|
||||
}
|
||||
cJSON *source = cJSON_GetObjectItemCaseSensitive(args, "source");
|
||||
cJSON *source_path = cJSON_GetObjectItemCaseSensitive(source, "path");
|
||||
cJSON *req_bps = cJSON_GetObjectItemCaseSensitive(args, "breakpoints");
|
||||
if (!cJSON_IsString(source_path) || !cJSON_IsArray(req_bps)) {
|
||||
cJSON_AddStringToObject(resp, "message", "invalid arguments");
|
||||
send_response(resp, false);
|
||||
return;
|
||||
}
|
||||
const char *path = source_path->valuestring;
|
||||
int file = dbg_info_find_file(dbg_info, path);
|
||||
if (file < 0) {
|
||||
cJSON_AddStringToObject(resp, "message", "no such file");
|
||||
send_response(resp, false);
|
||||
return;
|
||||
}
|
||||
|
||||
dbg_foreach_breakpoint(delete_breakpoints_in_file, &file);
|
||||
|
||||
cJSON *body, *resp_bps;
|
||||
cJSON_AddItemToObjectCS(resp, "body", body = cJSON_CreateObject());
|
||||
cJSON_AddItemToObjectCS(body, "breakpoints", resp_bps = cJSON_CreateArray());
|
||||
|
||||
char message[256];
|
||||
cJSON *req_bp;
|
||||
cJSON_ArrayForEach(req_bp, req_bps) {
|
||||
cJSON *resp_bp = cJSON_CreateObject();
|
||||
cJSON_AddItemToArray(resp_bps, resp_bp);
|
||||
|
||||
cJSON *line = cJSON_GetObjectItemCaseSensitive(req_bp, "line");
|
||||
int line_no = line->valueint;
|
||||
int addr = dbg_info_line2addr(dbg_info, file, line_no);
|
||||
if (addr < 0) {
|
||||
snprintf(message, sizeof(message), "no line %d in %s", line_no, path);
|
||||
cJSON_AddBoolToObject(resp_bp, "verified", false);
|
||||
cJSON_AddStringToObject(resp_bp, "message", message);
|
||||
continue;
|
||||
}
|
||||
bool verified = dbg_set_address_breakpoint(addr, NULL, NULL);
|
||||
if (!verified) {
|
||||
snprintf(message, sizeof(message), "failed to set breakpoint at 0x%x", addr);
|
||||
cJSON_AddBoolToObject(resp_bp, "verified", false);
|
||||
cJSON_AddStringToObject(resp_bp, "message", message);
|
||||
continue;
|
||||
}
|
||||
|
||||
dbg_info_addr2line(dbg_info, addr, NULL, &line_no);
|
||||
cJSON_AddNumberToObject(resp_bp, "id", addr);
|
||||
cJSON_AddBoolToObject(resp_bp, "verified", true);
|
||||
cJSON_AddNumberToObject(resp_bp, "line", line_no);
|
||||
}
|
||||
|
||||
send_response(resp, true);
|
||||
}
|
||||
|
||||
static void cmd_setInstructionBreakpoints(cJSON *args, cJSON *resp)
|
||||
{
|
||||
static uint32_t *old_breakpoints = NULL;
|
||||
@@ -554,6 +645,8 @@ static void cmd_continue(cJSON *args, cJSON *resp)
|
||||
{
|
||||
send_response(resp, true);
|
||||
|
||||
stepping = NOT_STEPPING;
|
||||
stepping_file = stepping_line = 0;
|
||||
dap_state = DAP_RUNNING;
|
||||
dbg_continue();
|
||||
}
|
||||
@@ -573,12 +666,35 @@ static void cmd_pause(cJSON *args, cJSON *resp)
|
||||
}
|
||||
}
|
||||
|
||||
enum stepping_granularity {
|
||||
STEPPING_GRANULARITY_STATEMENT,
|
||||
STEPPING_GRANULARITY_LINE,
|
||||
STEPPING_GRANULARITY_INSTRUCTION,
|
||||
};
|
||||
|
||||
static enum stepping_granularity get_granularity(cJSON *args)
|
||||
{
|
||||
cJSON *g = cJSON_GetObjectItemCaseSensitive(args, "granularity");
|
||||
if (cJSON_IsString(g)) {
|
||||
if (!strcmp(g->valuestring, "line"))
|
||||
return STEPPING_GRANULARITY_LINE;
|
||||
if (!strcmp(g->valuestring, "instruction"))
|
||||
return STEPPING_GRANULARITY_INSTRUCTION;
|
||||
}
|
||||
return STEPPING_GRANULARITY_STATEMENT;
|
||||
}
|
||||
|
||||
static void cmd_stepIn(cJSON *args, cJSON *resp)
|
||||
{
|
||||
stepping_file = stepping_line = 0;
|
||||
if (dbg_info && get_granularity(args) != STEPPING_GRANULARITY_INSTRUCTION)
|
||||
dbg_info_addr2line(dbg_info, instr_ptr, &stepping_file, &stepping_line);
|
||||
|
||||
dbg_set_step_into_breakpoint();
|
||||
send_response(resp, true);
|
||||
|
||||
dap_state = DAP_RUNNING;
|
||||
stepping = STEPPING_INTO;
|
||||
dbg_continue();
|
||||
}
|
||||
|
||||
@@ -588,15 +704,21 @@ static void cmd_stepOut(cJSON *args, cJSON *resp)
|
||||
send_response(resp, true);
|
||||
|
||||
dap_state = DAP_RUNNING;
|
||||
stepping = STEPPING_FINISH;
|
||||
dbg_continue();
|
||||
}
|
||||
|
||||
static void cmd_next(cJSON *args, cJSON *resp)
|
||||
{
|
||||
stepping_file = stepping_line = 0;
|
||||
if (dbg_info && get_granularity(args) != STEPPING_GRANULARITY_INSTRUCTION)
|
||||
dbg_info_addr2line(dbg_info, instr_ptr, &stepping_file, &stepping_line);
|
||||
|
||||
dbg_set_step_over_breakpoint();
|
||||
send_response(resp, true);
|
||||
|
||||
dap_state = DAP_RUNNING;
|
||||
stepping = STEPPING_OVER;
|
||||
dbg_continue();
|
||||
}
|
||||
|
||||
@@ -798,6 +920,7 @@ static struct {
|
||||
{ "next", cmd_next, false },
|
||||
{ "pause", cmd_pause, true },
|
||||
{ "evaluate", cmd_evaluate, true },
|
||||
{ "setBreakpoints", cmd_setBreakpoints, true },
|
||||
{ "setInstructionBreakpoints", cmd_setInstructionBreakpoints, true },
|
||||
{ "threads", cmd_threads, true },
|
||||
{ "scopes", cmd_scopes, true },
|
||||
@@ -906,6 +1029,22 @@ void dbg_dap_quit(void)
|
||||
|
||||
void dbg_dap_repl(struct dbg_stop *stop)
|
||||
{
|
||||
if (stepping) {
|
||||
if (stepping_line) {
|
||||
int file, line;
|
||||
dbg_info_addr2line(dbg_info, instr_ptr, &file, &line);
|
||||
if (file == stepping_file && line == stepping_line) {
|
||||
if (stepping == STEPPING_INTO)
|
||||
dbg_set_step_into_breakpoint();
|
||||
else if (stepping == STEPPING_OVER)
|
||||
dbg_set_step_over_breakpoint();
|
||||
dbg_continue();
|
||||
}
|
||||
}
|
||||
stepping = NOT_STEPPING;
|
||||
stepping_file = stepping_line = 0;
|
||||
}
|
||||
|
||||
emit_stopped_event(stop);
|
||||
dap_state = DAP_STOPPED;
|
||||
|
||||
|
||||
+1
-1
@@ -117,7 +117,7 @@ char *unix_path(const char *path)
|
||||
return utf;
|
||||
}
|
||||
|
||||
static bool is_absolute_path(const char *path)
|
||||
bool is_absolute_path(const char *path)
|
||||
{
|
||||
#if (defined(_WIN32) || defined(__WIN32__))
|
||||
int i = (isalpha(path[0]) && path[1] == ':') ? 2 : 0;
|
||||
|
||||
+1
-1
Submodule subprojects/libsys4 updated: df3793dd07...3f44e18aaa
Reference in New Issue
Block a user