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