mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-25 14:00:18 +00:00
Compare commits
No commits in common. "1b623ce2f8386e9b57fe481200e99beded25e16e" and "8d5cd7c1b89e0de5a928b1c904d4d9e957c3d35d" have entirely different histories.
1b623ce2f8
...
8d5cd7c1b8
@ -1,26 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "pocketpy/common/sstream.h"
|
|
||||||
#include "pocketpy/common/str.h"
|
|
||||||
#include "pocketpy/common/vector.h"
|
|
||||||
#include "pocketpy/pocketpy.h"
|
|
||||||
|
|
||||||
typedef enum { C11_STEP_IN, C11_STEP_OVER, C11_STEP_OUT, C11_STEP_CONTINUE } C11_STEP_MODE;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
C11_DEBUGGER_SUCCESS = 0,
|
|
||||||
C11_DEBUGGER_EXIT = 1,
|
|
||||||
C11_DEBUGGER_UNKNOW_ERROR = 3,
|
|
||||||
C11_DEBUGGER_FILEPATH_ERROR = 7
|
|
||||||
} C11_DEBUGGER_STATUS;
|
|
||||||
|
|
||||||
void c11_debugger_init(void);
|
|
||||||
void c11_debugger_set_step_mode(C11_STEP_MODE mode);
|
|
||||||
C11_DEBUGGER_STATUS 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);
|
|
||||||
@ -703,15 +703,12 @@ 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;
|
PK_API bool py_pickle_loads(const unsigned char* data, int size) PY_RAISE PY_RETURN;
|
||||||
|
|
||||||
/************* Profiler *************/
|
/************* Profiler *************/
|
||||||
|
|
||||||
PK_API void py_profiler_begin();
|
PK_API void py_profiler_begin();
|
||||||
PK_API void py_profiler_end();
|
PK_API void py_profiler_end();
|
||||||
PK_API void py_profiler_reset();
|
PK_API void py_profiler_reset();
|
||||||
PK_API char* py_profiler_report();
|
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 *************/
|
/************* Unchecked Functions *************/
|
||||||
|
|
||||||
PK_API py_ObjectRef py_tuple_data(py_Ref self);
|
PK_API py_ObjectRef py_tuple_data(py_Ref self);
|
||||||
|
|||||||
@ -1,312 +0,0 @@
|
|||||||
#include "pocketpy/interpreter/frame.h"
|
|
||||||
#include "pocketpy/pocketpy.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;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
const inline static char* format_filepath(const char* path) {
|
|
||||||
if(strstr(path, "..")) { return NULL; }
|
|
||||||
if(strstr(path + 1, "./") || strstr(path + 1, ".\\")) { return NULL; }
|
|
||||||
if(path[0] == '.' && (path[1] == '/' || path[1] == '\\')) { return path + 2; }
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
C11_DEBUGGER_STATUS c11_debugger_on_trace(py_Frame* frame, enum py_TraceEvent event) {
|
|
||||||
debugger.current_frame = frame;
|
|
||||||
debugger.current_event = event;
|
|
||||||
const char* source_name = py_Frame_sourceloc(debugger.current_frame, &debugger.current_line);
|
|
||||||
debugger.current_filename = format_filepath(source_name);
|
|
||||||
if(debugger.current_filename == NULL) { return C11_DEBUGGER_FILEPATH_ERROR; }
|
|
||||||
clear_structures();
|
|
||||||
switch(event) {
|
|
||||||
case TRACE_EVENT_PUSH: debugger.curr_stack_depth++; break;
|
|
||||||
case TRACE_EVENT_POP: debugger.curr_stack_depth--; break;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
if(debugger.curr_stack_depth == 0) return C11_DEBUGGER_EXIT;
|
|
||||||
return C11_DEBUGGER_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
void c11_debugger_set_step_mode(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 {
|
|
||||||
PK_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, '/');
|
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
|
||||||
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 and extend python_vars
|
|
||||||
py_Ref dap_obj = py_pushtmp();
|
|
||||||
py_newdict(dap_obj);
|
|
||||||
const char* dap_code =
|
|
||||||
"_2['variables'] = []\n"
|
|
||||||
"var_ref = _1\n"
|
|
||||||
"for k, v in _0:\n"
|
|
||||||
" has_children = isinstance(v, (dict, list, tuple)) or v.__dict__ is not None\n"
|
|
||||||
" _2['variables'].append({\n"
|
|
||||||
" 'name': k if type(k) == str else str(k),\n"
|
|
||||||
" 'value': repr(v) if type(v) == str else str(v),\n"
|
|
||||||
" 'variablesReference': var_ref if has_children else 0,\n"
|
|
||||||
" 'type': type(v).__name__\n"
|
|
||||||
" })\n"
|
|
||||||
" if has_children: var_ref += 1\n";
|
|
||||||
if(!py_smartexec(dap_code, NULL, kv_list, base_var_ref, dap_obj)) {
|
|
||||||
py_printexc();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. extend python_vars
|
|
||||||
if(!py_smartexec(
|
|
||||||
"_0.extend([v for k, v in _1 if isinstance(v, (dict, list, tuple)) or v.__dict__ is not None])",
|
|
||||||
NULL,
|
|
||||||
python_vars,
|
|
||||||
kv_list)) {
|
|
||||||
py_printexc();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. dump & write
|
|
||||||
if(!py_json_dumps(dap_obj, 0)) {
|
|
||||||
printf("dap_obj: %s\n", py_tpname(py_typeof(dap_obj)));
|
|
||||||
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
|
|
||||||
@ -1,403 +0,0 @@
|
|||||||
#include <stdbool.h>
|
|
||||||
#include <stdio.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 = c11_strdup(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, ',');
|
|
||||||
PK_FREE((void*)sourcename);
|
|
||||||
}
|
|
||||||
|
|
||||||
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",
|
|
||||||
"{\"threadId\":1,\"allThreadsStopped\":true}");
|
|
||||||
}
|
|
||||||
|
|
||||||
void c11_dap_send_exited_event(int exitCode) {
|
|
||||||
char body[64];
|
|
||||||
snprintf(body, sizeof(body), "{\"exitCode\":%d}", exitCode);
|
|
||||||
c11_dap_send_event("exited", body);
|
|
||||||
}
|
|
||||||
|
|
||||||
void c11_dap_send_fatal_event(const char* message) {
|
|
||||||
char body[128];
|
|
||||||
snprintf(body, sizeof(body), "{\"message\":\"%s\"}", message);
|
|
||||||
c11_dap_send_event("pkpy/fatalError", body);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
PK_FREE((void*)message);
|
|
||||||
PK_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_STATUS result = c11_debugger_on_trace(frame, event);
|
|
||||||
if(result == C11_DEBUGGER_EXIT) {
|
|
||||||
c11_dap_send_exited_event(0);
|
|
||||||
printf("[DEBUGGER INFO] : program exit\n");
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
if(result != C11_DEBUGGER_SUCCESS) {
|
|
||||||
const char* message = NULL;
|
|
||||||
switch(result) {
|
|
||||||
case C11_DEBUGGER_FILEPATH_ERROR:
|
|
||||||
message = "Invalid py_file path: '..' forbidden, './' only allowed at start.";
|
|
||||||
break;
|
|
||||||
case C11_DEBUGGER_UNKNOW_ERROR:
|
|
||||||
default: message = "Unknown debugger failure."; break;
|
|
||||||
}
|
|
||||||
if(message) { c11_dap_send_fatal_event(message); }
|
|
||||||
c11_dap_send_exited_event(1);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
19
src2/main.c
19
src2/main.c
@ -4,6 +4,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "pocketpy.h"
|
#include "pocketpy.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
@ -33,7 +34,6 @@ int main(int argc, char** argv) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool profile = false;
|
bool profile = false;
|
||||||
bool debug = false;
|
|
||||||
const char* filename = NULL;
|
const char* filename = NULL;
|
||||||
|
|
||||||
for(int i = 1; i < argc; i++) {
|
for(int i = 1; i < argc; i++) {
|
||||||
@ -41,20 +41,11 @@ int main(int argc, char** argv) {
|
|||||||
profile = true;
|
profile = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if(strcmp(argv[i], "--debug") == 0) {
|
|
||||||
debug = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if(filename == NULL) {
|
if(filename == NULL) {
|
||||||
filename = argv[i];
|
filename = argv[i];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
printf("Usage: pocketpy [--profile] [--debug] filename\n");
|
printf("Usage: pocketpy [--profile] filename\n");
|
||||||
}
|
|
||||||
|
|
||||||
if(debug && profile) {
|
|
||||||
printf("Error: --debug and --profile cannot be used together.\n");
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
py_initialize();
|
py_initialize();
|
||||||
@ -62,8 +53,6 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
if(filename == NULL) {
|
if(filename == NULL) {
|
||||||
if(profile) printf("Warning: --profile is ignored in REPL mode.\n");
|
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("pocketpy " PK_VERSION " (" __DATE__ ", " __TIME__ ") ");
|
||||||
printf("[%d bit] on %s", (int)(sizeof(void*) * 8), PY_SYS_PLATFORM_STRING);
|
printf("[%d bit] on %s", (int)(sizeof(void*) * 8), PY_SYS_PLATFORM_STRING);
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
@ -90,8 +79,6 @@ int main(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(profile) py_profiler_begin();
|
if(profile) py_profiler_begin();
|
||||||
if(debug) py_debugger_waitforattach("127.0.0.1", 6110);
|
|
||||||
|
|
||||||
char* source = read_file(filename);
|
char* source = read_file(filename);
|
||||||
if(source) {
|
if(source) {
|
||||||
if(!py_exec(source, filename, EXEC_MODE, NULL))
|
if(!py_exec(source, filename, EXEC_MODE, NULL))
|
||||||
@ -114,7 +101,5 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
int code = py_checkexc(false) ? 1 : 0;
|
int code = py_checkexc(false) ? 1 : 0;
|
||||||
py_finalize();
|
py_finalize();
|
||||||
|
|
||||||
if(debug) py_debugger_exit(code);
|
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user