~bzr-pqm/bzr/bzr.dev

4584.3.6 by Martin Pool
Move apport integration to bzrlib.crash and send output to a file.
1
# Copyright (C) 2009 Canonical Ltd
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
17
18
"""Handling and reporting crashes.
19
"""
20
4584.3.17 by Martin Pool
Better message in apport crash
21
# for interactive testing, try the 'bzr assert-fail' command 
22
# or see http://code.launchpad.net/~mbp/bzr/bzr-fail
4584.3.6 by Martin Pool
Move apport integration to bzrlib.crash and send output to a file.
23
24
import os
4584.3.30 by Martin Pool
Fix call to platform() when apport not present
25
import platform
4584.3.13 by Martin Pool
Refactor _format_plugin_list and include list of loaded modules in apport
26
import pprint
4584.3.6 by Martin Pool
Move apport integration to bzrlib.crash and send output to a file.
27
import sys
28
import time
4584.3.22 by Martin Pool
further tweaks to and tests of bzr apport reporting
29
from StringIO import StringIO
4584.3.6 by Martin Pool
Move apport integration to bzrlib.crash and send output to a file.
30
31
import bzrlib
32
from bzrlib import (
33
    config,
4584.3.16 by Martin Pool
Add -Dno_apport and fallback if apport fails
34
    debug,
4584.3.6 by Martin Pool
Move apport integration to bzrlib.crash and send output to a file.
35
    osutils,
36
    plugin,
37
    trace,
38
    )
39
40
41
def report_bug(exc_info, stderr):
4584.3.16 by Martin Pool
Add -Dno_apport and fallback if apport fails
42
    if 'no_apport' not in debug.debug_flags:
43
        try:
44
            report_bug_to_apport(exc_info, stderr)
45
            return
4584.3.25 by Martin Pool
Better handling of ImportError from apport
46
        except ImportError, e:
47
            trace.mutter("couldn't find apport bug-reporting library: %s" % e)
48
            pass
4584.3.16 by Martin Pool
Add -Dno_apport and fallback if apport fails
49
        except Exception, e:
4584.3.21 by Martin Pool
Start adding tests for apport
50
            # this should only happen if apport is installed but it didn't
51
            # work, eg because of an io error writing the crash file
4584.3.22 by Martin Pool
further tweaks to and tests of bzr apport reporting
52
            sys.stderr.write("bzr: failed to report crash using apport:\n "
53
                "    %r\n" % e)
4584.3.16 by Martin Pool
Add -Dno_apport and fallback if apport fails
54
            pass
55
    report_bug_legacy(exc_info, stderr)
4584.3.6 by Martin Pool
Move apport integration to bzrlib.crash and send output to a file.
56
57
58
def report_bug_legacy(exc_info, err_file):
59
    """Report a bug by just printing a message to the user."""
60
    trace.print_exception(exc_info, err_file)
61
    err_file.write('\n')
62
    err_file.write('bzr %s on python %s (%s)\n' % \
63
                       (bzrlib.__version__,
64
                        bzrlib._format_version_tuple(sys.version_info),
4584.3.30 by Martin Pool
Fix call to platform() when apport not present
65
                        platform.platform(aliased=1)))
4584.3.6 by Martin Pool
Move apport integration to bzrlib.crash and send output to a file.
66
    err_file.write('arguments: %r\n' % sys.argv)
67
    err_file.write(
68
        'encoding: %r, fsenc: %r, lang: %r\n' % (
69
            osutils.get_user_encoding(), sys.getfilesystemencoding(),
70
            os.environ.get('LANG')))
71
    err_file.write("plugins:\n")
4584.3.22 by Martin Pool
further tweaks to and tests of bzr apport reporting
72
    err_file.write(_format_plugin_list())
4584.3.6 by Martin Pool
Move apport integration to bzrlib.crash and send output to a file.
73
    err_file.write(
4584.3.22 by Martin Pool
further tweaks to and tests of bzr apport reporting
74
        "\n\n"
4584.3.19 by Martin Pool
Tweak crash message and use the same one with apport or without.
75
        "*** Bazaar has encountered an internal error.  This probably indicates a\n"
4584.3.22 by Martin Pool
further tweaks to and tests of bzr apport reporting
76
        "    bug in Bazaar.  You can help us fix it by filing a bug report at\n"
77
        "        https://bugs.launchpad.net/bzr/+filebug\n"
78
        "    including this traceback and a description of the problem.\n"
4584.3.19 by Martin Pool
Tweak crash message and use the same one with apport or without.
79
        )
4584.3.6 by Martin Pool
Move apport integration to bzrlib.crash and send output to a file.
80
81
82
def report_bug_to_apport(exc_info, stderr):
83
    """Report a bug to apport for optional automatic filing.
84
    """
85
    # this is based on apport_package_hook.py, but omitting some of the
86
    # Ubuntu-specific policy about what to report and when
4584.3.25 by Martin Pool
Better handling of ImportError from apport
87
88
    # if this fails its caught at a higher level; we don't want to open the
89
    # crash file unless apport can be loaded.
90
    import apport
4584.3.6 by Martin Pool
Move apport integration to bzrlib.crash and send output to a file.
91
4584.3.21 by Martin Pool
Start adding tests for apport
92
    crash_file = _open_crash_file()
93
    try:
4584.3.22 by Martin Pool
further tweaks to and tests of bzr apport reporting
94
        _write_apport_report_to_file(exc_info, crash_file)
4584.3.21 by Martin Pool
Start adding tests for apport
95
    finally:
96
        crash_file.close()
97
98
    stderr.write("bzr: ERROR: %s.%s: %s\n" 
99
        "\n"
100
        "*** Bazaar has encountered an internal error.  This probably indicates a\n"
4584.3.22 by Martin Pool
further tweaks to and tests of bzr apport reporting
101
        "    bug in Bazaar.  You can help us fix it by filing a bug report at\n"
102
        "        https://bugs.launchpad.net/bzr/+filebug\n"
103
        "    attaching the crash file\n"
104
        "        %s\n"
105
        "    and including a description of the problem.\n"
106
        "\n"
107
        "    The crash file is plain text and you can inspect or edit it to remove\n"
108
        "    private information.\n"
4584.3.21 by Martin Pool
Start adding tests for apport
109
        % (exc_info[0].__module__, exc_info[0].__name__, exc_info[1],
110
           crash_file.name))
111
112
113
def _write_apport_report_to_file(exc_info, crash_file):
4584.3.22 by Martin Pool
further tweaks to and tests of bzr apport reporting
114
    import traceback
4584.3.21 by Martin Pool
Start adding tests for apport
115
    from apport.report import Report
4584.3.22 by Martin Pool
further tweaks to and tests of bzr apport reporting
116
117
    exc_type, exc_object, exc_tb = exc_info
118
4584.3.6 by Martin Pool
Move apport integration to bzrlib.crash and send output to a file.
119
    pr = Report()
120
    # add_proc_info gives you the memory map of the process: this seems rarely
121
    # useful for Bazaar and it does make the report harder to scan, though it
122
    # does tell you what binary modules are loaded.
123
    # pr.add_proc_info()
124
    pr.add_user_info()
4584.3.22 by Martin Pool
further tweaks to and tests of bzr apport reporting
125
    pr['CommandLine'] = pprint.pformat(sys.argv)
4584.3.6 by Martin Pool
Move apport integration to bzrlib.crash and send output to a file.
126
    pr['BzrVersion'] = bzrlib.__version__
127
    pr['PythonVersion'] = bzrlib._format_version_tuple(sys.version_info)
128
    pr['Platform'] = platform.platform(aliased=1)
129
    pr['UserEncoding'] = osutils.get_user_encoding()
130
    pr['FileSystemEncoding'] = sys.getfilesystemencoding()
131
    pr['Locale'] = os.environ.get('LANG')
4584.3.13 by Martin Pool
Refactor _format_plugin_list and include list of loaded modules in apport
132
    pr['BzrPlugins'] = _format_plugin_list()
133
    pr['PythonLoadedModules'] = _format_module_list()
4584.3.22 by Martin Pool
further tweaks to and tests of bzr apport reporting
134
    pr['BzrDebugFlags'] = pprint.pformat(debug.debug_flags)
135
136
    tb_file = StringIO()
137
    traceback.print_exception(exc_type, exc_object, exc_tb, file=tb_file)
138
    pr['Traceback'] = tb_file.getvalue()
139
4584.3.21 by Martin Pool
Start adding tests for apport
140
    pr.write(crash_file)
4584.3.6 by Martin Pool
Move apport integration to bzrlib.crash and send output to a file.
141
142
143
def _open_crash_file():
144
    crash_dir = config.crash_dir()
145
    # user-readable only, just in case the contents are sensitive.
146
    if not osutils.isdir(crash_dir):
147
        os.makedirs(crash_dir, mode=0700)
148
    filename = 'bzr-%s-%s.crash' % (
149
        osutils.compact_date(time.time()),
150
        os.getpid(),)
151
    return open(osutils.pathjoin(crash_dir, filename), 'wt')
4584.3.13 by Martin Pool
Refactor _format_plugin_list and include list of loaded modules in apport
152
153
154
def _format_plugin_list():
155
    plugin_lines = []
156
    for name, a_plugin in sorted(plugin.plugins().items()):
157
        plugin_lines.append("  %-20s %s [%s]" %
158
            (name, a_plugin.path(), a_plugin.__version__))
159
    return '\n'.join(plugin_lines)
160
161
162
def _format_module_list():
163
    return pprint.pformat(sys.modules)