]> granicus.if.org Git - python/commitdiff
[3.7] bpo-32962: Backport python-gdb.py and test_gdb.py from master (GH-7710)
authorVictor Stinner <vstinner@redhat.com>
Fri, 15 Jun 2018 17:11:45 +0000 (19:11 +0200)
committerGitHub <noreply@github.com>
Fri, 15 Jun 2018 17:11:45 +0000 (19:11 +0200)
* bpo-32962: python-gdb catchs ValueError on read_var() (GH-7692)

python-gdb now catchs ValueError on read_var(): when Python has no
debug symbols for example.

(cherry picked from commit 019d33b7a447e78057842332fb5d3bad01922122)

* bpo-32962: python-gdb catchs UnicodeDecodeError (GH-7693)

python-gdb now catchs UnicodeDecodeError exceptions when calling
string().

(cherry picked from commit d22fc0bc7de7882da204abe50884bbde2da4f9e7)

* bpo-32962: Fix test_gdb failure in debug build with -mcet -fcf-protection -O0 (GH-6754)

When Python is built with the intel control-flow protection flags,
-mcet -fcf-protection, gdb is not able to read the stack without
actually jumping inside the function. This means an extra
'next' command is required to make the $pc (program counter)
enter the function and make the stack of the function exposed to gdb.

(cherry picked from commit 9b7c74ca32d1bec7128d550a9ab1b2ddc7046287)

Lib/test/test_gdb.py
Misc/NEWS.d/next/Tests/2018-05-10-16-59-15.bpo-32962.S-rcIN.rst [new file with mode: 0644]
Misc/NEWS.d/next/Tools-Demos/2018-06-14-16-16-53.bpo-32962.2YfdwI.rst [new file with mode: 0644]
Misc/NEWS.d/next/Tools-Demos/2018-06-14-16-23-07.bpo-32962.Q3Dwns.rst [new file with mode: 0644]
Tools/gdb/libpython.py

index 9e0eaea8c8f69219285318c9dd00c2855ebf0238..d341a17f1fec80f88be2c025b0320ee5d85e80ed 100644 (file)
@@ -162,7 +162,11 @@ class DebuggerTests(unittest.TestCase):
             commands += ['set print entry-values no']
 
         if cmds_after_breakpoint:
-            commands += cmds_after_breakpoint
+            # bpo-32962: When Python is compiled with -mcet -fcf-protection,
+            # arguments are unusable before running the first instruction
+            # of the function entry point. The 'next' command makes the
+            # required first step.
+            commands += ['next'] + cmds_after_breakpoint
         else:
             commands += ['backtrace']
 
@@ -847,9 +851,12 @@ id(42)
             id("first break point")
             l = MyList()
         ''')
+        # bpo-32962: same case as in get_stack_trace():
+        # we need an additional 'next' command in order to read
+        # arguments of the innermost function of the call stack.
         # Verify with "py-bt":
         gdb_output = self.get_stack_trace(cmd,
-                                          cmds_after_breakpoint=['break wrapper_call', 'continue', 'py-bt'])
+                                          cmds_after_breakpoint=['break wrapper_call', 'continue', 'next', 'py-bt'])
         self.assertRegex(gdb_output,
                          r"<method-wrapper u?'__init__' of MyList object at ")
 
diff --git a/Misc/NEWS.d/next/Tests/2018-05-10-16-59-15.bpo-32962.S-rcIN.rst b/Misc/NEWS.d/next/Tests/2018-05-10-16-59-15.bpo-32962.S-rcIN.rst
new file mode 100644 (file)
index 0000000..97328eb
--- /dev/null
@@ -0,0 +1 @@
+Fixed test_gdb when Python is compiled with flags -mcet -fcf-protection -O0.
diff --git a/Misc/NEWS.d/next/Tools-Demos/2018-06-14-16-16-53.bpo-32962.2YfdwI.rst b/Misc/NEWS.d/next/Tools-Demos/2018-06-14-16-16-53.bpo-32962.2YfdwI.rst
new file mode 100644 (file)
index 0000000..de40070
--- /dev/null
@@ -0,0 +1,2 @@
+python-gdb now catchs ValueError on read_var(): when Python has no debug
+symbols for example.
diff --git a/Misc/NEWS.d/next/Tools-Demos/2018-06-14-16-23-07.bpo-32962.Q3Dwns.rst b/Misc/NEWS.d/next/Tools-Demos/2018-06-14-16-23-07.bpo-32962.Q3Dwns.rst
new file mode 100644 (file)
index 0000000..fc14261
--- /dev/null
@@ -0,0 +1,2 @@
+python-gdb now catchs ``UnicodeDecodeError`` exceptions when calling
+``string()``.
index 7df7c9bd541672fab6ba2679ae0c01d066e79dea..27eabcb0d3d0916a929f34b7d91efab8526fde89 100755 (executable)
@@ -270,12 +270,13 @@ class PyObjectPtr(object):
 
     def safe_tp_name(self):
         try:
-            return self.type().field('tp_name').string()
-        except NullPyObjectPtr:
-            # NULL tp_name?
-            return 'unknown'
-        except RuntimeError:
-            # Can't even read the object at all?
+            ob_type = self.type()
+            tp_name = ob_type.field('tp_name')
+            return tp_name.string()
+        # NullPyObjectPtr: NULL tp_name?
+        # RuntimeError: Can't even read the object at all?
+        # UnicodeDecodeError: Failed to decode tp_name bytestring
+        except (NullPyObjectPtr, RuntimeError, UnicodeDecodeError):
             return 'unknown'
 
     def proxyval(self, visited):
@@ -349,7 +350,9 @@ class PyObjectPtr(object):
         try:
             tp_name = t.field('tp_name').string()
             tp_flags = int(t.field('tp_flags'))
-        except RuntimeError:
+        # RuntimeError: NULL pointers
+        # UnicodeDecodeError: string() fails to decode the bytestring
+        except (RuntimeError, UnicodeDecodeError):
             # Handle any kind of error e.g. NULL ptrs by simply using the base
             # class
             return cls
@@ -617,7 +620,10 @@ class PyCFunctionObjectPtr(PyObjectPtr):
 
     def proxyval(self, visited):
         m_ml = self.field('m_ml') # m_ml is a (PyMethodDef*)
-        ml_name = m_ml['ml_name'].string()
+        try:
+            ml_name = m_ml['ml_name'].string()
+        except UnicodeDecodeError:
+            ml_name = '<ml_name:UnicodeDecodeError>'
 
         pyop_m_self = self.pyop_field('m_self')
         if pyop_m_self.is_null():
@@ -1340,13 +1346,13 @@ class wrapperobject(PyObjectPtr):
         try:
             name = self.field('descr')['d_base']['name'].string()
             return repr(name)
-        except (NullPyObjectPtr, RuntimeError):
+        except (NullPyObjectPtr, RuntimeError, UnicodeDecodeError):
             return '<unknown name>'
 
     def safe_tp_name(self):
         try:
             return self.field('self')['ob_type']['tp_name'].string()
-        except (NullPyObjectPtr, RuntimeError):
+        except (NullPyObjectPtr, RuntimeError, UnicodeDecodeError):
             return '<unknown tp_name>'
 
     def safe_self_addresss(self):
@@ -1552,15 +1558,22 @@ class Frame(object):
                 # Use the prettyprinter for the func:
                 func = frame.read_var(arg_name)
                 return str(func)
+            except ValueError:
+                return ('PyCFunction invocation (unable to read %s: '
+                        'missing debuginfos?)' % arg_name)
             except RuntimeError:
                 return 'PyCFunction invocation (unable to read %s)' % arg_name
 
         if caller == 'wrapper_call':
+            arg_name = 'wp'
             try:
-                func = frame.read_var('wp')
+                func = frame.read_var(arg_name)
                 return str(func)
+            except ValueError:
+                return ('<wrapper_call invocation (unable to read %s: '
+                        'missing debuginfos?)>' % arg_name)
             except RuntimeError:
-                return '<wrapper_call invocation>'
+                return '<wrapper_call invocation (unable to read %s)>' % arg_name
 
         # This frame isn't worth reporting:
         return False