]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-44570: Fix line tracing for forward jumps to duplicated tails (GH-27067)
authorMark Shannon <mark@hotpy.org>
Thu, 8 Jul 2021 18:21:22 +0000 (19:21 +0100)
committerGitHub <noreply@github.com>
Thu, 8 Jul 2021 18:21:22 +0000 (19:21 +0100)
Lib/test/test_pdb.py
Lib/test/test_sys_settrace.py
Python/ceval.c

index cdd427a570e9fa390008f2168b7d70b97a2f9cae..63afb81cb168ff9b86c6f303b1d7e0c65304adb4 100644 (file)
@@ -1316,8 +1316,8 @@ def test_pdb_issue_20766():
     -> print('pdb %d: %s' % (i, sess._previous_sigint_handler))
     (Pdb) continue
     pdb 1: <built-in function default_int_handler>
-    > <doctest test.test_pdb.test_pdb_issue_20766[0]>(5)test_function()
-    -> sess.set_trace(sys._getframe())
+    > <doctest test.test_pdb.test_pdb_issue_20766[0]>(6)test_function()
+    -> print('pdb %d: %s' % (i, sess._previous_sigint_handler))
     (Pdb) continue
     pdb 2: <built-in function default_int_handler>
     """
index 0174f80dce603af3b0d05916713c05710274e344..09d0adca2ee7a139f14abedf24ee51f80749c3ea 100644 (file)
@@ -1041,6 +1041,41 @@ class TraceTestCase(unittest.TestCase):
              (-8, 'return'),
              (1, 'return')])
 
+    def test_flow_converges_on_same_line(self):
+
+        def foo(x):
+            if x:
+                try:
+                    1/(x - 1)
+                except ZeroDivisionError:
+                    pass
+            return x
+
+        def func():
+            for i in range(2):
+                foo(i)
+
+        self.run_and_compare(func,
+            [(0, 'call'),
+             (1, 'line'),
+             (2, 'line'),
+             (-8, 'call'),
+             (-7, 'line'),
+             (-2, 'line'),
+             (-2, 'return'),
+             (1, 'line'),
+             (2, 'line'),
+             (-8, 'call'),
+             (-7, 'line'),
+             (-6, 'line'),
+             (-5, 'line'),
+             (-5, 'exception'),
+             (-4, 'line'),
+             (-3, 'line'),
+             (-2, 'line'),
+             (-2, 'return'),
+             (1, 'line'),
+             (1, 'return')])
 
 class SkipLineEventsTraceTestCase(TraceTestCase):
     """Repeat the trace tests, but with per-line events skipped"""
index 25548e34db246b413b6e611820e50db191055efe..2674e46b3d60654428353877c5e63689717c15cc 100644 (file)
@@ -35,7 +35,6 @@
 
 typedef struct {
     PyCodeObject *code; // The code object for the bounds. May be NULL.
-    int instr_prev;  // Only valid if code != NULL.
     PyCodeAddressRange bounds; // Only valid if code != NULL.
     CFrame cframe;
 } PyTraceInfo;
@@ -78,8 +77,8 @@ static void call_exc_trace(Py_tracefunc, PyObject *,
                            PyTraceInfo *trace_info);
 static int maybe_call_line_trace(Py_tracefunc, PyObject *,
                                  PyThreadState *, PyFrameObject *,
-                                 PyTraceInfo *);
-static void maybe_dtrace_line(PyFrameObject *, PyTraceInfo *);
+                                 PyTraceInfo *, int);
+static void maybe_dtrace_line(PyFrameObject *, PyTraceInfo *, int);
 static void dtrace_function_entry(PyFrameObject *);
 static void dtrace_function_return(PyFrameObject *);
 
@@ -1781,11 +1780,13 @@ main_loop:
         }
 
     tracing_dispatch:
+    {
+        int instr_prev = f->f_lasti;
         f->f_lasti = INSTR_OFFSET();
         NEXTOPARG();
 
         if (PyDTrace_LINE_ENABLED())
-            maybe_dtrace_line(f, &trace_info);
+            maybe_dtrace_line(f, &trace_info, instr_prev);
 
         /* line-by-line tracing support */
 
@@ -1799,7 +1800,7 @@ main_loop:
             err = maybe_call_line_trace(tstate->c_tracefunc,
                                         tstate->c_traceobj,
                                         tstate, f,
-                                        &trace_info);
+                                        &trace_info, instr_prev);
             /* Reload possibly changed frame fields */
             JUMPTO(f->f_lasti);
             stack_pointer = f->f_valuestack+f->f_stackdepth;
@@ -1810,6 +1811,7 @@ main_loop:
             }
             NEXTOPARG();
         }
+    }
 
 #ifdef LLTRACE
         /* Instruction tracing */
@@ -4502,9 +4504,6 @@ exception_unwind:
                 PUSH(val);
                 PUSH(exc);
                 JUMPTO(handler);
-                if (trace_info.cframe.use_tracing) {
-                    trace_info.instr_prev = INT_MAX;
-                }
                 /* Resume normal execution */
                 f->f_state = FRAME_EXECUTING;
                 goto main_loop;
@@ -5455,7 +5454,6 @@ initialize_trace_info(PyTraceInfo *trace_info, PyFrameObject *frame)
 {
     if (trace_info->code != frame->f_code) {
         trace_info->code = frame->f_code;
-        trace_info->instr_prev = -1;
         _PyCode_InitAddressRange(frame->f_code, &trace_info->bounds);
     }
 }
@@ -5507,7 +5505,7 @@ _PyEval_CallTracing(PyObject *func, PyObject *args)
 static int
 maybe_call_line_trace(Py_tracefunc func, PyObject *obj,
                       PyThreadState *tstate, PyFrameObject *frame,
-                      PyTraceInfo *trace_info)
+                      PyTraceInfo *trace_info, int instr_prev)
 {
     int result = 0;
 
@@ -5516,13 +5514,11 @@ maybe_call_line_trace(Py_tracefunc func, PyObject *obj,
        then call the trace function if we're tracing source lines.
     */
     initialize_trace_info(trace_info, frame);
-    int lastline = trace_info->bounds.ar_line;
+    int lastline = _PyCode_CheckLineNumber(instr_prev*2, &trace_info->bounds);
     int line = _PyCode_CheckLineNumber(frame->f_lasti*2, &trace_info->bounds);
     if (line != -1 && frame->f_trace_lines) {
-        /* Trace backward edges or first instruction of a new line */
-        if (frame->f_lasti < trace_info->instr_prev ||
-            (line != lastline && frame->f_lasti*2 == trace_info->bounds.ar_start))
-        {
+        /* Trace backward edges or if line number has changed */
+        if (frame->f_lasti < instr_prev || line != lastline) {
             result = call_trace(func, obj, tstate, frame, trace_info, PyTrace_LINE, Py_None);
         }
     }
@@ -5530,7 +5526,6 @@ maybe_call_line_trace(Py_tracefunc func, PyObject *obj,
     if (frame->f_trace_opcodes) {
         result = call_trace(func, obj, tstate, frame, trace_info, PyTrace_OPCODE, Py_None);
     }
-    trace_info->instr_prev = frame->f_lasti;
     return result;
 }
 
@@ -6475,7 +6470,7 @@ dtrace_function_return(PyFrameObject *f)
 /* DTrace equivalent of maybe_call_line_trace. */
 static void
 maybe_dtrace_line(PyFrameObject *frame,
-                  PyTraceInfo *trace_info)
+                  PyTraceInfo *trace_info, int instr_prev)
 {
     const char *co_filename, *co_name;
 
@@ -6487,7 +6482,7 @@ maybe_dtrace_line(PyFrameObject *frame,
     /* If the last instruction falls at the start of a line or if
        it represents a jump backwards, update the frame's line
        number and call the trace function. */
-    if (line != frame->f_lineno || frame->f_lasti < trace_info->instr_prev) {
+    if (line != frame->f_lineno || frame->f_lasti < instr_prev) {
         if (line != -1) {
             frame->f_lineno = line;
             co_filename = PyUnicode_AsUTF8(frame->f_code->co_filename);
@@ -6499,7 +6494,6 @@ maybe_dtrace_line(PyFrameObject *frame,
             PyDTrace_LINE(co_filename, co_name, line);
         }
     }
-    trace_info->instr_prev = frame->f_lasti;
 }