Merge pull request #379 from lightovernight/gsoc-2025-debugger

Gsoc 2025 debugger
This commit is contained in:
lightovernight 2025-08-09 13:04:04 +08:00 committed by GitHub
parent 8d5cd7c1b8
commit ec8f8f473e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 709 additions and 2 deletions

View 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);

View File

@ -0,0 +1 @@
#pragma once

View File

@ -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
View 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
View 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);
}

View File

@ -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;
}