summaryrefslogtreecommitdiff
path: root/py/vm.c
diff options
context:
space:
mode:
authorMilan Rossa <rossa.milan@gmail.com>2019-08-14 16:09:36 +0200
committerDamien George <damien.p.george@gmail.com>2019-08-30 16:44:12 +1000
commit310b3d1b81d561e19d719acd89ee47b759e3795c (patch)
tree18beb2d50b30dd525fc57efd43a7e705e777fade /py/vm.c
parentc96aedad4691d864c073890a7a20abc7ebd2de27 (diff)
py: Integrate sys.settrace feature into the VM and runtime.
This commit adds support for sys.settrace, allowing to install Python handlers to trace execution of Python code. The interface follows CPython as closely as possible. The feature is disabled by default and can be enabled via MICROPY_PY_SYS_SETTRACE.
Diffstat (limited to 'py/vm.c')
-rw-r--r--py/vm.c89
1 files changed, 88 insertions, 1 deletions
diff --git a/py/vm.c b/py/vm.c
index 0bc203461..9abad8d83 100644
--- a/py/vm.c
+++ b/py/vm.c
@@ -34,6 +34,7 @@
#include "py/runtime.h"
#include "py/bc0.h"
#include "py/bc.h"
+#include "py/profile.h"
#if 0
#define TRACE(ip) printf("sp=%d ", (int)(sp - &code_state->state[0] + 1)); mp_bytecode_print2(ip, 1, code_state->fun_bc->const_table);
@@ -108,6 +109,55 @@
exc_sp--; /* pop back to previous exception handler */ \
CLEAR_SYS_EXC_INFO() /* just clear sys.exc_info(), not compliant, but it shouldn't be used in 1st place */
+#if MICROPY_PY_SYS_SETTRACE
+
+#define FRAME_SETUP() do { \
+ assert(code_state != code_state->prev_state); \
+ MP_STATE_THREAD(current_code_state) = code_state; \
+ assert(code_state != code_state->prev_state); \
+} while(0)
+
+#define FRAME_ENTER() do { \
+ assert(code_state != code_state->prev_state); \
+ code_state->prev_state = MP_STATE_THREAD(current_code_state); \
+ assert(code_state != code_state->prev_state); \
+ if (!mp_prof_is_executing) { \
+ mp_prof_frame_enter(code_state); \
+ } \
+} while(0)
+
+#define FRAME_LEAVE() do { \
+ assert(code_state != code_state->prev_state); \
+ MP_STATE_THREAD(current_code_state) = code_state->prev_state; \
+ assert(code_state != code_state->prev_state); \
+} while(0)
+
+#define FRAME_UPDATE() do { \
+ assert(MP_STATE_THREAD(current_code_state) == code_state); \
+ if (!mp_prof_is_executing) { \
+ code_state->frame = MP_OBJ_TO_PTR(mp_prof_frame_update(code_state)); \
+ } \
+} while(0)
+
+#define TRACE_TICK(current_ip, current_sp, is_exception) do { \
+ assert(code_state != code_state->prev_state); \
+ assert(MP_STATE_THREAD(current_code_state) == code_state); \
+ if (!mp_prof_is_executing && code_state->frame && MP_STATE_THREAD(prof_trace_callback)) { \
+ MP_PROF_INSTR_DEBUG_PRINT(code_state->ip); \
+ } \
+ if (!mp_prof_is_executing && code_state->frame && code_state->frame->callback) { \
+ mp_prof_instr_tick(code_state, is_exception); \
+ } \
+} while(0)
+
+#else // MICROPY_PY_SYS_SETTRACE
+#define FRAME_SETUP()
+#define FRAME_ENTER()
+#define FRAME_LEAVE()
+#define FRAME_UPDATE()
+#define TRACE_TICK(current_ip, current_sp, is_exception)
+#endif // MICROPY_PY_SYS_SETTRACE
+
// fastn has items in reverse order (fastn[0] is local[0], fastn[-1] is local[1], etc)
// sp points to bottom of stack which grows up
// returns:
@@ -128,6 +178,7 @@ mp_vm_return_kind_t mp_execute_bytecode(mp_code_state_t *code_state, volatile mp
#define DISPATCH() do { \
TRACE(ip); \
MARK_EXC_IP_GLOBAL(); \
+ TRACE_TICK(ip, sp, false); \
goto *entry_table[*ip++]; \
} while (0)
#define DISPATCH_WITH_PEND_EXC_CHECK() goto pending_exception_check
@@ -149,6 +200,13 @@ mp_vm_return_kind_t mp_execute_bytecode(mp_code_state_t *code_state, volatile mp
#if MICROPY_STACKLESS
run_code_state: ;
#endif
+FRAME_ENTER();
+
+#if MICROPY_STACKLESS
+run_code_state_from_return: ;
+#endif
+FRAME_SETUP();
+
// Pointers which are constant for particular invocation of mp_execute_bytecode()
mp_obj_t * /*const*/ fastn;
mp_exc_stack_t * /*const*/ exc_stack;
@@ -198,6 +256,7 @@ dispatch_loop:
#else
TRACE(ip);
MARK_EXC_IP_GLOBAL();
+ TRACE_TICK(ip, sp, false);
switch (*ip++) {
#endif
@@ -323,6 +382,7 @@ dispatch_loop:
#if !MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE
ENTRY(MP_BC_LOAD_ATTR): {
+ FRAME_UPDATE();
MARK_EXC_IP_SELECTIVE();
DECODE_QSTR;
SET_TOP(mp_load_attr(TOP(), qst));
@@ -330,6 +390,7 @@ dispatch_loop:
}
#else
ENTRY(MP_BC_LOAD_ATTR): {
+ FRAME_UPDATE();
MARK_EXC_IP_SELECTIVE();
DECODE_QSTR;
mp_obj_t top = TOP();
@@ -415,6 +476,7 @@ dispatch_loop:
#if !MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE
ENTRY(MP_BC_STORE_ATTR): {
+ FRAME_UPDATE();
MARK_EXC_IP_SELECTIVE();
DECODE_QSTR;
mp_store_attr(sp[0], qst, sp[-1]);
@@ -428,6 +490,7 @@ dispatch_loop:
// consequence of this is that we can't use MP_MAP_LOOKUP_ADD_IF_NOT_FOUND
// in the fast-path below, because that store could override a property.
ENTRY(MP_BC_STORE_ATTR): {
+ FRAME_UPDATE();
MARK_EXC_IP_SELECTIVE();
DECODE_QSTR;
mp_obj_t top = TOP();
@@ -738,6 +801,7 @@ unwind_jump:;
}
ENTRY(MP_BC_FOR_ITER): {
+ FRAME_UPDATE();
MARK_EXC_IP_SELECTIVE();
DECODE_ULABEL; // the jump offset if iteration finishes; for labels are always forward
code_state->sp = sp;
@@ -753,6 +817,12 @@ unwind_jump:;
ip += ulab; // jump to after for-block
} else {
PUSH(value); // push the next iteration value
+ #if MICROPY_PY_SYS_SETTRACE
+ // LINE event should trigger for every iteration so invalidate last trigger
+ if (code_state->frame) {
+ code_state->frame->lineno = 0;
+ }
+ #endif
}
DISPATCH();
}
@@ -887,6 +957,7 @@ unwind_jump:;
}
ENTRY(MP_BC_CALL_FUNCTION): {
+ FRAME_UPDATE();
MARK_EXC_IP_SELECTIVE();
DECODE_UINT;
// unum & 0xff == n_positional
@@ -921,6 +992,7 @@ unwind_jump:;
}
ENTRY(MP_BC_CALL_FUNCTION_VAR_KW): {
+ FRAME_UPDATE();
MARK_EXC_IP_SELECTIVE();
DECODE_UINT;
// unum & 0xff == n_positional
@@ -966,6 +1038,7 @@ unwind_jump:;
}
ENTRY(MP_BC_CALL_METHOD): {
+ FRAME_UPDATE();
MARK_EXC_IP_SELECTIVE();
DECODE_UINT;
// unum & 0xff == n_positional
@@ -1004,6 +1077,7 @@ unwind_jump:;
}
ENTRY(MP_BC_CALL_METHOD_VAR_KW): {
+ FRAME_UPDATE();
MARK_EXC_IP_SELECTIVE();
DECODE_UINT;
// unum & 0xff == n_positional
@@ -1096,9 +1170,10 @@ unwind_return:
#endif
code_state = new_code_state;
*code_state->sp = res;
- goto run_code_state;
+ goto run_code_state_from_return;
}
#endif
+ FRAME_LEAVE();
return MP_VM_RETURN_NORMAL;
ENTRY(MP_BC_RAISE_VARARGS): {
@@ -1136,6 +1211,7 @@ yield:
code_state->ip = ip;
code_state->sp = sp;
code_state->exc_sp = MP_TAGPTR_MAKE(exc_sp, 0);
+ FRAME_LEAVE();
return MP_VM_RETURN_YIELD;
ENTRY(MP_BC_YIELD_FROM): {
@@ -1192,6 +1268,7 @@ yield:
}
ENTRY(MP_BC_IMPORT_NAME): {
+ FRAME_UPDATE();
MARK_EXC_IP_SELECTIVE();
DECODE_QSTR;
mp_obj_t obj = POP();
@@ -1200,6 +1277,7 @@ yield:
}
ENTRY(MP_BC_IMPORT_FROM): {
+ FRAME_UPDATE();
MARK_EXC_IP_SELECTIVE();
DECODE_QSTR;
mp_obj_t obj = mp_import_from(TOP(), qst);
@@ -1266,6 +1344,7 @@ yield:
mp_obj_t obj = mp_obj_new_exception_msg(&mp_type_NotImplementedError, "opcode");
nlr_pop();
code_state->state[0] = obj;
+ FRAME_LEAVE();
return MP_VM_RETURN_EXCEPTION;
}
@@ -1356,6 +1435,13 @@ exception_handler:
}
}
+ #if MICROPY_PY_SYS_SETTRACE
+ // Exceptions are traced here
+ if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(((mp_obj_base_t*)nlr.ret_val)->type), MP_OBJ_FROM_PTR(&mp_type_Exception))) {
+ TRACE_TICK(code_state->ip, code_state->sp, true /* yes, it's an exception */);
+ }
+ #endif
+
#if MICROPY_STACKLESS
unwind_loop:
#endif
@@ -1438,6 +1524,7 @@ unwind_loop:
// propagate exception to higher level
// Note: ip and sp don't have usable values at this point
code_state->state[0] = MP_OBJ_FROM_PTR(nlr.ret_val); // put exception here because sp is invalid
+ FRAME_LEAVE();
return MP_VM_RETURN_EXCEPTION;
}
}