]> granicus.if.org Git - python/commitdiff
bpo-34989: python-gdb.py: fix current_line_num() (GH-9889)
authorVictor Stinner <vstinner@redhat.com>
Mon, 15 Oct 2018 21:19:57 +0000 (23:19 +0200)
committerGitHub <noreply@github.com>
Mon, 15 Oct 2018 21:19:57 +0000 (23:19 +0200)
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.

Misc/NEWS.d/next/Tools-Demos/2018-10-15-13-22-28.bpo-34989.hU4fra.rst [new file with mode: 0644]
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 (file)
index 0000000..53bb425
--- /dev/null
@@ -0,0 +1,2 @@
+python-gdb.py now handles errors on computing the line number of a Python
+frame.
index 27eabcb0d3d0916a929f34b7d91efab8526fde89..bfaa9403b7876461b7a732d14050fffac2867537 100755 (executable)
@@ -934,35 +934,50 @@ class PyFrameObjectPtr(PyObjectPtr):
         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 @@ class PyFrameObjectPtr(PyObjectPtr):
             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):
@@ -1732,6 +1749,9 @@ class PyList(gdb.Command):
 
         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