Merge lp:~ev/apport/handle-thread-crashes into lp:~apport-hackers/apport/trunk

Proposed by Evan
Status: Merged
Merged at revision: 2538
Proposed branch: lp:~ev/apport/handle-thread-crashes
Merge into: lp:~apport-hackers/apport/trunk
Diff against target: 134 lines (+50/-10)
5 files modified
apport/ui.py (+18/-1)
gtk/apport-gtk (+7/-5)
kde/apport-kde (+7/-4)
test/test_ui_gtk.py (+9/-0)
test/test_ui_kde.py (+9/-0)
To merge this branch: bzr merge lp:~ev/apport/handle-thread-crashes
Reviewer Review Type Date Requested Status
Martin Pitt (community) Approve
Review via email: mp+135911@code.launchpad.net

Description of the change

As mentioned in bug 1033902 and in the specification <https://wiki.ubuntu.com/ErrorTracker#thread>, we should not show the Relaunch button when an application thread crashes.

I've implemented this as a check for the process still existing, as multiple threads will share the same PID. I do not believe this will be racy - a dying PID should be gone by the time apport-{gtk,kde} is run. However, I'm happy to be told otherwise and sent back to the drawing board.

I've implemented this in both the GTK and KDE frontends and added a test that presumes we have PID 1 to fake a crash around.

To post a comment you must log in.
Revision history for this message
Martin Pitt (pitti) wrote :

> a dying PID should be gone by the time apport-{gtk,kde} is run.

This is correct. The kernel core pipeline handler relinquishes stdin as soon as possible, i. e. stdin will be closed (and thus the process will die) even before /usr/share/apport/apport finishes.

Thanks for fixing this, this looks great!

When merging, please remember to add a NEWS entry.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'apport/ui.py'
2--- apport/ui.py 2012-11-06 15:00:35 +0000
3+++ apport/ui.py 2012-11-23 14:31:22 +0000
4@@ -15,7 +15,7 @@
5
6 __version__ = '2.6.2'
7
8-import glob, sys, os.path, optparse, traceback, locale, gettext
9+import glob, sys, os.path, optparse, traceback, locale, gettext, re
10 import errno, zlib
11 import subprocess, threading, webbrowser
12 import signal
13@@ -45,6 +45,23 @@
14 PF_KTHREAD = 0x200000
15
16
17+def get_pid(report):
18+ try:
19+ pid = re.search('Pid:\t(.*)\n', report.get('ProcStatus', '')).group(1)
20+ return int(pid)
21+ except (IndexError, AttributeError):
22+ return None
23+
24+
25+def still_running(pid):
26+ try:
27+ os.kill(int(pid), 0)
28+ except OSError as e:
29+ if e.errno == errno.ESRCH:
30+ return False
31+ return True
32+
33+
34 def thread_collect_info(report, reportfile, package, ui, symptom_script=None,
35 ignore_uninstalled=False):
36 '''Collect information about report.
37
38=== modified file 'gtk/apport-gtk'
39--- gtk/apport-gtk 2012-08-31 09:05:45 +0000
40+++ gtk/apport-gtk 2012-11-23 14:31:22 +0000
41@@ -11,7 +11,7 @@
42 # option) any later version. See http://www.gnu.org/copyleft/gpl.html for
43 # the full text of the license.
44
45-import os.path, sys, subprocess, os, re
46+import os.path, sys, subprocess, os, re, errno
47
48 from gi.repository import GObject, GLib, Wnck, GdkX11, Gdk
49 try:
50@@ -273,13 +273,15 @@
51 self.w('title_label').set_label('<big><b>%s</b></big>' % t)
52 self.w('subtitle_label').hide()
53
54- if 'ProcCmdline' in self.report:
55+ pid = apport.ui.get_pid(self.report)
56+ still_running = pid and apport.ui.still_running(pid)
57+ if 'ProcCmdline' not in self.report or still_running:
58+ self.w('closed_button').hide()
59+ self.w('continue_button').set_label(_('Continue'))
60+ else:
61 self.w('closed_button').show()
62 self.w('closed_button').set_label(_('Leave Closed'))
63 self.w('continue_button').set_label(_('Relaunch'))
64- else:
65- self.w('closed_button').hide()
66- self.w('continue_button').set_label(_('Continue'))
67 else:
68 icon = 'distributor-logo'
69 if report_type == 'RecoverableProblem':
70
71=== modified file 'kde/apport-kde'
72--- kde/apport-kde 2012-10-11 05:47:44 +0000
73+++ kde/apport-kde 2012-11-23 14:31:22 +0000
74@@ -202,13 +202,16 @@
75 'unexpectedly.') %
76 desktop_info['name'])
77 self.text.hide()
78- if 'ProcCmdline' in report:
79+
80+ pid = apport.ui.get_pid(report)
81+ still_running = pid and apport.ui.still_running(pid)
82+ if 'ProcCmdline' not in report or still_running:
83+ self.closed_button.hide()
84+ self.continue_button.setText(_('Continue'))
85+ else:
86 self.closed_button.show()
87 self.closed_button.setText(_('Leave Closed'))
88 self.continue_button.setText(_('Relaunch'))
89- else:
90- self.closed_button.hide()
91- self.continue_button.setText(_('Continue'))
92 else:
93 icon = 'distributor-logo'
94 self.heading.setText(_('Sorry, %s has experienced an '
95
96=== modified file 'test/test_ui_gtk.py'
97--- test/test_ui_gtk.py 2012-08-31 09:05:45 +0000
98+++ test/test_ui_gtk.py 2012-11-23 14:31:22 +0000
99@@ -147,6 +147,15 @@
100 self.assertEqual(self.app.w('subtitle_label').get_text(),
101 _('Package: apport 1.2.3~0ubuntu1'))
102
103+ def test_regular_crash_thread_layout(self):
104+ '''A thread of execution has failed, but the application persists.'''
105+ self.app.report['ProblemType'] = 'Crash'
106+ self.app.report['ProcStatus'] = 'Name:\tupstart\nPid:\t1'
107+ GLib.idle_add(Gtk.main_quit)
108+ self.app.ui_present_report_details(True)
109+ self.assertFalse(self.app.w('closed_button').get_property('visible'))
110+ self.assertEqual(self.app.w('continue_button').get_label(), _('Continue'))
111+
112 def test_regular_crash_layout(self):
113 '''
114 +-----------------------------------------------------------------+
115
116=== modified file 'test/test_ui_kde.py'
117--- test/test_ui_kde.py 2012-08-31 09:05:45 +0000
118+++ test/test_ui_kde.py 2012-11-23 14:31:22 +0000
119@@ -139,6 +139,15 @@
120 self.assertEqual(self.app.dialog.text.text(),
121 _('Package: apport 1.2.3~0ubuntu1'))
122
123+ def test_regular_crash_thread_layout(self):
124+ '''A thread of execution has failed, but the application persists.'''
125+ self.app.report['ProblemType'] = 'Crash'
126+ self.app.report['ProcStatus'] = 'Name:\tupstart\nPid:\t1'
127+ QTimer.singleShot(0, QCoreApplication.quit)
128+ self.app.ui_present_report_details(True)
129+ self.assertFalse(self.app.dialog.closed_button.isVisible())
130+ self.assertEqual(self.app.dialog.continue_button.text(), _('Continue'))
131+
132 def test_regular_crash_layout(self):
133 '''
134 +-----------------------------------------------------------------+

Subscribers

People subscribed via source and target branches