Skip to content

Commit 2e438cc

Browse files
authored
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.
1 parent ee171a2 commit 2e438cc

File tree

2 files changed

+35
-13
lines changed

2 files changed

+35
-13
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
python-gdb.py now handles errors on computing the line number of a Python
2+
frame.

Tools/gdb/libpython.py

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -934,35 +934,50 @@ def current_line_num(self):
934934
if long(f_trace) != 0:
935935
# we have a non-NULL f_trace:
936936
return self.f_lineno
937-
else:
938-
#try:
937+
938+
try:
939939
return self.co.addr2line(self.f_lasti)
940-
#except ValueError:
941-
# return self.f_lineno
940+
except Exception:
941+
# bpo-34989: addr2line() is a complex function, it can fail in many
942+
# ways. For example, it fails with a TypeError on "FakeRepr" if
943+
# gdb fails to load debug symbols. Use a catch-all "except
944+
# Exception" to make the whole function safe. The caller has to
945+
# handle None anyway for optimized Python.
946+
return None
942947

943948
def current_line(self):
944949
'''Get the text of the current source line as a string, with a trailing
945950
newline character'''
946951
if self.is_optimized_out():
947952
return '(frame information optimized out)'
953+
954+
lineno = self.current_line_num()
955+
if lineno is None:
956+
return '(failed to get frame line number)'
957+
948958
filename = self.filename()
949959
try:
950-
f = open(os_fsencode(filename), 'r')
960+
with open(os_fsencode(filename), 'r') as fp:
961+
lines = fp.readlines()
951962
except IOError:
952963
return None
953-
with f:
954-
all_lines = f.readlines()
955-
# Convert from 1-based current_line_num to 0-based list offset:
956-
return all_lines[self.current_line_num()-1]
964+
965+
try:
966+
# Convert from 1-based current_line_num to 0-based list offset
967+
return lines[lineno - 1]
968+
except IndexError:
969+
return None
957970

958971
def write_repr(self, out, visited):
959972
if self.is_optimized_out():
960973
out.write('(frame information optimized out)')
961974
return
962-
out.write('Frame 0x%x, for file %s, line %i, in %s ('
975+
lineno = self.current_line_num()
976+
lineno = str(lineno) if lineno is not None else "?"
977+
out.write('Frame 0x%x, for file %s, line %s, in %s ('
963978
% (self.as_address(),
964979
self.co_filename.proxyval(visited),
965-
self.current_line_num(),
980+
lineno,
966981
self.co_name.proxyval(visited)))
967982
first = True
968983
for pyop_name, pyop_value in self.iter_locals():
@@ -981,9 +996,11 @@ def print_traceback(self):
981996
sys.stdout.write(' (frame information optimized out)\n')
982997
return
983998
visited = set()
984-
sys.stdout.write(' File "%s", line %i, in %s\n'
999+
lineno = self.current_line_num()
1000+
lineno = str(lineno) if lineno is not None else "?"
1001+
sys.stdout.write(' File "%s", line %s, in %s\n'
9851002
% (self.co_filename.proxyval(visited),
986-
self.current_line_num(),
1003+
lineno,
9871004
self.co_name.proxyval(visited)))
9881005

9891006
class PySetObjectPtr(PyObjectPtr):
@@ -1732,6 +1749,9 @@ def invoke(self, args, from_tty):
17321749

17331750
filename = pyop.filename()
17341751
lineno = pyop.current_line_num()
1752+
if lineno is None:
1753+
print('Unable to read python frame line number')
1754+
return
17351755

17361756
if start is None:
17371757
start = lineno - 5

0 commit comments

Comments
 (0)