mirror of
https://gitee.com/zlgopen/awtk.git
synced 2024-12-01 19:49:11 +08:00
838 lines
24 KiB
C++
838 lines
24 KiB
C++
/**
|
|
* File: fdb.c
|
|
* Author: AWTK Develop Team
|
|
* Brief: fscript remote debugger
|
|
*
|
|
* Copyright (c) 2022 - 2024 Guangzhou ZHIYUAN Electronics Co.,Ltd.
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* History:
|
|
* ================================================================
|
|
* 2022-01-20 Li XianJing <lixianjing@zlg.cn> created
|
|
*
|
|
*/
|
|
|
|
#include "tkc.h"
|
|
#include "debugger/debugger.h"
|
|
#include "debugger/debugger_lldb.h"
|
|
#include "debugger/debugger_client_tcp.h"
|
|
|
|
#ifndef WIN32
|
|
#include <readline/history.h>
|
|
#include <readline/readline.h>
|
|
|
|
static char* command_generator(const char* text, int state);
|
|
static char** command_completion(const char* text, int start, int end) {
|
|
char** matches = NULL;
|
|
|
|
if (start == 0) {
|
|
matches = rl_completion_matches(text, command_generator);
|
|
}
|
|
|
|
return (matches);
|
|
}
|
|
static ret_t aw_read_line_init(void) {
|
|
rl_readline_name = (char*)"fdb";
|
|
rl_attempted_completion_function = command_completion;
|
|
|
|
return RET_OK;
|
|
}
|
|
|
|
static char* aw_read_line(const char* tips) {
|
|
return readline(tips);
|
|
}
|
|
|
|
static ret_t aw_read_line_free(char* line) {
|
|
free(line);
|
|
return RET_OK;
|
|
}
|
|
|
|
static ret_t aw_read_line_add_history(char* line) {
|
|
add_history(line);
|
|
return RET_OK;
|
|
}
|
|
|
|
#else
|
|
static char s_line[1024];
|
|
static ret_t aw_read_line_init(void) {
|
|
return RET_OK;
|
|
}
|
|
|
|
static char* aw_read_line(const char* tips) {
|
|
printf("%s", tips);
|
|
fflush(stdout);
|
|
fgets(s_line, sizeof(s_line) - 1, stdin);
|
|
|
|
return s_line;
|
|
}
|
|
|
|
static ret_t aw_read_line_free(char* line) {
|
|
return RET_OK;
|
|
}
|
|
|
|
static ret_t aw_read_line_add_history(char* line) {
|
|
return RET_OK;
|
|
}
|
|
#endif /**/
|
|
|
|
typedef struct _app_info_t {
|
|
tk_object_t* obj;
|
|
bool_t completed;
|
|
debugger_t* debugger;
|
|
int32_t current_line;
|
|
const char* current_func;
|
|
} app_info_t;
|
|
|
|
#include "tkc/data_reader_mem.h"
|
|
#include "conf_io/conf_ubjson.h"
|
|
#include "tkc/time_now.h"
|
|
|
|
#define KNRM "\x1B[0m"
|
|
#define KGRN "\x1B[32m"
|
|
#define KYEL "\x1B[33m"
|
|
#define KMAG "\x1B[35m"
|
|
|
|
typedef ret_t (*cmd_line_func_t)(app_info_t* app, tokenizer_t* tokenizer);
|
|
|
|
typedef struct _cmd_entry_t {
|
|
const char* name;
|
|
const char* alias;
|
|
const char* desc;
|
|
const char* help;
|
|
cmd_line_func_t func;
|
|
} cmd_entry_t;
|
|
|
|
#define SHOW_CODE_LINES 10
|
|
|
|
static ret_t fdb_show_code(app_info_t* app, bool_t all) {
|
|
tokenizer_t t;
|
|
int32_t line = 0;
|
|
binary_data_t code = {0, NULL};
|
|
int32_t start = 0;
|
|
int32_t end = 0;
|
|
bool_t meet_return = FALSE;
|
|
|
|
if (debugger_get_code(app->debugger, &code) != RET_OK) {
|
|
return RET_FAIL;
|
|
}
|
|
|
|
end = app->current_line + SHOW_CODE_LINES;
|
|
start = tk_max_int(app->current_line - SHOW_CODE_LINES, 0);
|
|
tokenizer_init_ex(&t, (const char*)(code.data), code.size, "", "\r\n");
|
|
|
|
while (tokenizer_has_more(&t)) {
|
|
const char* text = tokenizer_next(&t);
|
|
if (*text == '\r') {
|
|
line++;
|
|
meet_return = TRUE;
|
|
continue;
|
|
} else if (*text == '\n') {
|
|
if (meet_return) {
|
|
meet_return = FALSE;
|
|
} else {
|
|
line++;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (line >= start && line <= end) {
|
|
if (line == app->current_line) {
|
|
log_debug(KGRN "%d: =>%s" KNRM, line, text);
|
|
} else {
|
|
log_debug("%d: %s", line, text);
|
|
}
|
|
log_debug("\n");
|
|
}
|
|
|
|
if (line > end) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
tokenizer_deinit(&t);
|
|
log_debug("\n");
|
|
|
|
return RET_OK;
|
|
}
|
|
|
|
static ret_t fdb_show_threads(const char* title, tk_object_t* obj) {
|
|
log_debug("%s:\n", title);
|
|
log_debug("---------------------\n");
|
|
if (obj != NULL) {
|
|
uint32_t i = 0;
|
|
uint64_t id = 0;
|
|
const char* name = NULL;
|
|
char path[MAX_PATH + 1] = {0};
|
|
uint32_t n = tk_object_get_prop_uint32(obj, "body.threads.#size", 0);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
tk_snprintf(path, sizeof(path) - 1, "body.threads.[%d].name", i);
|
|
name = tk_object_get_prop_str(obj, path);
|
|
|
|
tk_snprintf(path, sizeof(path) - 1, "body.threads.[%d].id", i);
|
|
id = tk_object_get_prop_uint64(obj, path, 0);
|
|
|
|
log_debug("[%" PRIu64 "]: %s\n", id, name);
|
|
}
|
|
}
|
|
|
|
return RET_OK;
|
|
}
|
|
|
|
static ret_t fdb_show_variables(const char* title, tk_object_t* obj) {
|
|
log_debug("%s:\n", title);
|
|
log_debug("---------------------\n");
|
|
if (obj != NULL) {
|
|
uint32_t i = 0;
|
|
const char* name = NULL;
|
|
const char* type = NULL;
|
|
const char* value = NULL;
|
|
char path[MAX_PATH + 1] = {0};
|
|
uint32_t n = tk_object_get_prop_uint32(obj, "body.variables.#size", 0);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
tk_snprintf(path, sizeof(path) - 1, "body.variables.[%d].evaluateName", i);
|
|
name = tk_object_get_prop_str(obj, path);
|
|
tk_snprintf(path, sizeof(path) - 1, "body.variables.[%d].value", i);
|
|
value = tk_object_get_prop_str(obj, path);
|
|
|
|
tk_snprintf(path, sizeof(path) - 1, "body.variables.[%d].type", i);
|
|
type = tk_object_get_prop_str(obj, path);
|
|
|
|
log_debug("[%2d] %s (%s): %s\n", i, name, type, value);
|
|
}
|
|
}
|
|
|
|
return RET_OK;
|
|
}
|
|
|
|
static ret_t func_launch(app_info_t* app, tokenizer_t* tokenizer) {
|
|
int argc = 0;
|
|
char* argv[32];
|
|
char exec[MAX_PATH + 1] = {0};
|
|
char work_dir[MAX_PATH + 1] = {0};
|
|
const char* program = tokenizer_next(tokenizer);
|
|
|
|
if (program == NULL) {
|
|
return RET_BAD_PARAMS;
|
|
}
|
|
|
|
if (tk_str_start_with(program, STR_SCHEMA_PID) || tk_str_start_with(program, STR_SCHEMA_WASM)) {
|
|
return debugger_launch_app(app->debugger, program, work_dir, argc, argv);
|
|
} else if (tk_str_eq(program, "wasm")) {
|
|
return debugger_launch_app(app->debugger, STR_LLDB_CONFIG, work_dir, argc, argv);
|
|
}
|
|
|
|
while (tokenizer_has_more(tokenizer)) {
|
|
argv[argc++] = strdup(tokenizer_next(tokenizer));
|
|
}
|
|
|
|
path_abs(program, exec, sizeof(exec) - 1);
|
|
path_cwd(work_dir);
|
|
debugger_launch_app(app->debugger, exec, work_dir, argc, argv);
|
|
|
|
while (argc > 0) {
|
|
TKMEM_FREE(argv[--argc]);
|
|
}
|
|
|
|
return RET_OK;
|
|
}
|
|
|
|
static ret_t fdb_on_debugger_events(void* ctx, event_t* e) {
|
|
str_t astr;
|
|
str_t* str = &astr;
|
|
app_info_t* app = (app_info_t*)ctx;
|
|
|
|
str_init(str, 100);
|
|
switch (e->type) {
|
|
case DEBUGGER_RESP_MSG_FRAME_CHANGED: {
|
|
debugger_frame_changed_event_t* event = debugger_frame_changed_event_cast(e);
|
|
app->current_func = event->func;
|
|
app->current_line = event->line;
|
|
str_append(str, ">[frame_info]");
|
|
if (event->file_path != NULL) {
|
|
str_append(str, event->file_path);
|
|
str_append(str, ":");
|
|
}
|
|
str_append_int64(str, event->line);
|
|
str_append(str, ":");
|
|
str_append(str, event->func);
|
|
break;
|
|
}
|
|
case DEBUGGER_RESP_MSG_BREAKED: {
|
|
debugger_breaked_event_t* event = debugger_breaked_event_cast(e);
|
|
app->current_line = event->line;
|
|
str_append(str, ">[bread]");
|
|
if (event->file_path != NULL) {
|
|
str_append(str, event->file_path);
|
|
str_append(str, ":");
|
|
} else {
|
|
str_append(str, "line:");
|
|
}
|
|
str_append_int64(str, event->line);
|
|
break;
|
|
}
|
|
case DEBUGGER_RESP_MSG_LOG: {
|
|
debugger_log_event_t* event = debugger_log_event_cast(e);
|
|
str_append(str, ">");
|
|
str_append_int(str, event->line);
|
|
str_append(str, ":");
|
|
str_append(str, event->message);
|
|
break;
|
|
}
|
|
case DEBUGGER_RESP_MSG_ERROR: {
|
|
debugger_error_event_t* event = debugger_error_event_cast(e);
|
|
str_append(str, ">>");
|
|
str_append_int(str, event->line);
|
|
str_append(str, ":");
|
|
str_append(str, event->message);
|
|
break;
|
|
}
|
|
case DEBUGGER_RESP_MSG_COMPLETED: {
|
|
str_append(str, "completed()");
|
|
app->completed = TRUE;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
log_debug("%s\n", str->str);
|
|
str_reset(str);
|
|
|
|
return RET_OK;
|
|
}
|
|
|
|
static ret_t func_target(app_info_t* app, tokenizer_t* tokenizer) {
|
|
debugger_t* debugger = NULL;
|
|
const char* target = tokenizer_next(tokenizer);
|
|
const char* host = "localhost";
|
|
|
|
if (target == NULL) {
|
|
return RET_BAD_PARAMS;
|
|
}
|
|
|
|
TK_OBJECT_UNREF(app->debugger);
|
|
if (tk_str_eq(target, "lldb")) {
|
|
debugger = debugger_lldb_create_ex(host, DEBUGGER_TCP_PORT, 5000);
|
|
} else {
|
|
const char* code_id = DEBUGGER_DEFAULT_CODE_ID;
|
|
|
|
debugger = debugger_client_tcp_create(host, DEBUGGER_TCP_PORT);
|
|
if (debugger == NULL) {
|
|
log_debug("connect to \"%s\" %d failed!\n", host, DEBUGGER_TCP_PORT);
|
|
exit(0);
|
|
}
|
|
debugger_attach(debugger, DEBUGGER_LANG_FSCRIPT, code_id);
|
|
debugger_set_break_point(debugger, DEBUGGER_START_LINE);
|
|
}
|
|
|
|
emitter_on(EMITTER(debugger), DEBUGGER_RESP_MSG_BREAKED, fdb_on_debugger_events, app);
|
|
emitter_on(EMITTER(debugger), DEBUGGER_RESP_MSG_FRAME_CHANGED, fdb_on_debugger_events, app);
|
|
emitter_on(EMITTER(debugger), DEBUGGER_RESP_MSG_LOG, fdb_on_debugger_events, app);
|
|
emitter_on(EMITTER(debugger), DEBUGGER_RESP_MSG_ERROR, fdb_on_debugger_events, app);
|
|
emitter_on(EMITTER(debugger), DEBUGGER_RESP_MSG_COMPLETED, fdb_on_debugger_events, app);
|
|
|
|
app->debugger = debugger;
|
|
|
|
return RET_OK;
|
|
}
|
|
|
|
static ret_t func_local(app_info_t* app, tokenizer_t* tokenizer) {
|
|
tk_object_t* obj = NULL;
|
|
int32_t index = tokenizer_next_int(tokenizer, -1);
|
|
|
|
if (index < 0) {
|
|
index = debugger_get_current_frame(app->debugger);
|
|
}
|
|
obj = debugger_get_local(app->debugger, index);
|
|
fdb_show_variables("local vars", obj);
|
|
TK_OBJECT_UNREF(obj);
|
|
|
|
return RET_OK;
|
|
}
|
|
|
|
static ret_t func_print(app_info_t* app, tokenizer_t* tokenizer) {
|
|
tk_object_t* obj = NULL;
|
|
const char* name = tokenizer->str + tokenizer->cursor;
|
|
uint32_t size = tk_strlen(name);
|
|
char* var_name = tk_strdup(name);
|
|
var_name[size - 1] = '\0';
|
|
obj = debugger_get_var(app->debugger, var_name);
|
|
fdb_show_variables("var", obj);
|
|
TK_OBJECT_UNREF(obj);
|
|
TKMEM_FREE(var_name);
|
|
|
|
return RET_OK;
|
|
}
|
|
|
|
static ret_t func_self(app_info_t* app, tokenizer_t* tokenizer) {
|
|
tk_object_t* obj = debugger_get_self(app->debugger);
|
|
fdb_show_variables("member vars", obj);
|
|
TK_OBJECT_UNREF(obj);
|
|
|
|
return RET_OK;
|
|
}
|
|
|
|
static ret_t func_threads(app_info_t* app, tokenizer_t* tokenizer) {
|
|
tk_object_t* obj = debugger_get_threads(app->debugger);
|
|
fdb_show_threads("threads:", obj);
|
|
TK_OBJECT_UNREF(obj);
|
|
|
|
return RET_OK;
|
|
}
|
|
|
|
static ret_t func_global(app_info_t* app, tokenizer_t* tokenizer) {
|
|
tk_object_t* obj = debugger_get_global(app->debugger);
|
|
fdb_show_variables("global vars", obj);
|
|
TK_OBJECT_UNREF(obj);
|
|
|
|
return RET_OK;
|
|
}
|
|
|
|
static ret_t func_pause(app_info_t* app, tokenizer_t* tokenizer) {
|
|
debugger_pause(app->debugger);
|
|
return RET_OK;
|
|
}
|
|
|
|
static ret_t func_stop(app_info_t* app, tokenizer_t* tokenizer) {
|
|
tk_object_t* obj = TK_OBJECT(app->debugger);
|
|
debugger_stop(app->debugger);
|
|
TK_OBJECT_UNREF(obj);
|
|
return RET_OK;
|
|
}
|
|
|
|
static ret_t func_restart(app_info_t* app, tokenizer_t* tokenizer) {
|
|
debugger_restart(app->debugger);
|
|
sleep_ms(300);
|
|
return fdb_show_code(app, FALSE);
|
|
}
|
|
|
|
static ret_t func_config(app_info_t* app, tokenizer_t* tokenizer) {
|
|
const char* filename = tokenizer_next(tokenizer);
|
|
return debugger_load_config(app->debugger, filename);
|
|
}
|
|
|
|
|
|
static ret_t func_set_break(app_info_t* app, tokenizer_t* tokenizer) {
|
|
if (app->debugger->vt->set_break_point_ex != NULL) {
|
|
const char* position = tokenizer_next(tokenizer);
|
|
return debugger_set_break_point_ex(app->debugger, position);
|
|
} else if (app->debugger->vt->set_break_point != NULL) {
|
|
int32_t line = tokenizer_next_int(tokenizer, 0);
|
|
return debugger_set_break_point(app->debugger, line);
|
|
}
|
|
|
|
return RET_BAD_PARAMS;
|
|
}
|
|
|
|
static ret_t fdb_show_break_points(app_info_t* app) {
|
|
int32_t i = 0;
|
|
tokenizer_t t;
|
|
binary_data_t data = {0, NULL};
|
|
debugger_get_break_points(app->debugger, &data);
|
|
|
|
tokenizer_init(&t, (char*)data.data, data.size, "\n");
|
|
log_debug("breakpoints:\n---------------------------\n");
|
|
while (tokenizer_has_more(&t)) {
|
|
const char* bp = tokenizer_next(&t);
|
|
log_debug(" [%d] %s\n", i, bp);
|
|
i++;
|
|
}
|
|
tokenizer_deinit(&t);
|
|
|
|
return RET_OK;
|
|
}
|
|
|
|
static ret_t func_list_break(app_info_t* app, tokenizer_t* tokenizer) {
|
|
fdb_show_break_points(app);
|
|
|
|
return RET_OK;
|
|
}
|
|
|
|
static ret_t func_list_debuggers(app_info_t* app, tokenizer_t* tokenizer) {
|
|
binary_data_t data = {0, NULL};
|
|
|
|
debugger_get_debuggers(app->debugger, &data);
|
|
log_debug("debuggers:\n%s", (char*)data.data);
|
|
|
|
return RET_OK;
|
|
}
|
|
|
|
static ret_t fdb_show_callstack(app_info_t* app, uint32_t start, uint32_t level, bool_t is_ex) {
|
|
int32_t i = 0, n = 0;
|
|
tk_object_t* obj = debugger_get_callstack(app->debugger);
|
|
return_value_if_fail(obj != NULL, RET_BAD_PARAMS);
|
|
if (is_ex) {
|
|
obj = debugger_get_callstack_ex(app->debugger, start, level, debugger_get_current_thread_id(app->debugger));
|
|
} else {
|
|
obj = debugger_get_callstack(app->debugger);
|
|
}
|
|
|
|
log_debug("thread_id:%" PRIu64 " callstack:\n---------------------------\n",
|
|
debugger_get_current_thread_id(app->debugger));
|
|
n = tk_object_get_prop_uint32(obj, DEBUGER_CALLSTACK_NODE_NAME ".#size", 0);
|
|
for (i = 0; i < n; i++) {
|
|
char path[MAX_PATH + 1] = {0};
|
|
uint32_t line_number = 0;
|
|
const char* func = NULL;
|
|
const char* file_path = NULL;
|
|
|
|
tk_snprintf(path, sizeof(path), DEBUGER_CALLSTACK_NODE_NAME ".[%d].name", i);
|
|
func = tk_object_get_prop_str(obj, path);
|
|
|
|
tk_snprintf(path, sizeof(path), DEBUGER_CALLSTACK_NODE_NAME ".[%d].path", i);
|
|
file_path = tk_object_get_prop_str(obj, path);
|
|
|
|
tk_snprintf(path, sizeof(path), DEBUGER_CALLSTACK_NODE_NAME ".[%d].line", i);
|
|
line_number = tk_object_get_prop_uint32(obj, path, 0);
|
|
|
|
if (i == debugger_get_current_frame(app->debugger)) {
|
|
log_debug(KGRN "=> [%d] ", i);
|
|
if (func != NULL) {
|
|
log_debug(" %s ", func);
|
|
}
|
|
if (line_number > 0) {
|
|
log_debug(" line:%d ", line_number);
|
|
}
|
|
if (file_path != NULL) {
|
|
log_debug(" (%s) ", file_path);
|
|
}
|
|
log_debug("\r\n" KNRM);
|
|
} else {
|
|
log_debug(" [%d] ", i);
|
|
if (func != NULL) {
|
|
log_debug(" %s ", func);
|
|
}
|
|
if (line_number > 0) {
|
|
log_debug(" line:%d ", line_number);
|
|
}
|
|
if (file_path != NULL) {
|
|
log_debug(" (%s) ", file_path);
|
|
}
|
|
log_debug("\r\n");
|
|
}
|
|
}
|
|
tk_object_unref(obj);
|
|
|
|
return RET_OK;
|
|
}
|
|
|
|
static ret_t func_backtrace(app_info_t* app, tokenizer_t* tokenizer) {
|
|
int32_t level = tokenizer_next_int(tokenizer, -1);
|
|
bool_t is_ex = level >= 0;
|
|
fdb_show_callstack(app, 0, level, is_ex);
|
|
|
|
return RET_OK;
|
|
}
|
|
|
|
static ret_t func_set_thread_id(app_info_t* app, tokenizer_t* tokenizer) {
|
|
uint64_t thread_id = tokenizer_next_int64(tokenizer, -1);
|
|
if (thread_id >= 0) {
|
|
ret_t ret = RET_OK;
|
|
bool_t find = FALSE;
|
|
uint32_t i = 0, n = 0;
|
|
tk_object_t* obj = debugger_get_threads(app->debugger);
|
|
n = tk_object_get_prop_uint32(obj, "body.threads.#size", 0);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
uint64_t id = 0;
|
|
char path[MAX_PATH + 1] = {0};
|
|
tk_snprintf(path, sizeof(path) - 1, "body.threads.[%d].id", i);
|
|
id = tk_object_get_prop_uint64(obj, path, 0);
|
|
if (thread_id == id) {
|
|
find = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (find) {
|
|
ret = debugger_set_current_thread_id(app->debugger, thread_id);
|
|
return_value_if_fail(ret == RET_OK, ret);
|
|
return fdb_show_callstack(app, 0, 0, FALSE);
|
|
} else {
|
|
log_debug("not found thread id, set fail ! \r\n");
|
|
return RET_NOT_FOUND;
|
|
}
|
|
} else {
|
|
return RET_BAD_PARAMS;
|
|
}
|
|
}
|
|
|
|
static ret_t func_remove_break(app_info_t* app, tokenizer_t* tokenizer) {
|
|
const char* bp = tokenizer_next(tokenizer);
|
|
if (bp != NULL) {
|
|
log_debug("remove break at line: %s\n", bp);
|
|
return debugger_remove_break_point_ex(app->debugger, bp);
|
|
} else {
|
|
log_debug("remove all break points\n");
|
|
return debugger_clear_break_points(app->debugger);
|
|
}
|
|
}
|
|
|
|
static ret_t func_step_in(app_info_t* app, tokenizer_t* tokenizer) {
|
|
uint32_t num = 0;
|
|
debugger_step_in(app->debugger);
|
|
debugger_dispatch_messages(app->debugger, 300, &num);
|
|
return fdb_show_code(app, FALSE);
|
|
}
|
|
|
|
static ret_t func_next(app_info_t* app, tokenizer_t* tokenizer) {
|
|
uint32_t num = 0;
|
|
debugger_step_over(app->debugger);
|
|
debugger_dispatch_messages(app->debugger, 300, &num);
|
|
return fdb_show_code(app, FALSE);
|
|
}
|
|
|
|
static ret_t func_continue(app_info_t* app, tokenizer_t* tokenizer) {
|
|
uint32_t num = 0;
|
|
debugger_continue(app->debugger);
|
|
debugger_dispatch_messages(app->debugger, 300, &num);
|
|
return debugger_is_running(app->debugger) ? RET_OK : fdb_show_code(app, FALSE);
|
|
}
|
|
|
|
static ret_t func_flush(app_info_t* app, tokenizer_t* tokenizer) {
|
|
uint32_t num = 0;
|
|
debugger_dispatch_messages(app->debugger, 300, &num);
|
|
return RET_OK;
|
|
}
|
|
|
|
static ret_t func_step_out(app_info_t* app, tokenizer_t* tokenizer) {
|
|
uint32_t num = 0;
|
|
debugger_step_out(app->debugger);
|
|
debugger_dispatch_messages(app->debugger, 300, &num);
|
|
return fdb_show_code(app, FALSE);
|
|
}
|
|
|
|
static ret_t func_step_loop_over(app_info_t* app, tokenizer_t* tokenizer) {
|
|
uint32_t num = 0;
|
|
debugger_step_loop_over(app->debugger);
|
|
debugger_dispatch_messages(app->debugger, 300, &num);
|
|
return fdb_show_code(app, FALSE);
|
|
}
|
|
|
|
static ret_t func_frame(app_info_t* app, tokenizer_t* tokenizer) {
|
|
int32_t index = tokenizer_next_int(tokenizer, -1);
|
|
if (index >= 0) {
|
|
debugger_set_current_frame(app->debugger, index);
|
|
return fdb_show_code(app, FALSE);
|
|
} else {
|
|
return RET_BAD_PARAMS;
|
|
}
|
|
}
|
|
|
|
static ret_t func_get_curr_frame(app_info_t* app, tokenizer_t* tokenizer) {
|
|
uint64_t thread_id = debugger_get_current_thread_id(app->debugger);
|
|
int32_t frame = debugger_get_current_frame(app->debugger);
|
|
log_debug("thread_id:%" PRIu64 ", frame:%d", thread_id, frame);
|
|
log_debug("\n");
|
|
return RET_OK;
|
|
}
|
|
|
|
static ret_t func_get_code(app_info_t* app, tokenizer_t* tokenizer) {
|
|
fdb_show_code(app, FALSE);
|
|
return RET_OK;
|
|
}
|
|
|
|
static ret_t func_quit(app_info_t* app, tokenizer_t* tokenizer) {
|
|
debugger_deinit(app->debugger);
|
|
return RET_STOP;
|
|
}
|
|
|
|
static ret_t func_help(app_info_t* app, tokenizer_t* tokenizer);
|
|
static const cmd_entry_t s_cmds[] = {
|
|
{"target", "t", "select target", "t [default|lldb]", func_target},
|
|
{"run", "r", "run a program", "r program arg1 arg2...", func_launch},
|
|
{"print", "p", "show a var, support path(eg: a.b[1].name).", "p name", func_print},
|
|
{"frame", "f", "select current frame", "f index", func_frame},
|
|
{"get_frame", "gf", "get current frame", "gf", func_get_curr_frame},
|
|
{"flush", "fl", "flush socket", "fl", func_flush},
|
|
{"next", "n", "run next line code", "n", func_next},
|
|
{"pause", "ps", "pause the running program", "pause", func_pause},
|
|
{"stop", "stop", "stop the running program", "stop", func_stop},
|
|
{"step_loop_over", "u", "step loop over", "u", func_step_loop_over},
|
|
{"step_in", "s", "step in function", "s", func_step_in},
|
|
{"step_out", "so", "step out function", "so", func_step_out},
|
|
{"continue", "c", "continue", "c", func_continue},
|
|
{"get_code", "l", "show source code", "l", func_get_code},
|
|
{"break", "b", "set break point", "b func\n b filename:line", func_set_break},
|
|
{"list_break", "lb", "list breakpoints", "lb", func_list_break},
|
|
{"list_debuggers", "ld", "list debuggers", "ld", func_list_debuggers},
|
|
{"delete_break", "d", "delete breakpoints", "d position", func_remove_break},
|
|
{"local", "local", "show local variables", "local", func_local},
|
|
{"self", "self", "show member variables", "self", func_self},
|
|
{"global", "global", "show global variables", "global", func_global},
|
|
{"threads", "threads", "show threads", "threads", func_threads},
|
|
{"set_thread", "st", "set curr thread id", "st id", func_set_thread_id},
|
|
{"backtrace (level)", "bt", "show backtrace (level)", "bt (level)", func_backtrace},
|
|
{"quit", "q", "Quit debugger", "q", func_quit},
|
|
{"restart", "rs", "restart app", "rs", func_restart},
|
|
{"config", "conf", "load config", "conf lldb.json", func_config},
|
|
{NULL, NULL, NULL}};
|
|
|
|
static char* command_generator(const char* text, int state) {
|
|
static int list_index, len;
|
|
|
|
if (!state) {
|
|
list_index = 0;
|
|
len = strlen(text);
|
|
}
|
|
|
|
while (s_cmds[list_index].name != NULL) {
|
|
const cmd_entry_t* iter = s_cmds + list_index;
|
|
list_index++;
|
|
|
|
if (iter->name == NULL) {
|
|
break;
|
|
}
|
|
|
|
if (strncmp(iter->name, text, len) == 0) {
|
|
return strdup(iter->name);
|
|
}
|
|
|
|
if (strncmp(iter->help, text, len) == 0) {
|
|
return strdup(iter->help);
|
|
}
|
|
}
|
|
|
|
return ((char*)NULL);
|
|
}
|
|
|
|
static ret_t fdb_show_help(app_info_t* app, const char* cmd) {
|
|
uint32_t i = 0;
|
|
printf(KMAG "================================================\n" KNRM);
|
|
while (s_cmds[i].name != NULL) {
|
|
const cmd_entry_t* iter = s_cmds + i;
|
|
if (cmd == NULL || tk_str_eq(iter->name, cmd) || tk_str_eq(iter->alias, cmd)) {
|
|
printf(KYEL "%u: %s(%s)\n" KNRM, i, iter->desc, iter->alias);
|
|
printf("------------------------------\n");
|
|
printf(" # %s\n", iter->help);
|
|
printf(KGRN "------------------------------------------------\n" KNRM);
|
|
}
|
|
i++;
|
|
}
|
|
printf(KMAG "================================================\n" KNRM);
|
|
|
|
return RET_OK;
|
|
}
|
|
|
|
static ret_t func_help(app_info_t* app, tokenizer_t* tokenizer) {
|
|
return fdb_show_help(app, NULL);
|
|
}
|
|
|
|
static ret_t register_functions(object_t* obj) {
|
|
uint32_t i = 0;
|
|
while (s_cmds[i].name != NULL) {
|
|
const cmd_entry_t* iter = s_cmds + i;
|
|
tk_object_set_prop_pointer(obj, iter->alias, (void*)(iter->func));
|
|
tk_object_set_prop_pointer(obj, iter->name, (void*)(iter->func));
|
|
i++;
|
|
}
|
|
return RET_OK;
|
|
}
|
|
|
|
static ret_t fdb_shell_exec(app_info_t* app, const char* line) {
|
|
tokenizer_t t;
|
|
ret_t ret = RET_OK;
|
|
const char* name = NULL;
|
|
cmd_line_func_t func = NULL;
|
|
|
|
return_value_if_fail(app != NULL && line != NULL, RET_BAD_PARAMS);
|
|
tokenizer_init(&t, line, strlen(line), " \t\r\n");
|
|
|
|
name = tokenizer_next(&t);
|
|
if (name == NULL || *name == '\0') {
|
|
return RET_OK;
|
|
}
|
|
|
|
func = (cmd_line_func_t)object_get_prop_pointer(app->obj, name);
|
|
if (func == NULL) {
|
|
func = func_help;
|
|
}
|
|
|
|
if (app->debugger == NULL) {
|
|
if (!tk_str_eq(name, "target") && !tk_str_eq(name, "t") && !tk_str_eq(name, "quit") &&
|
|
!tk_str_eq(name, "q") && !tk_str_eq(name, "help") && !tk_str_eq(name, "h")) {
|
|
printf("Please use target command to choose debug target.\n");
|
|
return RET_OK;
|
|
}
|
|
}
|
|
|
|
ret = func(app, &t);
|
|
if (ret == RET_BAD_PARAMS) {
|
|
fdb_show_help(app, name);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
ret_t fdb_shell_run(void) {
|
|
app_info_t app;
|
|
tk_object_t* obj = object_default_create();
|
|
|
|
memset(&app, 0x00, sizeof(app));
|
|
app.obj = obj;
|
|
app.debugger = NULL;
|
|
app.completed = FALSE;
|
|
app.current_line = 0xffff;
|
|
|
|
aw_read_line_init();
|
|
register_functions(obj);
|
|
|
|
while (TRUE) {
|
|
uint32_t num = 0;
|
|
char* line = aw_read_line(KMAG "[fdb] # " KNRM);
|
|
if (line == NULL) {
|
|
break;
|
|
}
|
|
|
|
if (app.debugger != NULL) {
|
|
debugger_dispatch_messages(app.debugger, 16, &num);
|
|
}
|
|
if (fdb_shell_exec(&app, line) == RET_STOP) {
|
|
aw_read_line_free(line);
|
|
break;
|
|
}
|
|
if (app.debugger != NULL) {
|
|
debugger_dispatch_messages(app.debugger, 16, &num);
|
|
}
|
|
|
|
aw_read_line_add_history(line);
|
|
aw_read_line_free(line);
|
|
|
|
if (app.completed) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
app.debugger = NULL;
|
|
TK_OBJECT_UNREF(obj);
|
|
|
|
return RET_OK;
|
|
}
|
|
|
|
#include "tkc/data_reader_factory.h"
|
|
#include "tkc/data_writer_factory.h"
|
|
#include "tkc/data_writer_file.h"
|
|
#include "tkc/data_writer_wbuffer.h"
|
|
#include "tkc/data_reader_file.h"
|
|
#include "tkc/data_reader_mem.h"
|
|
|
|
int main(int argc, char* argv[]) {
|
|
platform_prepare();
|
|
tk_socket_init();
|
|
|
|
data_writer_factory_set(data_writer_factory_create());
|
|
data_reader_factory_set(data_reader_factory_create());
|
|
data_writer_factory_register(data_writer_factory(), "file", data_writer_file_create);
|
|
data_reader_factory_register(data_reader_factory(), "file", data_reader_file_create);
|
|
data_reader_factory_register(data_reader_factory(), "mem", data_reader_mem_create);
|
|
data_writer_factory_register(data_writer_factory(), "wbuffer", data_writer_wbuffer_create);
|
|
|
|
fdb_shell_run();
|
|
|
|
tk_socket_deinit();
|
|
|
|
return 0;
|
|
}
|