core.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. #include "pocketpy/interpreter/frame.h"
  2. #include "pocketpy/pocketpy.h"
  3. #include "pocketpy/debugger/core.h"
  4. typedef struct c11_debugger_breakpoint {
  5. const char* sourcename;
  6. int lineno;
  7. } c11_debugger_breakpoint;
  8. typedef struct c11_debugger_scope_index {
  9. int locals_ref;
  10. int globals_ref;
  11. } c11_debugger_scope_index;
  12. #define SMALLMAP_T__HEADER
  13. #define SMALLMAP_T__SOURCE
  14. #define K int
  15. #define V c11_debugger_scope_index
  16. #define NAME c11_smallmap_d2index
  17. #include "pocketpy/xmacros/smallmap.h"
  18. #undef SMALLMAP_T__SOURCE
  19. #undef SMALLMAP_T__HEADER
  20. static struct c11_debugger {
  21. py_Frame* current_frame;
  22. const char* current_filename;
  23. enum py_TraceEvent current_event;
  24. int curr_stack_depth;
  25. int current_line;
  26. int pause_allowed_depth;
  27. int step_line;
  28. C11_STEP_MODE step_mode;
  29. bool keep_suspend;
  30. c11_vector breakpoints;
  31. c11_vector py_frames;
  32. c11_smallmap_d2index scopes_query_cache;
  33. #define python_vars py_r7()
  34. } debugger;
  35. inline static void init_structures() {
  36. c11_vector__ctor(&debugger.breakpoints, sizeof(c11_debugger_breakpoint));
  37. c11_vector__ctor(&debugger.py_frames, sizeof(py_Frame*));
  38. c11_smallmap_d2index__ctor(&debugger.scopes_query_cache);
  39. py_newlist(python_vars);
  40. py_newnil(py_list_emplace(python_vars));
  41. }
  42. inline static void clear_structures() {
  43. c11_vector__clear(&debugger.py_frames);
  44. c11_smallmap_d2index__clear(&debugger.scopes_query_cache);
  45. py_list_clear(python_vars);
  46. py_newnone(py_list_emplace(python_vars));
  47. }
  48. inline static py_Ref get_variable(int var_ref) {
  49. assert(var_ref < py_list_len(python_vars) && var_ref > 0);
  50. return py_list_getitem(python_vars, var_ref);
  51. }
  52. const inline static char* format_filepath(const char* path) {
  53. if(strstr(path, "..")) { return NULL; }
  54. if(strstr(path + 1, "./") || strstr(path + 1, ".\\")) { return NULL; }
  55. if(path[0] == '.' && (path[1] == '/' || path[1] == '\\')) { return path + 2; }
  56. return path;
  57. }
  58. void c11_debugger_init() {
  59. debugger.curr_stack_depth = 0;
  60. debugger.current_line = -1;
  61. debugger.pause_allowed_depth = -1;
  62. debugger.step_line = -1;
  63. debugger.keep_suspend = false;
  64. debugger.step_mode = C11_STEP_CONTINUE;
  65. init_structures();
  66. }
  67. C11_DEBUGGER_STATUS c11_debugger_on_trace(py_Frame* frame, enum py_TraceEvent event) {
  68. debugger.current_frame = frame;
  69. debugger.current_event = event;
  70. const char* source_name = py_Frame_sourceloc(debugger.current_frame, &debugger.current_line);
  71. debugger.current_filename = format_filepath(source_name);
  72. if(debugger.current_filename == NULL) { return C11_DEBUGGER_FILEPATH_ERROR; }
  73. clear_structures();
  74. switch(event) {
  75. case TRACE_EVENT_PUSH: debugger.curr_stack_depth++; break;
  76. case TRACE_EVENT_POP: debugger.curr_stack_depth--; break;
  77. default: break;
  78. }
  79. if(debugger.curr_stack_depth == 0) return C11_DEBUGGER_EXIT;
  80. return C11_DEBUGGER_SUCCESS;
  81. }
  82. void c11_debugger_set_step_mode(C11_STEP_MODE mode) {
  83. switch(mode) {
  84. case C11_STEP_IN: debugger.pause_allowed_depth = INT32_MAX; break;
  85. case C11_STEP_OVER:
  86. debugger.pause_allowed_depth = debugger.curr_stack_depth;
  87. debugger.step_line = debugger.current_line;
  88. break;
  89. case C11_STEP_OUT: debugger.pause_allowed_depth = debugger.curr_stack_depth - 1; break;
  90. case C11_STEP_CONTINUE: debugger.pause_allowed_depth = -1; break;
  91. }
  92. debugger.step_mode = mode;
  93. debugger.keep_suspend = false;
  94. }
  95. int c11_debugger_setbreakpoint(const char* filename, int lineno) {
  96. c11_debugger_breakpoint breakpoint = {.sourcename = c11_strdup(filename), .lineno = lineno};
  97. c11_vector__push(c11_debugger_breakpoint, &debugger.breakpoints, breakpoint);
  98. return debugger.breakpoints.length;
  99. }
  100. int c11_debugger_reset_breakpoints_by_source(const char* sourcesname) {
  101. c11_vector tmp_breakpoints;
  102. c11_vector__ctor(&tmp_breakpoints, sizeof(c11_debugger_breakpoint));
  103. c11__foreach(c11_debugger_breakpoint, &debugger.breakpoints, it) {
  104. if(strcmp(it->sourcename, sourcesname) != 0) {
  105. c11_debugger_breakpoint* dst =
  106. (c11_debugger_breakpoint*)c11_vector__emplace(&tmp_breakpoints);
  107. *dst = *it;
  108. } else {
  109. PK_FREE((void*)it->sourcename);
  110. }
  111. }
  112. c11_vector__swap(&tmp_breakpoints, &debugger.breakpoints);
  113. c11_vector__dtor(&tmp_breakpoints);
  114. return debugger.breakpoints.length;
  115. }
  116. int c11_debugger_should_pause() {
  117. if(debugger.current_event == TRACE_EVENT_POP) return false;
  118. bool should_pause = false;
  119. int is_out = debugger.curr_stack_depth <= debugger.pause_allowed_depth;
  120. int is_new_line = debugger.current_line != debugger.step_line;
  121. switch(debugger.step_mode) {
  122. case C11_STEP_IN: should_pause = true; break;
  123. case C11_STEP_OVER:
  124. if(is_new_line && is_out) should_pause = true;
  125. break;
  126. case C11_STEP_OUT:
  127. if(is_out) should_pause = true;
  128. break;
  129. case C11_STEP_CONTINUE:
  130. default: break;
  131. }
  132. if(debugger.step_mode == C11_STEP_CONTINUE) {
  133. c11__foreach(c11_debugger_breakpoint, &debugger.breakpoints, bp) {
  134. if(strcmp(debugger.current_filename, bp->sourcename) == 0 &&
  135. debugger.current_line == bp->lineno) {
  136. should_pause = true;
  137. break;
  138. }
  139. }
  140. }
  141. if(should_pause) { debugger.keep_suspend = true; }
  142. return should_pause;
  143. }
  144. int c11_debugger_should_keep_pause(void) { return debugger.keep_suspend; }
  145. inline static c11_sv sv_from_cstr(const char* str) {
  146. c11_sv sv = {.data = str, .size = strlen(str)};
  147. return sv;
  148. }
  149. const inline static char* get_basename(const char* path) {
  150. const char* last_slash = strrchr(path, '/');
  151. #if defined(_WIN32) || defined(_WIN64)
  152. const char* last_backslash = strrchr(path, '\\');
  153. if(!last_slash || (last_backslash && last_backslash > last_slash)) {
  154. last_slash = last_backslash;
  155. }
  156. #endif
  157. return last_slash ? last_slash + 1 : path;
  158. }
  159. void c11_debugger_frames(c11_sbuf* buffer) {
  160. c11_sbuf__write_cstr(buffer, "{\"stackFrames\": [");
  161. int idx = 0;
  162. py_Frame* now_frame = debugger.current_frame;
  163. debugger.py_frames.length = 0;
  164. while(now_frame) {
  165. if(idx > 0) c11_sbuf__write_char(buffer, ',');
  166. int line;
  167. const char* filename = py_Frame_sourceloc(now_frame, &line);
  168. const char* basename = get_basename(filename);
  169. const char* modname = now_frame->co->name->data;
  170. pk_sprintf(
  171. buffer,
  172. "{\"id\": %d, \"name\": %Q, \"line\": %d, \"column\": 1, \"source\": {\"name\": %Q, \"path\": %Q}}",
  173. idx,
  174. sv_from_cstr(modname),
  175. line,
  176. sv_from_cstr(basename),
  177. sv_from_cstr(filename));
  178. c11_vector__push(py_Frame*, &debugger.py_frames, now_frame);
  179. now_frame = now_frame->f_back;
  180. idx++;
  181. }
  182. pk_sprintf(buffer, "], \"totalFrames\": %d}", idx);
  183. }
  184. inline static c11_debugger_scope_index append_new_scope(int frameid) {
  185. assert(frameid < debugger.py_frames.length);
  186. py_Frame* requested_frame = c11__getitem(py_Frame*, &debugger.py_frames, frameid);
  187. int base_index = py_list_len(python_vars);
  188. py_Ref new_locals = py_list_emplace(python_vars);
  189. py_Ref new_globals = py_list_emplace(python_vars);
  190. py_Frame_newlocals(requested_frame, new_locals);
  191. py_Frame_newglobals(requested_frame, new_globals);
  192. c11_debugger_scope_index result = {.locals_ref = base_index, .globals_ref = base_index + 1};
  193. return result;
  194. }
  195. void c11_debugger_scopes(int frameid, c11_sbuf* buffer) {
  196. // query cache
  197. c11_debugger_scope_index* result =
  198. c11_smallmap_d2index__try_get(&debugger.scopes_query_cache, frameid);
  199. c11_sbuf__write_cstr(buffer, "{\"scopes\":");
  200. const char* scopes_fmt =
  201. "[{\"name\": \"locals\", \"variablesReference\": %d, \"expensive\": false}, "
  202. "{\"name\": \"globals\", \"variablesReference\": %d, \"expensive\": true}]";
  203. if(result != NULL) {
  204. pk_sprintf(buffer, scopes_fmt, result->locals_ref, result->globals_ref);
  205. } else {
  206. c11_debugger_scope_index new_record = append_new_scope(frameid);
  207. c11_smallmap_d2index__set(&debugger.scopes_query_cache, frameid, new_record);
  208. pk_sprintf(buffer, scopes_fmt, new_record.locals_ref, new_record.globals_ref);
  209. }
  210. c11_sbuf__write_char(buffer, '}');
  211. }
  212. bool c11_debugger_unfold_var(int var_id, c11_sbuf* buffer) {
  213. py_Ref var = get_variable(var_id);
  214. if(!var) return false;
  215. // 1. extend
  216. const char* expand_code = NULL;
  217. switch(py_typeof(var)) {
  218. case tp_dict:
  219. case tp_namedict: expand_code = "[(k,v) for k,v in _0.items()]"; break;
  220. case tp_list:
  221. case tp_tuple: expand_code = "[(f'[{i}]',v) for i,v in enumerate(_0)]"; break;
  222. default: expand_code = "[(k,v) for k,v in _0.__dict__.items()]"; break;
  223. }
  224. if(!py_smarteval(expand_code, NULL, var)) {
  225. py_printexc();
  226. return false;
  227. }
  228. py_Ref kv_list = py_pushtmp();
  229. py_assign(kv_list, py_retval());
  230. // 2. prepare base_ref
  231. int base_index = py_list_len(python_vars);
  232. py_Ref base_var_ref = py_pushtmp();
  233. py_newint(base_var_ref, base_index);
  234. // 3. construct DAP JSON and extend python_vars
  235. py_Ref dap_obj = py_pushtmp();
  236. py_newdict(dap_obj);
  237. const char* dap_code =
  238. "_2['variables'] = []\n"
  239. "var_ref = _1\n"
  240. "for k, v in _0:\n"
  241. " has_children = isinstance(v, (dict, list, tuple)) or v.__dict__ is not None\n"
  242. " _2['variables'].append({\n"
  243. " 'name': k if type(k) == str else str(k),\n"
  244. " 'value': repr(v) if type(v) == str else str(v),\n"
  245. " 'variablesReference': var_ref if has_children else 0,\n"
  246. " 'type': type(v).__name__\n"
  247. " })\n"
  248. " if has_children: var_ref += 1\n";
  249. if(!py_smartexec(dap_code, NULL, kv_list, base_var_ref, dap_obj)) {
  250. py_printexc();
  251. return false;
  252. }
  253. // 4. extend python_vars
  254. if(!py_smartexec(
  255. "_0.extend([v for k, v in _1 if isinstance(v, (dict, list, tuple)) or v.__dict__ is not None])",
  256. NULL,
  257. python_vars,
  258. kv_list)) {
  259. py_printexc();
  260. return false;
  261. }
  262. // 5. dump & write
  263. if(!py_json_dumps(dap_obj, 0)) {
  264. printf("dap_obj: %s\n", py_tpname(py_typeof(dap_obj)));
  265. py_printexc();
  266. return false;
  267. }
  268. c11_sbuf__write_cstr(buffer, py_tostr(py_retval()));
  269. // 6. clear
  270. py_pop(); // dap_obj
  271. py_pop(); // base_var_ref
  272. py_pop(); // kv_list
  273. return true;
  274. }
  275. #undef python_vars