Commit 2e438cc2 authored by Victor Stinner's avatar Victor Stinner Committed by GitHub

bpo-34989: python-gdb.py: fix current_line_num() (GH-9889)

python-gdb.py now handles errors on computing the line number
of a Python frame.

Changes:

* PyFrameObjectPtr.current_line_num() now catchs any Exception on
  calling addr2line(), instead of failing with a surprising "<class
  'TypeError'> 'FakeRepr' object is not subscriptable" error.
* All callers of current_line_num() now handle current_line_num()
  returning None.
* PyFrameObjectPtr.current_line() now also catchs IndexError on
  getting a line from the Python source file.
parent ee171a26
python-gdb.py now handles errors on computing the line number of a Python
frame.
...@@ -934,35 +934,50 @@ class PyFrameObjectPtr(PyObjectPtr): ...@@ -934,35 +934,50 @@ class PyFrameObjectPtr(PyObjectPtr):
if long(f_trace) != 0: if long(f_trace) != 0:
# we have a non-NULL f_trace: # we have a non-NULL f_trace:
return self.f_lineno return self.f_lineno
else:
#try: try:
return self.co.addr2line(self.f_lasti) return self.co.addr2line(self.f_lasti)
#except ValueError: except Exception:
# return self.f_lineno # bpo-34989: addr2line() is a complex function, it can fail in many
# ways. For example, it fails with a TypeError on "FakeRepr" if
# gdb fails to load debug symbols. Use a catch-all "except
# Exception" to make the whole function safe. The caller has to
# handle None anyway for optimized Python.
return None
def current_line(self): def current_line(self):
'''Get the text of the current source line as a string, with a trailing '''Get the text of the current source line as a string, with a trailing
newline character''' newline character'''
if self.is_optimized_out(): if self.is_optimized_out():
return '(frame information optimized out)' return '(frame information optimized out)'
lineno = self.current_line_num()
if lineno is None:
return '(failed to get frame line number)'
filename = self.filename() filename = self.filename()
try: try:
f = open(os_fsencode(filename), 'r') with open(os_fsencode(filename), 'r') as fp:
lines = fp.readlines()
except IOError: except IOError:
return None return None
with f:
all_lines = f.readlines() try:
# Convert from 1-based current_line_num to 0-based list offset: # Convert from 1-based current_line_num to 0-based list offset
return all_lines[self.current_line_num()-1] return lines[lineno - 1]
except IndexError:
return None
def write_repr(self, out, visited): def write_repr(self, out, visited):
if self.is_optimized_out(): if self.is_optimized_out():
out.write('(frame information optimized out)') out.write('(frame information optimized out)')
return return
out.write('Frame 0x%x, for file %s, line %i, in %s (' lineno = self.current_line_num()
lineno = str(lineno) if lineno is not None else "?"
out.write('Frame 0x%x, for file %s, line %s, in %s ('
% (self.as_address(), % (self.as_address(),
self.co_filename.proxyval(visited), self.co_filename.proxyval(visited),
self.current_line_num(), lineno,
self.co_name.proxyval(visited))) self.co_name.proxyval(visited)))
first = True first = True
for pyop_name, pyop_value in self.iter_locals(): for pyop_name, pyop_value in self.iter_locals():
...@@ -981,9 +996,11 @@ class PyFrameObjectPtr(PyObjectPtr): ...@@ -981,9 +996,11 @@ class PyFrameObjectPtr(PyObjectPtr):
sys.stdout.write(' (frame information optimized out)\n') sys.stdout.write(' (frame information optimized out)\n')
return return
visited = set() visited = set()
sys.stdout.write(' File "%s", line %i, in %s\n' lineno = self.current_line_num()
lineno = str(lineno) if lineno is not None else "?"
sys.stdout.write(' File "%s", line %s, in %s\n'
% (self.co_filename.proxyval(visited), % (self.co_filename.proxyval(visited),
self.current_line_num(), lineno,
self.co_name.proxyval(visited))) self.co_name.proxyval(visited)))
class PySetObjectPtr(PyObjectPtr): class PySetObjectPtr(PyObjectPtr):
...@@ -1732,6 +1749,9 @@ class PyList(gdb.Command): ...@@ -1732,6 +1749,9 @@ class PyList(gdb.Command):
filename = pyop.filename() filename = pyop.filename()
lineno = pyop.current_line_num() lineno = pyop.current_line_num()
if lineno is None:
print('Unable to read python frame line number')
return
if start is None: if start is None:
start = lineno - 5 start = lineno - 5
......
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