Skip to content

Commit aadb44e

Browse files
authored
bpo-34989: python-gdb.py: fix current_line_num() (GH-9889) (GH-9899)
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. (cherry picked from commit 2e438cc)
1 parent 43308df commit aadb44e

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
@@ -922,35 +922,50 @@ def current_line_num(self):
922922
if long(f_trace) != 0:
923923
# we have a non-NULL f_trace:
924924
return self.f_lineno
925-
else:
926-
#try:
925+
926+
try:
927927
return self.co.addr2line(self.f_lasti)
928-
#except ValueError:
929-
# return self.f_lineno
928+
except Exception:
929+
# bpo-34989: addr2line() is a complex function, it can fail in many
930+
# ways. For example, it fails with a TypeError on "FakeRepr" if
931+
# gdb fails to load debug symbols. Use a catch-all "except
932+
# Exception" to make the whole function safe. The caller has to
933+
# handle None anyway for optimized Python.
934+
return None
930935

931936
def current_line(self):
932937
'''Get the text of the current source line as a string, with a trailing
933938
newline character'''
934939
if self.is_optimized_out():
935940
return '(frame information optimized out)'
941+
942+
lineno = self.current_line_num()
943+
if lineno is None:
944+
return '(failed to get frame line number)'
945+
936946
filename = self.filename()
937947
try:
938-
f = open(filename, 'r')
948+
with open(filename, 'r') as fp:
949+
lines = fp.readlines()
939950
except IOError:
940951
return None
941-
with f:
942-
all_lines = f.readlines()
943-
# Convert from 1-based current_line_num to 0-based list offset:
944-
return all_lines[self.current_line_num()-1]
952+
953+
try:
954+
# Convert from 1-based current_line_num to 0-based list offset
955+
return lines[lineno - 1]
956+
except IndexError:
957+
return None
945958

946959
def write_repr(self, out, visited):
947960
if self.is_optimized_out():
948961
out.write('(frame information optimized out)')
949962
return
950-
out.write('Frame 0x%x, for file %s, line %i, in %s ('
963+
lineno = self.current_line_num()
964+
lineno = str(lineno) if lineno is not None else "?"
965+
out.write('Frame 0x%x, for file %s, line %s, in %s ('
951966
% (self.as_address(),
952967
self.co_filename.proxyval(visited),
953-
self.current_line_num(),
968+
lineno,
954969
self.co_name.proxyval(visited)))
955970
first = True
956971
for pyop_name, pyop_value in self.iter_locals():
@@ -969,9 +984,11 @@ def print_traceback(self):
969984
sys.stdout.write(' (frame information optimized out)\n')
970985
return
971986
visited = set()
972-
sys.stdout.write(' File "%s", line %i, in %s\n'
987+
lineno = self.current_line_num()
988+
lineno = str(lineno) if lineno is not None else "?"
989+
sys.stdout.write(' File "%s", line %s, in %s\n'
973990
% (self.co_filename.proxyval(visited),
974-
self.current_line_num(),
991+
lineno,
975992
self.co_name.proxyval(visited)))
976993

977994
class PySetObjectPtr(PyObjectPtr):
@@ -1546,6 +1563,9 @@ def invoke(self, args, from_tty):
15461563

15471564
filename = pyop.filename()
15481565
lineno = pyop.current_line_num()
1566+
if lineno is None:
1567+
print('Unable to read python frame line number')
1568+
return
15491569

15501570
if start is None:
15511571
start = lineno - 5

0 commit comments

Comments
 (0)