Package qubx :: Module crash_rep
[hide private]
[frames] | no frames]

Source Code for Module qubx.crash_rep

  1  """ 
  2  Crash detection and reporting 
  3   
  4  Copyright 2012 Research Foundation State University of New York  
  5  This file is part of QUB Express.                                           
  6   
  7  QUB Express is free software; you can redistribute it and/or modify           
  8  it under the terms of the GNU General Public License as published by  
  9  the Free Software Foundation, either version 3 of the License, or     
 10  (at your option) any later version.                                   
 11   
 12  QUB Express is distributed in the hope that it will be useful,                
 13  but WITHOUT ANY WARRANTY; without even the implied warranty of        
 14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         
 15  GNU General Public License for more details.                          
 16   
 17  You should have received a copy of the GNU General Public License,    
 18  named LICENSE.txt, in the QUB Express program directory.  If not, see         
 19  <http://www.gnu.org/licenses/>.                                       
 20   
 21  """ 
 22   
 23  import cStringIO 
 24  import datetime 
 25  import errno 
 26  import gobject 
 27  import gtk 
 28  import os 
 29  import platform 
 30  import re 
 31  import shutil 
 32  import sys 
 33  import traceback 
 34   
 35  import qubx.data_types 
 36  import qubx.global_namespace 
 37  import qubx.GTK 
 38  import qubx.pyenv 
 39  import qubx.settings 
 40  import qubx.task 
 41  import qubx.util_types 
 42   
 43  from qubx.http_post import * 
 44   
 45  MAX_CRASHREP_DATAPOINTS = 20000 # ~ 80 KiB per signal 
 46   
 47  PIDFILE = 'pid.txt' 
 48  State = qubx.util_types.Anon() 
 49   
50 -def LogScriptable(expr):
51 qubx.pyenv.env.log.write('>>> %s\n' % expr)
52
53 -def Init():
54 qubx.pyenv.env.OnScriptable += LogScriptable 55 pid_path = State.pid_path = os.path.join(qubx.pyenv.env.folder, PIDFILE) 56 if os.path.exists(pid_path): 57 try: 58 pid = int(open(pid_path, 'r').read()) 59 if pid_exists(pid): 60 State.other_qubx = True 61 else: 62 log_path = os.path.join(qubx.pyenv.env.folder, 'output.log.0') 63 if os.path.exists(log_path): 64 offer_report_crash("It looks like the program may have crashed last time. Send a bug report?", log_path) 65 except: # assume stale/broken/whatever 66 traceback.print_exc() 67 pass 68 if not State.other_qubx: 69 open(pid_path, 'w').write(str(os.getpid())) 70 71 # report unhandled exceptions: 72 old_exc_hook = sys.excepthook 73 def exc_hook(typ, val, tb): 74 old_exc_hook(typ, val, tb) 75 if typ not in (KeyboardInterrupt, SystemExit): 76 offer_report_crash()
77 sys.excepthook = exc_hook 78 79 # clean out stale crash report data 80 for base, dirs, files in os.walk(qubx.pyenv.env.folder): 81 dirs[:] = [] 82 for fname in files: 83 if 'crash_rep_' in fname: 84 os.remove(os.path.join(base, fname)) 85
86 -def Fini():
87 if not State.other_qubx: 88 try: 89 os.remove(State.pid_path) 90 except: 91 pass
92 93
94 -def pid_exists(pid):
95 """Check whether pid exists in the current process table.""" 96 if pid < 0: 97 return False 98 try: 99 os.kill(pid, 0) 100 except OSError, e: 101 return e.errno == errno.EPERM 102 except: # windows 103 return is_pid_running_on_windows(pid) 104 else: 105 return True
106
107 -def is_pid_running_on_windows(pid):
108 import ctypes.wintypes 109 110 kernel32 = ctypes.windll.kernel32 111 handle = kernel32.OpenProcess(1, 0, pid) 112 if handle == 0: 113 return False 114 115 # If the process exited recently, a pid may still exist for the handle. 116 # So, check if we can get the exit code. 117 exit_code = ctypes.wintypes.DWORD() 118 is_running = (kernel32.GetExitCodeProcess(handle, ctypes.byref(exit_code)) == 0) 119 kernel32.CloseHandle(handle) 120 121 # See if we couldn't get the exit code or the exit code indicates that the 122 # process is still running. 123 return is_running or exit_code.value == _STILL_ACTIVE
124 125
126 -def offer_report_crash(message="Error detected. Send a bug report?", logpath=""):
127 sys.stdout.flush() 128 log_path = logpath or os.path.join(qubx.pyenv.env.folder, "output.log") 129 try: 130 logtext = open(log_path, 'r').read() 131 except: 132 logtext = traceback.format_exc() 133 settings = qubx.settings.SettingsMgr['qubx.crash_rep'].active 134 dlg = gtk.Dialog('Bug report', None, gtk.DIALOG_MODAL, buttons=None) 135 dlg.set_size_request(600, 400) 136 qubx.GTK.pack_label("", dlg.get_content_area()) 137 h = qubx.GTK.pack_item(gtk.HBox(), dlg.get_content_area()) 138 qubx.GTK.pack_label(message, h) 139 if qubx.global_namespace.QubX.has_modeling: 140 chkModel = qubx.GTK.pack_check('attach model', dlg.get_content_area(), active=True) 141 chkData = qubx.GTK.pack_check('attach data', dlg.get_content_area(), active=True) 142 h = qubx.GTK.pack_item(gtk.HBox(), dlg.get_content_area()) 143 qubx.GTK.pack_label('Report contents:', h) 144 txtv = gtk.TextView() 145 qubx.GTK.pack_scrolled(txtv, dlg.get_content_area(), expand=True) 146 txtv.get_buffer().set_text("""Any comments about the problem and how to reproduce it? 147 148 (type here) 149 150 151 -------> Run log <--------------------------------------------------- 152 %s 153 154 %s 155 """ % (logtext, qubx.global_namespace.QubX.has_modeling and qubx.global_namespace.QubX.Models.file or "")) 156 h = qubx.GTK.pack_item(gtk.HBox(), dlg.get_content_area()) 157 qubx.GTK.pack_label('Your info (optional):', h) 158 h = qubx.GTK.pack_item(gtk.HBox(), dlg.get_content_area()) 159 qubx.GTK.pack_label('Name:', h) 160 txtName = qubx.GTK.pack_item(qubx.GTK.NumEntry(str(settings['name'].data)), h, expand=True) 161 qubx.GTK.pack_label('Email:', h) 162 txtEmail = qubx.GTK.pack_item(qubx.GTK.NumEntry(str(settings['email'].data)), h, expand=True) 163 qubx.GTK.pack_button('Cancel', dlg.action_area, lambda item: dlg.response(gtk.RESPONSE_REJECT), at_end=True) 164 qubx.GTK.pack_button('Send', dlg.action_area, lambda item: dlg.response(gtk.RESPONSE_ACCEPT), at_end=True) 165 response = dlg.run() 166 buf = txtv.get_buffer() 167 logtext = buf.get_text(buf.get_start_iter(), buf.get_end_iter()) 168 dlg.destroy() 169 if response == gtk.RESPONSE_ACCEPT: 170 settings['name'].data = txtName.value 171 settings['email'].data = txtEmail.value 172 report_crash(txtName.value, txtEmail.value, log_path, logtext, qubx.global_namespace.QubX.has_modeling and chkModel.get_active(), chkData.get_active())
173 174
175 -def report_crash(name="", email="", logpath="", logtext="", attach_model=True, attach_data=True):
176 task = CrashReportTask(name or "Anonymous", ('@' in email) and email or 'qub@www.qub.buffalo.edu', logpath, logtext, attach_model, attach_data) 177 task.start()
178
179 -class CrashReportTask(qubx.task.Task):
180 - def __init__(self, name, email, logpath, logtext, attach_model, attach_data):
181 qubx.task.Task.__init__(self, 'Bug Report') 182 self.name = name 183 self.email = email 184 self.logpath = logpath 185 self.logtext = logtext 186 self.attach_model = attach_model 187 self.attach_data = attach_data 188 try: 189 self.setup_model() 190 except: 191 self.attach_model = False 192 traceback.print_exc() 193 try: 194 self.setup_data() 195 except: 196 self.attach_data = False 197 traceback.print_exc() 198 self.status = 'Sending...' 199 self.__ref_onException = self.__onException 200 self.OnException += self.__ref_onException
201 - def __onException(self, task, typ, val, tb):
202 if typ != KeyboardInterrupt: 203 traceback.print_exception(typ, val, tb)
204 - def setup_model(self):
205 QubX = qubx.global_namespace.QubX 206 if self.attach_model and QubX.Models.file: 207 self.model_name = os.path.split(QubX.Models.file.path)[1] or 'Untitled.qmf' 208 self.model = QubX.Models.file.as_tree().getBytes()
209 - def setup_data(self):
210 QubX = qubx.global_namespace.QubX 211 if not (self.attach_data and QubX.Data.file): 212 return 213 self.data_name = os.path.split(QubX.Data.file.path)[1] or 'Untitled.qdf' 214 if self.data_name == '<simulation>': 215 self.data_name = 'simulation.qdf' 216 self.data = QubX.Data.file 217 self.data_segs = QubX.Data.view.get_segmentation_screen() 218 if len(self.data_segs) == 0: 219 self.attach_data = False 220 return 221 seg = self.data_segs[0] 222 self.data_segs = [seg] 223 if seg.n > MAX_CRASHREP_DATAPOINTS: 224 seg.l = seg.f + MAX_CRASHREP_DATAPOINTS - 1 225 seg.n = MAX_CRASHREP_DATAPOINTS 226 seg.chunks = [chunk for chunk in seg.chunks if chunk.f <= seg.l] 227 if seg.chunks and (seg.chunks[-1].l > seg.l): 228 seg.chunks[-1].l = seg.l 229 seg.chunks[-1].n = seg.l - seg.chunks[-1].f + 1 230 self.segs_of_signal = [qubx.data_types.get_segmentation_copy(self.data_segs, signal=i) for i in xrange(QubX.Data.file.signals.size)] 231 self.data_path = os.path.join(qubx.pyenv.env.folder, 'crash_rep_%s.qdf' % datetime.datetime.now().strftime('%Y%m%d_%H%M%S')) 232 self.session_name = os.path.splitext(self.data_name)[0]+'.qsf' 233 self.session_path = os.path.splitext(self.data_path)[0]+'.qsf' 234 qubx.data_types.Save(self.data_path, qubx.tree.Node(''), self.segs_of_signal, None, lambda pct: True, 235 QubX.Data.file.constants, QubX.Data.file.signals, 236 QubX.Data.file.stimuli, QubX.Data.view.signals)
237 - def run(self):
238 qubx.task.Tasks.add_task(self) 239 try: 240 profile_log = cStringIO.StringIO() 241 for cat_name in sorted(qubx.settings.SettingsMgr.cats.keys()): 242 profile_log.write("\n%s:--------------------\n" % cat_name) 243 profile_log.write(str(qubx.settings.SettingsMgr[cat_name].active)) 244 files = [('runlog', self.logpath, self.logtext), 245 ('profile_log', 'profile_log.txt', profile_log.getvalue())] 246 if self.attach_model: 247 files.append(('model', self.model_name, self.model)) 248 if self.attach_data: 249 files.append(('data', self.data_name, open(self.data_path, 'rb').read())) 250 files.append(('session', self.session_name, open(self.session_path, 'rb').read())) 251 252 post_multipart('www.qub.buffalo.edu', '/bugreport.cgi', 253 [('name', self.name), ('email', self.email)], 254 files) 255 except: 256 traceback.print_exc() 257 try: 258 if self.data_path: 259 os.remove(self.data_path) 260 os.remove(self.session_path) 261 except: 262 traceback.print_exc() 263 qubx.task.Tasks.remove_task(self)
264