From 02ff6a99522bf0c734831841661ebd385ae8c417 Mon Sep 17 00:00:00 2001 From: "Michael W. Hudson" Date: Wed, 11 Sep 2002 15:36:32 +0000 Subject: [PATCH] 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. --- Include/frameobject.h | 2 ++ Lib/test/test_trace.py | 2 +- Modules/_hotshot.c | 6 ++---- Objects/frameobject.c | 38 ++++++++++++++++++++++++++++++++++++-- Python/ceval.c | 23 ++++++++++++++++++----- 5 files changed, 59 insertions(+), 12 deletions(-) diff --git a/Include/frameobject.h b/Include/frameobject.h index 958b952fa53..2820e88bb0e 100644 --- a/Include/frameobject.h +++ b/Include/frameobject.h @@ -29,6 +29,8 @@ typedef struct _frame { PyObject *f_exc_type, *f_exc_value, *f_exc_traceback; PyThreadState *f_tstate; 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_restricted; /* Flag set if restricted operations in this scope */ diff --git a/Lib/test/test_trace.py b/Lib/test/test_trace.py index eca8c16c967..314801d8bd1 100644 --- a/Lib/test/test_trace.py +++ b/Lib/test/test_trace.py @@ -57,7 +57,7 @@ no_pop_tops.events = [(0, 'call'), (3, 'line'), (4, 'line'), (2, 'line'), - (6, 'return')] + (2, 'return')] def no_pop_blocks(): while 0: diff --git a/Modules/_hotshot.c b/Modules/_hotshot.c index 2ca448620ee..e12e5e5bf87 100644 --- a/Modules/_hotshot.c +++ b/Modules/_hotshot.c @@ -888,12 +888,10 @@ tracer_callback(ProfilerObject *self, PyFrameObject *frame, int what, case PyTrace_LINE: if (self->linetimings) - return pack_lineno_tdelta(self, PyCode_Addr2Line(frame->f_code, - frame->f_lasti), + return pack_lineno_tdelta(self, frame->f_lineno), get_tdelta(self)); else - return pack_lineno(self, PyCode_Addr2Line(frame->f_code, - frame->f_lasti)); + return pack_lineno(self, frame->f_lineno); default: /* ignore PyTrace_EXCEPTION */ diff --git a/Objects/frameobject.c b/Objects/frameobject.c index c65ca40a771..3036ab684d9 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -17,7 +17,6 @@ static PyMemberDef frame_memberlist[] = { {"f_globals", T_OBJECT, OFF(f_globals), RO}, {"f_lasti", T_INT, OFF(f_lasti), 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_value", T_OBJECT, OFF(f_exc_value)}, {"f_exc_traceback", T_OBJECT, OFF(f_exc_traceback)}, @@ -37,14 +36,49 @@ frame_getlineno(PyFrameObject *f, void *closure) { 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); } +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[] = { {"f_locals", (getter)frame_getlocals, NULL, NULL}, {"f_lineno", (getter)frame_getlineno, NULL, NULL}, + {"f_trace", (getter)frame_gettrace, (setter)frame_settrace, NULL}, {0} }; diff --git a/Python/ceval.c b/Python/ceval.c index 6364c3f681d..362cd0bce70 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2909,7 +2909,7 @@ maybe_call_line_trace(int opcode, Py_tracefunc func, PyObject *obj, >> 21 LOAD_CONST 0 (None) 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 line 3. This is at best misleading. In this case we could 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, current instruction offset matches the offset given for the 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. Then the co_lnotab would imply that execution has moved to line 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)) { PyCodeObject* co = frame->f_code; - int size, addr; + int size, addr, line; unsigned char* p; size = PyString_GET_SIZE(co->co_lnotab) / 2; p = (unsigned char*)PyString_AS_STRING(co->co_lnotab); addr = 0; + line = co->co_firstlineno; /* possible optimization: if f->f_lasti == instr_ub (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, if (addr + *p > frame->f_lasti) break; addr += *p++; - p++; + line += *p++; --size; } - if (addr == frame->f_lasti) + if (addr == frame->f_lasti) { + frame->f_lineno = line; call_trace(func, obj, frame, PyTrace_LINE, Py_None); + } *instr_lb = addr; if (size > 0) { while (--size >= 0) {