linuxOS_D21X/source/artinchip/awtk-ui/awtk/src/debugger/debugger_client.c
2024-11-29 16:23:11 +08:00

514 lines
17 KiB
C

/**
* File: debugger.c
* Author: AWTK Develop Team
* Brief: debugger
*
* Copyright (c) 2022 - 2022 Guangzhou ZHIYUAN Electronics Co.,Ltd.
*
* 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
* License file for more details.
*
*/
/**
* History:
* ================================================================
* 2022-01-11 Li XianJing <xianjimli@hotmail.com> created
*
*/
#include "tkc/str.h"
#include "tkc/buffer.h"
#include "tkc/data_reader_mem.h"
#include "conf_io/conf_json.h"
#include "ubjson/ubjson_parser.h"
#include "debugger/debugger_message.h"
#include "debugger/debugger_client.h"
static ret_t debugger_client_lock(debugger_t* debugger) {
return RET_OK;
}
static ret_t debugger_client_unlock(debugger_t* debugger) {
return RET_OK;
}
static ret_t debugger_client_write_data(tk_ostream_t* out, const void* data, uint32_t size) {
return tk_ostream_write_len(out, data, size, DEBUGGER_IO_WRITE_TIMEOUT) == size ? RET_OK : RET_IO;
}
static ret_t debugger_client_read_data(tk_istream_t* in, void* data, uint32_t size) {
return tk_istream_read_len(in, data, size, DEBUGGER_IO_READ_TIMEOUT) == size ? RET_OK : RET_IO;
}
static ret_t debugger_client_extend_buff(debugger_t* debugger, uint32_t size) {
ret_t ret = RET_OK;
debugger_client_t* client = DEBUGGER_CLIENT(debugger);
if (client->capacity < size) {
void* buff = TKMEM_REALLOC(client->buff, size);
if (buff != NULL) {
client->buff = buff;
client->capacity = size;
} else {
ret = RET_OOM;
}
}
return ret;
}
static ret_t debugger_client_dispatch_message(debugger_t* debugger, debugger_resp_t* resp) {
debugger_client_t* client = DEBUGGER_CLIENT(debugger);
switch (resp->code) {
case DEBUGGER_RESP_MSG_BREAKED: {
uint32_t line = 0;
debugger_breaked_event_t event;
tk_object_t* obj = ubjson_to_object(client->buff, resp->size);
return_value_if_fail(obj != NULL, RET_BAD_PARAMS);
line = tk_object_get_prop_int(obj, STR_DEBUGGER_EVENT_PROP_LINE, 0);
debugger_set_state(debugger, DEBUGGER_PROGRAM_STATE_PAUSED);
emitter_dispatch(EMITTER(debugger), debugger_breaked_event_init(&event, line));
TK_OBJECT_UNREF(obj);
break;
}
case DEBUGGER_RESP_MSG_LOG: {
uint32_t line = 0;
const char* message = NULL;
debugger_log_event_t event;
tk_object_t* obj = ubjson_to_object(client->buff, resp->size);
return_value_if_fail(obj != NULL, RET_BAD_PARAMS);
line = tk_object_get_prop_int(obj, STR_DEBUGGER_EVENT_PROP_LINE, 0);
message = tk_object_get_prop_str(obj, STR_DEBUGGER_EVENT_PROP_MESSAGE);
emitter_dispatch(EMITTER(debugger), debugger_log_event_init(&event, line, message));
TK_OBJECT_UNREF(obj);
break;
}
case DEBUGGER_RESP_MSG_ERROR: {
uint32_t line = 0;
const char* message = NULL;
debugger_error_event_t event;
tk_object_t* obj = ubjson_to_object(client->buff, resp->size);
return_value_if_fail(obj != NULL, RET_BAD_PARAMS);
line = tk_object_get_prop_int(obj, STR_DEBUGGER_EVENT_PROP_LINE, 0);
message = tk_object_get_prop_str(obj, STR_DEBUGGER_EVENT_PROP_MESSAGE);
emitter_dispatch(EMITTER(debugger), debugger_error_event_init(&event, line, message));
TK_OBJECT_UNREF(obj);
break;
}
case DEBUGGER_RESP_MSG_COMPLETED: {
client->program_completed = TRUE;
debugger_set_state(debugger, DEBUGGER_PROGRAM_STATE_TERMINATED);
emitter_dispatch_simple_event(EMITTER(debugger), DEBUGGER_RESP_MSG_COMPLETED);
break;
}
default: {
emitter_dispatch_simple_event(EMITTER(debugger), resp->code);
break;
}
}
return RET_OK;
}
static ret_t debugger_client_dispatch_one(debugger_t* debugger, debugger_resp_t* resp) {
debugger_client_t* client = DEBUGGER_CLIENT(debugger);
tk_istream_t* in = tk_iostream_get_istream(client->io);
return_value_if_fail(in != NULL, RET_BAD_PARAMS);
return_value_if_fail(debugger_client_read_data(in, resp, sizeof(*resp)) == RET_OK, RET_IO);
assert(DEBUGGER_VERSION == resp->version);
return_value_if_fail(debugger_client_extend_buff(debugger, resp->size) == RET_OK, RET_OOM);
if (resp->size > 0) {
return_value_if_fail(debugger_client_read_data(in, client->buff, resp->size) == RET_OK, RET_IO);
}
return debugger_client_dispatch_message(debugger, resp);
}
static ret_t debugger_client_dispatch_messages(debugger_t* debugger) {
debugger_resp_t resp;
tk_istream_t* in = NULL;
debugger_client_t* client = DEBUGGER_CLIENT(debugger);
return_value_if_fail(client != NULL, RET_BAD_PARAMS);
memset(&resp, 0x00, sizeof(resp));
in = tk_iostream_get_istream(client->io);
while (tk_istream_wait_for_data(in, 10) == RET_OK) {
if (debugger_client_dispatch_one(debugger, &resp) != RET_OK) {
break;
}
}
return RET_OK;
}
ret_t debugger_client_dispatch(debugger_t* debugger) {
return debugger_client_dispatch_messages(debugger);
}
ret_t debugger_client_wait_for_completed(debugger_t* debugger) {
debugger_resp_t resp;
debugger_client_t* client = DEBUGGER_CLIENT(debugger);
memset(&resp, 0x00, sizeof(resp));
while (!(client->program_completed)) {
break_if_fail(debugger_client_dispatch_one(debugger, &resp) == RET_OK);
if (resp.code == DEBUGGER_RESP_MSG_COMPLETED) {
break;
}
}
return RET_OK;
}
static ret_t debugger_client_read_packet(debugger_t* debugger, uint32_t resp_code, void** data,
uint32_t* size, ret_t* ret) {
debugger_resp_t resp;
debugger_client_t* client = DEBUGGER_CLIENT(debugger);
memset(&resp, 0x00, sizeof(resp));
while (TRUE) {
break_if_fail(debugger_client_dispatch_one(debugger, &resp) == RET_OK);
if (resp.code == resp_code) {
*data = client->buff;
*size = resp.size;
*ret = resp.error;
break;
}
}
return RET_OK;
}
static ret_t debugger_client_read_simple(debugger_t* debugger, uint32_t resp_code) {
void* data = NULL;
uint32_t size = 0;
ret_t ret = RET_FAIL;
debugger_client_read_packet(debugger, resp_code, &data, &size, &ret);
return ret;
}
static tk_object_t* debugger_client_read_object(debugger_t* debugger, uint32_t resp_code) {
void* data = NULL;
uint32_t size = 0;
ret_t ret = RET_FAIL;
if (debugger_client_read_packet(debugger, resp_code, &data, &size, &ret) == RET_OK &&
ret == RET_OK) {
assert(data != NULL && size > 0);
return ubjson_to_object(data, size);
}
return NULL;
}
static ret_t debugger_client_read_binary(debugger_t* debugger, uint32_t resp_code,
binary_data_t* data) {
ret_t ret = RET_FAIL;
debugger_client_read_packet(debugger, resp_code, &(data->data), &(data->size), &ret);
return ret;
}
static ret_t debugger_client_write_binary(debugger_t* debugger, uint32_t code, const void* data,
uint32_t size) {
debugger_req_t req;
debugger_client_t* client = DEBUGGER_CLIENT(debugger);
tk_ostream_t* out = tk_iostream_get_ostream(client->io);
memset(&req, 0x00, sizeof(req));
req.code = code;
req.size = size;
req.version = DEBUGGER_VERSION;
debugger_client_write_data(out, &req, sizeof(req));
if (req.size > 0) {
return debugger_client_write_data(out, data, size);
} else {
return RET_OK;
}
}
static ret_t debugger_client_write_simple(debugger_t* debugger, uint32_t code, uint32_t data) {
debugger_req_t req;
debugger_client_t* client = DEBUGGER_CLIENT(debugger);
tk_ostream_t* out = tk_iostream_get_ostream(client->io);
memset(&req, 0x00, sizeof(req));
req.code = code;
req.data = data;
req.size = 0;
req.version = DEBUGGER_VERSION;
return debugger_client_write_data(out, &req, sizeof(req));
}
static ret_t debugger_client_request_simple(debugger_t* debugger, uint32_t code, uint32_t data) {
return_value_if_fail(debugger_client_write_simple(debugger, code, data) == RET_OK, RET_FAIL);
return debugger_client_read_simple(debugger, code);
}
static ret_t debugger_client_request_binary(debugger_t* debugger, uint32_t code, const void* data,
uint32_t size) {
return_value_if_fail(debugger_client_write_binary(debugger, code, data, size) == RET_OK,
RET_FAIL);
return debugger_client_read_simple(debugger, code);
}
static ret_t debugger_client_stop(debugger_t* debugger) {
return debugger_client_request_simple(debugger, DEBUGGER_REQ_STOP, 0);
}
static ret_t debugger_client_pause(debugger_t* debugger) {
return debugger_client_request_simple(debugger, DEBUGGER_REQ_PAUSE, 0);
}
static ret_t debugger_client_restart(debugger_t* debugger) {
return debugger_client_request_simple(debugger, DEBUGGER_REQ_RESTART, 0);
}
static ret_t debugger_client_step_over(debugger_t* debugger) {
return debugger_client_request_simple(debugger, DEBUGGER_REQ_STEP_OVER, 0);
}
static ret_t debugger_client_step_in(debugger_t* debugger) {
return debugger_client_request_simple(debugger, DEBUGGER_REQ_STEP_IN, 0);
}
static ret_t debugger_client_step_out(debugger_t* debugger) {
return debugger_client_request_simple(debugger, DEBUGGER_REQ_STEP_OUT, 0);
}
static ret_t debugger_client_step_loop_over(debugger_t* debugger) {
return debugger_client_request_simple(debugger, DEBUGGER_REQ_STEP_LOOP_OVER, 0);
}
static ret_t debugger_client_continue(debugger_t* debugger) {
return debugger_client_request_simple(debugger, DEBUGGER_REQ_CONTINUE, 0);
}
typedef struct _visit_var_info_t {
uint32_t i;
str_t* str;
}visit_var_info_t;
static ret_t visit_var(void* ctx, const void* data) {
char buff[64] = {0};
visit_var_info_t* info = (visit_var_info_t*)ctx;
str_t* str = info->str;
named_value_t* nv = (named_value_t*)data;
if(info->i > 0) {
str_append(str, ",");
}
info->i++;
str_append(str, "{");
str_append_json_str_pair(str, "name", nv->name);
str_append(str, ",");
str_append_json_str_pair(str, "type", value_type_name(nv->value.type));
str_append(str, ",");
str_append_json_str_pair(str, "evaluateName", nv->name);
str_append(str, ",");
str_append_json_str_pair(str, "value", value_str_ex(&(nv->value), buff, sizeof(buff)));
str_append(str, "}");
return RET_OK;
}
static tk_object_t* debugger_client_variables_to_dap_format(tk_object_t* obj) {
str_t str;
char url[MAX_PATH+1] = {0};
visit_var_info_t info = {0, &str};
return_value_if_fail(obj != NULL, NULL);
str_init(&str, 1000);
str_append(&str, "{\"body\":{\"variables\":[");
tk_object_foreach_prop(obj, visit_var, &info);
TK_OBJECT_UNREF(obj);
str_append(&str, "]}}");
data_reader_mem_build_url(str.str, str.size, url);
obj = conf_json_load(url, FALSE);
str_reset(&str);
return obj;
}
static tk_object_t* debugger_client_get_local(debugger_t* debugger, uint32_t frame_index) {
if (debugger_client_write_simple(debugger, DEBUGGER_REQ_GET_LOCAL, frame_index) == RET_OK) {
tk_object_t* obj = debugger_client_read_object(debugger, DEBUGGER_RESP_GET_LOCAL);
return debugger_client_variables_to_dap_format(obj);
} else {
return NULL;
}
}
static tk_object_t* debugger_client_get_self(debugger_t* debugger) {
if (debugger_client_write_simple(debugger, DEBUGGER_REQ_GET_SELF, 0) == RET_OK) {
return debugger_client_read_object(debugger, DEBUGGER_RESP_GET_SELF);
} else {
return NULL;
}
}
static tk_object_t* debugger_client_get_global(debugger_t* debugger) {
if (debugger_client_write_simple(debugger, DEBUGGER_REQ_GET_GLOBAL, 0) == RET_OK) {
return debugger_client_read_object(debugger, DEBUGGER_RESP_GET_GLOBAL);
} else {
return NULL;
}
}
static ret_t debugger_client_get_callstack(debugger_t* debugger, binary_data_t* callstack) {
if (debugger_client_write_simple(debugger, DEBUGGER_REQ_GET_CALLSTACK, 0) == RET_OK) {
return debugger_client_read_binary(debugger, DEBUGGER_RESP_GET_CALLSTACK, callstack);
} else {
return RET_FAIL;
}
}
static ret_t debugger_client_clear_break_points(debugger_t* debugger) {
return debugger_client_request_simple(debugger, DEBUGGER_REQ_CLEAR_BREAK_POINTS, 0);
}
static ret_t debugger_client_set_break_point(debugger_t* debugger, uint32_t line) {
return debugger_client_request_simple(debugger, DEBUGGER_REQ_SET_BREAK_POINT, line);
}
static ret_t debugger_client_remove_break_point(debugger_t* debugger, uint32_t line) {
return debugger_client_request_simple(debugger, DEBUGGER_REQ_REMOVE_BREAK_POINT, line);
}
static ret_t debugger_client_attach(debugger_t* debugger, const char* lang, const char* code_id) {
char data[256];
tk_snprintf(data, sizeof(data) - 1, "%s:%s", lang, code_id);
return debugger_client_request_binary(debugger, DEBUGGER_REQ_ATTACH, data, strlen(data) + 1);
}
static ret_t debugger_client_deinit(debugger_t* debugger) {
return debugger_client_request_simple(debugger, DEBUGGER_REQ_DEINIT, 0);
}
static ret_t debugger_client_update_code(debugger_t* debugger, const binary_data_t* code) {
return debugger_client_request_binary(debugger, DEBUGGER_REQ_UPDATE_CODE, code->data, code->size);
}
static ret_t debugger_client_launch(debugger_t* debugger, const char* lang,
const binary_data_t* code) {
wbuffer_t wb;
ret_t ret = RET_FAIL;
wbuffer_init_extendable(&wb);
return_value_if_fail(wbuffer_extend_capacity(&wb, strlen(lang) + code->size + 1) == RET_OK,
RET_OOM);
wbuffer_write_binary(&wb, lang, strlen(lang));
wbuffer_write_binary(&wb, ":", 1);
wbuffer_write_binary(&wb, code->data, code->size);
ret = debugger_client_request_binary(debugger, DEBUGGER_REQ_LAUNCH, wb.data, wb.cursor);
wbuffer_deinit(&wb);
return ret;
}
static ret_t debugger_client_get_code(debugger_t* debugger, binary_data_t* code) {
if (debugger_client_write_simple(debugger, DEBUGGER_REQ_GET_CODE, 0) == RET_OK) {
return debugger_client_read_binary(debugger, DEBUGGER_RESP_GET_CODE, code);
} else {
return RET_FAIL;
}
}
static ret_t debugger_client_get_debuggers(debugger_t* debugger, binary_data_t* debuggers) {
if (debugger_client_write_simple(debugger, DEBUGGER_REQ_GET_DEBUGGERS, 0) == RET_OK) {
return debugger_client_read_binary(debugger, DEBUGGER_RESP_GET_DEBUGGERS, debuggers);
} else {
return RET_FAIL;
}
}
static ret_t debugger_client_get_break_points(debugger_t* debugger, binary_data_t* break_points) {
if (debugger_client_write_simple(debugger, DEBUGGER_REQ_GET_BREAK_POINTS, 0) == RET_OK) {
return debugger_client_read_binary(debugger, DEBUGGER_RESP_GET_BREAK_POINTS, break_points);
} else {
return RET_FAIL;
}
}
static const debugger_vtable_t s_debugger_client_vtable = {
.attach = debugger_client_attach,
.launch = debugger_client_launch,
.lang = "client",
.lock = debugger_client_lock,
.unlock = debugger_client_unlock,
.stop = debugger_client_stop,
.pause = debugger_client_pause,
.restart = debugger_client_restart,
.step_in = debugger_client_step_in,
.step_out = debugger_client_step_out,
.step_over = debugger_client_step_over,
.step_loop_over = debugger_client_step_loop_over,
.continve = debugger_client_continue,
.get_local = debugger_client_get_local,
.get_self = debugger_client_get_self,
.get_global = debugger_client_get_global,
.get_code = debugger_client_get_code,
.get_debuggers = debugger_client_get_debuggers,
.get_break_points = debugger_client_get_break_points,
.get_callstack = debugger_client_get_callstack,
.update_code = debugger_client_update_code,
.set_break_point = debugger_client_set_break_point,
.remove_break_point = debugger_client_remove_break_point,
.clear_break_points = debugger_client_clear_break_points,
.dispatch_messages = debugger_client_dispatch_messages,
.deinit = debugger_client_deinit,
};
debugger_client_t* debugger_client_cast(debugger_t* debugger) {
return_value_if_fail(debugger != NULL && debugger->vt == &s_debugger_client_vtable, NULL);
return (debugger_client_t*)debugger;
}
static ret_t debugger_client_on_destroy(tk_object_t* obj) {
debugger_client_t* debugger = DEBUGGER_CLIENT(obj);
return_value_if_fail(debugger != NULL, RET_BAD_PARAMS);
TKMEM_FREE(debugger->buff);
TK_OBJECT_UNREF(debugger->io);
return RET_OK;
}
static const object_vtable_t s_object_debugger_client_vtable = {
.type = "object_debugger_client",
.desc = "object_debugger_client",
.size = sizeof(debugger_client_t),
.is_collection = FALSE,
.on_destroy = debugger_client_on_destroy};
debugger_t* debugger_client_create(tk_iostream_t* io) {
debugger_client_t* debugger = NULL;
return_value_if_fail(io != NULL, NULL);
debugger = (debugger_client_t*)tk_object_create(&s_object_debugger_client_vtable);
return_value_if_fail(debugger != NULL, NULL);
debugger->io = io;
TK_OBJECT_REF(debugger->io);
debugger->debugger.vt = &s_debugger_client_vtable;
debugger->capacity = 10 * 1024;
debugger->buff = TKMEM_ALLOC(debugger->capacity);
return (debugger_t*)debugger;
}