[(5, 'line'), (5, 'return')])
+def lineno_matches_lasti(frame):
+ last_line = None
+ for start, end, line in frame.f_code.co_lines():
+ if start <= frame.f_lasti < end:
+ last_line = line
+ return last_line == frame.f_lineno
+
class Tracer:
def __init__(self, trace_line_events=None, trace_opcode_events=None):
self.trace_line_events = trace_line_events
frame.f_trace_opcodes = self.trace_opcode_events
def trace(self, frame, event, arg):
+ assert lineno_matches_lasti(frame)
self._reconfigure_frame(frame)
self.events.append((frame.f_lineno, event))
return self.trace
def trace(self, frame, event, arg):
if self.done:
return
+ assert lineno_matches_lasti(frame)
# frame.f_code.co_firstlineno is the first line of the decorator when
# 'function' is decorated and the decorator may be written using
# multiple physical lines when it is too long. Use the first line
assert(event == PY_MONITORING_EVENT_JUMP ||
event == PY_MONITORING_EVENT_BRANCH);
assert(frame->prev_instr == instr);
- /* Event should occur after the jump */
- frame->prev_instr = target;
PyCodeObject *code = _PyFrame_GetCode(frame);
int to = (int)(target - _PyCode_CODE(code));
PyObject *to_obj = PyLong_FromLong(to * (int)sizeof(_Py_CODEUNIT));
if (err) {
return NULL;
}
- if (frame->prev_instr != target) {
+ if (frame->prev_instr != instr) {
/* The callback has caused a jump (by setting the line number) */
return frame->prev_instr;
}
- /* Reset prev_instr for INSTRUMENTED_LINE */
- frame->prev_instr = instr;
return target;
}
int
_Py_call_instrumentation_line(PyThreadState *tstate, _PyInterpreterFrame* frame, _Py_CODEUNIT *instr, _Py_CODEUNIT *prev)
{
- frame->prev_instr = instr;
+ assert(frame->prev_instr == instr);
PyCodeObject *code = _PyFrame_GetCode(frame);
assert(is_version_up_to_date(code, tstate->interp));
assert(instrumentation_cross_checks(tstate->interp, code));