Rev 5002: (mbp) use apport to send bugs, not just store them in file:///home/pqm/archives/thelove/bzr/%2Btrunk/
Canonical.com Patch Queue Manager
pqm at pqm.ubuntu.com
Wed Feb 3 10:06:22 GMT 2010
At file:///home/pqm/archives/thelove/bzr/%2Btrunk/
------------------------------------------------------------
revno: 5002 [merge]
revision-id: pqm at pqm.ubuntu.com-20100203100619-f76bo5y5bd5c14wk
parent: pqm at pqm.ubuntu.com-20100203093522-2j28jn5huknayntu
parent: mbp at sourcefrog.net-20100203000823-fcyf2791xrl3fbfo
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Wed 2010-02-03 10:06:19 +0000
message:
(mbp) use apport to send bugs, not just store them
added:
contrib/apport/ contribapport-20100131162357-aladkx1ilh730byb-1
contrib/apport/README readme-20100131163221-0u77xzj0p9auj54u-1
contrib/apport/bzr.conf bzr.conf-20100131163221-0u77xzj0p9auj54u-2
contrib/apport/source_bzr.py source_bzr.py-20100131163221-0u77xzj0p9auj54u-3
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
bzrlib/config.py config.py-20051011043216-070c74f4e9e338e8
bzrlib/crash.py crash.py-20090812083334-d6volool4lktdjcx-1
bzrlib/tests/__init__.py selftest.py-20050531073622-8d0e3c8845c97a64
bzrlib/tests/test_crash.py test_crash.py-20090820042958-jglgza3wrn03ha9e-2
=== modified file 'NEWS'
--- a/NEWS 2010-02-03 09:35:22 +0000
+++ b/NEWS 2010-02-03 10:06:19 +0000
@@ -19,6 +19,18 @@
New Features
************
+* If the Apport crash-reporting tool is available, bzr crashes are now
+ stored into the ``/var/crash`` apport spool directory, and the user is
+ invited to report them to the developers from there, either
+ automatically or by running ``apport-bug``. No information is sent
+ without specific permission from the user. (Martin Pool, #515052)
+
+Testing
+*******
+
+* Stop sending apport crash files to ``.cache`` in the directory from
+ which ``bzr selftest`` was run. (Martin Pool, #422350)
+
bzr 2.1.0 (not released yet)
############################
=== modified file 'bzrlib/config.py'
--- a/bzrlib/config.py 2009-12-17 10:01:25 +0000
+++ b/bzrlib/config.py 2010-02-02 23:02:18 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2005, 2007, 2008 Canonical Ltd
+# Copyright (C) 2005, 2007, 2008, 2010 Canonical Ltd
# Authors: Robert Collins <robert.collins at canonical.com>
# and others
#
@@ -866,12 +866,16 @@
This doesn't implicitly create it.
- On Windows it's in the config directory; elsewhere in the XDG cache directory.
+ On Windows it's in the config directory; elsewhere it's /var/crash
+ which may be monitored by apport. It can be overridden by
+ $APPORT_CRASH_DIR.
"""
if sys.platform == 'win32':
return osutils.pathjoin(config_dir(), 'Crash')
else:
- return osutils.pathjoin(xdg_cache_dir(), 'crash')
+ # XXX: hardcoded in apport_python_hook.py; therefore here too -- mbp
+ # 2010-01-31
+ return os.environ.get('APPORT_CRASH_DIR', '/var/crash')
def xdg_cache_dir():
=== modified file 'bzrlib/crash.py'
--- a/bzrlib/crash.py 2009-08-20 05:47:53 +0000
+++ b/bzrlib/crash.py 2010-02-03 00:08:23 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2009 Canonical Ltd
+# Copyright (C) 2009, 2010 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
@@ -16,10 +16,32 @@
"""Handling and reporting crashes.
+
+A crash is an exception propagated up almost to the top level of Bazaar.
+
+If we have apport <https://launchpad.net/apport/>, we store a report of the
+crash using apport into it's /var/crash spool directory, from where the user
+can either manually send it to Launchpad. In some cases (at least Ubuntu
+development releases), Apport may pop up a window asking if they want
+to send it.
+
+Without apport, we just write a crash report to stderr and the user can report
+this manually if the wish.
+
+We never send crash data across the network without user opt-in.
+
+In principle apport can run on any platform though as of Feb 2010 there seem
+to be some portability bugs.
+
+To force this off in bzr turn set APPORT_DISBLE in the environment or
+-Dno_apport.
"""
# for interactive testing, try the 'bzr assert-fail' command
# or see http://code.launchpad.net/~mbp/bzr/bzr-fail
+#
+# to test with apport it's useful to set
+# export APPORT_IGNORE_OBSOLETE_PACKAGES=1
import os
import platform
@@ -39,20 +61,23 @@
def report_bug(exc_info, stderr):
- if 'no_apport' not in debug.debug_flags:
- try:
- report_bug_to_apport(exc_info, stderr)
+ if ('no_apport' in debug.debug_flags) or \
+ os.environ.get('APPORT_DISABLE', None):
+ return report_bug_legacy(exc_info, stderr)
+ try:
+ if report_bug_to_apport(exc_info, stderr):
+ # wrote a file; if None then report the old way
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)
+ 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
+ stderr.write("bzr: failed to report crash using apport:\n "
+ " %r\n" % e)
+ pass
+ return report_bug_legacy(exc_info, stderr)
def report_bug_legacy(exc_info, err_file):
@@ -81,47 +106,54 @@
def report_bug_to_apport(exc_info, stderr):
"""Report a bug to apport for optional automatic filing.
+
+ :returns: The name of the crash file, or None if we didn't write one.
"""
- # this is based on apport_package_hook.py, but omitting some of the
+ # this function 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.
+ # if the import fails, the exception will be caught at a higher level and
+ # we'll report the error by other means
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):
+ crash_filename = _write_apport_report_to_file(exc_info)
+
+ if crash_filename is None:
+ stderr.write("\n"
+ "apport is set to ignore crashes in this version of bzr.\n"
+ )
+ else:
+ trace.print_exception(exc_info, stderr)
+ stderr.write("\n"
+ "You can report this problem to Bazaar's developers by running\n"
+ " apport-bug %s\n"
+ "if a bug-reporting window does not automatically appear.\n"
+ % (crash_filename))
+ # XXX: on Windows, Mac, and other platforms where we might have the
+ # apport libraries but not have an apport always running, we could
+ # synchronously file now
+
+ return crash_filename
+
+
+def _write_apport_report_to_file(exc_info):
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()
+ # add_proc_info gets the executable and interpreter path, which is needed,
+ # plus some less useful stuff like the memory map
+ pr.add_proc_info()
pr.add_user_info()
+
+ # Package and SourcePackage are needed so that apport will report about even
+ # non-packaged versions of bzr; also this reports on their packaged
+ # dependencies which is useful.
+ pr['SourcePackage'] = 'bzr'
+ pr['Package'] = 'bzr'
+
pr['CommandLine'] = pprint.pformat(sys.argv)
pr['BzrVersion'] = bzrlib.__version__
pr['PythonVersion'] = bzrlib._format_version_tuple(sys.version_info)
@@ -137,18 +169,74 @@
traceback.print_exception(exc_type, exc_object, exc_tb, file=tb_file)
pr['Traceback'] = tb_file.getvalue()
- pr.write(crash_file)
+ _attach_log_tail(pr)
+
+ # We want to use the 'bzr' crashdb so that it gets sent directly upstream,
+ # which is a reasonable default for most internal errors. However, if we
+ # set it here then apport will crash later if it doesn't know about that
+ # crashdb. Instead, we rely on the bzr package installing both a
+ # source hook telling crashes to go to this crashdb, and a crashdb
+ # configuration describing it.
+
+ # these may contain some sensitive info (smtp_passwords)
+ # TODO: strip that out and attach the rest
+ #
+ #attach_file_if_exists(report,
+ # os.path.join(dot_bzr, 'bazaar.conf', 'BzrConfig')
+ #attach_file_if_exists(report,
+ # os.path.join(dot_bzr, 'locations.conf', 'BzrLocations')
+
+ # strip username, hostname, etc
+ pr.anonymize()
+
+ if pr.check_ignored():
+ # eg configured off in ~/.apport-ignore.xml
+ return None
+ else:
+ crash_file_name, crash_file = _open_crash_file()
+ pr.write(crash_file)
+ crash_file.close()
+ return crash_file_name
+
+
+def _attach_log_tail(pr):
+ try:
+ bzr_log = open(trace._get_bzr_log_filename(), 'rt')
+ except (IOError, OSError), e:
+ pr['BzrLogTail'] = repr(e)
+ return
+ try:
+ lines = bzr_log.readlines()
+ pr['BzrLogTail'] = ''.join(lines[-40:])
+ finally:
+ bzr_log.close()
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')
+ # on unix this should be /var/crash and should already exist; on
+ # Windows or if it's manually configured it might need to be created,
+ # and then it should be private
+ os.makedirs(crash_dir, mode=0600)
+ date_string = time.strftime('%Y-%m-%dT%H:%M', time.gmtime())
+ # XXX: getuid doesn't work on win32, but the crash directory is per-user
+ if sys.platform == 'win32':
+ user_part = ''
+ else:
+ user_part = '.%d' % os.getuid()
+ filename = osutils.pathjoin(
+ crash_dir,
+ 'bzr%s.%s.crash' % (
+ user_part,
+ date_string))
+ # be careful here that people can't play tmp-type symlink mischief in the
+ # world-writable directory
+ return filename, os.fdopen(
+ os.open(filename,
+ os.O_WRONLY|os.O_CREAT|os.O_EXCL,
+ 0600),
+ 'w')
def _format_plugin_list():
=== modified file 'bzrlib/tests/__init__.py'
--- a/bzrlib/tests/__init__.py 2010-02-03 08:32:14 +0000
+++ b/bzrlib/tests/__init__.py 2010-02-03 10:06:19 +0000
@@ -1540,6 +1540,11 @@
'ftp_proxy': None,
'FTP_PROXY': None,
'BZR_REMOTE_PATH': None,
+ # Generally speaking, we don't want apport reporting on crashes in
+ # the test envirnoment unless we're specifically testing apport,
+ # so that it doesn't leak into the real system environment. We
+ # use an env var so it propagates to subprocesses.
+ 'APPORT_DISABLE': '1',
}
self.__old_env = {}
self.addCleanup(self._restoreEnvironment)
=== modified file 'bzrlib/tests/test_crash.py'
--- a/bzrlib/tests/test_crash.py 2009-12-22 15:39:20 +0000
+++ b/bzrlib/tests/test_crash.py 2010-02-02 23:02:18 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2009 Canonical Ltd
+# Copyright (C) 2009, 2010 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
@@ -18,31 +18,62 @@
from StringIO import StringIO
import sys
+
+import os
+
+
from bzrlib import (
+ config,
crash,
tests,
- )
-
-from bzrlib.tests import features
-
-
-class TestApportReporting(tests.TestCase):
+ osutils,
+ )
+
+from bzrlib.tests import (
+ features,
+ TestCaseInTempDir,
+ )
+from bzrlib.tests.features import ApportFeature
+
+
+class TestApportReporting(TestCaseInTempDir):
_test_needs_features = [features.apport]
- def test_apport_report_contents(self):
+ def setUp(self):
+ TestCaseInTempDir.setUp(self)
+ self.requireFeature(ApportFeature)
+
+ def test_apport_report(self):
+ crash_dir = osutils.joinpath((self.test_base_dir, 'crash'))
+ os.mkdir(crash_dir)
+ os.environ['APPORT_CRASH_DIR'] = crash_dir
+ self.assertEquals(crash_dir, config.crash_dir())
+
+ stderr = StringIO()
+
try:
raise AssertionError("my error")
except AssertionError, e:
pass
- outf = StringIO()
- crash._write_apport_report_to_file(sys.exc_info(), outf)
- report = outf.getvalue()
-
- self.assertContainsRe(report, '(?m)^BzrVersion:')
- # should be in the traceback
+
+ crash_filename = crash.report_bug_to_apport(sys.exc_info(),
+ stderr)
+
+ # message explaining the crash
+ self.assertContainsRe(stderr.getvalue(),
+ " apport-bug %s" % crash_filename)
+
+ crash_file = open(crash_filename)
+ try:
+ report = crash_file.read()
+ finally:
+ crash_file.close()
+
+ 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')
+ self.assertContainsRe(report, 'test_apport_report')
# should also be in there
self.assertContainsRe(report, '(?m)^CommandLine:')
=== added directory 'contrib/apport'
=== added file 'contrib/apport/README'
--- a/contrib/apport/README 1970-01-01 00:00:00 +0000
+++ b/contrib/apport/README 2010-01-31 16:32:34 +0000
@@ -0,0 +1,13 @@
+Bazaar supports semi-automatic bug reporting through Apport
+<https://launchpad.net/apport/>.
+
+If apport is not installed, an exception is printed to stderr in the usual way.
+
+For this to work properly it's suggested that two files be installed when a
+package of bzr is installed:
+
+``bzr.conf``
+ into ``/etc/apport/crashdb.conf.d``
+
+``source_bzr.py``
+ into ``/usr/share/apport/package-hooks``
=== added file 'contrib/apport/bzr.conf'
--- a/contrib/apport/bzr.conf 1970-01-01 00:00:00 +0000
+++ b/contrib/apport/bzr.conf 2010-01-31 16:32:34 +0000
@@ -0,0 +1,6 @@
+bzr = {
+ # most bzr bugs are upstream bugs; file them there
+ 'impl': 'launchpad',
+ 'project': 'bzr',
+ 'bug_pattern_base': 'http://people.canonical.com/~pitti/bugpatterns',
+}
=== added file 'contrib/apport/source_bzr.py'
--- a/contrib/apport/source_bzr.py 1970-01-01 00:00:00 +0000
+++ b/contrib/apport/source_bzr.py 2010-01-31 19:37:11 +0000
@@ -0,0 +1,54 @@
+'''apport package hook for Bazaar'''
+
+# Copyright (c) 2009, 2010 Canonical Ltd.
+# Author: Matt Zimmerman <mdz at canonical.com>
+# and others
+
+from apport.hookutils import *
+import os
+
+bzr_log = os.path.expanduser('~/.bzr.log')
+dot_bzr = os.path.expanduser('~/.bazaar')
+
+def _add_log_tail(report):
+ # may have already been added in-process
+ if 'BzrLogTail' in report:
+ return
+
+ bzr_log_lines = open(bzr_log).readlines()
+ bzr_log_lines.reverse()
+
+ bzr_log_tail = []
+ blanks = 0
+ for line in bzr_log_lines:
+ if line == '\n':
+ blanks += 1
+ bzr_log_tail.append(line)
+ if blanks >= 2:
+ break
+
+ bzr_log_tail.reverse()
+ report['BzrLogTail'] = ''.join(bzr_log_tail)
+
+
+def add_info(report):
+
+ _add_log_tail()
+ if 'BzrPlugins' not in report:
+ # may already be present in-process
+ report['BzrPlugins'] = command_output(['bzr', 'plugins', '-v'])
+
+ # by default assume bzr crashes are upstream bugs; this relies on
+ # having a bzr entry under /etc/apport/crashdb.conf.d/
+ report['CrashDB'] = 'bzr'
+
+ # these may contain some sensitive info (smtp_passwords)
+ # TODO: strip that out and attach the rest
+
+ #attach_file_if_exists(report,
+ # os.path.join(dot_bzr, 'bazaar.conf', 'BzrConfig')
+ #attach_file_if_exists(report,
+ # os.path.join(dot_bzr, 'locations.conf', 'BzrLocations')
+
+
+# vim: expandtab shiftwidth=4
More information about the bazaar-commits
mailing list