1679
by Martin Pitt
Update all copyright and description headers and consistently format them. |
1 |
'''Python sys.excepthook hook to generate apport crash dumps.'''
|
2 |
||
3 |
# Copyright (c) 2006 - 2009 Canonical Ltd.
|
|
4 |
# Authors: Robert Collins <robert@ubuntu.com>
|
|
5 |
# Martin Pitt <martin.pitt@ubuntu.com>
|
|
2189
by Martin Pitt
remove trailing whitespace |
6 |
#
|
1679
by Martin Pitt
Update all copyright and description headers and consistently format them. |
7 |
# This program is free software; you can redistribute it and/or modify it
|
8 |
# under the terms of the GNU General Public License as published by the
|
|
9 |
# Free Software Foundation; either version 2 of the License, or (at your
|
|
10 |
# option) any later version. See http://www.gnu.org/copyleft/gpl.html for
|
|
11 |
# the full text of the license.
|
|
359
by martin at piware
* Add apport/python_hook.py: Default exception handler for Python, to create |
12 |
|
13 |
import os |
|
14 |
import sys |
|
15 |
||
1896
by Martin Pitt
apport-checkreports: Exit with status 2 if there are new reports, but apport is disabled. This helps crash notification GUIs to not display new crash reports in that case. Thanks to Michael Vogt for the original patch. |
16 |
CONFIG = '/etc/default/apport' |
17 |
||
2277
by Martin Pitt
* Fix the whole code to be PEP-8 compatible, and enforce this in test/run by running the "pep8" tool. |
18 |
|
1896
by Martin Pitt
apport-checkreports: Exit with status 2 if there are new reports, but apport is disabled. This helps crash notification GUIs to not display new crash reports in that case. Thanks to Michael Vogt for the original patch. |
19 |
def enabled(): |
20 |
'''Return whether Apport should generate crash reports.'''
|
|
21 |
||
2277
by Martin Pitt
* Fix the whole code to be PEP-8 compatible, and enforce this in test/run by running the "pep8" tool. |
22 |
# This doesn't use apport.packaging.enabled() because it is too heavyweight
|
23 |
# See LP: #528355
|
|
2787
by Martin Pitt
* Delay the import of the glob and re modules in the python apport hook, and only import them when needed. Speeds up interpreter startup time by 50%. Thanks Matthias Klose! (LP: #1307684) |
24 |
import re |
1896
by Martin Pitt
apport-checkreports: Exit with status 2 if there are new reports, but apport is disabled. This helps crash notification GUIs to not display new crash reports in that case. Thanks to Michael Vogt for the original patch. |
25 |
try: |
2304.1.2
by Martin Pitt
More Python 3 fixes |
26 |
with open(CONFIG) as f: |
27 |
conf = f.read() |
|
1896
by Martin Pitt
apport-checkreports: Exit with status 2 if there are new reports, but apport is disabled. This helps crash notification GUIs to not display new crash reports in that case. Thanks to Michael Vogt for the original patch. |
28 |
return re.search('^\s*enabled\s*=\s*0\s*$', conf, re.M) is None |
29 |
except IOError: |
|
30 |
# if the file does not exist, assume it's enabled
|
|
31 |
return True |
|
1714
by Martin Pitt
apport_python_hook.py: Directly check /etc/default/apport instead of |
32 |
|
2277
by Martin Pitt
* Fix the whole code to be PEP-8 compatible, and enforce this in test/run by running the "pep8" tool. |
33 |
|
359
by martin at piware
* Add apport/python_hook.py: Default exception handler for Python, to create |
34 |
def apport_excepthook(exc_type, exc_obj, exc_tb): |
35 |
'''Catch an uncaught exception and make a traceback.'''
|
|
36 |
||
37 |
# create and save a problem report. Note that exceptions in this code
|
|
688
by Martin Pitt
* Remove trailing white space in all Python files. |
38 |
# are bad, and we probably need a per-thread reentrancy guard to
|
359
by martin at piware
* Add apport/python_hook.py: Default exception handler for Python, to create |
39 |
# prevent that happening. However, on Ubuntu there should never be
|
40 |
# a reason for an exception here, other than [say] a read only var
|
|
41 |
# or some such. So what we do is use a try - finally to ensure that
|
|
688
by Martin Pitt
* Remove trailing white space in all Python files. |
42 |
# the original excepthook is invoked, and until we get bug reports
|
359
by martin at piware
* Add apport/python_hook.py: Default exception handler for Python, to create |
43 |
# ignore the other issues.
|
44 |
||
45 |
# import locally here so that there is no routine overhead on python
|
|
46 |
# startup time - only when a traceback occurs will this trigger.
|
|
47 |
try: |
|
48 |
# ignore 'safe' exit types.
|
|
49 |
if exc_type in (KeyboardInterrupt, ): |
|
50 |
return
|
|
1111
by Martin Pitt
* apport_python_hook.py: Do not create reports if Apport is disabled (in |
51 |
|
52 |
# do not do anything if apport was disabled
|
|
1896
by Martin Pitt
apport-checkreports: Exit with status 2 if there are new reports, but apport is disabled. This helps crash notification GUIs to not display new crash reports in that case. Thanks to Michael Vogt for the original patch. |
53 |
if not enabled(): |
1111
by Martin Pitt
* apport_python_hook.py: Do not create reports if Apport is disabled (in |
54 |
return
|
55 |
||
1827
by Martin Pitt
Python 3 compatible "print" (not everything yet) |
56 |
try: |
57 |
from cStringIO import StringIO |
|
2337
by Martin Pitt
* Clean up module imports. * test/run: Run pyflakes, if available. |
58 |
StringIO # pyflakes |
1827
by Martin Pitt
Python 3 compatible "print" (not everything yet) |
59 |
except ImportError: |
60 |
from io import StringIO |
|
61 |
||
2134.2.22
by Evan Dandrea
Unused imports. |
62 |
import re, traceback |
2015
by Martin Pitt
apport_python_hook: Limit successive crashes per program and user to 3 per day, just like signal crashes. (LP: #603503) |
63 |
from apport.fileutils import likely_packaged, get_recent_crashes |
359
by martin at piware
* Add apport/python_hook.py: Default exception handler for Python, to create |
64 |
|
65 |
# apport will look up the package from the executable path.
|
|
1083
by Martin Pitt
* apport_python_hook.py: If the Python script has mutilated sys.argv so that |
66 |
try: |
2314
by Martin Pitt
apport_python_hook.py: Fix for Python 3 |
67 |
binary = os.path.realpath(os.path.join(os.getcwd(), sys.argv[0])) |
1087
by Martin Pitt
* apport_python_hook.py: Catch IndexError for invalid sys.argv[0], too. |
68 |
except (TypeError, AttributeError, IndexError): |
1083
by Martin Pitt
* apport_python_hook.py: If the Python script has mutilated sys.argv so that |
69 |
# the module has mutated sys.argv, plan B
|
70 |
try: |
|
71 |
binary = os.readlink('/proc/%i/exe' % os.getpid()) |
|
72 |
except OSError: |
|
73 |
return
|
|
802
by Martin Pitt
* apport_python_hook.py: Move the apport.* imports into the try: block and |
74 |
|
687
by Martin Pitt
* Replace tabs with spaces in all Python files. (LP: #93561) |
75 |
# for interactive python sessions, sys.argv[0] == ''; catch that and
|
76 |
# other irregularities
|
|
77 |
if not os.access(binary, os.X_OK) or not os.path.isfile(binary): |
|
78 |
return
|
|
802
by Martin Pitt
* apport_python_hook.py: Move the apport.* imports into the try: block and |
79 |
|
80 |
# filter out binaries in user accessible paths
|
|
81 |
if not likely_packaged(binary): |
|
82 |
return
|
|
83 |
||
84 |
import apport.report |
|
85 |
||
86 |
pr = apport.report.Report() |
|
2430
by Martin Pitt
* apport_python_hook.py: For org.freedesktop.DBus.Error.ServiceUnknown exceptions, add a 'DbusErrorAnalysis' field to the report which points out whether any .service file provides the service it tried to talk to, and whether the processes for those are running. This helps to determine the root cause for such errors (missing dependencies, broken .service files, talking to the wrong bus, etc.) (LP: #1020572) |
87 |
|
88 |
# special handling of dbus-python exceptions
|
|
89 |
if hasattr(exc_obj, 'get_dbus_name'): |
|
90 |
if exc_obj.get_dbus_name() == 'org.freedesktop.DBus.Error.NoReply': |
|
91 |
# NoReply is an useless crash, we do not even get the method it
|
|
92 |
# was trying to call; needs actual crash from D-BUS backend (LP #914220)
|
|
93 |
return
|
|
94 |
if exc_obj.get_dbus_name() == 'org.freedesktop.DBus.Error.ServiceUnknown': |
|
95 |
dbus_service_unknown_analysis(exc_obj, pr) |
|
96 |
||
359
by martin at piware
* Add apport/python_hook.py: Default exception handler for Python, to create |
97 |
# append a basic traceback. In future we may want to include
|
98 |
# additional data such as the local variables, loaded modules etc.
|
|
99 |
tb_file = StringIO() |
|
100 |
traceback.print_exception(exc_type, exc_obj, exc_tb, file=tb_file) |
|
101 |
pr['Traceback'] = tb_file.getvalue().strip() |
|
2494.1.1
by Brian Murray
include pythonhome and pythonpath in procenviron for python crashes |
102 |
pr.add_proc_info(extraenv=['PYTHONPATH', 'PYTHONHOME']) |
1009
by martin at piware
* apport_python_hook.py: Add user info, too. Also add check for this to the |
103 |
pr.add_user_info() |
2277
by Martin Pitt
* Fix the whole code to be PEP-8 compatible, and enforce this in test/run by running the "pep8" tool. |
104 |
# override the ExecutablePath with the script that was actually running
|
359
by martin at piware
* Add apport/python_hook.py: Default exception handler for Python, to create |
105 |
pr['ExecutablePath'] = binary |
2570
by Martin Pitt
* apport_python_hook.py: Update "ExecutableTimestamp" field when mangling "ExecutablePath". (LP: #1077253) |
106 |
if 'ExecutableTimestamp' in pr: |
107 |
pr['ExecutableTimestamp'] = str(int(os.stat(binary).st_mtime)) |
|
1546
by Martin Pitt
apport_python_hook.py: Protect against nonexisting sys.argv. (LP: #418051) |
108 |
try: |
109 |
pr['PythonArgs'] = '%r' % sys.argv |
|
110 |
except AttributeError: |
|
111 |
pass
|
|
538
by Martin Pitt
* apport/python_hook: Do not create a report if the binary is ignored. Add |
112 |
if pr.check_ignored(): |
113 |
return
|
|
359
by martin at piware
* Add apport/python_hook.py: Default exception handler for Python, to create |
114 |
mangled_program = re.sub('/', '_', binary) |
115 |
# get the uid for now, user name later
|
|
116 |
user = os.getuid() |
|
2407
by Martin Pitt
* Fix PEP-8 violations picked up by latest pep8 checker. |
117 |
pr_filename = '%s/%s.%i.crash' % (os.environ.get( |
118 |
'APPORT_REPORT_DIR', '/var/crash'), mangled_program, user) |
|
2015
by Martin Pitt
apport_python_hook: Limit successive crashes per program and user to 3 per day, just like signal crashes. (LP: #603503) |
119 |
crash_counter = 0 |
1320
by Martin Pitt
apport_python_hook.py: Fix crash for already existing reports, and make |
120 |
if os.path.exists(pr_filename): |
121 |
if apport.fileutils.seen_report(pr_filename): |
|
2015
by Martin Pitt
apport_python_hook: Limit successive crashes per program and user to 3 per day, just like signal crashes. (LP: #603503) |
122 |
# flood protection
|
2302
by Martin Pitt
problem_report.py: Fix for Python 3 |
123 |
with open(pr_filename, 'rb') as f: |
124 |
crash_counter = get_recent_crashes(f) + 1 |
|
2015
by Martin Pitt
apport_python_hook: Limit successive crashes per program and user to 3 per day, just like signal crashes. (LP: #603503) |
125 |
if crash_counter > 1: |
126 |
return
|
|
127 |
||
1320
by Martin Pitt
apport_python_hook.py: Fix crash for already existing reports, and make |
128 |
# remove the old file, so that we can create the new one with
|
129 |
# os.O_CREAT|os.O_EXCL
|
|
130 |
os.unlink(pr_filename) |
|
131 |
else: |
|
132 |
# don't clobber existing report
|
|
133 |
return
|
|
2015
by Martin Pitt
apport_python_hook: Limit successive crashes per program and user to 3 per day, just like signal crashes. (LP: #603503) |
134 |
|
135 |
if crash_counter: |
|
136 |
pr['CrashCounter'] = str(crash_counter) |
|
2302
by Martin Pitt
problem_report.py: Fix for Python 3 |
137 |
with os.fdopen(os.open(pr_filename, |
2407
by Martin Pitt
* Fix PEP-8 violations picked up by latest pep8 checker. |
138 |
os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o640), 'wb') as f: |
2302
by Martin Pitt
problem_report.py: Fix for Python 3 |
139 |
pr.write(f) |
359
by martin at piware
* Add apport/python_hook.py: Default exception handler for Python, to create |
140 |
|
141 |
finally: |
|
1096.1.2
by Daniel Hahler
Fix uncaught exceptions in apport itself (LP: #215929): |
142 |
# resume original processing to get the default behaviour,
|
143 |
# but do not trigger an AttributeError on interpreter shutdown.
|
|
144 |
if sys: |
|
145 |
sys.__excepthook__(exc_type, exc_obj, exc_tb) |
|
359
by martin at piware
* Add apport/python_hook.py: Default exception handler for Python, to create |
146 |
|
147 |
||
2430
by Martin Pitt
* apport_python_hook.py: For org.freedesktop.DBus.Error.ServiceUnknown exceptions, add a 'DbusErrorAnalysis' field to the report which points out whether any .service file provides the service it tried to talk to, and whether the processes for those are running. This helps to determine the root cause for such errors (missing dependencies, broken .service files, talking to the wrong bus, etc.) (LP: #1020572) |
148 |
def dbus_service_unknown_analysis(exc_obj, report): |
2787
by Martin Pitt
* Delay the import of the glob and re modules in the python apport hook, and only import them when needed. Speeds up interpreter startup time by 50%. Thanks Matthias Klose! (LP: #1307684) |
149 |
from glob import glob |
150 |
import subprocess, re |
|
2430
by Martin Pitt
* apport_python_hook.py: For org.freedesktop.DBus.Error.ServiceUnknown exceptions, add a 'DbusErrorAnalysis' field to the report which points out whether any .service file provides the service it tried to talk to, and whether the processes for those are running. This helps to determine the root cause for such errors (missing dependencies, broken .service files, talking to the wrong bus, etc.) (LP: #1020572) |
151 |
try: |
152 |
from configparser import ConfigParser, NoSectionError, NoOptionError |
|
153 |
(ConfigParser, NoSectionError, NoOptionError) # pyflakes |
|
154 |
except ImportError: |
|
155 |
# Python 2
|
|
156 |
from ConfigParser import ConfigParser, NoSectionError, NoOptionError |
|
157 |
||
158 |
# determine D-BUS name
|
|
159 |
m = re.search('name\s+(\S+)\s+was not provided by any .service', |
|
160 |
exc_obj.get_dbus_message()) |
|
161 |
if not m: |
|
162 |
if sys.stderr: |
|
163 |
sys.stderr.write('Error: cannot parse D-BUS name from exception: ' |
|
164 |
+ exc_obj.get_dbus_message()) |
|
165 |
return
|
|
166 |
||
167 |
dbus_name = m.group(1) |
|
168 |
||
169 |
# determine .service file and Exec name for the D-BUS name
|
|
170 |
services = [] # tuples of (service file, exe name, running) |
|
171 |
for f in glob('/usr/share/dbus-1/*services/*.service'): |
|
172 |
cp = ConfigParser(interpolation=None) |
|
173 |
cp.read(f, encoding='UTF-8') |
|
174 |
try: |
|
175 |
if cp.get('D-BUS Service', 'Name') == dbus_name: |
|
176 |
exe = cp.get('D-BUS Service', 'Exec') |
|
177 |
running = (subprocess.call(['pidof', '-sx', exe], stdout=subprocess.PIPE) == 0) |
|
178 |
services.append((f, exe, running)) |
|
179 |
except (NoSectionError, NoOptionError): |
|
180 |
if sys.stderr: |
|
181 |
sys.stderr.write('Invalid D-BUS .service file %s: %s' % ( |
|
182 |
f, exc_obj.get_dbus_message())) |
|
183 |
continue
|
|
184 |
||
185 |
if not services: |
|
186 |
report['DbusErrorAnalysis'] = 'no service file providing ' + dbus_name |
|
187 |
else: |
|
188 |
report['DbusErrorAnalysis'] = 'provided by' |
|
189 |
for (service, exe, running) in services: |
|
190 |
report['DbusErrorAnalysis'] += ' %s (%s is %srunning)' % ( |
|
191 |
service, exe, ('' if running else 'not ')) |
|
192 |
||
193 |
||
359
by martin at piware
* Add apport/python_hook.py: Default exception handler for Python, to create |
194 |
def install(): |
195 |
'''Install the python apport hook.'''
|
|
196 |
||
197 |
sys.excepthook = apport_excepthook |