diff --git a/Misc/NEWS.d/next/Tools-Demos/2018-10-15-13-22-28.bpo-34989.hU4fra.rst b/Misc/NEWS.d/next/Tools-Demos/2018-10-15-13-22-28.bpo-34989.hU4fra.rst new file mode 100644 index 00000000000000..53bb425ea7b487 --- /dev/null +++ b/Misc/NEWS.d/next/Tools-Demos/2018-10-15-13-22-28.bpo-34989.hU4fra.rst @@ -0,0 +1,2 @@ +python-gdb.py now handles errors on computing the line number of a Python +frame. diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index 3ae70974cc1c7e..9def56e61024c8 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -922,35 +922,50 @@ def current_line_num(self): if long(f_trace) != 0: # we have a non-NULL f_trace: return self.f_lineno - else: - #try: + + try: return self.co.addr2line(self.f_lasti) - #except ValueError: - # return self.f_lineno + except Exception: + # 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): '''Get the text of the current source line as a string, with a trailing newline character''' if self.is_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() try: - f = open(filename, 'r') + with open(filename, 'r') as fp: + lines = fp.readlines() except IOError: return None - with f: - all_lines = f.readlines() - # Convert from 1-based current_line_num to 0-based list offset: - return all_lines[self.current_line_num()-1] + + try: + # Convert from 1-based current_line_num to 0-based list offset + return lines[lineno - 1] + except IndexError: + return None def write_repr(self, out, visited): if self.is_optimized_out(): out.write('(frame information optimized out)') 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.co_filename.proxyval(visited), - self.current_line_num(), + lineno, self.co_name.proxyval(visited))) first = True for pyop_name, pyop_value in self.iter_locals(): @@ -969,9 +984,11 @@ def print_traceback(self): sys.stdout.write(' (frame information optimized out)\n') return 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.current_line_num(), + lineno, self.co_name.proxyval(visited))) class PySetObjectPtr(PyObjectPtr): @@ -1546,6 +1563,9 @@ def invoke(self, args, from_tty): filename = pyop.filename() lineno = pyop.current_line_num() + if lineno is None: + print('Unable to read python frame line number') + return if start is None: start = lineno - 5