Commit 1327c85b authored by Mark Florisson's avatar Mark Florisson

dispatch based on frame

python code stepping (for libpython and libcython)
generic stepper class
fix step-into functions
have cygdb accept a '--' command line argument to disable automatic importing
replace gdb.execute() with something that actually captures all output
have 'cy break' break properly on line numbers
parent f1e63d52
......@@ -185,7 +185,7 @@ class Context(object):
from ParseTreeTransforms import DebugTransform
self.debug_outputwriter = DebugWriter.CythonDebugWriter(
options.output_dir)
debug_transform = [DebugTransform(self)]
debug_transform = [DebugTransform(self, options)]
else:
debug_transform = []
......
......@@ -1441,18 +1441,23 @@ class DebugTransform(CythonTransform):
to enable debugging.
"""
def __init__(self, context):
def __init__(self, context, options):
super(DebugTransform, self).__init__(context)
self.visited = set()
# our treebuilder and debug output writer
# (see Cython.Debugger.debug_output.CythonDebugWriter)
self.tb = self.context.debug_outputwriter
self.c_output_file = options.output_file
# tells visit_NameNode whether it should register step-into functions
self.register_stepinto = False
def visit_ModuleNode(self, node):
self.tb.module_name = node.full_module_name
attrs = dict(
module_name=node.full_module_name,
filename=node.pos[0].filename)
filename=node.pos[0].filename,
c_filename=self.c_output_file)
self.tb.start('Module', attrs)
......@@ -1503,20 +1508,25 @@ class DebugTransform(CythonTransform):
self.tb.end('Arguments')
self.tb.start('StepIntoFunctions')
self.register_stepinto = True
self.visitchildren(node)
self.register_stepinto = False
self.tb.end('StepIntoFunctions')
self.tb.end('Function')
return node
def visit_NameNode(self, node):
if (node.type.is_cfunction and
node.is_called and
node.entry.in_cinclude):
if (self.register_stepinto and node.type.is_cfunction and node.is_called):
# don't check node.entry.in_cinclude, as 'cdef extern: ...'
# declared functions are not 'in_cinclude'.
# This means we will list called 'cdef' functions as
# "step into functions", but this is not an issue as they will be
# recognized as Cython functions anyway.
attrs = dict(name=node.entry.func_cname)
self.tb.start('StepIntoFunction', attrs=attrs)
self.tb.end('StepIntoFunction')
self.visitchildren(node)
return node
......
......@@ -20,25 +20,29 @@ import subprocess
def usage():
print("Usage: cygdb [PATH GDB_ARGUMENTS]")
def make_command_file(path_to_debug_info):
debug_files = glob.glob(
os.path.join(path_to_debug_info, 'cython_debug/cython_debug_info_*'))
if not debug_files:
usage()
sys.exit('No debug files were found in %s. Aborting.' % (
os.path.abspath(path_to_debug_info)))
def make_command_file(path_to_debug_info, prefix_code='', no_import=False):
if not no_import:
pattern = os.path.join(path_to_debug_info,
'cython_debug/cython_debug_info_*')
debug_files = glob.glob(pattern)
if not debug_files:
usage()
sys.exit('No debug files were found in %s. Aborting.' % (
os.path.abspath(path_to_debug_info)))
fd, tempfilename = tempfile.mkstemp()
f = os.fdopen(fd, 'w')
f.write(prefix_code)
f.write('set breakpoint pending on\n')
f.write('python from Cython.Debugger import libcython\n')
f.write('\n'.join('cy import %s\n' % fn for fn in debug_files))
if not no_import:
f.write('\n'.join('cy import %s\n' % fn for fn in debug_files))
f.close()
return tempfilename
def main(gdb_argv=[], path_to_debug_info=os.curdir):
def main(path_to_debug_info=os.curdir, gdb_argv=[], no_import=False):
"""
Start the Cython debugger. This tells gdb to import the Cython and Python
extensions (libpython.py and libcython.py) and it enables gdb's pending
......@@ -46,7 +50,7 @@ def main(gdb_argv=[], path_to_debug_info=os.curdir):
path_to_debug_info is the path to the cython_debug directory
"""
tempfilename = make_command_file(path_to_debug_info)
tempfilename = make_command_file(path_to_debug_info, no_import=no_import)
p = subprocess.Popen(['gdb', '-command', tempfilename] + gdb_argv)
while True:
try:
......
import os
import errno
try:
from lxml import etree
have_lxml = True
except ImportError:
have_lxml = False
try:
# Python 2.5
from xml.etree import cElementTree as etree
except ImportError:
try:
# Python 2.5
from xml.etree import ElementTree as etree
except ImportError:
try:
# normal cElementTree install
import cElementTree as etree
except ImportError:
try:
# normal ElementTree install
import elementtree.ElementTree as etree
except ImportError:
etree = None
from Cython.Compiler import Errors
class CythonDebugWriter(object):
"""
Class to output debugging information for cygdb
It writes debug information to cython_debug/cython_debug_info_<modulename>
in the build directory.
"""
def __init__(self, output_dir):
if etree is None:
raise Errors.NoElementTreeInstalledException()
self.output_dir = os.path.join(output_dir, 'cython_debug')
self.tb = etree.TreeBuilder()
# set by Cython.Compiler.ParseTreeTransforms.DebugTransform
self.module_name = None
self.start('cython_debug', attrs=dict(version='1.0'))
def start(self, name, attrs=None):
self.tb.start(name, attrs or {})
def end(self, name):
self.tb.end(name)
def serialize(self):
self.tb.end('Module')
self.tb.end('cython_debug')
xml_root_element = self.tb.close()
try:
os.makedirs(self.output_dir)
except OSError, e:
if e.errno != errno.EEXIST:
raise
et = etree.ElementTree(xml_root_element)
kw = {}
if have_lxml:
kw['pretty_print'] = True
fn = "cython_debug_info_" + self.module_name
et.write(os.path.join(self.output_dir, fn), encoding="UTF-8", **kw)
\ No newline at end of file
......@@ -90,20 +90,43 @@ def require_cython_frame(function):
'Cython function we know about.')
return function(self, *args, **kwargs)
return wrapper
def dispatch_on_frame(c_command, python_command=None):
def decorator(function):
@functools.wraps(function)
def wrapper(self, *args, **kwargs):
is_cy = self.is_cython_function()
is_py = self.is_python_function()
if is_cy or (is_py and not python_command):
function(self, *args, **kwargs)
elif is_py:
gdb.execute(python_command)
elif self.is_relevant_function():
gdb.execute(c_command)
else:
raise gdb.GdbError("Not a function cygdb knows about. "
"Use the normal GDB commands instead.")
return wrapper
return decorator
# Classes that represent the debug information
# Don't rename the parameters of these classes, they come directly from the XML
class CythonModule(object):
def __init__(self, module_name, filename):
def __init__(self, module_name, filename, c_filename):
self.name = module_name
self.filename = filename
self.c_filename = c_filename
self.globals = {}
# {cython_lineno: min(c_linenos)}
self.lineno_cy2c = {}
# {c_lineno: cython_lineno}
self.lineno_c2cy = {}
self.functions = {}
def qualified_name(self, varname):
return '.'.join(self.name, varname)
......@@ -146,12 +169,15 @@ class CythonBase(object):
@default_selected_gdb_frame(err=False)
def is_python_function(self, frame):
return frame.name() == 'PyEval_EvalFrameEx'
@default_selected_gdb_frame()
def is_python_function(self, frame):
return libpython.Frame(frame).is_evalframeex()
"""
Tells if a frame is associated with a Python function.
If we can't read the Python frame information, don't regard it as such.
"""
if frame.name() == 'PyEval_EvalFrameEx':
pyframe = libpython.Frame(frame).get_pyop()
return pyframe and not pyframe.is_optimized_out()
return False
@default_selected_gdb_frame()
def get_c_function_name(self, frame):
return frame.name()
......@@ -170,9 +196,13 @@ class CythonBase(object):
@default_selected_gdb_frame()
def get_cython_lineno(self, frame):
"""
Get the current Cython line number. Returns 0 if there is no
correspondence between the C and Cython code.
"""
cyfunc = self.get_cython_function(frame)
return cyfunc.module.lineno_c2cy.get(self.get_c_lineno(frame))
return cyfunc.module.lineno_c2cy.get(self.get_c_lineno(frame), 0)
@default_selected_gdb_frame()
def get_source_desc(self, frame):
filename = lineno = lexer = None
......@@ -185,7 +215,7 @@ class CythonBase(object):
pyframeobject = libpython.Frame(frame).get_pyop()
if not pyframeobject:
raise GdbError('Unable to read information on python frame')
raise gdb.GdbError('Unable to read information on python frame')
filename = pyframeobject.filename()
lineno = pyframeobject.current_line_num()
......@@ -198,6 +228,25 @@ class CythonBase(object):
def get_source_line(self, frame):
source_desc, lineno = self.get_source_desc()
return source_desc.get_source(lineno)
def is_relevant_function(self, frame):
"""
returns whether we care about a frame on the user-level when debugging
Cython code
"""
name = frame.name()
older_frame = frame.older()
if self.is_cython_function(frame) or self.is_python_function(frame):
return True
elif (parameters.step_into_c_code and
older_frame and self.is_cython_function(older_frame)):
# direct C function call from a Cython function
cython_func = self.get_cython_function(older_frame)
return name in cython_func.step_into_functions
return False
class SourceFileDescriptor(object):
def __init__(self, filename, lexer, formatter=None):
......@@ -238,13 +287,20 @@ class SourceFileDescriptor(object):
yield '%s %4d %s' % (prefix, start + idx, line)
def get_source(self, start, stop=None, lex_source=True, mark_line=0):
exc = gdb.GdbError('Unable to retrieve source code')
if not self.filename:
raise GdbError('Unable to retrieve source code')
raise exc
if stop is None:
stop = start + 1
return '\n'.join(self._get_source(start, stop, lex_source, mark_line))
try:
return '\n'.join(
self._get_source(start, stop, lex_source, mark_line))
except IOError:
raise exc
# Errors
......@@ -299,18 +355,24 @@ class CompleteUnqualifiedFunctionNames(CythonParameter):
class ColorizeSourceCode(CythonParameter):
"""
Tell cygdb whether to colorize source code
Tell cygdb whether to colorize source code.
"""
class TerminalBackground(CythonParameter):
"""
Tell cygdb about the user's terminal background (light or dark)
Tell cygdb about the user's terminal background (light or dark).
"""
class StepIntoCCode(CythonParameter):
"""
Tells cygdb whether to step into C functions called directly from Cython
code.
"""
class CythonParameters(object):
"""
Simple container class that might get more functionality in the distant
future (mostly to remind us that we're dealing with parameters)
future (mostly to remind us that we're dealing with parameters).
"""
def __init__(self):
......@@ -329,7 +391,12 @@ class CythonParameters(object):
gdb.COMMAND_FILES,
gdb.PARAM_STRING,
"dark")
self.step_into_c_code = StepIntoCCode(
'cy_step_into_c_code',
gdb.COMMAND_RUNNING,
gdb.PARAM_BOOLEAN,
True)
parameters = CythonParameters()
......@@ -340,6 +407,14 @@ class CythonCommand(gdb.Command, CythonBase):
Base class for Cython commands
"""
@classmethod
def register(cls, *args, **kwargs):
if not hasattr(cls, 'completer_class'):
return cls(cls.name, cls.command_class, *args, **kwargs)
else:
return cls(cls.name, cls.command_class, cls.completer_class,
*args, **kwargs)
class CyCy(CythonCommand):
"""
......@@ -348,6 +423,7 @@ class CyCy(CythonCommand):
cy import
cy break
cy step
cy next
cy print
cy list
cy locals
......@@ -357,31 +433,29 @@ class CyCy(CythonCommand):
cy down
"""
def __init__(self):
super(CythonCommand, self).__init__(
'cy', gdb.COMMAND_NONE, gdb.COMPLETE_COMMAND, prefix=True)
self.import_ = CyImport(
'cy import', gdb.COMMAND_STATUS, gdb.COMPLETE_FILENAME)
self.break_ = CyBreak('cy break', gdb.COMMAND_BREAKPOINTS)
self.step = CyStep('cy step', gdb.COMMAND_RUNNING, gdb.COMPLETE_NONE)
self.next = CyNext('cy next', gdb.COMMAND_RUNNING, gdb.COMPLETE_NONE)
self.list = CyList('cy list', gdb.COMMAND_FILES, gdb.COMPLETE_NONE)
self.print_ = CyPrint('cy print', gdb.COMMAND_DATA)
name = 'cy'
command_class = gdb.COMMAND_NONE
completer_class = gdb.COMPLETE_COMMAND
def __init__(self, *args):
super(CythonCommand, self).__init__(*args, prefix=True)
self.locals = CyLocals(
'cy locals', gdb.COMMAND_STACK, gdb.COMPLETE_NONE)
self.globals = CyGlobals(
'cy globals', gdb.COMMAND_STACK, gdb.COMPLETE_NONE)
commands = dict(
import_ = CyImport.register(),
break_ = CyBreak.register(),
step = CyStep.register(),
next = CyNext.register(),
list = CyList.register(),
print_ = CyPrint.register(),
locals = CyLocals.register(),
globals = CyGlobals.register(),
cy_cname = CyCName('cy_cname'),
cy_line = CyLine('cy_line'),
)
self.cy_cname = CyCName('cy_cname')
objs = (self.import_, self.break_, self.step, self.list, self.print_,
self.locals, self.globals, self.cy_cname)
for obj in objs:
obj.cy = self
for command_name, command in commands.iteritems():
command.cy = self
setattr(self, command_name, command)
# Cython module namespace
self.cython_namespace = {}
......@@ -403,15 +477,19 @@ class CyImport(CythonCommand):
Import debug information outputted by the Cython compiler
Example: cy import FILE...
"""
name = 'cy import'
command_class = gdb.COMMAND_STATUS
completer_class = gdb.COMPLETE_FILENAME
def invoke(self, args, from_tty):
args = args.encode(_filesystemencoding)
for arg in string_to_argv(args):
try:
f = open(arg)
except OSError, e:
print('Unable to open file %r: %s' % (args, e.args[1]))
return
raise gdb.GdbError('Unable to open file %r: %s' %
(args, e.args[1]))
t = etree.parse(f)
......@@ -426,15 +504,20 @@ class CyImport(CythonCommand):
for function in module.find('Functions'):
cython_function = CythonFunction(module=cython_module,
**function.attrib)
# update the global function mappings
self.cy.functions_by_name[cython_function.name].append(
cython_function)
name = cython_function.name
self.cy.functions_by_name[name].append(cython_function)
self.cy.functions_by_qualified_name[
cython_function.qualified_name] = cython_function
self.cy.functions_by_cname[
cython_function.cname] = cython_function
d = cython_module.functions
L = d.setdefault(cython_function.qualified_name, [])
L.append(cython_function)
for local in function.find('Locals'):
d = local.attrib
cython_function.locals[d['name']] = CythonVariable(**d)
......@@ -469,13 +552,16 @@ class CyBreak(CythonCommand):
cy break cython_module:lineno...
"""
name = 'cy break'
command_class = gdb.COMMAND_BREAKPOINTS
def _break_pyx(self, name):
modulename, _, lineno = name.partition(':')
lineno = int(lineno)
cython_module = self.cy.cython_namespace[modulename]
if lineno in cython_module.lineno_cy2c:
c_lineno = cython_module.lineno_cy2c[lineno]
breakpoint = '%s:%s' % (cython_module.name, c_lineno)
breakpoint = '%s:%s' % (cython_module.c_filename, c_lineno)
gdb.execute('break ' + breakpoint)
else:
raise GdbError("Not a valid line number. "
......@@ -554,78 +640,74 @@ class CyBreak(CythonCommand):
return compl
class CodeStepperMixin(object):
class CythonCodeStepper(CythonCommand, libpython.GenericCodeStepper):
"""
Base class for CyStep and CyNext. It implements the interface dictated by
libpython.GenericCodeStepper.
"""
def init_stepping(self):
self.cython_func = self.get_cython_function()
self.beginline = self.get_cython_lineno()
self.curframe = gdb.selected_frame()
def next_step(self, command):
"returns whether to continue stepping"
result = gdb.execute(command, to_string=True)
newframe = gdb.selected_frame()
def lineno(self, frame):
# Take care of the Python and Cython levels. We need to care for both
# as we can't simply dispath to 'py-step', since that would work for
# stepping through Python code, but it would not step back into Cython-
# 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)
def get_source_line(self, frame):
# We may have ended up in a Python, Cython, or C function
# In case of C, don't display any additional data (gdb already
# does this)
result = ''
if self.is_cython_function(frame) or self.is_python_function(frame):
try:
result = super(CythonCodeStepper, self).get_source_line(frame)
except gdb.GdbError:
result = ''
c1 = result.startswith('Breakpoint')
c2 = (newframe == self.curframe and
self.get_cython_lineno() > self.beginline)
return not c1 and not c2
return result.lstrip()
def end_stepping(self):
sys.stdout.write(self.get_source_line())
@classmethod
def register(cls):
return cls(cls.name, stepper=cls.stepper)
class CyStep(CythonCodeStepper):
"Step through Python code."
name = 'cy step'
stepper = True
@dispatch_on_frame(c_command='step')
def invoke(self, *args, **kwargs):
super(CythonCodeStepper, self).invoke(*args, **kwargs)
class CyStep(CythonCommand, CodeStepperMixin):
def step(self, nsteps=1):
for nthstep in xrange(nsteps):
self.init_stepping()
while self.next_step('step'):
newframe = gdb.selected_frame()
if newframe != self.curframe:
# we entered a function
funcname = self.get_c_function_name(newframe)
if (self.is_cython_function() or
self.is_python_function() or
funcname in cython_function.step_into_functions):
break
self.end_stepping()
def invoke(self, steps, from_tty):
if self.is_cython_function():
if steps:
self.step(int(steps))
else:
self.step()
else:
gdb.execute('step ' + steps, from_tty)
class CyNext(CythonCodeStepper):
"Step-over Python code."
name = 'cy next'
stepper = False
class CyNext(CythonCommand, CodeStepperMixin):
def next(self, nsteps=1):
for nthstep in xrange(nsteps):
self.init_stepping()
while self.next_step('next'):
pass
self.end_stepping()
def invoke(self, steps, from_tty):
if self.is_cython_function():
if steps:
self.next(int(steps))
else:
self.next()
else:
gdb.execute('next ' + steps, from_tty)
@dispatch_on_frame(c_command='next')
def invoke(self, *args, **kwargs):
super(CythonCodeStepper, self).invoke(*args, **kwargs)
class CyList(CythonCommand):
"""
List Cython source code. To disable to customize colouring see the cy_*
parameters.
"""
name = 'cy list'
command_class = gdb.COMMAND_FILES
completer_class = gdb.COMPLETE_NONE
@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)
......@@ -637,27 +719,34 @@ class CyPrint(CythonCommand):
Print a Cython variable using 'cy-print x' or 'cy-print module.function.x'
"""
name = 'cy print'
command_class = gdb.COMMAND_DATA
@dispatch_on_frame(c_command='print', python_command='py-print')
def invoke(self, name, from_tty):
try:
cname = cy.cy_cname.invoke(name)
except gdb.GdbError:
cname = name
gdb.execute('print ' + cname)
gdb.execute('print ' + self.cy.cy_cname.invoke(name, string=True))
def complete(self):
if self.is_cython_function():
f = self.get_cython_function()
return list(itertools.chain(f.locals, f.globals))
return []
else:
return []
class CyLocals(CythonCommand):
"""
List the locals from the current Cython frame.
"""
name = 'cy locals'
command_class = gdb.COMMAND_STACK
completer_class = gdb.COMPLETE_NONE
def ns(self):
return self.get_cython_function().locals
@require_cython_frame
@dispatch_on_frame(c_command='info locals', python_command='py-locals')
def invoke(self, name, from_tty):
try:
ns = self.ns()
......@@ -682,10 +771,18 @@ class CyLocals(CythonCommand):
class CyGlobals(CythonCommand):
"""
List the globals from the current Cython module.
"""
name = 'cy globals'
command_class = gdb.COMMAND_STACK
completer_class = gdb.COMPLETE_NONE
def ns(self):
return self.get_cython_function().globals
@require_cython_frame
@dispatch_on_frame(c_command='info variables', python_command='py-globals')
def invoke(self, name, from_tty):
# include globals from the debug info XML file!
m = gdb.parse_and_eval('__pyx_m')
......@@ -709,26 +806,54 @@ class CyGlobals(CythonCommand):
class CyCName(gdb.Function, CythonBase):
"""
Get the C name of a Cython variable.
Get the C name of a Cython variable in the current context.
Examples:
print $cy_cname("function")
print $cy_cname("Class.method")
print $cy_cname("module.function")
"""
@require_cython_frame
def invoke(self, cyname, frame=None):
def invoke(self, cyname, string=False, frame=None):
frame = frame or gdb.selected_frame()
cname = None
cyname = cyname.string()
if isinstance(cyname, gdb.Value):
# convert to a python string so it supports proper hashing
cyname = cyname.string()
if self.is_cython_function(frame):
cython_function = self.get_cython_function(frame)
if cyname in cython_function.locals:
cname = cython_function.locals[cyname].cname
elif cyname in cython_function.module.globals:
cname = cython_function.module.globals[cyname].cname
else:
qname = '%s.%s' % (cython_function.module.name, cyname)
if qname in cython_function.module.functions:
cname = cython_function.module.functions[qname].cname
if not cname:
cname = self.cy.functions_by_qualified_name.get(cyname)
if not cname:
raise gdb.GdbError('No such Cython variable: %s' % cyname)
return cname
if string:
return cname
else:
return gdb.parse_and_eval(cname)
class CyLine(gdb.Function, CythonBase):
"""
Get the current Cython line.
"""
@require_cython_frame
def invoke(self):
return self.get_cython_lineno()
cy = CyCy()
\ No newline at end of file
cy = CyCy.register()
\ No newline at end of file
......@@ -44,8 +44,12 @@ the type names are known to the debugger
The module also extends gdb with some python-specific commands.
'''
from __future__ import with_statement
import gdb
import os
import sys
import tempfile
import gdb
# Look up the gdb.Type for some standard types:
_type_char_ptr = gdb.lookup_type('char').pointer() # char*
......@@ -1448,3 +1452,165 @@ class PyLocals(gdb.Command):
pyop_value.get_truncated_repr(MAX_OUTPUT_LEN)))
PyLocals()
def execute(command, from_tty=False, to_string=False):
"""
Replace gdb.execute() with this function and have it accept a 'to_string'
argument (new in 7.2). Have it properly capture stderr also.
Unfortuntaly, this function is not reentrant.
"""
if not to_string:
return _execute(command, from_tty)
fd, filename = tempfile.mkstemp()
try:
_execute("set logging file %s" % filename)
_execute("set logging redirect on")
_execute("set logging on")
_execute("set pagination off")
_execute(command, from_tty)
finally:
data = os.fdopen(fd).read()
os.remove(filename)
_execute("set logging off")
_execute("set pagination on")
return data
_execute = gdb.execute
gdb.execute = execute
class GenericCodeStepper(gdb.Command):
"""
Superclass for code stepping. Subclasses must implement the following
methods:
lineno(frame) - tells the current line number (only
called for a relevant frame)
is_relevant_function(frame) - tells whether we care about frame 'frame'
get_source_line(frame) - get the line of source code for the
current line (only called for a relevant
frame)
This class provides an 'invoke' method that invokes a 'step' or 'step-over'
depending on the 'stepper' argument.
"""
def __init__(self, name, stepper=False):
super(GenericCodeStepper, self).__init__(name,
gdb.COMMAND_RUNNING,
gdb.COMPLETE_NONE)
self.stepper = stepper
def _init_stepping(self):
self.beginframe = gdb.selected_frame()
self.beginline = self.lineno(self.beginframe)
if not self.stepper:
self.depth = self._stackdepth(self.beginframe)
def _next_step(self, gdb_command):
"""
Teturns whether to continue stepping. This method sets the instance
attributes 'result' and 'stopped_running'. 'result' hold the output
of the executed gdb command ('step' or 'next')
"""
self.result = gdb.execute(gdb_command, to_string=True)
self.stopped_running = gdb.inferiors()[0].pid == 0
if self.stopped_running:
# We stopped running
return False
newframe = gdb.selected_frame()
hit_breakpoint = self.result.startswith('Breakpoint')
is_relevant_function = self.is_relevant_function(newframe)
if newframe != self.beginframe:
# new function
if not self.stepper:
is_relevant_function = (
self._stackdepth(newframe) < self.depth and
is_relevant_function)
new_lineno = False
else:
is_relevant_function = False
new_lineno = self.lineno(newframe) > self.beginline
return not (hit_breakpoint or new_lineno or is_relevant_function)
def _end_stepping(self):
# sys.stdout.write(self.result)
if not self.stopped_running:
frame = gdb.selected_frame()
if self.is_relevant_function(frame):
print self.get_source_line(frame)
def _stackdepth(self, frame):
depth = 0
while frame:
frame = frame.older()
depth += 1
return depth
def invoke(self, args, from_tty):
if args:
nsteps = int(args)
else:
nsteps = 1
if self.stepper:
gdb_command = 'step'
else:
gdb_command= 'next'
for nthstep in xrange(nsteps):
self._init_stepping()
while self._next_step(gdb_command):
pass
self._end_stepping()
class PythonCodeStepper(GenericCodeStepper):
def pyframe(self, frame):
pyframe = Frame(frame).get_pyop()
if pyframe:
return pyframe
else:
raise gdb.GdbError(
"Unable to find the Python frame, run your code with a debug "
"build (configure with --with-pydebug or compile with -g).")
def lineno(self, frame):
return self.pyframe(frame).current_line_num()
def is_relevant_function(self, frame):
return Frame(frame).is_evalframeex()
def get_source_line(self, frame):
try:
return self.pyframe(frame).current_line().rstrip()
except IOError, e:
gdb.GdbError('Unable to retrieve source code: %s' % (e,))
class PyStep(PythonCodeStepper):
"Step through Python code."
class PyNext(PythonCodeStepper):
"Step-over Python code."
py_step = PyStep('py-step', stepper=True)
py_next = PyNext('py-next', stepper=False)
class PyShowCCode(gdb.Parameter):
pass
\ No newline at end of file
......@@ -6,7 +6,14 @@ from Cython.Debugger import Cygdb as cygdb
if __name__ == '__main__':
if len(sys.argv) > 1:
cygdb.main(path_to_debug_info=sys.argv[1],
gdb_argv=sys.argv[2:])
path_to_debug_info = sys.argv[1]
no_import = False
if path_to_debug_info == '--':
no_import = True
cygdb.main(path_to_debug_info,
gdb_argv=sys.argv[2:],
no_import=no_import)
else:
cygdb.main()
......@@ -6,7 +6,14 @@ from Cython.Debugger import Cygdb as cygdb
if __name__ == '__main__':
if len(sys.argv) > 1:
cygdb.main(path_to_debug_info=sys.argv[1],
gdb_argv=sys.argv[2:])
path_to_debug_info = sys.argv[1]
no_import = False
if path_to_debug_info == '--':
no_import = True
cygdb.main(path_to_debug_info,
gdb_argv=sys.argv[2:],
no_import=no_import)
else:
cygdb.main()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment