class CythonVariable(object):
- def __init__(self, name, cname, qualified_name, type):
+ def __init__(self, name, cname, qualified_name, type, lineno):
self.name = name
self.cname = cname
self.qualified_name = qualified_name
self.type = type
+ self.lineno = int(lineno)
class CythonFunction(CythonVariable):
def __init__(self,
super(CythonFunction, self).__init__(name,
cname,
qualified_name,
- type)
+ type,
+ lineno)
self.module = module
self.pf_cname = pf_cname
- self.lineno = int(lineno)
self.locals = {}
self.arguments = []
self.step_into_functions = set()
filename = None
lineno = 0
else:
- filename = symbol_and_line_obj.symtab.filename
+ filename = symbol_and_line_obj.symtab.fullname()
lineno = symbol_and_line_obj.line
if pygments:
lexer = pygments.lexers.CLexer(stripall=False)
print '%s%-*s = %s%s' % (prefix, max_name_length, name, typename,
value)
+ def is_initialized(self, cython_func, local_name):
+ cur_lineno = self.get_cython_lineno()
+ return (local_name in cython_func.arguments or
+ (local_name in cython_func.locals and
+ cur_lineno > cython_func.locals[local_name].lineno))
class SourceFileDescriptor(object):
def __init__(self, filename, lexer, formatter=None):
cy next
cy run
cy cont
+ cy finish
cy up
cy down
+ cy select
cy bt / cy backtrace
- cy print
cy list
+ cy print
cy locals
cy globals
+ cy exec
"""
name = 'cy'
command_class = gdb.COMMAND_NONE
completer_class = gdb.COMPLETE_COMMAND
- def __init__(self, *args):
- super(CythonCommand, self).__init__(*args, prefix=True)
+ def __init__(self, name, command_class, completer_class):
+ # keep the signature 2.5 compatible (i.e. do not use f(*a, k=v)
+ super(CythonCommand, self).__init__(name, command_class,
+ completer_class, prefix=True)
commands = dict(
import_ = CyImport.register(),
finish = CyFinish.register(),
up = CyUp.register(),
down = CyDown.register(),
+ select = CySelect.register(),
bt = CyBacktrace.register(),
list = CyList.register(),
print_ = CyPrint.register(),
return compl
-class CythonCodeStepper(CythonCommand, libpython.GenericCodeStepper):
+class CythonInfo(CythonBase, libpython.PythonInfo):
"""
- Base class for CyStep and CyNext. It implements the interface dictated by
- libpython.GenericCodeStepper.
+ Implementation of the interface dictated by libpython.LanguageInfo.
"""
def lineno(self, frame):
# related code. The C level should be dispatched to the 'step' command.
if self.is_cython_function(frame):
return self.get_cython_lineno(frame)
- else:
- return libpython.py_step.lineno(frame)
+ return super(CythonInfo, self).lineno(frame)
def get_source_line(self, frame):
try:
- line = super(CythonCodeStepper, self).get_source_line(frame)
+ line = super(CythonInfo, self).get_source_line(frame)
except gdb.GdbError:
return None
else:
return line.strip() or None
- @classmethod
- def register(cls):
- return cls(cls.name, stepinto=getattr(cls, 'stepinto', False))
+ def exc_info(self, frame):
+ if self.is_python_function:
+ return super(CythonInfo, self).exc_info(frame)
def runtime_break_functions(self):
if self.is_cython_function():
result.extend(self.cy.functions_by_cname)
return result
+
+class CythonExecutionControlCommand(CythonCommand,
+ libpython.ExecutionControlCommandBase):
+
+ @classmethod
+ def register(cls):
+ return cls(cls.name, cython_info)
+
+
+class CyStep(CythonExecutionControlCommand, libpython.PythonStepperMixin):
+ "Step through Cython, Python or C code."
+
+ name = 'cy step'
+ stepinto = True
+
def invoke(self, args, from_tty):
- if not self.is_cython_function() and not self.is_python_function():
+ if self.is_python_function():
+ self.python_step(args, self.stepinto)
+ elif not self.is_cython_function():
if self.stepinto:
command = 'step'
else:
self.finish_executing(gdb.execute(command, to_string=True))
else:
- self.step()
-
-
-class CyStep(CythonCodeStepper):
- "Step through Cython, Python or C code."
-
- name = 'cy step'
- stepinto = True
+ self.step(stepinto=self.stepinto)
-class CyNext(CythonCodeStepper):
+class CyNext(CyStep):
"Step-over Python code."
name = 'cy next'
stepinto = False
-class CyRun(CythonCodeStepper):
+class CyRun(CythonExecutionControlCommand):
"""
Run a Cython program. This is like the 'run' command, except that it
displays Cython or Python source lines as well
name = 'cy run'
- invoke = CythonCodeStepper.run
+ invoke = CythonExecutionControlCommand.run
-class CyCont(CyRun):
+class CyCont(CythonExecutionControlCommand):
"""
Continue a Cython program. This is like the 'run' command, except that it
displays Cython or Python source lines as well.
"""
name = 'cy cont'
- invoke = CythonCodeStepper.cont
+ invoke = CythonExecutionControlCommand.cont
-class CyFinish(CyRun):
+class CyFinish(CythonExecutionControlCommand):
"""
Execute until the function returns.
"""
name = 'cy finish'
- invoke = CythonCodeStepper.finish
+ invoke = CythonExecutionControlCommand.finish
class CyUp(CythonCommand):
_command = 'down'
+class CySelect(CythonCommand):
+ """
+ Select a frame. Use frame numbers as listed in `cy backtrace`.
+ This command is useful because `cy backtrace` prints a reversed backtrace.
+ """
+
+ name = 'cy select'
+
+ def invoke(self, stackno, from_tty):
+ try:
+ stackno = int(stackno)
+ except ValueError:
+ raise gdb.GdbError("Not a valid number: %r" % (stackno,))
+
+ frame = gdb.selected_frame()
+ while frame.newer():
+ frame = frame.newer()
+
+ stackdepth = libpython.stackdepth(frame)
+
+ try:
+ gdb.execute('select %d' % (stackdepth - stackno - 1,))
+ except RuntimeError, e:
+ raise gdb.GdbError(*e.args)
+
+
class CyBacktrace(CythonCommand):
'Print the Cython stack'
command_class = gdb.COMMAND_FILES
completer_class = gdb.COMPLETE_NONE
- @dispatch_on_frame(c_command='list')
+ # @dispatch_on_frame(c_command='list')
def invoke(self, _, from_tty):
sd, lineno = self.get_source_desc()
source = sd.get_source(lineno - 5, lineno + 5, mark_line=lineno,
command_class = gdb.COMMAND_STACK
completer_class = gdb.COMPLETE_NONE
- def _print_if_initialized(self, cyvar, max_name_length, prefix=''):
- try:
- value = gdb.parse_and_eval(cyvar.cname)
- except RuntimeError:
- # variable not initialized yet
- pass
- else:
- self.print_gdb_value(cyvar.name, value, max_name_length, prefix)
-
@dispatch_on_frame(c_command='info locals', python_command='py-locals')
def invoke(self, args, from_tty):
local_cython_vars = self.get_cython_function().locals
max_name_length = len(max(local_cython_vars, key=len))
for name, cyvar in sorted(local_cython_vars.iteritems(), key=sortkey):
- self._print_if_initialized(cyvar, max_name_length)
+ if self.is_initialized(self.get_cython_function(), cyvar.name):
+ value = gdb.parse_and_eval(cyvar.cname)
+ if not value.is_optimized_out:
+ self.print_gdb_value(cyvar.name, value,
+ max_name_length, '')
class CyGlobals(CyLocals):
print 'C globals:'
for name, cyvar in sorted(module_globals.iteritems(), key=sortkey):
if name not in seen:
- self._print_if_initialized(cyvar, max_name_length,
- prefix=' ')
+ try:
+ value = gdb.parse_and_eval(cyvar.cname)
+ except RuntimeError:
+ pass
+ else:
+ if not value.is_optimized_out:
+ self.print_gdb_value(cyvar.name, value,
+ max_name_length, ' ')
-class CyExec(CythonCommand):
+class CyExec(CythonCommand, libpython.PyExec):
+ """
+ Execute Python code in the nearest Python or Cython frame.
+ """
+
name = '-cy-exec'
command_class = gdb.COMMAND_STACK
completer_class = gdb.COMPLETE_NONE
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:
- # skip unitialized Cython variables
+ if (cyvar.type == PythonObject and
+ self.is_initialized(cython_func, name)):
+
try:
val = gdb.parse_and_eval(cyvar.cname)
except RuntimeError:
continue
else:
- # Fortunately, Cython initializes all local (automatic)
- # variables to NULL
- if libpython.pointervalue(val) == 0:
+ if val.is_optimized_out:
continue
-
+
pystringp = executor.alloc_pystring(name)
code = '''
- PyDict_SetItem(
- (PyObject *) %d,
- (PyObject *) %d,
+ (PyObject *) PyDict_SetItem(
+ (PyObject *) %d,
+ (PyObject *) %d,
(PyObject *) %s)
''' % (local_dict_pointer, pystringp, cyvar.cname)
-
- # PyDict_SetItem doesn't steal our reference
- executor.decref(pystringp)
-
- if gdb.parse_and_eval(code) < 0:
- gdb.parse_and_eval('PyErr_Print()')
- raise gdb.GdbError("Unable to execute Python code.")
+
+ try:
+ if gdb.parse_and_eval(code) < 0:
+ gdb.parse_and_eval('PyErr_Print()')
+ raise gdb.GdbError("Unable to execute Python code.")
+ finally:
+ # PyDict_SetItem doesn't steal our reference
+ executor.decref(pystringp)
def _find_first_cython_or_python_frame(self):
frame = gdb.selected_frame()
libpython.py_exec.invoke(expr, from_tty)
return
+ expr, input_type = self.readcode(expr)
executor = libpython.PythonCodeExecutor()
- # get the dict of Cython globals and construct a dict in the inferior
- # with Cython locals
- global_dict = gdb.parse_and_eval(
- '(PyObject *) PyModule_GetDict(__pyx_m)')
- local_dict = gdb.parse_and_eval('(PyObject *) PyDict_New()')
-
- try:
- self._fill_locals_dict(executor, libpython.pointervalue(local_dict))
- executor.evalcode(expr, global_dict, local_dict)
- finally:
- executor.decref(libpython.pointervalue(local_dict))
+ with libpython.FetchAndRestoreError():
+ # get the dict of Cython globals and construct a dict in the
+ # inferior with Cython locals
+ global_dict = gdb.parse_and_eval(
+ '(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))
+ executor.evalcode(expr, input_type, global_dict, local_dict)
+ finally:
+ executor.decref(libpython.pointervalue(local_dict))
# Functions
try:
cname = super(CyCValue, self).invoke(cyname, frame=frame)
return gdb.parse_and_eval(cname)
- except (gdb.GdbError, RuntimeError):
+ except (gdb.GdbError, RuntimeError), e:
# variable exists but may not have been initialized yet, or may be
# in the globals dict of the Cython module
d = self.get_cython_globals_dict()
if cyname in d:
return d[cyname]._gdbval
- raise gdb.GdbError("Variable %s not initialized yet." % cyname)
+ raise gdb.GdbError(str(e))
class CyLine(gdb.Function, CythonBase):
def invoke(self):
return self.get_cython_lineno()
-
-cy = CyCy.register()
\ No newline at end of file
+cython_info = CythonInfo()
+cy = CyCy.register()
+cython_info.cy = cy
\ No newline at end of file