diff --git a/Include/pystate.h b/Include/pystate.h --- a/Include/pystate.h +++ b/Include/pystate.h @@ -78,10 +78,10 @@ to handle the runtime error. */ char recursion_critical; /* The current calls must not cause a stack overflow. */ - /* 'tracing' keeps track of the execution depth when tracing/profiling. - This is to prevent the actual trace/profile code from being recorded in - the trace/profile. */ - int tracing; + /* 'traced_frame' is the frame being traced. + This is also used to prevent the actual trace/profile code from being + recorded in the trace/profile. */ + struct _frame *traced_frame; int use_tracing; Py_tracefunc c_profilefunc; diff --git a/Lib/bdb.py b/Lib/bdb.py --- a/Lib/bdb.py +++ b/Lib/bdb.py @@ -230,10 +230,6 @@ if not self.breaks: # no breakpoints; run without debugger overhead sys.settrace(None) - frame = sys._getframe().f_back - while frame and frame is not self.botframe: - del frame.f_trace - frame = frame.f_back def set_quit(self): self.stopframe = self.botframe diff --git a/Lib/test/test_sys_settrace.py b/Lib/test/test_sys_settrace.py --- a/Lib/test/test_sys_settrace.py +++ b/Lib/test/test_sys_settrace.py @@ -5,6 +5,7 @@ import sys import difflib import gc +import textwrap # A very basic example. If this fails, we're in deep trouble. def basic(): @@ -388,6 +389,34 @@ (257, 'line'), (257, 'return')]) + def test_17_backtrace_lineno(self): + # issue17277: line numbers in backtrace after removing the trace + # function. + tracer = Tracer() + sys.settrace(tracer.trace) + source = """ + def bar(): + sys.settrace(None) + + def foo(): + bar() + line_number = 7 + 1 / 0 + + foo() + """ + + lno = 0 + try: + exec(textwrap.dedent(source), globals()) + except ZeroDivisionError: + tb = sys.exc_info()[2] + while tb: + lno = tb.tb_lineno + tb = tb.tb_next + self.assertEqual(lno, 8, 'Bad line number in traceback after' + ' removing the trace function.') + class RaisingTraceFuncTestCase(unittest.TestCase): def setUp(self): diff --git a/Modules/pyexpat.c b/Modules/pyexpat.c --- a/Modules/pyexpat.c +++ b/Modules/pyexpat.c @@ -222,25 +222,25 @@ trace_frame(PyThreadState *tstate, PyFrameObject *f, int code, PyObject *val) { int result = 0; - if (!tstate->use_tracing || tstate->tracing) + if (!tstate->use_tracing || tstate->traced_frame != NULL) return 0; if (tstate->c_profilefunc != NULL) { - tstate->tracing++; + tstate->traced_frame = f; result = tstate->c_profilefunc(tstate->c_profileobj, f, code , val); tstate->use_tracing = ((tstate->c_tracefunc != NULL) || (tstate->c_profilefunc != NULL)); - tstate->tracing--; + tstate->traced_frame = NULL; if (result) return result; } if (tstate->c_tracefunc != NULL) { - tstate->tracing++; + tstate->traced_frame = f; result = tstate->c_tracefunc(tstate->c_traceobj, f, code , val); tstate->use_tracing = ((tstate->c_tracefunc != NULL) || (tstate->c_profilefunc != NULL)); - tstate->tracing--; + tstate->traced_frame = NULL; } return result; } diff --git a/Objects/frameobject.c b/Objects/frameobject.c --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -34,7 +34,7 @@ int PyFrame_GetLineNumber(PyFrameObject *f) { - if (f->f_trace) + if (f->f_trace && f->f_tstate->traced_frame == f) return f->f_lineno; else return PyCode_Addr2Line(f->f_code, f->f_lasti); @@ -95,13 +95,16 @@ return -1; } - /* You can only do this from within a trace function, not via - * _getframe or similar hackery. */ - if (!f->f_trace) + /* You can only do this from within a trace function (not via _getframe or + * similar hackery) in the frame being traced. It must be a 'line' trace + * function, not a 'call' trace function (hence the test on f->f_trace + * which is NULL on a 'call' tracing event) as f_lasti arithmetic cannot be + * tinkered with at the start of frame evaluation. */ + if (!f->f_trace || f->f_tstate->traced_frame != f) { PyErr_Format(PyExc_ValueError, - "f_lineno can only be set by a" - " line trace function"); + "f_lineno can only be set in the frame being traced" + " by a line trace function"); return -1; } diff --git a/Python/ceval.c b/Python/ceval.c --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1277,7 +1277,7 @@ /* line-by-line tracing support */ if (_Py_TracingPossible && - tstate->c_tracefunc != NULL && !tstate->tracing) { + tstate->c_tracefunc != NULL && tstate->traced_frame == NULL) { int err; /* see maybe_call_line_trace for expository comments */ @@ -3838,14 +3838,14 @@ { register PyThreadState *tstate = frame->f_tstate; int result; - if (tstate->tracing) + if (tstate->traced_frame != NULL) return 0; - tstate->tracing++; + tstate->traced_frame = frame; tstate->use_tracing = 0; result = func(obj, frame, what, arg); tstate->use_tracing = ((tstate->c_tracefunc != NULL) || (tstate->c_profilefunc != NULL)); - tstate->tracing--; + tstate->traced_frame = NULL; return result; } @@ -3854,15 +3854,15 @@ { PyFrameObject *frame = PyEval_GetFrame(); PyThreadState *tstate = frame->f_tstate; - int save_tracing = tstate->tracing; + PyFrameObject *save_tracing = tstate->traced_frame; int save_use_tracing = tstate->use_tracing; PyObject *result; - tstate->tracing = 0; + tstate->traced_frame = NULL; tstate->use_tracing = ((tstate->c_tracefunc != NULL) || (tstate->c_profilefunc != NULL)); result = PyObject_Call(func, args, NULL); - tstate->tracing = save_tracing; + tstate->traced_frame = save_tracing; tstate->use_tracing = save_use_tracing; return result; } diff --git a/Python/pystate.c b/Python/pystate.c --- a/Python/pystate.c +++ b/Python/pystate.c @@ -180,7 +180,7 @@ tstate->recursion_depth = 0; tstate->overflowed = 0; tstate->recursion_critical = 0; - tstate->tracing = 0; + tstate->traced_frame = NULL; tstate->use_tracing = 0; tstate->tick_counter = 0; tstate->gilstate_counter = 0;

AltStyle によって変換されたページ (->オリジナル) /