mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-19 19:10:17 +00:00
Merge pull request #379 from lightovernight/gsoc-2025-debugger
Gsoc 2025 debugger
This commit is contained in:
parent
8d5cd7c1b8
commit
ec8f8f473e
19
include/pocketpy/debugger/core.h
Normal file
19
include/pocketpy/debugger/core.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "pocketpy/common/sstream.h"
|
||||
#include "pocketpy/common/str.h"
|
||||
#include "pocketpy/common/vector.h"
|
||||
#include "pocketpy/pocketpy.h"
|
||||
|
||||
enum C11_STEP_MODE { C11_STEP_IN, C11_STEP_OVER, C11_STEP_OUT, C11_STEP_CONTINUE };
|
||||
|
||||
void c11_debugger_init(void);
|
||||
void c11_debugger_set_step_mode(enum C11_STEP_MODE mode);
|
||||
void c11_debugger_on_trace(py_Frame* frame, enum py_TraceEvent event);
|
||||
void c11_debugger_frames(c11_sbuf* buffer);
|
||||
void c11_debugger_scopes(int frameid, c11_sbuf* buffer);
|
||||
bool c11_debugger_unfold_var(int var_id, c11_sbuf* buffer);
|
||||
int c11_debugger_setbreakpoint(const char* filename, int lineno);
|
||||
int c11_debugger_reset_breakpoints_by_source(const char* sourcesname);
|
||||
int c11_debugger_should_pause(void);
|
||||
int c11_debugger_should_keep_pause(void);
|
1
include/pocketpy/debugger/dap.h
Normal file
1
include/pocketpy/debugger/dap.h
Normal file
@ -0,0 +1 @@
|
||||
#pragma once
|
@ -703,12 +703,15 @@ PK_API bool py_pickle_dumps(py_Ref val) PY_RAISE PY_RETURN;
|
||||
PK_API bool py_pickle_loads(const unsigned char* data, int size) PY_RAISE PY_RETURN;
|
||||
|
||||
/************* Profiler *************/
|
||||
|
||||
PK_API void py_profiler_begin();
|
||||
PK_API void py_profiler_end();
|
||||
PK_API void py_profiler_reset();
|
||||
PK_API char* py_profiler_report();
|
||||
|
||||
/************* DAP *************/
|
||||
PK_API void py_debugger_waitforattach(const char* hostname, unsigned short port);
|
||||
PK_API void py_debugger_exit(int exitCode);
|
||||
|
||||
/************* Unchecked Functions *************/
|
||||
|
||||
PK_API py_ObjectRef py_tuple_data(py_Ref self);
|
||||
|
294
src/debugger/core.c
Normal file
294
src/debugger/core.c
Normal file
@ -0,0 +1,294 @@
|
||||
|
||||
|
||||
#include "pocketpy/interpreter/frame.h"
|
||||
#include "pocketpy/pocketpy.h"
|
||||
#include <ctype.h>
|
||||
|
||||
#include "pocketpy/debugger/core.h"
|
||||
|
||||
typedef struct c11_debugger_breakpoint {
|
||||
const char* sourcename;
|
||||
int lineno;
|
||||
} c11_debugger_breakpoint;
|
||||
|
||||
typedef struct c11_debugger_scope_index {
|
||||
int locals_ref;
|
||||
int globals_ref;
|
||||
} c11_debugger_scope_index;
|
||||
|
||||
#define SMALLMAP_T__HEADER
|
||||
#define SMALLMAP_T__SOURCE
|
||||
#define K int
|
||||
#define V c11_debugger_scope_index
|
||||
#define NAME c11_smallmap_d2index
|
||||
#include "pocketpy/xmacros/smallmap.h"
|
||||
#undef SMALLMAP_T__SOURCE
|
||||
#undef SMALLMAP_T__HEADER
|
||||
|
||||
static struct c11_debugger {
|
||||
py_Frame* current_frame;
|
||||
const char* current_filename;
|
||||
enum py_TraceEvent current_event;
|
||||
|
||||
int curr_stack_depth;
|
||||
int current_line;
|
||||
int pause_allowed_depth;
|
||||
int step_line;
|
||||
enum C11_STEP_MODE step_mode;
|
||||
bool keep_suspend;
|
||||
|
||||
c11_vector breakpoints;
|
||||
c11_vector py_frames;
|
||||
c11_smallmap_d2index scopes_query_cache;
|
||||
|
||||
#define python_vars py_r7()
|
||||
|
||||
} debugger;
|
||||
|
||||
inline static void init_structures() {
|
||||
c11_vector__ctor(&debugger.breakpoints, sizeof(c11_debugger_breakpoint));
|
||||
c11_vector__ctor(&debugger.py_frames, sizeof(py_Frame*));
|
||||
c11_smallmap_d2index__ctor(&debugger.scopes_query_cache);
|
||||
py_newlist(python_vars);
|
||||
py_newnil(py_list_emplace(python_vars));
|
||||
}
|
||||
|
||||
inline static void clear_structures() {
|
||||
c11_vector__clear(&debugger.py_frames);
|
||||
c11_smallmap_d2index__clear(&debugger.scopes_query_cache);
|
||||
py_list_clear(python_vars);
|
||||
py_newnone(py_list_emplace(python_vars));
|
||||
}
|
||||
|
||||
inline static py_Ref get_variable(int var_ref) {
|
||||
assert(var_ref < py_list_len(python_vars) && var_ref > 0);
|
||||
return py_list_getitem(python_vars, var_ref);
|
||||
}
|
||||
|
||||
void c11_debugger_init() {
|
||||
debugger.curr_stack_depth = 0;
|
||||
debugger.current_line = -1;
|
||||
debugger.pause_allowed_depth = -1;
|
||||
debugger.step_line = -1;
|
||||
debugger.keep_suspend = false;
|
||||
debugger.step_mode = C11_STEP_CONTINUE;
|
||||
init_structures();
|
||||
}
|
||||
|
||||
void c11_debugger_on_trace(py_Frame* frame, enum py_TraceEvent event) {
|
||||
debugger.current_frame = frame;
|
||||
debugger.current_event = event;
|
||||
debugger.current_filename = py_Frame_sourceloc(debugger.current_frame, &debugger.current_line);
|
||||
clear_structures();
|
||||
if(event == TRACE_EVENT_LINE) return;
|
||||
event == TRACE_EVENT_PUSH ? debugger.curr_stack_depth++ : debugger.curr_stack_depth--;
|
||||
}
|
||||
|
||||
void c11_debugger_set_step_mode(enum C11_STEP_MODE mode) {
|
||||
switch(mode) {
|
||||
case C11_STEP_IN: debugger.pause_allowed_depth = INT32_MAX; break;
|
||||
case C11_STEP_OVER:
|
||||
debugger.pause_allowed_depth = debugger.curr_stack_depth;
|
||||
debugger.step_line = debugger.current_line;
|
||||
break;
|
||||
case C11_STEP_OUT: debugger.pause_allowed_depth = debugger.curr_stack_depth - 1; break;
|
||||
case C11_STEP_CONTINUE: debugger.pause_allowed_depth = -1; break;
|
||||
}
|
||||
debugger.step_mode = mode;
|
||||
debugger.keep_suspend = false;
|
||||
}
|
||||
|
||||
|
||||
int c11_debugger_setbreakpoint(const char* filename, int lineno) {
|
||||
c11_debugger_breakpoint breakpoint = {.sourcename = c11_strdup(filename), .lineno = lineno};
|
||||
c11_vector__push(c11_debugger_breakpoint, &debugger.breakpoints, breakpoint);
|
||||
return debugger.breakpoints.length;
|
||||
}
|
||||
|
||||
int c11_debugger_reset_breakpoints_by_source(const char* sourcesname) {
|
||||
c11_vector tmp_breakpoints;
|
||||
c11_vector__ctor(&tmp_breakpoints, sizeof(c11_debugger_breakpoint));
|
||||
|
||||
c11__foreach(c11_debugger_breakpoint, &debugger.breakpoints, it) {
|
||||
if(strcmp(it->sourcename, sourcesname) != 0) {
|
||||
c11_debugger_breakpoint* dst =
|
||||
(c11_debugger_breakpoint*)c11_vector__emplace(&tmp_breakpoints);
|
||||
*dst = *it;
|
||||
} else {
|
||||
free((void*)it->sourcename);
|
||||
}
|
||||
}
|
||||
|
||||
c11_vector__swap(&tmp_breakpoints, &debugger.breakpoints);
|
||||
c11_vector__dtor(&tmp_breakpoints);
|
||||
return debugger.breakpoints.length;
|
||||
}
|
||||
|
||||
int c11_debugger_should_pause() {
|
||||
if(debugger.current_event == TRACE_EVENT_POP) return false;
|
||||
bool should_pause = false;
|
||||
int is_out = debugger.curr_stack_depth <= debugger.pause_allowed_depth;
|
||||
int is_new_line = debugger.current_line != debugger.step_line;
|
||||
switch(debugger.step_mode) {
|
||||
case C11_STEP_IN: should_pause = true; break;
|
||||
|
||||
case C11_STEP_OVER:
|
||||
if(is_new_line && is_out) should_pause = true;
|
||||
break;
|
||||
case C11_STEP_OUT:
|
||||
if(is_out) should_pause = true;
|
||||
break;
|
||||
case C11_STEP_CONTINUE:
|
||||
default: break;
|
||||
}
|
||||
if(debugger.step_mode == C11_STEP_CONTINUE) {
|
||||
c11__foreach(c11_debugger_breakpoint, &debugger.breakpoints, bp) {
|
||||
if(strcmp(debugger.current_filename, bp->sourcename) == 0 &&
|
||||
debugger.current_line == bp->lineno) {
|
||||
should_pause = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(should_pause) { debugger.keep_suspend = true; }
|
||||
return should_pause;
|
||||
}
|
||||
|
||||
int c11_debugger_should_keep_pause(void) { return debugger.keep_suspend; }
|
||||
|
||||
|
||||
inline static c11_sv sv_from_cstr(const char* str) {
|
||||
c11_sv sv = {.data = str, .size = strlen(str)};
|
||||
return sv;
|
||||
}
|
||||
|
||||
const inline static char* get_basename(const char* path) {
|
||||
const char* last_slash = strrchr(path, '/');
|
||||
#ifdef _WIN32
|
||||
const char* last_backslash = strrchr(path, '\\');
|
||||
if(!last_slash || (last_backslash && last_backslash > last_slash)) {
|
||||
last_slash = last_backslash;
|
||||
}
|
||||
#endif
|
||||
return last_slash ? last_slash + 1 : path;
|
||||
}
|
||||
|
||||
void c11_debugger_frames(c11_sbuf* buffer) {
|
||||
c11_sbuf__write_cstr(buffer, "{\"stackFrames\": [");
|
||||
int idx = 0;
|
||||
py_Frame* now_frame = debugger.current_frame;
|
||||
debugger.py_frames.length = 0;
|
||||
while(now_frame) {
|
||||
if(idx > 0) c11_sbuf__write_char(buffer, ',');
|
||||
int line;
|
||||
const char* filename = py_Frame_sourceloc(now_frame, &line);
|
||||
const char* basename = get_basename(filename);
|
||||
const char* modname = now_frame->co->name->data;
|
||||
pk_sprintf(
|
||||
buffer,
|
||||
"{\"id\": %d, \"name\": %Q, \"line\": %d, \"column\": 1, \"source\": {\"name\": %Q, \"path\": %Q}}",
|
||||
idx,
|
||||
sv_from_cstr(modname),
|
||||
line,
|
||||
sv_from_cstr(basename),
|
||||
sv_from_cstr(filename));
|
||||
c11_vector__push(py_Frame*, &debugger.py_frames, now_frame);
|
||||
now_frame = now_frame->f_back;
|
||||
idx++;
|
||||
}
|
||||
pk_sprintf(buffer, "], \"totalFrames\": %d}", idx);
|
||||
}
|
||||
|
||||
inline static c11_debugger_scope_index append_new_scope(int frameid) {
|
||||
assert(frameid < debugger.py_frames.length);
|
||||
py_Frame* requested_frame = c11__getitem(py_Frame*, &debugger.py_frames, frameid);
|
||||
int base_index = py_list_len(python_vars);
|
||||
py_Ref new_locals = py_list_emplace(python_vars);
|
||||
py_Ref new_globals = py_list_emplace(python_vars);
|
||||
py_Frame_newlocals(requested_frame, new_locals);
|
||||
py_Frame_newglobals(requested_frame, new_globals);
|
||||
c11_debugger_scope_index result = {.locals_ref = base_index, .globals_ref = base_index + 1};
|
||||
return result;
|
||||
}
|
||||
|
||||
void c11_debugger_scopes(int frameid, c11_sbuf* buffer) {
|
||||
// query cache
|
||||
c11_debugger_scope_index* result =
|
||||
c11_smallmap_d2index__try_get(&debugger.scopes_query_cache, frameid);
|
||||
|
||||
c11_sbuf__write_cstr(buffer, "{\"scopes\":");
|
||||
const char* scopes_fmt =
|
||||
"[{\"name\": \"locals\", \"variablesReference\": %d, \"expensive\": false}, "
|
||||
"{\"name\": \"globals\", \"variablesReference\": %d, \"expensive\": true}]";
|
||||
if(result != NULL) {
|
||||
pk_sprintf(buffer, scopes_fmt, result->locals_ref, result->globals_ref);
|
||||
} else {
|
||||
c11_debugger_scope_index new_record = append_new_scope(frameid);
|
||||
c11_smallmap_d2index__set(&debugger.scopes_query_cache, frameid, new_record);
|
||||
pk_sprintf(buffer, scopes_fmt, new_record.locals_ref, new_record.globals_ref);
|
||||
}
|
||||
c11_sbuf__write_char(buffer, '}');
|
||||
}
|
||||
|
||||
bool c11_debugger_unfold_var(int var_id, c11_sbuf* buffer) {
|
||||
py_Ref var = get_variable(var_id);
|
||||
if(!var) return false;
|
||||
|
||||
// 1. extend
|
||||
const char* expand_code = NULL;
|
||||
switch(py_typeof(var)) {
|
||||
case tp_dict:
|
||||
case tp_namedict: expand_code = "[(k,v) for k,v in _0.items()]"; break;
|
||||
case tp_list:
|
||||
case tp_tuple: expand_code = "[(f'[{i}]',v) for i,v in enumerate(_0)]"; break;
|
||||
default: expand_code = "[(k,v) for k,v in _0.__dict__.items()]"; break;
|
||||
}
|
||||
if(!py_smarteval(expand_code, NULL, var)) {
|
||||
py_printexc();
|
||||
return false;
|
||||
}
|
||||
py_Ref kv_list = py_pushtmp();
|
||||
py_assign(kv_list, py_retval());
|
||||
// 2. prepare base_ref
|
||||
int base_index = py_list_len(python_vars);
|
||||
py_Ref base_var_ref = py_pushtmp();
|
||||
py_newint(base_var_ref, base_index);
|
||||
|
||||
// 3. construct DAP JSON
|
||||
const char* dap_code =
|
||||
"{'variables': ["
|
||||
" {"
|
||||
" 'name': kv[0],"
|
||||
" 'value': repr(kv[1]),"
|
||||
" 'variablesReference': (_1 + i) if (isinstance(kv[1], (dict, list, tuple)) or kv[1].__dict__ is not None) else 0,"
|
||||
" 'type': type(kv[1]).__name__"
|
||||
" }"
|
||||
" for i, kv in enumerate(_0)"
|
||||
"]}";
|
||||
if(!py_smarteval(dap_code, NULL, kv_list, base_var_ref)) {
|
||||
py_printexc();
|
||||
return false;
|
||||
}
|
||||
py_Ref dap_obj = py_pushtmp();
|
||||
py_assign(dap_obj, py_retval());
|
||||
|
||||
// 4. extend python_vars
|
||||
if(!py_smartexec("_0.extend([kv[1] for kv in _1])", NULL, python_vars, kv_list)) {
|
||||
py_printexc();
|
||||
return false;
|
||||
}
|
||||
// 5. dump & write
|
||||
if(!py_json_dumps(dap_obj, 0)) {
|
||||
py_printexc();
|
||||
return false;
|
||||
}
|
||||
|
||||
c11_sbuf__write_cstr(buffer, py_tostr(py_retval()));
|
||||
|
||||
// 6. clear
|
||||
py_pop(); // dap_obj
|
||||
py_pop(); // base_var_ref
|
||||
py_pop(); // kv_list
|
||||
return true;
|
||||
}
|
||||
#undef python_vars
|
374
src/debugger/dap.c
Normal file
374
src/debugger/dap.c
Normal file
@ -0,0 +1,374 @@
|
||||
#include <stdbool.h>
|
||||
#include "pocketpy/common/socket.h"
|
||||
#include "pocketpy/debugger/core.h"
|
||||
#include "pocketpy/objects/base.h"
|
||||
|
||||
#define DAP_COMMAND_LIST(X) \
|
||||
X(initialize) \
|
||||
X(setBreakpoints) \
|
||||
X(attach) \
|
||||
X(next) \
|
||||
X(stepIn) \
|
||||
X(stepOut) \
|
||||
X(continue) \
|
||||
X(stackTrace) \
|
||||
X(scopes) \
|
||||
X(variables) \
|
||||
X(threads) \
|
||||
X(configurationDone)
|
||||
|
||||
#define DECLARE_HANDLE_FN(name) void c11_dap_handle_##name(py_Ref arguments, c11_sbuf*);
|
||||
DAP_COMMAND_LIST(DECLARE_HANDLE_FN)
|
||||
#undef DECLARE_ARG_FN
|
||||
|
||||
typedef void (*c11_dap_arg_parser_fn)(py_Ref, c11_sbuf*);
|
||||
|
||||
typedef struct {
|
||||
const char* command;
|
||||
c11_dap_arg_parser_fn parser;
|
||||
} dap_command_entry;
|
||||
|
||||
#define DAP_ENTRY(name) {#name, c11_dap_handle_##name},
|
||||
static dap_command_entry dap_command_table[] = {
|
||||
DAP_COMMAND_LIST(DAP_ENTRY){NULL, NULL}
|
||||
};
|
||||
|
||||
#undef DAP_ENTRY
|
||||
|
||||
// #undef DAP_COMMAND_LIST
|
||||
|
||||
// static int dap_next_seq = 1;
|
||||
static struct c11_dap_server {
|
||||
int dap_next_seq;
|
||||
char buffer_data[1024];
|
||||
char* buffer_begin;
|
||||
int buffer_length;
|
||||
c11_socket_handler server;
|
||||
c11_socket_handler toclient;
|
||||
bool isconfiguredone;
|
||||
bool isatttach;
|
||||
} server;
|
||||
|
||||
void c11_dap_handle_initialize(py_Ref arguments, c11_sbuf* buffer) {
|
||||
c11_sbuf__write_cstr(buffer, "\"body\":{\"supportsConfigurationDoneRequest\":true}");
|
||||
c11_sbuf__write_char(buffer, ',');
|
||||
}
|
||||
|
||||
void c11_dap_handle_attach(py_Ref arguments, c11_sbuf* buffer) {
|
||||
server.isatttach = true;
|
||||
|
||||
}
|
||||
|
||||
void c11_dap_handle_next(py_Ref arguments, c11_sbuf* buffer) {
|
||||
c11_debugger_set_step_mode(C11_STEP_OVER);
|
||||
}
|
||||
|
||||
void c11_dap_handle_stepIn(py_Ref arguments, c11_sbuf* buffer) {
|
||||
c11_debugger_set_step_mode(C11_STEP_IN);
|
||||
}
|
||||
|
||||
void c11_dap_handle_stepOut(py_Ref arguments, c11_sbuf* buffer) {
|
||||
c11_debugger_set_step_mode(C11_STEP_OUT);
|
||||
}
|
||||
|
||||
void c11_dap_handle_continue(py_Ref arguments, c11_sbuf* buffer) {
|
||||
c11_debugger_set_step_mode(C11_STEP_CONTINUE);
|
||||
}
|
||||
|
||||
void c11_dap_handle_threads(py_Ref arguments, c11_sbuf* buffer) {
|
||||
c11_sbuf__write_cstr(buffer,
|
||||
"\"body\":{\"threads\":["
|
||||
"{\"id\":1,\"name\":\"MainThread\"}"
|
||||
"]}");
|
||||
c11_sbuf__write_char(buffer, ',');
|
||||
}
|
||||
|
||||
void c11_dap_handle_configurationDone(py_Ref arguments, c11_sbuf* buffer) {
|
||||
server.isconfiguredone = true;
|
||||
}
|
||||
|
||||
inline static void c11_dap_build_Breakpoint(int id, int line, c11_sbuf* buffer) {
|
||||
pk_sprintf(buffer, "{\"id\":%d,\"verified\":true,\"line\":%d}", id, line);
|
||||
}
|
||||
|
||||
void c11_dap_handle_setBreakpoints(py_Ref arguments, c11_sbuf* buffer) {
|
||||
if(!py_smarteval("_0['source']['path']", NULL, arguments)) {
|
||||
py_printexc();
|
||||
return;
|
||||
}
|
||||
const char* sourcename = py_tostr(py_retval());
|
||||
if(!py_smarteval("[bp['line'] for bp in _0['breakpoints']]", NULL, arguments)) {
|
||||
py_printexc();
|
||||
return;
|
||||
}
|
||||
int bp_numbers = c11_debugger_reset_breakpoints_by_source(sourcename);
|
||||
c11_sbuf__write_cstr(buffer, "\"body\":");
|
||||
c11_sbuf__write_cstr(buffer, "{\"breakpoints\":[");
|
||||
for(int i = 0; i < py_list_len(py_retval()); i++) {
|
||||
if(i != 0) c11_sbuf__write_char(buffer, ',');
|
||||
int line = py_toint(py_list_getitem(py_retval(), i));
|
||||
c11_debugger_setbreakpoint(sourcename, line);
|
||||
c11_dap_build_Breakpoint(i + bp_numbers, line, buffer);
|
||||
}
|
||||
c11_sbuf__write_cstr(buffer, "]}");
|
||||
c11_sbuf__write_char(buffer, ',');
|
||||
}
|
||||
|
||||
void c11_dap_handle_stackTrace(py_Ref arguments, c11_sbuf* buffer) {
|
||||
c11_sbuf__write_cstr(buffer, "\"body\":");
|
||||
c11_debugger_frames(buffer);
|
||||
c11_sbuf__write_char(buffer, ',');
|
||||
}
|
||||
|
||||
void c11_dap_handle_scopes(py_Ref arguments, c11_sbuf* buffer) {
|
||||
int res = py_dict_getitem_by_str(arguments, "frameId");
|
||||
if(res <= 0) {
|
||||
if(res == 0) {
|
||||
printf("[DEBUGGER ERROR] no frameID found\n");
|
||||
} else {
|
||||
py_printexc();
|
||||
}
|
||||
return;
|
||||
}
|
||||
int frameid = py_toint(py_retval());
|
||||
c11_sbuf__write_cstr(buffer, "\"body\":");
|
||||
c11_debugger_scopes(frameid, buffer);
|
||||
c11_sbuf__write_char(buffer, ',');
|
||||
}
|
||||
|
||||
void c11_dap_handle_variables(py_Ref arguments, c11_sbuf* buffer) {
|
||||
int res = py_dict_getitem_by_str(arguments, "variablesReference");
|
||||
if(res <= 0) {
|
||||
if(res == 0) {
|
||||
printf("[DEBUGGER ERROR] no frameID found\n");
|
||||
} else {
|
||||
py_printexc();
|
||||
}
|
||||
return;
|
||||
}
|
||||
int variablesReference = py_toint(py_retval());
|
||||
c11_sbuf__write_cstr(buffer, "\"body\":");
|
||||
c11_debugger_unfold_var(variablesReference, buffer);
|
||||
c11_sbuf__write_char(buffer, ',');
|
||||
}
|
||||
|
||||
const char* c11_dap_handle_request(const char* message) {
|
||||
if(!py_json_loads(message)) {
|
||||
py_printexc();
|
||||
return NULL;
|
||||
}
|
||||
py_Ref py_request = py_pushtmp();
|
||||
py_Ref py_arguments = py_pushtmp();
|
||||
py_Ref py_command = py_pushtmp();
|
||||
py_assign(py_request, py_retval());
|
||||
|
||||
int res = py_dict_getitem_by_str(py_request, "command");
|
||||
if(res == -1) {
|
||||
py_printexc();
|
||||
return NULL;
|
||||
} else if(res == 0) {
|
||||
return "cannot find attribute command";
|
||||
}
|
||||
py_assign(py_command, py_retval());
|
||||
const char* command = py_tostr(py_command);
|
||||
|
||||
res = py_dict_getitem_by_str(py_request, "arguments");
|
||||
if(res == -1) {
|
||||
py_printexc();
|
||||
return NULL;
|
||||
}
|
||||
py_assign(py_arguments, py_retval());
|
||||
|
||||
res = py_dict_getitem_by_str(py_request, "seq");
|
||||
if(res == -1) {
|
||||
py_printexc();
|
||||
return NULL;
|
||||
}
|
||||
int request_seq = (res == 1) ? py_toint(py_retval()) : 0;
|
||||
|
||||
c11_sbuf response_buffer;
|
||||
c11_sbuf__ctor(&response_buffer);
|
||||
pk_sprintf(&response_buffer,
|
||||
"{\"seq\":%d,\"type\":\"response\",\"request_seq\":%d,\"command\":\"%s\",",
|
||||
server.dap_next_seq++,
|
||||
request_seq,
|
||||
command);
|
||||
for(dap_command_entry* entry = dap_command_table; entry->command != NULL; entry++) {
|
||||
if(strcmp(entry->command, command) == 0) {
|
||||
entry->parser(py_arguments, &response_buffer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
c11_sbuf__write_cstr(&response_buffer, "\"success\":true}");
|
||||
|
||||
c11_string* c11_string_response = c11_sbuf__submit(&response_buffer);
|
||||
const char* response = c11_strdup(c11_string_response->data);
|
||||
c11_string__delete(c11_string_response);
|
||||
|
||||
py_pop(); // py_arguments
|
||||
py_pop(); // py_request
|
||||
py_pop(); // py_command
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
void c11_dap_send_event(const char* event_name, const char* body_json) {
|
||||
char json[256];
|
||||
int json_len = snprintf(json,
|
||||
sizeof(json),
|
||||
"{\"seq\":%d,\"type\":\"event\",\"event\":\"%s\",\"body\":%s}",
|
||||
server.dap_next_seq++,
|
||||
event_name,
|
||||
body_json);
|
||||
|
||||
char header[64];
|
||||
int header_len = snprintf(header, sizeof(header), "Content-Length: %d\r\n\r\n", json_len);
|
||||
|
||||
c11_socket_send(server.toclient, header, header_len);
|
||||
c11_socket_send(server.toclient, json, json_len);
|
||||
}
|
||||
|
||||
void c11_dap_send_stop_event() {
|
||||
c11_dap_send_event("stopped",
|
||||
"{\"reason\":\"breakpoint\",\"threadId\":1,\"allThreadsStopped\":true}");
|
||||
}
|
||||
|
||||
void c11_dap_send_initialized_event() { c11_dap_send_event("initialized", "{}"); }
|
||||
|
||||
int c11_dap_read_content_length(const char* buffer, int* header_length) {
|
||||
const char* length_begin = strstr(buffer, "Content-Length: ");
|
||||
if(!length_begin) {
|
||||
printf("[DEBUGGER ERROR] : no Content-Length filed found\n");
|
||||
*header_length = 0;
|
||||
return -1;
|
||||
}
|
||||
length_begin += strlen("Content-Length: ");
|
||||
const char* length_end = strstr(length_begin, "\r\n\r\n");
|
||||
if(!length_end) {
|
||||
printf("[DEBUGGER ERROR] : the seperator should br \\r\\n\\r\\n\n");
|
||||
*header_length = 0;
|
||||
return -1;
|
||||
}
|
||||
char* endptr = NULL;
|
||||
long value = strtol(length_begin, &endptr, 10);
|
||||
if(endptr == length_begin) {
|
||||
printf("[DEBUGGER EORRO] : the number is empty\n");
|
||||
*header_length = 0;
|
||||
return -1;
|
||||
}
|
||||
*header_length = (int)(endptr - buffer) + 4;
|
||||
return (int)value;
|
||||
}
|
||||
|
||||
const char* c11_dap_read_message() {
|
||||
int message_length =
|
||||
c11_socket_recv(server.toclient, server.buffer_begin, 1024 - server.buffer_length);
|
||||
if(message_length == 0) {
|
||||
printf("[DEBUGGER INFO] : client quit\n");
|
||||
exit(0);
|
||||
}
|
||||
if(message_length < 0) { return NULL; }
|
||||
server.buffer_length += message_length;
|
||||
if(server.buffer_length == 0) return NULL;
|
||||
int header_length;
|
||||
int content_length = c11_dap_read_content_length(server.buffer_begin, &header_length);
|
||||
if(content_length <= 0 || header_length <= 0) {
|
||||
printf("[DEBUGGER ERROR]: invalid DAP header\n");
|
||||
server.buffer_length = 0;
|
||||
server.buffer_begin = server.buffer_data;
|
||||
return NULL;
|
||||
}
|
||||
server.buffer_begin += header_length;
|
||||
server.buffer_length -= header_length;
|
||||
c11_sbuf result;
|
||||
c11_sbuf__ctor(&result);
|
||||
while(content_length > server.buffer_length) {
|
||||
c11_sbuf__write_cstrn(&result, server.buffer_begin, server.buffer_length);
|
||||
content_length -= server.buffer_length;
|
||||
message_length = c11_socket_recv(server.toclient, server.buffer_data, 1024);
|
||||
if(message_length == 0) {
|
||||
printf("[DEBUGGER INFO] : client quit\n");
|
||||
exit(0);
|
||||
}
|
||||
if(message_length < 0) continue;
|
||||
server.buffer_begin = server.buffer_data;
|
||||
server.buffer_length = message_length;
|
||||
}
|
||||
c11_sbuf__write_cstrn(&result, server.buffer_begin, content_length);
|
||||
server.buffer_begin += content_length;
|
||||
server.buffer_length -= content_length;
|
||||
memmove(server.buffer_data, server.buffer_begin, server.buffer_length);
|
||||
server.buffer_begin = server.buffer_data;
|
||||
c11_string* tmp_result = c11_sbuf__submit(&result);
|
||||
const char* dap_message = c11_strdup(tmp_result->data);
|
||||
c11_string__delete(tmp_result);
|
||||
return dap_message;
|
||||
}
|
||||
|
||||
void c11_dap_init_server(const char* hostname, unsigned short port) {
|
||||
server.dap_next_seq = 1;
|
||||
server.isconfiguredone = false;
|
||||
server.buffer_begin = server.buffer_data;
|
||||
server.server = c11_socket_create(C11_AF_INET, C11_SOCK_STREAM, 0);
|
||||
c11_socket_bind(server.server, hostname, port);
|
||||
c11_socket_listen(server.server, 0);
|
||||
printf("[DEBUGGER INFO] : listen on %s:%hu\n", hostname, port);
|
||||
server.toclient = c11_socket_accept(server.server, NULL, NULL);
|
||||
printf("[DEBUGGER INFO] : connected client\n");
|
||||
}
|
||||
|
||||
inline static void c11_dap_handle_message() {
|
||||
const char* message = c11_dap_read_message();
|
||||
if(message == NULL) return;
|
||||
printf("[DEBUGGER INFO] read request %s\n", message);
|
||||
const char* response_content = c11_dap_handle_request(message);
|
||||
if(response_content != NULL) { printf("[DEBUGGER INFO] send response %s\n", response_content); }
|
||||
c11_sbuf buffer;
|
||||
c11_sbuf__ctor(&buffer);
|
||||
pk_sprintf(&buffer, "Content-Length: %d\r\n\r\n%s", strlen(response_content), response_content);
|
||||
c11_string* response = c11_sbuf__submit(&buffer);
|
||||
c11_socket_send(server.toclient, response->data, response->size);
|
||||
free((void*)message);
|
||||
free((void*)response_content);
|
||||
c11_string__delete(response);
|
||||
}
|
||||
|
||||
void c11_dap_configure_debugger() {
|
||||
while(server.isconfiguredone == false) {
|
||||
c11_dap_handle_message();
|
||||
if(server.isatttach) {
|
||||
c11_dap_send_initialized_event();
|
||||
server.isatttach = false;
|
||||
}
|
||||
}
|
||||
printf("[DEBUGGER INFO] : configure done\n");
|
||||
}
|
||||
|
||||
void c11_dap_tracefunc(py_Frame* frame, enum py_TraceEvent event) {
|
||||
py_sys_settrace(NULL, false);
|
||||
c11_debugger_on_trace(frame, event);
|
||||
c11_dap_handle_message();
|
||||
if(!c11_debugger_should_pause()) {
|
||||
py_sys_settrace(c11_dap_tracefunc, false);
|
||||
return;
|
||||
}
|
||||
c11_dap_send_stop_event();
|
||||
while(c11_debugger_should_keep_pause()) {
|
||||
c11_dap_handle_message();
|
||||
}
|
||||
py_sys_settrace(c11_dap_tracefunc, false);
|
||||
}
|
||||
|
||||
void py_debugger_waitforattach(const char* hostname, unsigned short port) {
|
||||
c11_dap_init_server(hostname, port);
|
||||
c11_debugger_init();
|
||||
c11_dap_configure_debugger();
|
||||
c11_socket_set_block(server.toclient, 0);
|
||||
py_sys_settrace(c11_dap_tracefunc, true);
|
||||
}
|
||||
|
||||
void py_debugger_exit(int exitCode) {
|
||||
char body[64];
|
||||
snprintf(body, sizeof(body), "{\"exitCode\":%d}", exitCode);
|
||||
c11_dap_send_event("exited", body);
|
||||
}
|
18
src2/main.c
18
src2/main.c
@ -34,6 +34,7 @@ int main(int argc, char** argv) {
|
||||
#endif
|
||||
|
||||
bool profile = false;
|
||||
bool debug = false;
|
||||
const char* filename = NULL;
|
||||
|
||||
for(int i = 1; i < argc; i++) {
|
||||
@ -41,11 +42,20 @@ int main(int argc, char** argv) {
|
||||
profile = true;
|
||||
continue;
|
||||
}
|
||||
if(strcmp(argv[i], "--debug") == 0) {
|
||||
debug = true;
|
||||
continue;
|
||||
}
|
||||
if(filename == NULL) {
|
||||
filename = argv[i];
|
||||
continue;
|
||||
}
|
||||
printf("Usage: pocketpy [--profile] filename\n");
|
||||
printf("Usage: pocketpy [--profile] [--debug] filename\n");
|
||||
}
|
||||
|
||||
if(debug && profile) {
|
||||
printf("Error: --debug and --profile cannot be used together.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
py_initialize();
|
||||
@ -53,6 +63,8 @@ int main(int argc, char** argv) {
|
||||
|
||||
if(filename == NULL) {
|
||||
if(profile) printf("Warning: --profile is ignored in REPL mode.\n");
|
||||
if(debug) printf("Warning: --debug is ignored in REPL mode.\n");
|
||||
|
||||
printf("pocketpy " PK_VERSION " (" __DATE__ ", " __TIME__ ") ");
|
||||
printf("[%d bit] on %s", (int)(sizeof(void*) * 8), PY_SYS_PLATFORM_STRING);
|
||||
#ifndef NDEBUG
|
||||
@ -79,6 +91,8 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
} else {
|
||||
if(profile) py_profiler_begin();
|
||||
if(debug) py_debugger_waitforattach("127.0.0.1", 6110);
|
||||
|
||||
char* source = read_file(filename);
|
||||
if(source) {
|
||||
if(!py_exec(source, filename, EXEC_MODE, NULL))
|
||||
@ -101,5 +115,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
int code = py_checkexc(false) ? 1 : 0;
|
||||
py_finalize();
|
||||
|
||||
if(debug) py_debugger_exit(code);
|
||||
return code;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user