[Python-checkins] bpo-34989: python-gdb.py: fix current_line_num() (GH-9889)

Miss Islington (bot) webhook-mailer at python.org
Mon Oct 15 17:50:45 EDT 2018


https://github.com/python/cpython/commit/71e601eb0857fb03c1dd3c349afb030ef84f95d0
commit: 71e601eb0857fb03c1dd3c349afb030ef84f95d0
branch: 3.6
author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com>
committer: GitHub <noreply at github.com>
date: 2018-10-15T14:50:41-07:00
summary:

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.
(cherry picked from commit 2e438cc2554495b28480a3ffe5cdf41b6ab823a0)

Co-authored-by: Victor Stinner <vstinner at redhat.com>

files:
A Misc/NEWS.d/next/Tools-Demos/2018-10-15-13-22-28.bpo-34989.hU4fra.rst
M Tools/gdb/libpython.py

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 000000000000..53bb425ea7b4
--- /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 6d3d52f397d4..073aca858859 100755
--- a/Tools/gdb/libpython.py
+++ b/Tools/gdb/libpython.py
@@ -934,35 +934,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(os_fsencode(filename), 'r')
+            with open(os_fsencode(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():
@@ -981,9 +996,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):
@@ -1742,6 +1759,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



More information about the Python-checkins mailing list