From b0f6eba4f3e05b53f4f81feebe8993e651c79806 Mon Sep 17 00:00:00 2001 From: Mark Florisson Date: Tue, 25 Jan 2011 21:41:31 +0100 Subject: [PATCH] Debugger: Fix a few libpython.py bugs --- Cython/Debugger/libcython.py | 24 +++++---------- Cython/Debugger/libpython.py | 59 +++++++++++++++++++++++++++++------- 2 files changed, 55 insertions(+), 28 deletions(-) diff --git a/Cython/Debugger/libcython.py b/Cython/Debugger/libcython.py index 0aa4bcfa..8d7771b5 100644 --- a/Cython/Debugger/libcython.py +++ b/Cython/Debugger/libcython.py @@ -4,10 +4,8 @@ GDB extension that adds Cython support. from __future__ import with_statement -import os import sys import textwrap -import operator import traceback import functools import itertools @@ -16,8 +14,8 @@ import collections import gdb try: - from lxml import etree - have_lxml = True + from lxml import etree + have_lxml = True except ImportError: have_lxml = False try: @@ -279,7 +277,7 @@ class CythonBase(object): if self.is_cython_function(frame) or self.is_python_function(frame): return True elif older_frame and self.is_cython_function(older_frame): - # direct C function call from a Cython function + # check for direct C function call from a Cython function cython_func = self.get_cython_function(older_frame) return name in cython_func.step_into_functions @@ -756,8 +754,8 @@ class CyBreak(CythonCommand): breakpoint = '%s:%s' % (cython_module.c_filename, c_lineno) gdb.execute('break ' + breakpoint) else: - raise GdbError("Not a valid line number. " - "Does it contain actual code?") + raise gdb.GdbError("Not a valid line number. " + "Does it contain actual code?") def _break_funcname(self, funcname): func = self.cy.functions_by_qualified_name.get(funcname) @@ -1030,7 +1028,7 @@ class CyBacktrace(CythonCommand): @require_running_program def invoke(self, args, from_tty): # get the first frame - selected_frame = frame = gdb.selected_frame() + frame = gdb.selected_frame() while frame.older(): frame = frame.older() @@ -1038,13 +1036,10 @@ class CyBacktrace(CythonCommand): index = 0 while frame: - is_c = False - - is_relevant = False try: is_relevant = self.is_relevant_function(frame) except CyGDBError: - pass + is_relevant = False if print_all or is_relevant: self.print_stackframe(frame, index) @@ -1052,8 +1047,6 @@ class CyBacktrace(CythonCommand): index += 1 frame = frame.newer() - selected_frame.select() - class CyList(CythonCommand): """ @@ -1188,7 +1181,6 @@ class CyExec(CythonCommand, libpython.PyExec): def _fill_locals_dict(self, executor, local_dict_pointer): "Fill a remotely allocated dict with values from the Cython C stack" cython_func = self.get_cython_function() - current_lineno = self.get_cython_lineno() for name, cyvar in cython_func.locals.iteritems(): if (cyvar.type == PythonObject and @@ -1245,8 +1237,6 @@ class CyExec(CythonCommand, libpython.PyExec): '(PyObject *) PyModule_GetDict(__pyx_m)') local_dict = gdb.parse_and_eval('(PyObject *) PyDict_New()') - cython_function = self.get_cython_function() - try: self._fill_locals_dict(executor, libpython.pointervalue(local_dict)) diff --git a/Cython/Debugger/libpython.py b/Cython/Debugger/libpython.py index 78534a49..4729412c 100644 --- a/Cython/Debugger/libpython.py +++ b/Cython/Debugger/libpython.py @@ -1322,6 +1322,16 @@ class PyUnicodeObjectPtr(PyObjectPtr): out.write(quote) + def __unicode__(self): + return self.proxyval(set()) + + def __str__(self): + # In Python 3, everything is unicode (including attributes of e.g. + # code objects, such as function names). The Python 2 debugger code + # uses PyUnicodePtr objects to format strings etc, whereas with a + # Python 2 debuggee we'd get PyStringObjectPtr instances with __str__. + # Be compatible with that. + return unicode(self).encode('UTF-8') def int_from_int(gdbval): return int(str(gdbval)) @@ -1452,12 +1462,39 @@ class Frame(object): return False + def read_var(self, varname): + """ + read_var with respect to code blocks (gdbframe.read_var works with + respect to the most recent block) + + Apparently this function doesn't work, though, as it seems to read + variables in other frames also sometimes. + """ + block = self._gdbframe.block() + var = None + + while block and var is None: + try: + var = self._gdbframe.read_var(varname, block) + except ValueError: + pass + + block = block.superblock + + return var + def get_pyop(self): try: - f = self._gdbframe.read_var('f') - return PyFrameObjectPtr.from_pyobject_ptr(f) - except ValueError: + # self.read_var does not always work properly, so select our frame + # and restore the previously selected frame + selected_frame = gdb.selected_frame() + self._gdbframe.select() + f = gdb.parse_and_eval('f') + selected_frame.select() + except RuntimeError: return None + else: + return PyFrameObjectPtr.from_pyobject_ptr(f) @classmethod def get_selected_frame(cls): @@ -2174,9 +2211,10 @@ class PythonStepperMixin(object): """ def python_step(self, stepinto): - frame = gdb.selected_frame() - framewrapper = Frame(frame) - + """ + Set a watchpoint on the Python bytecode instruction pointer and try + to finish the frame + """ output = gdb.execute('watch f->f_lasti', to_string=True) watchpoint = int(re.search(r'[Ww]atchpoint (\d+):', output).group(1)) self.step(stepinto=stepinto, stepover_command='finish') @@ -2392,10 +2430,9 @@ class FixGdbCommand(gdb.Command): def fix_gdb(self): """ - So, you must be wondering what the story is this time! Yeeees, indeed, - I have quite the story for you! It seems that invoking either 'cy exec' - and 'py-exec' work perfectly fine, but after this gdb's python API is - entirely broken. Some unset exception value is still set? + It seems that invoking either 'cy exec' and 'py-exec' work perfectly + fine, but after this gdb's python API is entirely broken. + Maybe some uncleared exception value is still set? sys.exc_clear() didn't help. A demonstration: (gdb) cy exec 'hello' @@ -2443,7 +2480,7 @@ class PyExec(gdb.Command): lines.append(line) - return '\n'.join(lines), Py_file_input + return '\n'.join(lines), PythonCodeExecutor.Py_file_input def invoke(self, expr, from_tty): expr, input_type = self.readcode(expr) -- 2.26.2