← Back to team overview

apport-hackers team mailing list archive

[Merge] lp:~ev/apport/handle-thread-crashes into lp:apport

 

Evan Dandrea has proposed merging lp:~ev/apport/handle-thread-crashes into lp:apport.

Requested reviews:
  Apport upstream developers (apport-hackers)
Related bugs:
  Bug #1033902 in apport (Ubuntu): "Application thread crash shows application crash error alert"
  https://bugs.launchpad.net/ubuntu/+source/apport/+bug/1033902

For more details, see:
https://code.launchpad.net/~ev/apport/handle-thread-crashes/+merge/135911

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.
-- 
https://code.launchpad.net/~ev/apport/handle-thread-crashes/+merge/135911
Your team Apport upstream developers is requested to review the proposed merge of lp:~ev/apport/handle-thread-crashes into lp:apport.
=== modified file 'apport/ui.py'
--- apport/ui.py	2012-11-06 15:00:35 +0000
+++ apport/ui.py	2012-11-23 14:31:22 +0000
@@ -15,7 +15,7 @@
 
 __version__ = '2.6.2'
 
-import glob, sys, os.path, optparse, traceback, locale, gettext
+import glob, sys, os.path, optparse, traceback, locale, gettext, re
 import errno, zlib
 import subprocess, threading, webbrowser
 import signal
@@ -45,6 +45,23 @@
 PF_KTHREAD = 0x200000
 
 
+def get_pid(report):
+    try:
+        pid = re.search('Pid:\t(.*)\n', report.get('ProcStatus', '')).group(1)
+        return int(pid)
+    except (IndexError, AttributeError):
+        return None
+
+
+def still_running(pid):
+    try:
+        os.kill(int(pid), 0)
+    except OSError as e:
+        if e.errno == errno.ESRCH:
+            return False
+    return True
+
+
 def thread_collect_info(report, reportfile, package, ui, symptom_script=None,
                         ignore_uninstalled=False):
     '''Collect information about report.

=== modified file 'gtk/apport-gtk'
--- gtk/apport-gtk	2012-08-31 09:05:45 +0000
+++ gtk/apport-gtk	2012-11-23 14:31:22 +0000
@@ -11,7 +11,7 @@
 # option) any later version.  See http://www.gnu.org/copyleft/gpl.html for
 # the full text of the license.
 
-import os.path, sys, subprocess, os, re
+import os.path, sys, subprocess, os, re, errno
 
 from gi.repository import GObject, GLib, Wnck, GdkX11, Gdk
 try:
@@ -273,13 +273,15 @@
                 self.w('title_label').set_label('<big><b>%s</b></big>' % t)
                 self.w('subtitle_label').hide()
 
-                if 'ProcCmdline' in self.report:
+                pid = apport.ui.get_pid(self.report)
+                still_running = pid and apport.ui.still_running(pid)
+                if 'ProcCmdline' not in self.report or still_running:
+                    self.w('closed_button').hide()
+                    self.w('continue_button').set_label(_('Continue'))
+                else:
                     self.w('closed_button').show()
                     self.w('closed_button').set_label(_('Leave Closed'))
                     self.w('continue_button').set_label(_('Relaunch'))
-                else:
-                    self.w('closed_button').hide()
-                    self.w('continue_button').set_label(_('Continue'))
             else:
                 icon = 'distributor-logo'
                 if report_type == 'RecoverableProblem':

=== modified file 'kde/apport-kde'
--- kde/apport-kde	2012-10-11 05:47:44 +0000
+++ kde/apport-kde	2012-11-23 14:31:22 +0000
@@ -202,13 +202,16 @@
                                            'unexpectedly.') %
                                            desktop_info['name'])
                 self.text.hide()
-                if 'ProcCmdline' in report:
+
+                pid = apport.ui.get_pid(report)
+                still_running = pid and apport.ui.still_running(pid)
+                if 'ProcCmdline' not in report or still_running:
+                    self.closed_button.hide()
+                    self.continue_button.setText(_('Continue'))
+                else:
                     self.closed_button.show()
                     self.closed_button.setText(_('Leave Closed'))
                     self.continue_button.setText(_('Relaunch'))
-                else:
-                    self.closed_button.hide()
-                    self.continue_button.setText(_('Continue'))
             else:
                 icon = 'distributor-logo'
                 self.heading.setText(_('Sorry, %s has experienced an '

=== modified file 'test/test_ui_gtk.py'
--- test/test_ui_gtk.py	2012-08-31 09:05:45 +0000
+++ test/test_ui_gtk.py	2012-11-23 14:31:22 +0000
@@ -147,6 +147,15 @@
         self.assertEqual(self.app.w('subtitle_label').get_text(),
                          _('Package: apport 1.2.3~0ubuntu1'))
 
+    def test_regular_crash_thread_layout(self):
+        '''A thread of execution has failed, but the application persists.'''
+        self.app.report['ProblemType'] = 'Crash'
+        self.app.report['ProcStatus'] = 'Name:\tupstart\nPid:\t1'
+        GLib.idle_add(Gtk.main_quit)
+        self.app.ui_present_report_details(True)
+        self.assertFalse(self.app.w('closed_button').get_property('visible'))
+        self.assertEqual(self.app.w('continue_button').get_label(), _('Continue'))
+
     def test_regular_crash_layout(self):
         '''
         +-----------------------------------------------------------------+

=== modified file 'test/test_ui_kde.py'
--- test/test_ui_kde.py	2012-08-31 09:05:45 +0000
+++ test/test_ui_kde.py	2012-11-23 14:31:22 +0000
@@ -139,6 +139,15 @@
         self.assertEqual(self.app.dialog.text.text(),
                          _('Package: apport 1.2.3~0ubuntu1'))
 
+    def test_regular_crash_thread_layout(self):
+        '''A thread of execution has failed, but the application persists.'''
+        self.app.report['ProblemType'] = 'Crash'
+        self.app.report['ProcStatus'] = 'Name:\tupstart\nPid:\t1'
+        QTimer.singleShot(0, QCoreApplication.quit)
+        self.app.ui_present_report_details(True)
+        self.assertFalse(self.app.dialog.closed_button.isVisible())
+        self.assertEqual(self.app.dialog.continue_button.text(), _('Continue'))
+
     def test_regular_crash_layout(self):
         '''
         +-----------------------------------------------------------------+