Commit 415e7ea3 authored by Mark Florisson's avatar Mark Florisson

Ensure 'cy locals' and 'cy exec' don't use uninitialized local variables...

Ensure 'cy locals' and 'cy exec' don't use uninitialized local variables (which fixes a lot of 'cy exec' core dumps! :)
parent 814c6376
...@@ -1606,13 +1606,22 @@ class DebugTransform(CythonTransform): ...@@ -1606,13 +1606,22 @@ class DebugTransform(CythonTransform):
cname = entry.cname cname = entry.cname
# if entry.type.is_extension_type: # if entry.type.is_extension_type:
# cname = entry.type.typeptr_cname # cname = entry.type.typeptr_cname
if not entry.pos:
# this happens for variables that are not in the user's code,
# e.g. for the global __builtins__, __doc__, etc. We can just
# set the lineno to 0 for those.
lineno = '0'
else:
lineno = str(entry.pos[1])
attrs = dict( attrs = dict(
name=entry.name, name=entry.name,
cname=cname, cname=cname,
qualified_name=entry.qualified_name, qualified_name=entry.qualified_name,
type=vartype) type=vartype,
lineno=lineno)
self.tb.start('LocalVar', attrs) self.tb.start('LocalVar', attrs)
self.tb.end('LocalVar') self.tb.end('LocalVar')
...@@ -159,11 +159,12 @@ class CythonModule(object): ...@@ -159,11 +159,12 @@ class CythonModule(object):
class CythonVariable(object): class CythonVariable(object):
def __init__(self, name, cname, qualified_name, type): def __init__(self, name, cname, qualified_name, type, lineno):
self.name = name self.name = name
self.cname = cname self.cname = cname
self.qualified_name = qualified_name self.qualified_name = qualified_name
self.type = type self.type = type
self.lineno = int(lineno)
class CythonFunction(CythonVariable): class CythonFunction(CythonVariable):
def __init__(self, def __init__(self,
...@@ -177,10 +178,10 @@ class CythonFunction(CythonVariable): ...@@ -177,10 +178,10 @@ class CythonFunction(CythonVariable):
super(CythonFunction, self).__init__(name, super(CythonFunction, self).__init__(name,
cname, cname,
qualified_name, qualified_name,
type) type,
lineno)
self.module = module self.module = module
self.pf_cname = pf_cname self.pf_cname = pf_cname
self.lineno = int(lineno)
self.locals = {} self.locals = {}
self.arguments = [] self.arguments = []
self.step_into_functions = set() self.step_into_functions = set()
...@@ -387,6 +388,10 @@ class CythonBase(object): ...@@ -387,6 +388,10 @@ class CythonBase(object):
print '%s%-*s = %s%s' % (prefix, max_name_length, name, typename, print '%s%-*s = %s%s' % (prefix, max_name_length, name, typename,
value) value)
def is_initialized(self, cython_func, local_name):
cur_lineno = self.get_cython_lineno()
return (local_name in cython_func.arguments or
cur_lineno > cython_func.locals[local_name].lineno)
class SourceFileDescriptor(object): class SourceFileDescriptor(object):
def __init__(self, filename, lexer, formatter=None): def __init__(self, filename, lexer, formatter=None):
...@@ -1073,12 +1078,8 @@ class CyLocals(CythonCommand): ...@@ -1073,12 +1078,8 @@ class CyLocals(CythonCommand):
completer_class = gdb.COMPLETE_NONE completer_class = gdb.COMPLETE_NONE
def _print_if_initialized(self, cyvar, max_name_length, prefix=''): def _print_if_initialized(self, cyvar, max_name_length, prefix=''):
try: if self.is_initialized(self.get_cython_function(), cyvar.name):
value = gdb.parse_and_eval(cyvar.cname) 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) self.print_gdb_value(cyvar.name, value, max_name_length, prefix)
@dispatch_on_frame(c_command='info locals', python_command='py-locals') @dispatch_on_frame(c_command='info locals', python_command='py-locals')
...@@ -1134,20 +1135,12 @@ class CyExec(CythonCommand, libpython.PyExec): ...@@ -1134,20 +1135,12 @@ class CyExec(CythonCommand, libpython.PyExec):
def _fill_locals_dict(self, executor, local_dict_pointer): def _fill_locals_dict(self, executor, local_dict_pointer):
"Fill a remotely allocated dict with values from the Cython C stack" "Fill a remotely allocated dict with values from the Cython C stack"
cython_func = self.get_cython_function() cython_func = self.get_cython_function()
current_lineno = self.get_cython_lineno()
for name, cyvar in cython_func.locals.iteritems(): for name, cyvar in cython_func.locals.iteritems():
if cyvar.type == PythonObject: if (cyvar.type == PythonObject and
# skip unitialized Cython variables 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:
continue
pystringp = executor.alloc_pystring(name) pystringp = executor.alloc_pystring(name)
code = ''' code = '''
PyDict_SetItem( PyDict_SetItem(
...@@ -1155,13 +1148,14 @@ class CyExec(CythonCommand, libpython.PyExec): ...@@ -1155,13 +1148,14 @@ class CyExec(CythonCommand, libpython.PyExec):
(PyObject *) %d, (PyObject *) %d,
(PyObject *) %s) (PyObject *) %s)
''' % (local_dict_pointer, pystringp, cyvar.cname) ''' % (local_dict_pointer, pystringp, cyvar.cname)
# PyDict_SetItem doesn't steal our reference try:
executor.decref(pystringp) if gdb.parse_and_eval(code) < 0:
gdb.parse_and_eval('PyErr_Print()')
if gdb.parse_and_eval(code) < 0: raise gdb.GdbError("Unable to execute Python code.")
gdb.parse_and_eval('PyErr_Print()') finally:
raise gdb.GdbError("Unable to execute Python code.") # PyDict_SetItem doesn't steal our reference
executor.decref(pystringp)
def _find_first_cython_or_python_frame(self): def _find_first_cython_or_python_frame(self):
frame = gdb.selected_frame() frame = gdb.selected_frame()
......
...@@ -1929,7 +1929,7 @@ class PythonCodeExecutor(object): ...@@ -1929,7 +1929,7 @@ class PythonCodeExecutor(object):
pointer = pointervalue(chunk) pointer = pointervalue(chunk)
if pointer == 0: if pointer == 0:
err("No memory could be allocated in the inferior.") raise gdb.GdbError("No memory could be allocated in the inferior.")
return pointer return pointer
...@@ -1950,7 +1950,7 @@ class PythonCodeExecutor(object): ...@@ -1950,7 +1950,7 @@ class PythonCodeExecutor(object):
pointer = pointervalue(result) pointer = pointervalue(result)
if pointer == 0: if pointer == 0:
err("Unable to allocate Python string in " raise gdb.GdbError("Unable to allocate Python string in "
"the inferior.") "the inferior.")
return pointer return pointer
...@@ -1958,6 +1958,10 @@ class PythonCodeExecutor(object): ...@@ -1958,6 +1958,10 @@ class PythonCodeExecutor(object):
def free(self, pointer): def free(self, pointer):
gdb.parse_and_eval("free((void *) %d)" % pointer) gdb.parse_and_eval("free((void *) %d)" % pointer)
def incref(self, pointer):
"Increment the reference count of a Python object in the inferior."
gdb.parse_and_eval('Py_IncRef((PyObject *) %d)' % pointer)
def decref(self, pointer): def decref(self, pointer):
"Decrement the reference count of a Python object in the inferior." "Decrement the reference count of a Python object in the inferior."
# Py_DecRef is like Py_XDECREF, but a function. So we don't have # Py_DecRef is like Py_XDECREF, but a function. So we don't have
...@@ -1975,7 +1979,7 @@ class PythonCodeExecutor(object): ...@@ -1975,7 +1979,7 @@ class PythonCodeExecutor(object):
leave the debuggee in an unsafe state or terminate it alltogether. leave the debuggee in an unsafe state or terminate it alltogether.
""" """
if '\0' in code: if '\0' in code:
err("String contains NUL byte.") raise gdb.GdbError("String contains NUL byte.")
code += '\0' code += '\0'
......
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