Rev 4632: (mbp) re-add apport support in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Thu Aug 20 09:02:27 BST 2009


At file:///home/pqm/archives/thelove/bzr/%2Btrunk/

------------------------------------------------------------
revno: 4632 [merge]
revision-id: pqm at pqm.ubuntu.com-20090820080226-97krol4wohcyh2ba
parent: pqm at pqm.ubuntu.com-20090820051203-riyg1vh31w486hc6
parent: mbp at sourcefrog.net-20090820062502-o0tu3p01oc3koq3e
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Thu 2009-08-20 09:02:26 +0100
message:
  (mbp) re-add apport support
added:
  bzrlib/crash.py                crash.py-20090812083334-d6volool4lktdjcx-1
  bzrlib/tests/features.py       features.py-20090820042958-jglgza3wrn03ha9e-1
  bzrlib/tests/test_crash.py     test_crash.py-20090820042958-jglgza3wrn03ha9e-2
  doc/developers/apport.txt      apport.txt-20090812083313-9nz21aq6dtwv7hmm-1
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/config.py               config.py-20051011043216-070c74f4e9e338e8
  bzrlib/help_topics/en/debug-flags.txt debugflags.txt-20090312050229-rdspqbqq4fzbjtpe-1
  bzrlib/tests/__init__.py       selftest.py-20050531073622-8d0e3c8845c97a64
  bzrlib/tests/blackbox/test_exceptions.py test_exceptions.py-20060604211237-yi2cxg0ose3xk4id-1
  bzrlib/tests/test_config.py    testconfig.py-20051011041908-742d0c15d8d8c8eb
  bzrlib/tests/test_trace.py     testtrace.py-20051110225523-a21117fc7a07eeff
  bzrlib/trace.py                trace.py-20050309040759-c8ed824bdcd4748a
  doc/developers/index.txt       index.txt-20070508041241-qznziunkg0nffhiw-1
=== modified file 'NEWS'
--- a/NEWS	2009-08-20 04:01:16 +0000
+++ b/NEWS	2009-08-20 05:32:57 +0000
@@ -31,6 +31,17 @@
 Improvements
 ************
 
+* A better description of the platform is shown in crash tracebacks, ``bzr
+  --version`` and ``bzr selftest``.
+  (Martin Pool, #409137)
+
+* bzr can now (again) capture crash data through the apport library, 
+  so that a single human-readable file can be attached to bug reports.
+  This can be disabled by using ``-Dno_apport`` on the command line, or by
+  putting ``no_apport`` into the ``debug_flags`` section of
+  ``bazaar.conf``.
+  (Martin Pool, Robert Collins, #389328)
+
 Documentation
 *************
 

=== modified file 'bzrlib/config.py'
--- a/bzrlib/config.py	2009-07-02 08:59:16 +0000
+++ b/bzrlib/config.py	2009-08-20 04:53:23 +0000
@@ -821,6 +821,29 @@
     return osutils.pathjoin(config_dir(), 'ignore')
 
 
+def crash_dir():
+    """Return the directory name to store crash files.
+
+    This doesn't implicitly create it.
+
+    On Windows it's in the config directory; elsewhere in the XDG cache directory.
+    """
+    if sys.platform == 'win32':
+        return osutils.pathjoin(config_dir(), 'Crash')
+    else:
+        return osutils.pathjoin(xdg_cache_dir(), 'crash')
+
+
+def xdg_cache_dir():
+    # See http://standards.freedesktop.org/basedir-spec/latest/ar01s03.html
+    # Possibly this should be different on Windows?
+    e = os.environ.get('XDG_CACHE_DIR', None)
+    if e:
+        return e
+    else:
+        return os.path.expanduser('~/.cache')
+
+
 def _auto_user_id():
     """Calculate automatic user identification.
 

=== added file 'bzrlib/crash.py'
--- a/bzrlib/crash.py	1970-01-01 00:00:00 +0000
+++ b/bzrlib/crash.py	2009-08-20 05:47:53 +0000
@@ -0,0 +1,163 @@
+# Copyright (C) 2009 Canonical Ltd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+"""Handling and reporting crashes.
+"""
+
+# for interactive testing, try the 'bzr assert-fail' command 
+# or see http://code.launchpad.net/~mbp/bzr/bzr-fail
+
+import os
+import platform
+import pprint
+import sys
+import time
+from StringIO import StringIO
+
+import bzrlib
+from bzrlib import (
+    config,
+    debug,
+    osutils,
+    plugin,
+    trace,
+    )
+
+
+def report_bug(exc_info, stderr):
+    if 'no_apport' not in debug.debug_flags:
+        try:
+            report_bug_to_apport(exc_info, stderr)
+            return
+        except ImportError, e:
+            trace.mutter("couldn't find apport bug-reporting library: %s" % e)
+            pass
+        except Exception, e:
+            # this should only happen if apport is installed but it didn't
+            # work, eg because of an io error writing the crash file
+            sys.stderr.write("bzr: failed to report crash using apport:\n "
+                "    %r\n" % e)
+            pass
+    report_bug_legacy(exc_info, stderr)
+
+
+def report_bug_legacy(exc_info, err_file):
+    """Report a bug by just printing a message to the user."""
+    trace.print_exception(exc_info, err_file)
+    err_file.write('\n')
+    err_file.write('bzr %s on python %s (%s)\n' % \
+                       (bzrlib.__version__,
+                        bzrlib._format_version_tuple(sys.version_info),
+                        platform.platform(aliased=1)))
+    err_file.write('arguments: %r\n' % sys.argv)
+    err_file.write(
+        'encoding: %r, fsenc: %r, lang: %r\n' % (
+            osutils.get_user_encoding(), sys.getfilesystemencoding(),
+            os.environ.get('LANG')))
+    err_file.write("plugins:\n")
+    err_file.write(_format_plugin_list())
+    err_file.write(
+        "\n\n"
+        "*** Bazaar has encountered an internal error.  This probably indicates a\n"
+        "    bug in Bazaar.  You can help us fix it by filing a bug report at\n"
+        "        https://bugs.launchpad.net/bzr/+filebug\n"
+        "    including this traceback and a description of the problem.\n"
+        )
+
+
+def report_bug_to_apport(exc_info, stderr):
+    """Report a bug to apport for optional automatic filing.
+    """
+    # this is based on apport_package_hook.py, but omitting some of the
+    # Ubuntu-specific policy about what to report and when
+
+    # if this fails its caught at a higher level; we don't want to open the
+    # crash file unless apport can be loaded.
+    import apport
+
+    crash_file = _open_crash_file()
+    try:
+        _write_apport_report_to_file(exc_info, crash_file)
+    finally:
+        crash_file.close()
+
+    stderr.write("bzr: ERROR: %s.%s: %s\n" 
+        "\n"
+        "*** Bazaar has encountered an internal error.  This probably indicates a\n"
+        "    bug in Bazaar.  You can help us fix it by filing a bug report at\n"
+        "        https://bugs.launchpad.net/bzr/+filebug\n"
+        "    attaching the crash file\n"
+        "        %s\n"
+        "    and including a description of the problem.\n"
+        "\n"
+        "    The crash file is plain text and you can inspect or edit it to remove\n"
+        "    private information.\n"
+        % (exc_info[0].__module__, exc_info[0].__name__, exc_info[1],
+           crash_file.name))
+
+
+def _write_apport_report_to_file(exc_info, crash_file):
+    import traceback
+    from apport.report import Report
+
+    exc_type, exc_object, exc_tb = exc_info
+
+    pr = Report()
+    # add_proc_info gives you the memory map of the process: this seems rarely
+    # useful for Bazaar and it does make the report harder to scan, though it
+    # does tell you what binary modules are loaded.
+    # pr.add_proc_info()
+    pr.add_user_info()
+    pr['CommandLine'] = pprint.pformat(sys.argv)
+    pr['BzrVersion'] = bzrlib.__version__
+    pr['PythonVersion'] = bzrlib._format_version_tuple(sys.version_info)
+    pr['Platform'] = platform.platform(aliased=1)
+    pr['UserEncoding'] = osutils.get_user_encoding()
+    pr['FileSystemEncoding'] = sys.getfilesystemencoding()
+    pr['Locale'] = os.environ.get('LANG')
+    pr['BzrPlugins'] = _format_plugin_list()
+    pr['PythonLoadedModules'] = _format_module_list()
+    pr['BzrDebugFlags'] = pprint.pformat(debug.debug_flags)
+
+    tb_file = StringIO()
+    traceback.print_exception(exc_type, exc_object, exc_tb, file=tb_file)
+    pr['Traceback'] = tb_file.getvalue()
+
+    pr.write(crash_file)
+
+
+def _open_crash_file():
+    crash_dir = config.crash_dir()
+    # user-readable only, just in case the contents are sensitive.
+    if not osutils.isdir(crash_dir):
+        os.makedirs(crash_dir, mode=0700)
+    filename = 'bzr-%s-%s.crash' % (
+        osutils.compact_date(time.time()),
+        os.getpid(),)
+    return open(osutils.pathjoin(crash_dir, filename), 'wt')
+
+
+def _format_plugin_list():
+    plugin_lines = []
+    for name, a_plugin in sorted(plugin.plugins().items()):
+        plugin_lines.append("  %-20s %s [%s]" %
+            (name, a_plugin.path(), a_plugin.__version__))
+    return '\n'.join(plugin_lines)
+
+
+def _format_module_list():
+    return pprint.pformat(sys.modules)

=== modified file 'bzrlib/help_topics/en/debug-flags.txt'
--- a/bzrlib/help_topics/en/debug-flags.txt	2009-08-14 13:55:30 +0000
+++ b/bzrlib/help_topics/en/debug-flags.txt	2009-08-20 05:02:45 +0000
@@ -27,6 +27,7 @@
 -Dlock            Trace when lockdir locks are taken or released.
 -Dprogress        Trace progress bar operations.
 -Dmerge           Emit information for debugging merges.
+-Dno_apport       Don't use apport to report crashes.
 -Dunlock          Some errors during unlock are treated as warnings.
 -Dpack            Emit information about pack operations.
 -Dsftp            Trace SFTP internals.

=== modified file 'bzrlib/tests/__init__.py'
--- a/bzrlib/tests/__init__.py	2009-08-19 18:04:49 +0000
+++ b/bzrlib/tests/__init__.py	2009-08-20 05:05:59 +0000
@@ -3468,6 +3468,7 @@
                    'bzrlib.tests.test_config',
                    'bzrlib.tests.test_conflicts',
                    'bzrlib.tests.test_counted_lock',
+                   'bzrlib.tests.test_crash',
                    'bzrlib.tests.test_decorators',
                    'bzrlib.tests.test_delta',
                    'bzrlib.tests.test_debug',

=== modified file 'bzrlib/tests/blackbox/test_exceptions.py'
--- a/bzrlib/tests/blackbox/test_exceptions.py	2009-03-23 14:59:43 +0000
+++ b/bzrlib/tests/blackbox/test_exceptions.py	2009-08-20 06:25:02 +0000
@@ -33,23 +33,6 @@
 
 class TestExceptionReporting(TestCase):
 
-    def test_report_exception(self):
-        """When an error occurs, display bug report details to stderr"""
-        try:
-            raise AssertionError("failed")
-        except AssertionError, e:
-            erf = StringIO()
-            trace.report_exception(sys.exc_info(), erf)
-        err = erf.getvalue()
-        self.assertContainsRe(err,
-            r'bzr: ERROR: exceptions\.AssertionError: failed\n')
-        self.assertContainsRe(err,
-            r'Please report a bug at https://bugs\.launchpad\.net/bzr/\+filebug')
-        self.assertContainsRe(err,
-            '(?m)^encoding: .*, fsenc: .*, lang: .*')
-        self.assertContainsRe(err,
-            '(?m)^plugins:$')
-
     def test_exception_exitcode(self):
         # we must use a subprocess, because the normal in-memory mechanism
         # allows errors to propagate up through the test suite
@@ -58,8 +41,8 @@
             retcode=errors.EXIT_INTERNAL_ERROR)
         self.assertEqual(4, errors.EXIT_INTERNAL_ERROR)
         self.assertContainsRe(err,
-                r'bzr: ERROR: exceptions\.AssertionError: always fails\n')
-        self.assertContainsRe(err, r'Please report a bug at')
+                r'exceptions\.AssertionError: always fails\n')
+        self.assertContainsRe(err, r'Bazaar has encountered an internal error')
 
 
 class TestDeprecationWarning(TestCaseInTempDir):

=== added file 'bzrlib/tests/features.py'
--- a/bzrlib/tests/features.py	1970-01-01 00:00:00 +0000
+++ b/bzrlib/tests/features.py	2009-08-20 04:57:58 +0000
@@ -0,0 +1,35 @@
+# Copyright (C) 2009 Canonical Ltd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+from bzrlib.tests import Feature
+
+
+class _ApportFeature(Feature):
+    
+    def _probe(self):
+        try:
+            import apport
+        except ImportError, e:
+            return False
+        else:
+            return True
+
+    def feature_name(self):
+        return 'apport'
+
+
+ApportFeature = _ApportFeature()

=== modified file 'bzrlib/tests/test_config.py'
--- a/bzrlib/tests/test_config.py	2009-07-22 06:00:45 +0000
+++ b/bzrlib/tests/test_config.py	2009-08-20 04:53:23 +0000
@@ -320,6 +320,7 @@
     def setUp(self):
         super(TestConfigPath, self).setUp()
         os.environ['HOME'] = '/home/bogus'
+        os.environ['XDG_CACHE_DIR'] = ''
         if sys.platform == 'win32':
             os.environ['BZR_HOME'] = \
                 r'C:\Documents and Settings\bogus\Application Data'
@@ -347,6 +348,10 @@
         self.assertEqual(config.authentication_config_filename(),
                          self.bzr_home + '/authentication.conf')
 
+    def test_xdg_cache_dir(self):
+        self.assertEqual(config.xdg_cache_dir(),
+            '/home/bogus/.cache')
+
 
 class TestIniConfig(tests.TestCase):
 

=== added file 'bzrlib/tests/test_crash.py'
--- a/bzrlib/tests/test_crash.py	1970-01-01 00:00:00 +0000
+++ b/bzrlib/tests/test_crash.py	2009-08-20 04:45:48 +0000
@@ -0,0 +1,57 @@
+# Copyright (C) 2009 Canonical Ltd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+from StringIO import StringIO
+import sys
+
+
+from bzrlib.crash import (
+    report_bug,
+    _write_apport_report_to_file,
+    )
+from bzrlib.tests import TestCase
+from bzrlib.tests.features import ApportFeature
+
+
+class TestApportReporting(TestCase):
+
+    def setUp(self):
+        TestCase.setUp(self)
+        self.requireFeature(ApportFeature)
+
+    def test_apport_report_contents(self):
+        try:
+            raise AssertionError("my error")
+        except AssertionError, e:
+            pass
+        outf = StringIO()
+        _write_apport_report_to_file(sys.exc_info(),
+            outf)
+        report = outf.getvalue()
+
+        self.assertContainsRe(report,
+            '(?m)^BzrVersion:')
+        # should be in the traceback
+        self.assertContainsRe(report,
+            'my error')
+        self.assertContainsRe(report,
+            'AssertionError')
+        self.assertContainsRe(report,
+            'test_apport_report_contents')
+        # should also be in there
+        self.assertContainsRe(report,
+            '(?m)^CommandLine:.*selftest')

=== modified file 'bzrlib/tests/test_trace.py'
--- a/bzrlib/tests/test_trace.py	2009-03-23 14:59:43 +0000
+++ b/bzrlib/tests/test_trace.py	2009-08-20 04:30:16 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
+# Copyright (C) 2005, 2006, 2007, 2008, 2009 Canonical Ltd
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -48,6 +48,10 @@
 class TestTrace(TestCase):
 
     def test_format_sys_exception(self):
+        # Test handling of an internal/unexpected error that probably
+        # indicates a bug in bzr.  The details of the message may vary
+        # depending on whether apport is available or not.  See test_crash for
+        # more.
         try:
             raise NotImplementedError, "time travel"
         except NotImplementedError:
@@ -56,7 +60,7 @@
         self.assertEqualDiff(err.splitlines()[0],
                 'bzr: ERROR: exceptions.NotImplementedError: time travel')
         self.assertContainsRe(err,
-                r'File.*test_trace.py')
+            'Bazaar has encountered an internal error.')
 
     def test_format_interrupt_exception(self):
         try:
@@ -122,7 +126,7 @@
             pass
         msg = _format_exception()
         self.assertContainsRe(msg,
-            r"Traceback \(most recent call last\)")
+            r'Bazaar has encountered an internal error')
 
     def test_trace_unicode(self):
         """Write Unicode to trace log"""

=== modified file 'bzrlib/trace.py'
--- a/bzrlib/trace.py	2009-08-14 12:08:08 +0000
+++ b/bzrlib/trace.py	2009-08-20 05:02:45 +0000
@@ -478,26 +478,5 @@
 
 def report_bug(exc_info, err_file):
     """Report an exception that probably indicates a bug in bzr"""
-    import platform
-    print_exception(exc_info, err_file)
-    err_file.write('\n')
-    err_file.write('bzr %s on python %s %s\n' % \
-                       (bzrlib.__version__,
-                        bzrlib._format_version_tuple(sys.version_info),
-                        platform.platform(aliased=1)))
-    err_file.write('arguments: %r\n' % sys.argv)
-    err_file.write(
-        'encoding: %r, fsenc: %r, lang: %r\n' % (
-            osutils.get_user_encoding(), sys.getfilesystemencoding(),
-            os.environ.get('LANG')))
-    err_file.write("plugins:\n")
-    for name, a_plugin in sorted(plugin.plugins().items()):
-        err_file.write("  %-20s %s [%s]\n" %
-            (name, a_plugin.path(), a_plugin.__version__))
-    err_file.write(
-"""\
-*** Bazaar has encountered an internal error.
-    Please report a bug at https://bugs.launchpad.net/bzr/+filebug
-    including this traceback, and a description of what you
-    were doing when the error occurred.
-""")
+    from bzrlib.crash import report_bug
+    report_bug(exc_info, err_file)

=== added file 'doc/developers/apport.txt'
--- a/doc/developers/apport.txt	1970-01-01 00:00:00 +0000
+++ b/doc/developers/apport.txt	2009-08-13 14:31:20 +0000
@@ -0,0 +1,87 @@
+*************************
+Bazaar Apport Integration
+*************************
+
+Bazaar can use Apport <http://launchpad.net/apport/> to capture data about
+unexpected errors (probably, bugs in Bazaar) and report them to the
+developers.
+
+This is only active for errors that are believed to be internal errors (ie
+bugs) not user or environmental errors.  (See the Developer Guide.)
+
+Consequences for users
+----------------------
+
+* They shouldn't normally need to see or copy&paste a traceback.
+
+* They will be able to inspect the files before sending them to be sure
+  there's no sensitive data included.
+
+* As at present, they'll need a Launchpad account to report bugs in the
+  normal way.
+
+
+Implementation notes
+--------------------
+
+The use of apport by Bazaar is independent of the configuration in the OS.
+For example in Ubuntu, apport is normally inactive in release builds, and
+normally excludes software not installed from a package.  We'll bypass
+both of them.
+
+Putting in this handler may mean that an OS-wide exception handler never
+sees the error, but that was true with our existing exception-printer.
+
+The user should have the option to: forget about the crash (and ignore the
+bug report), see the contents of the report, file a bug, or save the
+report to file later.  At the moment we just show them the filename and
+let them take it from there.
+
+The process is 
+
+#. An exception reaches the top-level handler.
+
+#. We log it in apport-format to a file in ~/.bazaar/crash.
+
+#. We tell the user where that file is, and invite them to file a bug
+   report.
+
+This won't be active for bugs that cause the whole Python interpreter to
+crash.  This can be handled at the OS level.  The nice thing is that if
+apport is active system-wide, it will catch either exceptions in our
+in-process apport handler, or errors that crash the intrepreter.
+
+
+Future ideas
+------------
+
+* Capture apport data even for things not believed to be internal errors,
+  because sometimes they are in fact bugs.  Then the user can attach the
+  apport report later if they decide to file a bug.  There may be quite a
+  lot of them so we might need to limit the number that are stored, or do
+  this when a debug flag is set.  At the moment they go into .bzr.log and
+  that's probably ok to start with.
+
+* Raising an error from the breakin debugger should cause this to fire.
+
+* Developers looking at a crash on their own machine will probably in the
+  first instance just want to see the traceback. Apport files may be more
+  longwinded than our current output and might make the traceback scroll
+  off the screen.
+
+* Automatically trace messages (ie from .bzr.log) in the report.  We could
+  just include the whole file, but it may be long, and including the whole
+  thing has a greater risk of including sensitive data.
+
+* Ask the user what they want to do with the report: automatically file
+  it, look at it, see just the traceback, just be told where it is.  This
+  could be done through the UIFactory so that it can be done through a
+  graphical dialog.
+
+  However, if we've already had an unhandled error in this process there
+  may be problems in Bazaar that prevent us presenting a clean message...
+
+  Possibly these bugs are better reported in the next time bzr runs.
+
+.. 
+    vim: ft=rst

=== modified file 'doc/developers/index.txt'
--- a/doc/developers/index.txt	2009-07-22 13:41:01 +0000
+++ b/doc/developers/index.txt	2009-08-12 08:03:11 +0000
@@ -70,6 +70,9 @@
 
 * `API versioning <api-versioning.html>`_ |--| bzrlib API versioning.
 
+* `Apport error reporting <apport.html>`_ |--| Capture data to report
+  bugs.
+
 * `Authentication ring <authentication-ring.html>`_ |--| Configuring
   authentication.
 




More information about the bazaar-commits mailing list