From 5279759f52cc5397acfb04351b34feea862864de Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 15 Jun 2018 19:44:00 +0200 Subject: [PATCH] bpo-32962: Backport python-gdb.py and test_gdb.py from master (GH-7710) (GH-7711) * 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) (cherry picked from commit ca4cb8492c643d1fcac2c5b749595ad5377673ab) --- Lib/test/test_gdb.py | 11 +++- .../2018-05-10-16-59-15.bpo-32962.S-rcIN.rst | 1 + .../2018-06-14-16-16-53.bpo-32962.2YfdwI.rst | 2 + .../2018-06-14-16-23-07.bpo-32962.Q3Dwns.rst | 2 + Tools/gdb/libpython.py | 50 +++++++++++++------ 5 files changed, 48 insertions(+), 18 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2018-05-10-16-59-15.bpo-32962.S-rcIN.rst create mode 100644 Misc/NEWS.d/next/Tools-Demos/2018-06-14-16-16-53.bpo-32962.2YfdwI.rst create mode 100644 Misc/NEWS.d/next/Tools-Demos/2018-06-14-16-23-07.bpo-32962.Q3Dwns.rst diff --git a/Lib/test/test_gdb.py b/Lib/test/test_gdb.py index b7554d698c9..2b1c6cd0c38 100644 --- a/Lib/test/test_gdb.py +++ b/Lib/test/test_gdb.py @@ -168,7 +168,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'] @@ -859,9 +863,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"' def safe_tp_name(self): try: return self.field('self')['ob_type']['tp_name'].string() - except (NullPyObjectPtr, RuntimeError): + except (NullPyObjectPtr, RuntimeError, UnicodeDecodeError): return '' def safe_self_addresss(self): @@ -1541,6 +1547,7 @@ class Frame(object): return False if caller == 'PyCFunction_Call': + arg_name = 'func' # Within that frame: # "func" is the local containing the PyObject* of the # PyCFunctionObject instance @@ -1548,24 +1555,35 @@ class Frame(object): # "self" is the (PyObject*) of the 'self' try: # Use the prettyprinter for the func: - func = frame.read_var('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 "func")' + return 'PyCFunction invocation (unable to read %s)' % arg_name elif caller == '_PyCFunction_FastCallDict': + arg_name = 'func_obj' try: - func = frame.read_var('func_obj') + 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 "func_obj")' + 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 ('' % arg_name) except RuntimeError: - return '' + return '' % arg_name # This frame isn't worth reporting: return False