14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
18
"""Handling and reporting crashes.
19
20
A crash is an exception propagated up almost to the top level of Bazaar.
21
22
If we have apport <https://launchpad.net/apport/>, we store a report of the
22
crash using apport into its /var/crash spool directory, from where the user
23
crash using apport into it's /var/crash spool directory, from where the user
23
24
can either manually send it to Launchpad. In some cases (at least Ubuntu
24
25
development releases), Apport may pop up a window asking if they want
32
33
In principle apport can run on any platform though as of Feb 2010 there seem
33
34
to be some portability bugs.
35
To force this off in brz turn set APPORT_DISABLE in the environment or
36
To force this off in bzr turn set APPORT_DISABLE in the environment or
39
from __future__ import absolute_import
41
# for interactive testing, try the 'brz assert-fail' command
40
# for interactive testing, try the 'bzr assert-fail' command
42
41
# or see http://code.launchpad.net/~mbp/bzr/bzr-fail
44
43
# to test with apport it's useful to set
71
68
if report_bug_to_apport(exc_info, stderr):
72
69
# wrote a file; if None then report the old way
74
except ImportError as e:
71
except ImportError, e:
75
72
trace.mutter("couldn't find apport bug-reporting library: %s" % e)
76
except Exception as e:
77
75
# this should only happen if apport is installed but it didn't
78
76
# work, eg because of an io error writing the crash file
79
trace.mutter("brz: failed to report crash using apport: %r" % e)
80
trace.log_exception_quietly()
77
stderr.write("bzr: failed to report crash using apport:\n "
81
80
return report_bug_legacy(exc_info, stderr)
85
84
"""Report a bug by just printing a message to the user."""
86
85
trace.print_exception(exc_info, err_file)
87
86
err_file.write('\n')
90
err_file.write(textwrap.fill(l,
91
width=78, subsequent_indent=' ') + '\n')
92
print_wrapped('brz %s on python %s (%s)\n' % \
94
breezy._format_version_tuple(sys.version_info),
95
platform.platform(aliased=1)))
96
print_wrapped('arguments: %r\n' % sys.argv)
97
print_wrapped(textwrap.fill(
98
'plugins: ' + plugin.format_concise_plugin_list(),
100
subsequent_indent=' ',
87
err_file.write('bzr %s on python %s (%s)\n' % \
89
bzrlib._format_version_tuple(sys.version_info),
90
platform.platform(aliased=1)))
91
err_file.write('arguments: %r\n' % sys.argv)
103
93
'encoding: %r, fsenc: %r, lang: %r\n' % (
104
94
osutils.get_user_encoding(), sys.getfilesystemencoding(),
105
95
os.environ.get('LANG')))
106
# We used to show all the plugins here, but it's too verbose.
96
err_file.write("plugins:\n")
97
err_file.write(_format_plugin_list())
109
100
"*** Bazaar has encountered an internal error. This probably indicates a\n"
110
101
" bug in Bazaar. You can help us fix it by filing a bug report at\n"
111
" https://bugs.launchpad.net/brz/+filebug\n"
102
" https://bugs.launchpad.net/bzr/+filebug\n"
112
103
" including this traceback and a description of the problem.\n"
121
112
# this function is based on apport_package_hook.py, but omitting some of the
122
113
# Ubuntu-specific policy about what to report and when
124
# This import is apparently not used, but we're doing it so that if the
125
# import fails, the exception will be caught at a higher level and we'll
126
# report the error by other means.
115
# if the import fails, the exception will be caught at a higher level and
116
# we'll report the error by other means
129
119
crash_filename = _write_apport_report_to_file(exc_info)
131
121
if crash_filename is None:
132
122
stderr.write("\n"
133
"apport is set to ignore crashes in this version of brz.\n"
123
"apport is set to ignore crashes in this version of bzr.\n"
136
126
trace.print_exception(exc_info, stderr)
153
143
exc_type, exc_object, exc_tb = exc_info
156
# add_proc_info sets the ExecutablePath, InterpreterPath, etc.
146
# add_proc_info gives you the memory map of the process, which is not so
147
# useful for Bazaar but does tell you what binary libraries are loaded.
148
# More importantly it sets the ExecutablePath, InterpreterPath, etc.
157
149
pr.add_proc_info()
158
# It also adds ProcMaps which for us is rarely useful and mostly noise, so
161
150
pr.add_user_info()
163
152
# Package and SourcePackage are needed so that apport will report about even
164
# non-packaged versions of brz; also this reports on their packaged
153
# non-packaged versions of bzr; also this reports on their packaged
165
154
# dependencies which is useful.
166
pr['SourcePackage'] = 'brz'
167
pr['Package'] = 'brz'
155
pr['SourcePackage'] = 'bzr'
156
pr['Package'] = 'bzr'
169
158
pr['CommandLine'] = pprint.pformat(sys.argv)
170
pr['BrzVersion'] = breezy.__version__
171
pr['PythonVersion'] = breezy._format_version_tuple(sys.version_info)
159
pr['BzrVersion'] = bzrlib.__version__
160
pr['PythonVersion'] = bzrlib._format_version_tuple(sys.version_info)
172
161
pr['Platform'] = platform.platform(aliased=1)
173
162
pr['UserEncoding'] = osutils.get_user_encoding()
174
163
pr['FileSystemEncoding'] = sys.getfilesystemencoding()
175
pr['Locale'] = os.environ.get('LANG', 'C')
176
pr['BrzPlugins'] = _format_plugin_list()
164
pr['Locale'] = os.environ.get('LANG')
165
pr['BzrPlugins'] = _format_plugin_list()
177
166
pr['PythonLoadedModules'] = _format_module_list()
178
pr['BrzDebugFlags'] = pprint.pformat(debug.debug_flags)
167
pr['BzrDebugFlags'] = pprint.pformat(debug.debug_flags)
180
169
# actually we'd rather file directly against the upstream product, but
181
170
# apport does seem to count on there being one in there; we might need to
182
171
# redirect it elsewhere anyhow
183
pr['SourcePackage'] = 'brz'
184
pr['Package'] = 'brz'
172
pr['SourcePackage'] = 'bzr'
173
pr['Package'] = 'bzr'
186
# tell apport to file directly against the brz package using
187
# <https://bugs.launchpad.net/bzr/+bug/391015>
175
# tell apport to file directly against the bzr package using
176
# <https://bugs.edge.launchpad.net/bzr/+bug/391015>
189
178
# XXX: unfortunately apport may crash later if the crashdb definition
190
179
# file isn't present
191
pr['CrashDb'] = 'brz'
180
pr['CrashDb'] = 'bzr'
193
182
tb_file = StringIO()
194
183
traceback.print_exception(exc_type, exc_object, exc_tb, file=tb_file)
197
186
_attach_log_tail(pr)
199
# We want to use the 'brz' crashdb so that it gets sent directly upstream,
188
# We want to use the 'bzr' crashdb so that it gets sent directly upstream,
200
189
# which is a reasonable default for most internal errors. However, if we
201
190
# set it here then apport will crash later if it doesn't know about that
202
# crashdb. Instead, we rely on the brz package installing both a
191
# crashdb. Instead, we rely on the bzr package installing both a
203
192
# source hook telling crashes to go to this crashdb, and a crashdb
204
193
# configuration describing it.
207
196
# TODO: strip that out and attach the rest
209
198
#attach_file_if_exists(report,
210
# os.path.join(dot_brz, 'breezy.conf', 'BrzConfig')
199
# os.path.join(dot_bzr, 'bazaar.conf', 'BzrConfig')
211
200
#attach_file_if_exists(report,
212
# os.path.join(dot_brz, 'locations.conf', 'BrzLocations')
201
# os.path.join(dot_bzr, 'locations.conf', 'BzrLocations')
214
203
# strip username, hostname, etc
227
216
def _attach_log_tail(pr):
229
brz_log = open(trace._get_brz_log_filename(), 'rt')
230
except (IOError, OSError) as e:
231
pr['BrzLogTail'] = repr(e)
218
bzr_log = open(trace._get_bzr_log_filename(), 'rt')
219
except (IOError, OSError), e:
220
pr['BzrLogTail'] = repr(e)
234
lines = brz_log.readlines()
235
pr['BrzLogTail'] = ''.join(lines[-40:])
223
lines = bzr_log.readlines()
224
pr['BzrLogTail'] = ''.join(lines[-40:])
240
229
def _open_crash_file():
243
232
# on unix this should be /var/crash and should already exist; on
244
233
# Windows or if it's manually configured it might need to be created,
245
234
# and then it should be private
246
os.makedirs(crash_dir, mode=0o600)
235
os.makedirs(crash_dir, mode=0600)
247
236
date_string = time.strftime('%Y-%m-%dT%H:%M', time.gmtime())
248
237
# XXX: getuid doesn't work on win32, but the crash directory is per-user
249
238
if sys.platform == 'win32':
260
249
return filename, os.fdopen(
261
250
os.open(filename,
262
251
os.O_WRONLY|os.O_CREAT|os.O_EXCL,
267
256
def _format_plugin_list():
268
return ''.join(plugin.describe_plugins(show_paths=True))
258
for name, a_plugin in sorted(plugin.plugins().items()):
259
plugin_lines.append(" %-20s %s [%s]" %
260
(name, a_plugin.path(), a_plugin.__version__))
261
return '\n'.join(plugin_lines)
271
264
def _format_module_list():