I did a merge head with Victor's change in 2.7 before pushing my change.
Can someone confirm if I did it right? If anything was wrong, how to
correct it?

Thank you,
Senthil


On Thu, Sep 3, 2015 at 2:51 AM, senthil.kumaran <python-check...@python.org>
wrote:

> https://hg.python.org/cpython/rev/d687912d499f
> changeset:   97616:d687912d499f
> branch:      2.7
> parent:      97615:cb781d3b1e6b
> parent:      97611:d739bc20d7b2
> user:        Senthil Kumaran <sent...@uthcode.com>
> date:        Thu Sep 03 02:50:51 2015 -0700
> summary:
>   merge heads.
>
> files:
>   Lib/test/test_gdb.py      |  168 +++++++++++++++++++++--
>   Lib/test/test_py3kwarn.py |   16 ++
>   Tools/gdb/libpython.py    |  183 ++++++++++++++++++++++++-
>   3 files changed, 338 insertions(+), 29 deletions(-)
>
>
> diff --git a/Lib/test/test_gdb.py b/Lib/test/test_gdb.py
> --- a/Lib/test/test_gdb.py
> +++ b/Lib/test/test_gdb.py
> @@ -10,21 +10,42 @@
>  import unittest
>  import sysconfig
>
> +from test import test_support
>  from test.test_support import run_unittest, findfile
>
> +# Is this Python configured to support threads?
>  try:
> -    gdb_version, _ = subprocess.Popen(["gdb", "-nx", "--version"],
> -
> stdout=subprocess.PIPE).communicate()
> -except OSError:
> -    # This is what "no gdb" looks like.  There may, however, be other
> -    # errors that manifest this way too.
> -    raise unittest.SkipTest("Couldn't find gdb on the path")
> -gdb_version_number = re.search("^GNU gdb [^\d]*(\d+)\.(\d)", gdb_version)
> -gdb_major_version = int(gdb_version_number.group(1))
> -gdb_minor_version = int(gdb_version_number.group(2))
> +    import thread
> +except ImportError:
> +    thread = None
> +
> +def get_gdb_version():
> +    try:
> +        proc = subprocess.Popen(["gdb", "-nx", "--version"],
> +                                stdout=subprocess.PIPE,
> +                                universal_newlines=True)
> +        version = proc.communicate()[0]
> +    except OSError:
> +        # This is what "no gdb" looks like.  There may, however, be other
> +        # errors that manifest this way too.
> +        raise unittest.SkipTest("Couldn't find gdb on the path")
> +
> +    # Regex to parse:
> +    # 'GNU gdb (GDB; SUSE Linux Enterprise 12) 7.7\n' -> 7.7
> +    # 'GNU gdb (GDB) Fedora 7.9.1-17.fc22\n' -> 7.9
> +    # 'GNU gdb 6.1.1 [FreeBSD]\n'
> +    match = re.search("^GNU gdb.*? (\d+)\.(\d)", version)
> +    if match is None:
> +        raise Exception("unable to parse GDB version: %r" % version)
> +    return (version, int(match.group(1)), int(match.group(2)))
> +
> +gdb_version, gdb_major_version, gdb_minor_version = get_gdb_version()
>  if gdb_major_version < 7:
> -    raise unittest.SkipTest("gdb versions before 7.0 didn't support
> python embedding"
> -                            " Saw:\n" + gdb_version)
> +    raise unittest.SkipTest("gdb versions before 7.0 didn't support
> python "
> +                            "embedding. Saw %s.%s:\n%s"
> +                            % (gdb_major_version, gdb_minor_version,
> +                               gdb_version))
> +
>  if sys.platform.startswith("sunos"):
>      raise unittest.SkipTest("test doesn't work very well on Solaris")
>
> @@ -713,20 +734,133 @@
>  class PyBtTests(DebuggerTests):
>      @unittest.skipIf(python_is_optimized(),
>                       "Python was compiled with optimizations")
> -    def test_basic_command(self):
> +    def test_bt(self):
>          'Verify that the "py-bt" command works'
>          bt = self.get_stack_trace(script=self.get_sample_script(),
>                                    cmds_after_breakpoint=['py-bt'])
>          self.assertMultilineMatches(bt,
>                                      r'''^.*
> -#[0-9]+ Frame 0x[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar
> \(a=1, b=2, c=3\)
> +Traceback \(most recent call first\):
> +  File ".*gdb_sample.py", line 10, in baz
> +    print\(42\)
> +  File ".*gdb_sample.py", line 7, in bar
>      baz\(a, b, c\)
> -#[0-9]+ Frame 0x[0-9a-f]+, for file .*gdb_sample.py, line 4, in foo
> \(a=1, b=2, c=3\)
> +  File ".*gdb_sample.py", line 4, in foo
>      bar\(a, b, c\)
> -#[0-9]+ Frame 0x[0-9a-f]+, for file .*gdb_sample.py, line 12, in <module>
> \(\)
> +  File ".*gdb_sample.py", line 12, in <module>
>      foo\(1, 2, 3\)
>  ''')
>
> +    @unittest.skipIf(python_is_optimized(),
> +                     "Python was compiled with optimizations")
> +    def test_bt_full(self):
> +        'Verify that the "py-bt-full" command works'
> +        bt = self.get_stack_trace(script=self.get_sample_script(),
> +                                  cmds_after_breakpoint=['py-bt-full'])
> +        self.assertMultilineMatches(bt,
> +                                    r'''^.*
> +#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar
> \(a=1, b=2, c=3\)
> +    baz\(a, b, c\)
> +#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 4, in foo
> \(a=1, b=2, c=3\)
> +    bar\(a, b, c\)
> +#[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 12, in
> <module> \(\)
> +    foo\(1, 2, 3\)
> +''')
> +
> +    @unittest.skipUnless(thread,
> +                         "Python was compiled without thread support")
> +    def test_threads(self):
> +        'Verify that "py-bt" indicates threads that are waiting for the
> GIL'
> +        cmd = '''
> +from threading import Thread
> +
> +class TestThread(Thread):
> +    # These threads would run forever, but we'll interrupt things with the
> +    # debugger
> +    def run(self):
> +        i = 0
> +        while 1:
> +             i += 1
> +
> +t = {}
> +for i in range(4):
> +   t[i] = TestThread()
> +   t[i].start()
> +
> +# Trigger a breakpoint on the main thread
> +print 42
> +
> +'''
> +        # Verify with "py-bt":
> +        gdb_output = self.get_stack_trace(cmd,
> +                                          cmds_after_breakpoint=['thread
> apply all py-bt'])
> +        self.assertIn('Waiting for the GIL', gdb_output)
> +
> +        # Verify with "py-bt-full":
> +        gdb_output = self.get_stack_trace(cmd,
> +                                          cmds_after_breakpoint=['thread
> apply all py-bt-full'])
> +        self.assertIn('Waiting for the GIL', gdb_output)
> +
> +    @unittest.skipIf(python_is_optimized(),
> +                     "Python was compiled with optimizations")
> +    # Some older versions of gdb will fail with
> +    #  "Cannot find new threads: generic error"
> +    # unless we add LD_PRELOAD=PATH-TO-libpthread.so.1 as a workaround
> +    @unittest.skipUnless(thread,
> +                         "Python was compiled without thread support")
> +    def test_gc(self):
> +        'Verify that "py-bt" indicates if a thread is garbage-collecting'
> +        cmd = ('from gc import collect\n'
> +               'print 42\n'
> +               'def foo():\n'
> +               '    collect()\n'
> +               'def bar():\n'
> +               '    foo()\n'
> +               'bar()\n')
> +        # Verify with "py-bt":
> +        gdb_output = self.get_stack_trace(cmd,
> +                                          cmds_after_breakpoint=['break
> update_refs', 'continue', 'py-bt'],
> +                                          )
> +        self.assertIn('Garbage-collecting', gdb_output)
> +
> +        # Verify with "py-bt-full":
> +        gdb_output = self.get_stack_trace(cmd,
> +                                          cmds_after_breakpoint=['break
> update_refs', 'continue', 'py-bt-full'],
> +                                          )
> +        self.assertIn('Garbage-collecting', gdb_output)
> +
> +    @unittest.skipIf(python_is_optimized(),
> +                     "Python was compiled with optimizations")
> +    # Some older versions of gdb will fail with
> +    #  "Cannot find new threads: generic error"
> +    # unless we add LD_PRELOAD=PATH-TO-libpthread.so.1 as a workaround
> +    @unittest.skipUnless(thread,
> +                         "Python was compiled without thread support")
> +    def test_pycfunction(self):
> +        'Verify that "py-bt" displays invocations of PyCFunction
> instances'
> +        # Tested function must not be defined with METH_NOARGS or METH_O,
> +        # otherwise call_function() doesn't call PyCFunction_Call()
> +        cmd = ('from time import gmtime\n'
> +               'def foo():\n'
> +               '    gmtime(1)\n'
> +               'def bar():\n'
> +               '    foo()\n'
> +               'bar()\n')
> +        # Verify with "py-bt":
> +        gdb_output = self.get_stack_trace(cmd,
> +                                          breakpoint='time_gmtime',
> +                                          cmds_after_breakpoint=['bt',
> 'py-bt'],
> +                                          )
> +        self.assertIn('<built-in function gmtime', gdb_output)
> +
> +        # Verify with "py-bt-full":
> +        gdb_output = self.get_stack_trace(cmd,
> +                                          breakpoint='time_gmtime',
> +
> cmds_after_breakpoint=['py-bt-full'],
> +                                          )
> +        self.assertIn('#0 <built-in function gmtime', gdb_output)
> +
> +
>  class PyPrintTests(DebuggerTests):
>      @unittest.skipIf(python_is_optimized(),
>                       "Python was compiled with optimizations")
> @@ -781,6 +915,10 @@
>                                      r".*\na = 1\nb = 2\nc = 3\n.*")
>
>  def test_main():
> +    if test_support.verbose:
> +        print("GDB version %s.%s:" % (gdb_major_version,
> gdb_minor_version))
> +        for line in gdb_version.splitlines():
> +            print(" " * 4 + line)
>      run_unittest(PrettyPrintTests,
>                   PyListTests,
>                   StackNavigationTests,
> diff --git a/Lib/test/test_py3kwarn.py b/Lib/test/test_py3kwarn.py
> --- a/Lib/test/test_py3kwarn.py
> +++ b/Lib/test/test_py3kwarn.py
> @@ -2,6 +2,7 @@
>  import sys
>  from test.test_support import check_py3k_warnings, CleanImport,
> run_unittest
>  import warnings
> +from test import test_support
>
>  if not sys.py3kwarning:
>      raise unittest.SkipTest('%s must be run with the -3 flag' % __name__)
> @@ -356,6 +357,21 @@
>      def check_removal(self, module_name, optional=False):
>          """Make sure the specified module, when imported, raises a
>          DeprecationWarning and specifies itself in the message."""
> +        if module_name in sys.modules:
> +            mod = sys.modules[module_name]
> +            filename = getattr(mod, '__file__', '')
> +            mod = None
> +            # the module is not implemented in C?
> +            if not filename.endswith(('.py', '.pyc', '.pyo')):
> +                # Issue #23375: If the module was already loaded,
> reimporting
> +                # the module will not emit again the warning. The warning
> is
> +                # emited when the module is loaded, but C modules cannot
> +                # unloaded.
> +                if test_support.verbose:
> +                    print("Cannot test the Python 3 DeprecationWarning of
> the "
> +                          "%s module, the C module is already loaded"
> +                          % module_name)
> +                return
>          with CleanImport(module_name), warnings.catch_warnings():
>              warnings.filterwarnings("error", ".+ (module|package) .+
> removed",
>                                      DeprecationWarning, __name__)
> diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py
> --- a/Tools/gdb/libpython.py
> +++ b/Tools/gdb/libpython.py
> @@ -45,6 +45,7 @@
>
>  from __future__ import print_function, with_statement
>  import gdb
> +import locale
>  import os
>  import sys
>
> @@ -76,6 +77,8 @@
>
>  MAX_OUTPUT_LEN=1024
>
> +ENCODING = locale.getpreferredencoding()
> +
>  class NullPyObjectPtr(RuntimeError):
>      pass
>
> @@ -92,6 +95,18 @@
>      # threshold in case the data was corrupted
>      return xrange(safety_limit(int(val)))
>
> +if sys.version_info[0] >= 3:
> +    def write_unicode(file, text):
> +        file.write(text)
> +else:
> +    def write_unicode(file, text):
> +        # Write a byte or unicode string to file. Unicode strings are
> encoded to
> +        # ENCODING encoding with 'backslashreplace' error handler to avoid
> +        # UnicodeEncodeError.
> +        if isinstance(text, unicode):
> +            text = text.encode(ENCODING, 'backslashreplace')
> +        file.write(text)
> +
>
>  class StringTruncated(RuntimeError):
>      pass
> @@ -903,7 +918,12 @@
>          newline character'''
>          if self.is_optimized_out():
>              return '(frame information optimized out)'
> -        with open(self.filename(), 'r') as f:
> +        filename = self.filename()
> +        try:
> +            f = open(filename, 'r')
> +        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]
> @@ -914,9 +934,9 @@
>              return
>          out.write('Frame 0x%x, for file %s, line %i, in %s ('
>                    % (self.as_address(),
> -                     self.co_filename,
> +                     self.co_filename.proxyval(visited),
>                       self.current_line_num(),
> -                     self.co_name))
> +                     self.co_name.proxyval(visited)))
>          first = True
>          for pyop_name, pyop_value in self.iter_locals():
>              if not first:
> @@ -929,6 +949,16 @@
>
>          out.write(')')
>
> +    def print_traceback(self):
> +        if self.is_optimized_out():
> +            sys.stdout.write('  (frame information optimized out)\n')
> +            return
> +        visited = set()
> +        sys.stdout.write('  File "%s", line %i, in %s\n'
> +                  % (self.co_filename.proxyval(visited),
> +                     self.current_line_num(),
> +                     self.co_name.proxyval(visited)))
> +
>  class PySetObjectPtr(PyObjectPtr):
>      _typename = 'PySetObject'
>
> @@ -1222,6 +1252,23 @@
>              iter_frame = iter_frame.newer()
>          return index
>
> +    # We divide frames into:
> +    #   - "python frames":
> +    #       - "bytecode frames" i.e. PyEval_EvalFrameEx
> +    #       - "other python frames": things that are of interest from a
> python
> +    #         POV, but aren't bytecode (e.g. GC, GIL)
> +    #   - everything else
> +
> +    def is_python_frame(self):
> +        '''Is this a PyEval_EvalFrameEx frame, or some other important
> +        frame? (see is_other_python_frame for what "important" means in
> this
> +        context)'''
> +        if self.is_evalframeex():
> +            return True
> +        if self.is_other_python_frame():
> +            return True
> +        return False
> +
>      def is_evalframeex(self):
>          '''Is this a PyEval_EvalFrameEx frame?'''
>          if self._gdbframe.name() == 'PyEval_EvalFrameEx':
> @@ -1238,6 +1285,50 @@
>
>          return False
>
> +    def is_other_python_frame(self):
> +        '''Is this frame worth displaying in python backtraces?
> +        Examples:
> +          - waiting on the GIL
> +          - garbage-collecting
> +          - within a CFunction
> +         If it is, return a descriptive string
> +         For other frames, return False
> +         '''
> +        if self.is_waiting_for_gil():
> +            return 'Waiting for the GIL'
> +        elif self.is_gc_collect():
> +            return 'Garbage-collecting'
> +        else:
> +            # Detect invocations of PyCFunction instances:
> +            older = self.older()
> +            if older and older._gdbframe.name() == 'PyCFunction_Call':
> +                # Within that frame:
> +                #   "func" is the local containing the PyObject* of the
> +                # PyCFunctionObject instance
> +                #   "f" is the same value, but cast to
> (PyCFunctionObject*)
> +                #   "self" is the (PyObject*) of the 'self'
> +                try:
> +                    # Use the prettyprinter for the func:
> +                    func = older._gdbframe.read_var('func')
> +                    return str(func)
> +                except RuntimeError:
> +                    return 'PyCFunction invocation (unable to read
> "func")'
> +
> +        # This frame isn't worth reporting:
> +        return False
> +
> +    def is_waiting_for_gil(self):
> +        '''Is this frame waiting on the GIL?'''
> +        # This assumes the _POSIX_THREADS version of Python/ceval_gil.h:
> +        name = self._gdbframe.name()
> +        if name:
> +            return ('PyThread_acquire_lock' in name
> +                    and 'lock_PyThread_acquire_lock' not in name)
> +
> +    def is_gc_collect(self):
> +        '''Is this frame "collect" within the garbage-collector?'''
> +        return self._gdbframe.name() == 'collect'
> +
>      def get_pyop(self):
>          try:
>              f = self._gdbframe.read_var('f')
> @@ -1267,8 +1358,22 @@
>
>      @classmethod
>      def get_selected_python_frame(cls):
> -        '''Try to obtain the Frame for the python code in the selected
> frame,
> -        or None'''
> +        '''Try to obtain the Frame for the python-related code in the
> selected
> +        frame, or None'''
> +        frame = cls.get_selected_frame()
> +
> +        while frame:
> +            if frame.is_python_frame():
> +                return frame
> +            frame = frame.older()
> +
> +        # Not found:
> +        return None
> +
> +    @classmethod
> +    def get_selected_bytecode_frame(cls):
> +        '''Try to obtain the Frame for the python bytecode interpreter in
> the
> +        selected GDB frame, or None'''
>          frame = cls.get_selected_frame()
>
>          while frame:
> @@ -1283,14 +1388,38 @@
>          if self.is_evalframeex():
>              pyop = self.get_pyop()
>              if pyop:
> -                sys.stdout.write('#%i %s\n' % (self.get_index(),
> pyop.get_truncated_repr(MAX_OUTPUT_LEN)))
> +                line = pyop.get_truncated_repr(MAX_OUTPUT_LEN)
> +                write_unicode(sys.stdout, '#%i %s\n' % (self.get_index(),
> line))
>                  if not pyop.is_optimized_out():
>                      line = pyop.current_line()
> -                    sys.stdout.write('    %s\n' % line.strip())
> +                    if line is not None:
> +                        sys.stdout.write('    %s\n' % line.strip())
>              else:
>                  sys.stdout.write('#%i (unable to read python frame
> information)\n' % self.get_index())
>          else:
> -            sys.stdout.write('#%i\n' % self.get_index())
> +            info = self.is_other_python_frame()
> +            if info:
> +                sys.stdout.write('#%i %s\n' % (self.get_index(), info))
> +            else:
> +                sys.stdout.write('#%i\n' % self.get_index())
> +
> +    def print_traceback(self):
> +        if self.is_evalframeex():
> +            pyop = self.get_pyop()
> +            if pyop:
> +                pyop.print_traceback()
> +                if not pyop.is_optimized_out():
> +                    line = pyop.current_line()
> +                    if line is not None:
> +                        sys.stdout.write('    %s\n' % line.strip())
> +            else:
> +                sys.stdout.write('  (unable to read python frame
> information)\n')
> +        else:
> +            info = self.is_other_python_frame()
> +            if info:
> +                sys.stdout.write('  %s\n' % info)
> +            else:
> +                sys.stdout.write('  (not a python frame)\n')
>
>  class PyList(gdb.Command):
>      '''List the current Python source code, if any
> @@ -1326,9 +1455,10 @@
>          if m:
>              start, end = map(int, m.groups())
>
> -        frame = Frame.get_selected_python_frame()
> +        # py-list requires an actual PyEval_EvalFrameEx frame:
> +        frame = Frame.get_selected_bytecode_frame()
>          if not frame:
> -            print('Unable to locate python frame')
> +            print('Unable to locate gdb frame for python bytecode
> interpreter')
>              return
>
>          pyop = frame.get_pyop()
> @@ -1346,7 +1476,13 @@
>          if start<1:
>              start = 1
>
> -        with open(filename, 'r') as f:
> +        try:
> +            f = open(filename, 'r')
> +        except IOError as err:
> +            sys.stdout.write('Unable to open %s: %s\n'
> +                             % (filename, err))
> +            return
> +        with f:
>              all_lines = f.readlines()
>              # start and end are 1-based, all_lines is 0-based;
>              # so [start-1:end] as a python slice gives us [start, end] as
> a
> @@ -1374,7 +1510,7 @@
>          if not iter_frame:
>              break
>
> -        if iter_frame.is_evalframeex():
> +        if iter_frame.is_python_frame():
>              # Result:
>              if iter_frame.select():
>                  iter_frame.print_summary()
> @@ -1416,6 +1552,24 @@
>      PyUp()
>      PyDown()
>
> +class PyBacktraceFull(gdb.Command):
> +    'Display the current python frame and all the frames within its call
> stack (if any)'
> +    def __init__(self):
> +        gdb.Command.__init__ (self,
> +                              "py-bt-full",
> +                              gdb.COMMAND_STACK,
> +                              gdb.COMPLETE_NONE)
> +
> +
> +    def invoke(self, args, from_tty):
> +        frame = Frame.get_selected_python_frame()
> +        while frame:
> +            if frame.is_python_frame():
> +                frame.print_summary()
> +            frame = frame.older()
> +
> +PyBacktraceFull()
> +
>  class PyBacktrace(gdb.Command):
>      'Display the current python frame and all the frames within its call
> stack (if any)'
>      def __init__(self):
> @@ -1426,10 +1580,11 @@
>
>
>      def invoke(self, args, from_tty):
> +        sys.stdout.write('Traceback (most recent call first):\n')
>          frame = Frame.get_selected_python_frame()
>          while frame:
> -            if frame.is_evalframeex():
> -                frame.print_summary()
> +            if frame.is_python_frame():
> +                frame.print_traceback()
>              frame = frame.older()
>
>  PyBacktrace()
>
> --
> Repository URL: https://hg.python.org/cpython
>
> _______________________________________________
> Python-checkins mailing list
> python-check...@python.org
> https://mail.python.org/mailman/listinfo/python-checkins
>
>
_______________________________________________
python-committers mailing list
python-committers@python.org
https://mail.python.org/mailman/listinfo/python-committers

Reply via email to