Commit 02ff6a99 authored by Michael W. Hudson's avatar Michael W. Hudson

A slight change to SET_LINENO-less tracing.

This makes things a touch more like 2.2.  Read the comments in
Python/ceval.c for more details.
parent 519a342d
...@@ -29,6 +29,8 @@ typedef struct _frame { ...@@ -29,6 +29,8 @@ typedef struct _frame {
PyObject *f_exc_type, *f_exc_value, *f_exc_traceback; PyObject *f_exc_type, *f_exc_value, *f_exc_traceback;
PyThreadState *f_tstate; PyThreadState *f_tstate;
int f_lasti; /* Last instruction if called */ int f_lasti; /* Last instruction if called */
/* As of 2.3 f_lineno is only valid when tracing is active (i.e. when
f_trace is set) -- at other times use PyCode_Addr2Line instead. */
int f_lineno; /* Current line number */ int f_lineno; /* Current line number */
int f_restricted; /* Flag set if restricted operations int f_restricted; /* Flag set if restricted operations
in this scope */ in this scope */
......
...@@ -57,7 +57,7 @@ no_pop_tops.events = [(0, 'call'), ...@@ -57,7 +57,7 @@ no_pop_tops.events = [(0, 'call'),
(3, 'line'), (3, 'line'),
(4, 'line'), (4, 'line'),
(2, 'line'), (2, 'line'),
(6, 'return')] (2, 'return')]
def no_pop_blocks(): def no_pop_blocks():
while 0: while 0:
......
...@@ -888,12 +888,10 @@ tracer_callback(ProfilerObject *self, PyFrameObject *frame, int what, ...@@ -888,12 +888,10 @@ tracer_callback(ProfilerObject *self, PyFrameObject *frame, int what,
case PyTrace_LINE: case PyTrace_LINE:
if (self->linetimings) if (self->linetimings)
return pack_lineno_tdelta(self, PyCode_Addr2Line(frame->f_code, return pack_lineno_tdelta(self, frame->f_lineno),
frame->f_lasti),
get_tdelta(self)); get_tdelta(self));
else else
return pack_lineno(self, PyCode_Addr2Line(frame->f_code, return pack_lineno(self, frame->f_lineno);
frame->f_lasti));
default: default:
/* ignore PyTrace_EXCEPTION */ /* ignore PyTrace_EXCEPTION */
......
...@@ -17,7 +17,6 @@ static PyMemberDef frame_memberlist[] = { ...@@ -17,7 +17,6 @@ static PyMemberDef frame_memberlist[] = {
{"f_globals", T_OBJECT, OFF(f_globals), RO}, {"f_globals", T_OBJECT, OFF(f_globals), RO},
{"f_lasti", T_INT, OFF(f_lasti), RO}, {"f_lasti", T_INT, OFF(f_lasti), RO},
{"f_restricted",T_INT, OFF(f_restricted),RO}, {"f_restricted",T_INT, OFF(f_restricted),RO},
{"f_trace", T_OBJECT, OFF(f_trace)},
{"f_exc_type", T_OBJECT, OFF(f_exc_type)}, {"f_exc_type", T_OBJECT, OFF(f_exc_type)},
{"f_exc_value", T_OBJECT, OFF(f_exc_value)}, {"f_exc_value", T_OBJECT, OFF(f_exc_value)},
{"f_exc_traceback", T_OBJECT, OFF(f_exc_traceback)}, {"f_exc_traceback", T_OBJECT, OFF(f_exc_traceback)},
...@@ -37,14 +36,49 @@ frame_getlineno(PyFrameObject *f, void *closure) ...@@ -37,14 +36,49 @@ frame_getlineno(PyFrameObject *f, void *closure)
{ {
int lineno; int lineno;
lineno = PyCode_Addr2Line(f->f_code, f->f_lasti); if (f->f_trace)
lineno = f->f_lineno;
else
lineno = PyCode_Addr2Line(f->f_code, f->f_lasti);
return PyInt_FromLong(lineno); return PyInt_FromLong(lineno);
} }
static PyObject *
frame_gettrace(PyFrameObject *f, void *closure)
{
PyObject* trace = f->f_trace;
if (trace == NULL)
trace = Py_None;
Py_INCREF(trace);
return trace;
}
static int
frame_settrace(PyFrameObject *f, PyObject* v, void *closure)
{
/* We rely on f_lineno being accurate when f_trace is set. */
PyObject* old_value = f->f_trace;
Py_XINCREF(v);
f->f_trace = v;
if (v != NULL)
f->f_lineno = PyCode_Addr2Line(f->f_code, f->f_lasti);
Py_XDECREF(old_value);
return 0;
}
static PyGetSetDef frame_getsetlist[] = { static PyGetSetDef frame_getsetlist[] = {
{"f_locals", (getter)frame_getlocals, NULL, NULL}, {"f_locals", (getter)frame_getlocals, NULL, NULL},
{"f_lineno", (getter)frame_getlineno, NULL, NULL}, {"f_lineno", (getter)frame_getlineno, NULL, NULL},
{"f_trace", (getter)frame_gettrace, (setter)frame_settrace, NULL},
{0} {0}
}; };
......
...@@ -2909,7 +2909,7 @@ maybe_call_line_trace(int opcode, Py_tracefunc func, PyObject *obj, ...@@ -2909,7 +2909,7 @@ maybe_call_line_trace(int opcode, Py_tracefunc func, PyObject *obj,
>> 21 LOAD_CONST 0 (None) >> 21 LOAD_CONST 0 (None)
24 RETURN_VALUE 24 RETURN_VALUE
If a is false, execution will jump to instruction at offset If 'a' is false, execution will jump to instruction at offset
15 and the co_lnotab will claim that execution has moved to 15 and the co_lnotab will claim that execution has moved to
line 3. This is at best misleading. In this case we could line 3. This is at best misleading. In this case we could
associate the POP_TOP with line 4, but that doesn't make associate the POP_TOP with line 4, but that doesn't make
...@@ -2920,21 +2920,32 @@ maybe_call_line_trace(int opcode, Py_tracefunc func, PyObject *obj, ...@@ -2920,21 +2920,32 @@ maybe_call_line_trace(int opcode, Py_tracefunc func, PyObject *obj,
current instruction offset matches the offset given for the current instruction offset matches the offset given for the
start of a line by the co_lnotab. start of a line by the co_lnotab.
This also takes care of the situation where a is true. This also takes care of the situation where 'a' is true.
Execution will jump from instruction offset 12 to offset 21. Execution will jump from instruction offset 12 to offset 21.
Then the co_lnotab would imply that execution has moved to line Then the co_lnotab would imply that execution has moved to line
5, which is again misleading. 5, which is again misleading.
Why do we set f_lineno when tracing? Well, consider the code
above when 'a' is true. If stepping through this with 'n' in
pdb, you would stop at line 1 with a "call" type event, then
line events on lines 2 and 3, then a "return" type event -- but
you would be shown line 5 during this event. This is a change
from the behaviour in 2.2 and before, and I've found it
confusing in practice. By setting and using f_lineno when
tracing, one can report a line number different from that
suggested by f_lasti on this one occasion where it's desirable.
*/ */
if ((frame->f_lasti < *instr_lb || frame->f_lasti >= *instr_ub)) { if ((frame->f_lasti < *instr_lb || frame->f_lasti >= *instr_ub)) {
PyCodeObject* co = frame->f_code; PyCodeObject* co = frame->f_code;
int size, addr; int size, addr, line;
unsigned char* p; unsigned char* p;
size = PyString_GET_SIZE(co->co_lnotab) / 2; size = PyString_GET_SIZE(co->co_lnotab) / 2;
p = (unsigned char*)PyString_AS_STRING(co->co_lnotab); p = (unsigned char*)PyString_AS_STRING(co->co_lnotab);
addr = 0; addr = 0;
line = co->co_firstlineno;
/* possible optimization: if f->f_lasti == instr_ub /* possible optimization: if f->f_lasti == instr_ub
(likely to be a common case) then we already know (likely to be a common case) then we already know
...@@ -2951,12 +2962,14 @@ maybe_call_line_trace(int opcode, Py_tracefunc func, PyObject *obj, ...@@ -2951,12 +2962,14 @@ maybe_call_line_trace(int opcode, Py_tracefunc func, PyObject *obj,
if (addr + *p > frame->f_lasti) if (addr + *p > frame->f_lasti)
break; break;
addr += *p++; addr += *p++;
p++; line += *p++;
--size; --size;
} }
if (addr == frame->f_lasti) if (addr == frame->f_lasti) {
frame->f_lineno = line;
call_trace(func, obj, frame, call_trace(func, obj, frame,
PyTrace_LINE, Py_None); PyTrace_LINE, Py_None);
}
*instr_lb = addr; *instr_lb = addr;
if (size > 0) { if (size > 0) {
while (--size >= 0) { while (--size >= 0) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment