Package Peach :: Package Agent :: Module debugger
[hide private]

Source Code for Module Peach.Agent.debugger

   1   
   2  ''' 
   3  Debugging monitor for Peach Agent.  Uses pydbgeng to monitor processes and 
   4  detect faults.  Would be nice to also eventually do other things like 
   5  "if we hit this method" or whatever. 
   6   
   7  @author: Michael Eddington 
   8  @version: $Id: Peach.Agent.debugger-pysrc.html 1138 2008-08-16 19:39:03Z meddingt $ 
   9  ''' 
  10   
  11  # 
  12  # Copyright (c) 2007-2008 Michael Eddington 
  13  # 
  14  # Permission is hereby granted, free of charge, to any person obtaining a copy  
  15  # of this software and associated documentation files (the "Software"), to deal 
  16  # in the Software without restriction, including without limitation the rights  
  17  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell  
  18  # copies of the Software, and to permit persons to whom the Software is  
  19  # furnished to do so, subject to the following conditions: 
  20  # 
  21  # The above copyright notice and this permission notice shall be included in     
  22  # all copies or substantial portions of the Software. 
  23  # 
  24  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  
  25  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,  
  26  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE  
  27  # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER  
  28  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
  29  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
  30  # SOFTWARE. 
  31  # 
  32   
  33  # Authors: 
  34  #   Michael Eddington (mike@phed.org) 
  35   
  36  # $Id: Peach.Agent.debugger-pysrc.html 1138 2008-08-16 19:39:03Z meddingt $ 
  37   
  38  ''' 
  39  <!-- Use a windows debugger to monitor for faults --> 
  40  <Monitor class="debugger.WindowsDebugger"> 
  41          <!-- [Optional] Specify a symbol path.  By default microsoft symbols server is used --> 
  42          <Param name="SymbolsPath" value="SRV*http://msdl.microsoft.com/download/symbols" /> 
  43           
  44          <!-- *** Must select only one of the following: CommandLine, ProcessName, or KernelConnectionString *** --> 
  45           
  46          <!-- Command line.  This agent can optionally start the process instead --> 
  47          <!-- of attaching to it. --> 
  48          <Param name="CommandLine" value="c:\fullpath\program.exe" /> 
  49           
  50          <!-- Name of process to attach debugger. --> 
  51          <Param name="ProcessName" value="program.exe" /> 
  52           
  53          <!-- KD connection string.  Kernel debugger connection string. --> 
  54          <Param name="KernelConnectionString" value="com:pipe,port=\\\\.\\pipe\\com_1,resets=0" /> 
  55           
  56  </Monitor> 
  57  ''' 
  58   
  59  ''' 
  60  <!-- Use Microsoft AppVerifier to monitor for faults --> 
  61  <Monitor class="debugger.WindowsAppVerifier"> 
  62   
  63          <!-- Application to monitor --> 
  64          <Param name="Application" value="notepad.exe" /> 
  65           
  66          <!-- Enable one or more checks --> 
  67          <Param name="EnableTest" value="Detect heap corruptions" /> 
  68          <Param name="EnableTest" value="Detect invalid handle usage" /> 
  69  </Monitor> 
  70  ''' 
  71   
  72  try: 
  73          import pydbg, win32process, win32api, win32pdhutil, win32con 
  74          import struct, sys, time 
  75          from threading import Thread, Event, Lock 
  76          from Peach.agent import Monitor 
  77           
  78          import struct, sys, time,os 
  79          import comtypes 
  80          from ctypes import * 
  81          from comtypes import HRESULT, COMError 
  82          from comtypes.client import CreateObject, GetEvents, PumpEvents#, ReleaseEvents 
  83          from comtypes.hresult import S_OK, E_FAIL, E_UNEXPECTED, E_INVALIDARG 
  84          from comtypes.automation import IID 
  85           
86 - class WindowsAppVerifier(Monitor):
87 ''' 88 Agent that uses the Microsoft AppVerifier tool to detect faults on 89 running processes. AppVerifier can be downloaded from Microsoft via 90 the following url: http://www.microsoft.com/technet/prodtechnol/windows/appcompatibility/appverifier.mspx 91 ''' 92 93 checks = ["COM", "Exceptions", "Handles", "Heaps", "Locks", "Memory", "RPC", "Threadpool", "TLS"] 94
95 - def __init__(self, args):
96 self._image = str(args['Application']) 97 self._appManager = CreateObject("{597c1ef7-fc28-451e-8273-417c6c9244ed}") 98 99 try: 100 self._appManager._DisableImage(self._image) 101 self._appManager._RemoveImageLogs(self._image) 102 except: 103 pass
104
105 - def _EnableImage(self, image):
106 ''' 107 Enables the default checks for an image (exe). 108 ''' 109 image = self._appManager.Images.Add(image) 110 for check in image.Checks: 111 if check.Name in self.checks: 112 check.Enabled = True 113 else: 114 check.Enabled = False
115
116 - def _DisableImage(self, image):
117 self._appManager.Images.Remove(image)
118
119 - def _GetImageLogs(self, image):
120 ''' 121 Get lof files for an image 122 ''' 123 124 logFiles = [] 125 for log in self._appManager.Logs(image): 126 127 try: 128 os.unlink("appVerifierTmp.xml") 129 except: 130 pass 131 132 try: 133 log.SaveAsXML("appVerifierTmp.xml", "SRV*http://msdl.microsoft.com/download/symbols") 134 except: 135 pass 136 137 fd = open("appVerifierTmp.xml","rb+") 138 logFiles.append(fd.read()) 139 fd.close() 140 os.unlink("appVerifierTmp.xml") 141 142 return logFiles
143
144 - def _RemoveImageLogs(self, image):
145 logs = self._appManager.Logs(image) 146 for idx in range(logs.Count): 147 try: 148 logs.Remove(0) 149 except: 150 print "Warning: Caught error removing App Verifier logs." 151 pass
152
153 - def OnTestStarting(self):
154 ''' 155 Called right before start of test. 156 ''' 157 self._EnableImage(self._image)
158
159 - def OnTestFinished(self):
160 ''' 161 Called right after a test. 162 ''' 163 self._DisableImage(self._image)
164
165 - def GetMonitorData(self):
166 ''' 167 Get any monitored data. 168 ''' 169 ret = {} 170 count = 0 171 logs = self.imageLogs 172 173 #print "WindowsAppVerifier.GetMonitorData: Waiting for data to come in" 174 #while logs == None or len(logs) < 1: 175 # time.sleep(0.25) 176 # logs = self.imageLogs = self._GetImageLogs(self._image) 177 if logs == None: 178 logs = self.imageLogs = self._GetImageLogs(self._image) 179 180 self._RemoveImageLogs(self._image) 181 182 for log in logs: 183 count += 1 184 ret["AppVerifier_%d.xml" % count] = log 185 186 return ret
187
188 - def DetectedFault(self):
189 ''' 190 Check if a fault was detected. 191 ''' 192 193 time.sleep(0.15) # Pause a sec 194 195 self.imageLogs = self._GetImageLogs(self._image) 196 self._RemoveImageLogs(self._image) 197 198 for log in self.imageLogs: 199 if len(log) > 300: 200 return True 201 202 self.imageLogs = None 203 return False
204
205 - def OnFault(self):
206 ''' 207 Called when a fault was detected. 208 ''' 209 pass
210
211 - def OnShutdown(self):
212 ''' 213 Called when Agent is shutting down. 214 ''' 215 try: 216 self._DisableImage(self._image) 217 except: 218 pass
219 220
221 - def handleAccessViolation(dbg):
222 #print "!!!!!!!!! AV !!!!!!!!!!!!!!!" 223 crash_bin = crash_binning() 224 crash_bin.record_crash(dbg) 225 226 WindowsDebugger.lock.acquire() 227 WindowsDebugger.crashInfo = crash_bin.crash_synopsis() 228 WindowsDebugger.fault = True 229 WindowsDebugger.lock.release() 230 231 dbg.terminate_process()
232
233 - class WindowsDebuggerThread(Thread):
234 - def run(self):
235 print "WindowsDebuggerThread: Starting process" 236 self.dbg = pydbg.pydbg() 237 self.dbg.set_callback(pydbg.EXCEPTION_ACCESS_VIOLATION, handleAccessViolation) 238 239 if self._command != None: 240 self.dbg.load(self._command, self._params) 241 else: 242 self.dbg.attach(self._pid) 243 244 WindowsDebugger.started.set() 245 self.dbg.debug_event_loop()
246
247 - class WindowsDebugger(Monitor, Thread):
248 ''' 249 Windows debugger monitor. Uses PaiMei pydbg module 250 '''
251 - def __init__(self, args):
252 Thread.__init__(self) 253 254 self._stopRun = False 255 WindowsDebugger.started = Event() 256 WindowsDebugger.lock = Lock() 257 WindowsDebugger.crashInfo = None 258 WindowsDebugger.fault = False 259 WindowsDebugger.started.clear() 260 261 if args.has_key('Command'): 262 self._command = str(args['Command']) 263 self._params = str(args['Params']) 264 self._pid = None 265 266 elif args.has_key('ProcessName'): 267 self._command = None 268 self._params = None 269 self._pid = self.GetProcessIdByName(str(args['ProcessName'])) 270 271 else: 272 raise Exception("Unable to create WindowsDebugger! Error in params!") 273 274 self._StartDebugger()
275
276 - def _StartDebugger(self):
277 WindowsDebugger.crashInfo = None 278 WindowsDebugger.fault = False 279 WindowsDebugger.started.clear() 280 281 self.thread = WindowsDebuggerThread() 282 283 self.thread._command = self._command 284 self.thread._params = self._params 285 self.thread._pid = self._pid 286 287 self.thread.start() 288 WindowsDebugger.started.wait()
289
290 - def _StopDebugger(self):
291 if self.thread.isAlive(): 292 try: 293 win32process.TerminateProcess(self.thread.dbg.h_process, 0) 294 except: 295 pass 296 297 self.thread.join()
298
299 - def GetProcessIdByName(self, str):
300 ''' 301 Try and get pid for a process by name. 302 ''' 303 304 try: 305 win32pdhutil.GetPerformanceAttributes('Process','ID Process',str) 306 except: 307 sys.stdout.write("WindowsDebugger: Unable to locate process [%s]\n" % str) 308 raise 309 310 pids = win32pdhutil.FindPerformanceAttributesByName(str) 311 312 # If _my_ pid in there, remove it! 313 try: 314 pids.remove(win32api.GetCurrentProcessId()) 315 except ValueError: 316 pass 317 318 return pids[0]
319
320 - def OnTestStarting(self):
321 ''' 322 Called right before start of test. 323 ''' 324 if not self.thread.isAlive(): 325 self.thread.join() 326 self._StartDebugger()
327
328 - def OnTestFinished(self):
329 ''' 330 Called right after a test. 331 ''' 332 pass
333
334 - def GetMonitorData(self):
335 ''' 336 Get any monitored data. 337 ''' 338 WindowsDebugger.lock.acquire() 339 if WindowsDebugger.crashInfo != None: 340 ret = WindowsDebugger.crashInfo 341 WindowsDebugger.crashInfo = None 342 WindowsDebugger.lock.release() 343 return { 'StackTrace.txt' : ret } 344 345 WindowsDebugger.lock.release() 346 return None
347
348 - def DetectedFault(self):
349 ''' 350 Check if a fault was detected. 351 ''' 352 time.sleep(0.1) 353 WindowsDebugger.lock.acquire() 354 if WindowsDebugger.fault or not self.thread.isAlive(): 355 print ">>>>>> RETURNING FAULT <<<<<<<<<" 356 WindowsDebugger.fault = False 357 WindowsDebugger.lock.release() 358 return True 359 360 WindowsDebugger.lock.release() 361 return False
362
363 - def OnFault(self):
364 ''' 365 Called when a fault was detected. 366 ''' 367 self._StopDebugger() 368 369 # If we are attaching we cannot restart 370 if self._pid != None: 371 self._stopRun = True
372
373 - def OnShutdown(self):
374 ''' 375 Called when Agent is shutting down. 376 ''' 377 self._StopDebugger()
378
379 - def StopRun(self):
380 return self._stopRun
381 382 ## ############################################################################ 383 ## ############################################################################ 384 ## ############################################################################ 385 386 ## 387 ## The following code from here 388 ## is taken verbatum from PaiMei v1.1 rev122 and is licensed 389 ## using the GNU General Public License 2.0 or later 390 ## 391 392 ''' 393 @author: Pedram Amini 394 @license: GNU General Public License 2.0 or later 395 @contact: pedram.amini@gmail.com 396 @organization: www.openrce.org 397 ''' 398
399 - class __crash_bin_struct__:
400 exception_address = 0 401 write_violation = 0 402 violation_address = 0 403 violation_thread_id = 0 404 context = None 405 context_dump = None 406 disasm = None 407 disasm_around = [] 408 stack_unwind = [] 409 seh_unwind = [] 410 extra = None
411 412
413 - class crash_binning:
414 ''' 415 @todo: Add persistant data support (disk / MySQL) 416 ''' 417 418 bins = {} 419 last_crash = None 420 pydbg = None 421 422 ####################################################################################################################
423 - def __init__ (self):
424 ''' 425 ''' 426 427 self.bins = {} 428 self.last_crash = None 429 self.pydbg = None
430 431 432 ####################################################################################################################
433 - def record_crash (self, pydbg, extra=None):
434 ''' 435 Given a PyDbg instantiation that at the current time is assumed to have "crashed" (access violation for example) 436 record various details such as the disassemly around the violating address, the ID of the offending thread, the 437 call stack and the SEH unwind. Store the recorded data in an internal dictionary, binning them by the exception 438 address. 439 440 @type pydbg: pydbg 441 @param pydbg: Instance of pydbg 442 @type extra: Mixed 443 @param extra: (Optional, Def=None) Whatever extra data you want to store with this bin 444 ''' 445 446 self.pydbg = pydbg 447 crash = __crash_bin_struct__() 448 449 crash.exception_address = pydbg.dbg.u.Exception.ExceptionRecord.ExceptionAddress 450 crash.write_violation = pydbg.dbg.u.Exception.ExceptionRecord.ExceptionInformation[0] 451 crash.violation_address = pydbg.dbg.u.Exception.ExceptionRecord.ExceptionInformation[1] 452 crash.violation_thread_id = pydbg.dbg.dwThreadId 453 crash.context = pydbg.context 454 crash.context_dump = pydbg.dump_context(pydbg.context, print_dots=False) 455 crash.disasm = pydbg.disasm(crash.exception_address) 456 crash.disasm_around = pydbg.disasm_around(crash.exception_address) 457 crash.stack_unwind = pydbg.stack_unwind() 458 crash.seh_unwind = pydbg.seh_unwind() 459 crash.extra = extra 460 461 if not self.bins.has_key(crash.exception_address): 462 self.bins[crash.exception_address] = [] 463 464 self.bins[crash.exception_address].append(crash) 465 self.last_crash = crash
466 467 468 ####################################################################################################################
469 - def crash_synopsis (self):
470 ''' 471 For the last recorded crash, generate and return a report containing the disassemly around the violating 472 address, the ID of the offending thread, the call stack and the SEH unwind. 473 ''' 474 475 if self.last_crash.write_violation: 476 direction = "write to" 477 else: 478 direction = "read from" 479 480 synopsis = "0x%08x %s from thread %d caused access violation\nwhen attempting to %s 0x%08x\n\n" % \ 481 ( 482 self.last_crash.exception_address, \ 483 self.last_crash.disasm, \ 484 self.last_crash.violation_thread_id, \ 485 direction, \ 486 self.last_crash.violation_address \ 487 ) 488 489 synopsis += self.last_crash.context_dump 490 491 synopsis += "\ndisasm around:\n" 492 for (ea, inst) in self.last_crash.disasm_around: 493 synopsis += "\t0x%08x %s\n" % (ea, inst) 494 495 if len(self.last_crash.stack_unwind): 496 synopsis += "\nstack unwind:\n" 497 for addr in self.last_crash.stack_unwind: 498 synopsis += "\t%08x\n" % addr 499 500 if len(self.last_crash.seh_unwind): 501 synopsis += "\nSEH unwind:\n" 502 for (addr, handler) in self.last_crash.seh_unwind: 503 try: 504 disasm = self.pydbg.disasm(handler) 505 except: 506 disasm = "[INVALID]" 507 508 synopsis += "\t%08x -> %08x: %s\n" % (addr, handler, disasm) 509 510 return synopsis + "\n"
511 512 except: 513 pass 514 515 try: 516 517 # ############################################################################################### 518 # ############################################################################################### 519 # ############################################################################################### 520 # ############################################################################################### 521 522 import PyDbgEng 523 import comtypes 524 from ctypes import * 525 from comtypes import HRESULT, COMError 526 from comtypes.client import CreateObject, GetEvents, PumpEvents#, ReleaseEvents 527 from comtypes.hresult import S_OK 528 from comtypes.automation import IID 529 from comtypes.gen import DbgEng 530
531 - class _DbgEventHandler(PyDbgEng.IDebugOutputCallbacksSink, PyDbgEng.IDebugEventCallbacksSink):
532 533 buff = '' 534
535 - def Output(self, this, Mask, Text):
536 #sys.stdout.write(Text) 537 _DbgEventHandler.buff += Text
538
539 - def GetInterestMask(self):
540 #sys.stdout.write("_DbgEventHandler::GetInterestMask\n") 541 return PyDbgEng.DbgEng.DEBUG_EVENT_EXCEPTION | PyDbgEng.DbgEng.DEBUG_FILTER_INITIAL_BREAKPOINT
542
543 - def Exception(self, dbg, ExceptionCode, ExceptionFlags, ExceptionRecord, 544 ExceptionAddress, NumberParameters, ExceptionInformation0, ExceptionInformation1, 545 ExceptionInformation2, ExceptionInformation3, ExceptionInformation4, 546 ExceptionInformation5, ExceptionInformation6, ExceptionInformation7, 547 ExceptionInformation8, ExceptionInformation9, ExceptionInformation10, 548 ExceptionInformation11, ExceptionInformation12, ExceptionInformation13, 549 ExceptionInformation14, FirstChance):
550 551 if WindowsDebugEngine.handlingFault.isSet() or WindowsDebugEngine.handledFault.isSet(): 552 # We are already handling, so skip 553 #sys.stdout.write("_DbgEventHandler::Exception(): handlingFault set, skipping.\n") 554 return DbgEng.DEBUG_STATUS_NO_CHANGE 555 556 try: 557 558 WindowsDebugEngine.handlingFault.set() 559 560 # 1. Calculate no. of frames 561 #sys.stdout.write("_DbgEventHandler::Exception(): 1\n") 562 563 frames_filled = 0 564 stack_frames = dbg.get_stack_trace(100) 565 for i in range(100): 566 eip = stack_frames[i].InstructionOffset 567 if (eip == 0): 568 break 569 frames_filled += 1 570 571 # 2. Output registers 572 #sys.stdout.write("_DbgEventHandler::Exception(): 2\n") 573 574 dbg.idebug_registers.OutputRegisters(DbgEng.DEBUG_OUTCTL_THIS_CLIENT, DbgEng.DEBUG_REGISTERS_ALL) 575 _DbgEventHandler.buff += "\n\n" 576 577 # 3. Ouput stack trace 578 #sys.stdout.write("_DbgEventHandler::Exception(): 3\n") 579 580 frames_count = 100 581 frames_buffer = create_string_buffer( frames_count * sizeof(DbgEng._DEBUG_STACK_FRAME) ) 582 frames_buffer_ptr = cast(frames_buffer, POINTER(DbgEng._DEBUG_STACK_FRAME)) 583 584 dbg.idebug_control.GetStackTrace(0, 0, 0, frames_buffer_ptr, frames_count) 585 dbg.idebug_control.OutputStackTrace(DbgEng.DEBUG_OUTCTL_THIS_CLIENT, frames_buffer_ptr, frames_filled, 586 DbgEng.DEBUG_STACK_ARGUMENTS | 587 DbgEng.DEBUG_STACK_FUNCTION_INFO | 588 DbgEng.DEBUG_STACK_SOURCE_LINE | 589 DbgEng.DEBUG_STACK_FRAME_ADDRESSES | 590 DbgEng.DEBUG_STACK_COLUMN_NAMES | 591 DbgEng.DEBUG_STACK_FRAME_NUMBERS | 592 DbgEng.DEBUG_STACK_PARAMETERS ) 593 594 _DbgEventHandler.buff += "\n\n" 595 596 # 4. Write dump file 597 #sys.stdout.write("_DbgEventHandler::Exception(): 4\n") 598 599 dbg.idebug_client.WriteDumpFile(c_char_p("dumpfile.core"), DbgEng.DEBUG_DUMP_SMALL) 600 minidump = None 601 602 try: 603 f = open('dumpfile.core', 'rb+') 604 minidump = f.read() 605 f.close() 606 607 except: 608 pass 609 610 # 5. !analyze -v 611 612 #sys.stdout.write("_DbgEventHandler::Exception(): 5\n") 613 handle = dbg.idebug_control.AddExtension(c_char_p("C:\\Program Files\\Debugging Tools for Windows\\winext\\ext.dll"), 0) 614 try: 615 dbg.idebug_control.Execute(DbgEng.DEBUG_OUTCTL_THIS_CLIENT, c_char_p("!analyze -v"), DbgEng.DEBUG_EXECUTE_ECHO) 616 except: 617 pass 618 619 dbg.idebug_control.RemoveExtension(handle) 620 621 WindowsDebugEngine.lock.acquire() 622 623 if minidump: 624 WindowsDebugEngine.crashInfo = { 'StackTrace.txt' : _DbgEventHandler.buff, 'MiniDump.dmp' : minidump } 625 else: 626 WindowsDebugEngine.crashInfo = { 'StackTrace.txt' : _DbgEventHandler.buff } 627 628 WindowsDebugEngine.fault = True 629 WindowsDebugEngine.lock.release() 630 631 except: 632 sys.stdout.write(repr(sys.exc_info()[0]) + "\n") 633 raise 634 635 WindowsDebugEngine.handledFault.set() 636 637 #return DbgEng.DEBUG_STATUS_BREAK 638 return DbgEng.DEBUG_STATUS_NO_CHANGE
639 640
641 - class WindowsDebugEngineThread(Thread):
642 - def run(self):
643 WindowsDebugEngine.handlingFault.clear() 644 645 # Hack for comtypes early version 646 comtypes._ole32.CoInitializeEx(None, comtypes.COINIT_APARTMENTTHREADED) 647 648 self._eventHandler = _DbgEventHandler() 649 650 if self.KernelConnectionString: 651 self.dbg = PyDbgEng.KernelAttacher( connection_string = connection_string, 652 follow_forks = True, 653 event_callbacks_sink = self._eventHandler, 654 output_callbacks_sink = self._eventHandler, 655 symbols_path = self.SymbolsPath) 656 657 elif self.CommandLine: 658 self.dbg = PyDbgEng.ProcessCreator(command_line = self.CommandLine, 659 follow_forks = True, 660 event_callbacks_sink = self._eventHandler, 661 output_callbacks_sink = self._eventHandler, 662 symbols_path = self.SymbolsPath) 663 664 elif self.ProcessName: 665 self.dbg = PyDbgEng.ProcessAttacher(pid = self.GetProcessIdByName(self.ProcessName), 666 event_callbacks_sink = self._eventHandler, 667 output_callbacks_sink = self._eventHandler, 668 symbols_path = self.SymbolsPath) 669 670 else: 671 raise Exception("Didn't find way to start debugger... bye bye!!") 672 673 WindowsDebugEngineThread.Quit = Event() 674 WindowsDebugEngine.started.set() 675 676 self.dbg.event_loop_with_user_callback(self.Callback, 10) 677 self.dbg.__del__()
678
679 - def Callback(self = None, stuff = None, stuff2 = None):
680 PumpEvents(0.1) 681 if WindowsDebugEngineThread.Quit.isSet(): 682 self.dbg.idebug_client.TerminateProcesses() 683 return True
684
685 - def GetProcessIdByName(self, procname):
686 ''' 687 Try and get pid for a process by name. 688 ''' 689 690 ourPid = -1 691 procname = procname.lower() 692 693 try: 694 ourPid = win32api.GetCurrentProcessId() 695 696 except: 697 pass 698 699 pids = win32process.EnumProcesses() 700 for pid in pids: 701 if ourPid == pid: 702 continue 703 704 try: 705 hPid = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION | win32con.PROCESS_VM_READ, 0, pid) 706 707 try: 708 mids = win32process.EnumProcessModules(hPid) 709 for mid in mids: 710 name = str(win32process.GetModuleFileNameEx(hPid, mid)) 711 #print "Filename: ", name 712 if name.lower().find(procname) != -1: 713 return pid 714 715 finally: 716 win32api.CloseHandle(hPid) 717 except: 718 pass 719 720 return None
721
722 - class WindowsDebugEngine(Monitor, Thread):
723 ''' 724 Windows debugger agent. This debugger agent is based on the windbg engine and 725 supports the following features: 726 727 * User mode debugging 728 * Kernel mode debugging 729 * x86 and x64 730 * Symbols and symbol server 731 732 '''
733 - def __init__(self, args):
734 Thread.__init__(self) 735 WindowsDebugEngine.started = Event() 736 737 # Set at start of exception handling 738 WindowsDebugEngine.handlingFault = Event() 739 # Set when collection finished 740 WindowsDebugEngine.handledFault = Event() 741 WindowsDebugEngine.lock = Lock() 742 WindowsDebugEngine.crashInfo = None 743 WindowsDebugEngine.fault = False 744 self.thread = None 745 746 if args.has_key('CommandLine'): 747 self.CommandLine = str(args['CommandLine']) 748 else: 749 self.CommandLine = None 750 751 if args.has_key('ProcessName'): 752 self.ProcessName = str(args['ProcessName']) 753 else: 754 self.ProcessName = None 755 756 if args.has_key('KernelConnectionString'): 757 self.KernelConnectionString = str(args['KernelConnectionString']) 758 else: 759 self.KernelConnectionString = None 760 761 if args.has_key('SymbolsPath'): 762 self.SymbolsPath = str(args['SymbolsPath']) 763 else: 764 self.SymbolsPath = "SRV*http://msdl.microsoft.com/download/symbols" 765 766 if self.CommandLine == None and self.ProcessName == None and self.KernelConnectionString == None: 767 raise Exception("Unable to create WindowsDebugger Instance!!!!!")
768 769
770 - def _StartDebugger(self):
771 772 # Need to reinitialize in another thread. 773 # Note: This must occur on every PyDbgEng restart to properly 774 # clear out the old state. A matching initialize call 775 # is performed in the created thread. 776 comtypes._ole32.CoUninitialize() 777 778 # Clear all our event handlers 779 WindowsDebugEngine.started.clear() 780 WindowsDebugEngine.handlingFault.clear() 781 WindowsDebugEngine.handledFault.clear() 782 783 self.thread = WindowsDebugEngineThread() 784 785 self.thread.CommandLine = self.CommandLine 786 self.thread.ProcessName = self.ProcessName 787 self.thread.KernelConnectionString = self.KernelConnectionString 788 self.thread.SymbolsPath = self.SymbolsPath 789 790 # Kick off our thread: 791 self.thread.start() 792 793 # Wait it...! 794 WindowsDebugEngine.started.wait() 795 796 # Let things get spun up 797 time.sleep(2)
798
799 - def _StopDebugger(self):
800 print "_StopDebugger" 801 if self.thread.isAlive(): 802 WindowsDebugEngineThread.Quit.set() 803 WindowsDebugEngine.started.clear() 804 self.thread.join() 805 time.sleep(0.25) # Take a breath
806
807 - def _IsDebuggerAlive(self):
808 return self.thread and self.thread.isAlive()
809
810 - def OnTestStarting(self):
811 ''' 812 Called right before start of test. 813 ''' 814 if not self._IsDebuggerAlive(): 815 self._StartDebugger()
816
817 - def GetMonitorData(self):
818 ''' 819 Get any monitored data. 820 ''' 821 WindowsDebugEngine.lock.acquire() 822 if WindowsDebugEngine.crashInfo != None: 823 ret = WindowsDebugEngine.crashInfo 824 WindowsDebugEngine.crashInfo = None 825 _DbgEventHandler.buff = "" 826 WindowsDebugEngine.lock.release() 827 return ret 828 829 WindowsDebugEngine.lock.release() 830 return None
831
832 - def DetectedFault(self):
833 ''' 834 Check if a fault was detected. 835 ''' 836 837 time.sleep(0.15) 838 839 if not WindowsDebugEngine.handlingFault.isSet(): 840 return False 841 842 WindowsDebugEngine.handledFault.wait() 843 WindowsDebugEngine.lock.acquire() 844 845 if WindowsDebugEngine.fault or not self.thread.isAlive(): 846 print ">>>>>> RETURNING FAULT <<<<<<<<<" 847 WindowsDebugEngine.fault = False 848 WindowsDebugEngine.lock.release() 849 return True 850 851 WindowsDebugEngine.lock.release() 852 return False
853
854 - def OnFault(self):
855 ''' 856 Called when a fault was detected. 857 ''' 858 self._StopDebugger()
859
860 - def OnShutdown(self):
861 ''' 862 Called when Agent is shutting down. 863 ''' 864 self._StopDebugger()
865 866 except: 867 pass 868 #raise 869 870 try: 871 872 from pygdb.mi import Gdb 873
874 - class _GdbThread(Thread):
875
876 - def __init__(self):
877 Thread.__init__(self)
878
879 - def run(self):
880 881 self.gdb = gdb = Gdb(None) 882 gdb.file(self._command) 883 #gdb.start(args=self._params).wait() 884 gdb.run(self._params).wait() 885 886 if UnixGdb.quit.isSet(): 887 return 888 889 UnixGdb.handlingFault.set() 890 891 buff = "Crash output from GDB\n" 892 buff+= "=====================\n\n" 893 buff+= "break: reason: %s, thread-id: %s\n\n" % (result.reason, result.thread_id) 894 buff+= self._getLocals(gdb) 895 buff+= "\n-------------------\n" 896 buff+= self._getArguments(gdb) 897 buff+= "\n-------------------\n" 898 buff+= self._getStack(gdb) 899 buff+= "\n-------------------\n" 900 buff+= self._getRegisters(gdb) 901 902 gdb.quit() 903 904 UnixGdb.lock.acquire() 905 UnixGdb.creashInfo = { 'DebuggerOutput.txt' : buff } 906 UnixGdb.fault = True 907 UnixGdb.lock.release() 908 UnixGdb.handledFault.set()
909
910 - def _getLocals(self, gdb):
911 buff = "Frame Locals:\n" 912 try: 913 result = gdb.stack_list_locals().wait() 914 for local in resul.locals: 915 buff += "\t%s = %s\n" % (local.name, local.value) 916 except: 917 pass 918 919 return buff
920
921 - def _getArguments(self, gdb):
922 buff = "Frame Arguments:\n" 923 try: 924 result = gdb.stack_list_arguments().wait() 925 for frame in result.stack_args.frame: 926 buff += "\tframe: " + frame.level + "\n" 927 for arg in frame.args: 928 buff += "\t\t%s = %s\n" % (arg.name, arg.value) 929 except: 930 pass 931 932 return buff
933
934 - def _getStack(self, gdb):
935 buff = "Stack:\n" 936 try: 937 result = gdb.stack_list_frames().wait() 938 for frame in result.stack.frame: 939 buff += "\t%s, at %s:%s\n" % (frame.func, frame.file, frame.line) 940 except: 941 pass 942 943 return buff
944
945 - def _getRegisters(self, gdb):
946 buff = "Registers:\n" 947 try: 948 names = gdb.data_list_register_names().wait() 949 result = gdb.data_list_register_values(regno=registers).wait() 950 for register in result.register_values: 951 name = names.register_names[int(register.number)] 952 buff += "\t%s: %s\n" % (name, register.value) 953 except: 954 pass 955 956 return buff
957
958 - class UnixGdb(Monitor):
959 ''' 960 Unix GDB monitor. This debugger monitor uses the gdb 961 debugger via pygdb wrapper. Tested under Linux and OS X. 962 963 * Collect core files 964 * User mode debugging 965 * Capturing stack trace, registers, etc 966 * Symbols is available 967 ''' 968
969 - def __init__(self, args):
970 971 UnixGdb.quit = Event() 972 UnixGdb.started = Event() 973 UnixGdb.handlingFault = Event() 974 UnixGdb.handledFault = Event() 975 UnixGdb.lock = Lock() 976 UnixGdb.crashInfo = None 977 UnixGdb.fault = False 978 979 if args.has_key('Command'): 980 self._command = str(args['Command']) 981 self._params = str(args['Params']) 982 self._pid = None 983 984 elif args.has_key('ProcessName'): 985 self._command = None 986 self._params = None 987 self._pid = self.GetProcessIdByName(str(args['ProcessName'])) 988 989 else: 990 raise Exception("Unable to create UnixGdb! Error in params!")
991
992 - def _StartDebugger(self):
993 UnixGdb.quit.clear() 994 UnixGdb.started.clear() 995 UnixGdb.handlingFault.clear() 996 UnixGdb.handledFault.clear() 997 UnixGdb.fault = False 998 UnixGdb.crashInfo = None 999 1000 self.thread = _GdbThread() 1001 self.thread._command = self._command 1002 self.thread._params = self._params 1003 self.thread._pid = self._pid 1004 1005 self.thread.start() 1006 UnixGdb.started.wait() 1007 time.sleep(2) # Let things spin up!
1008
1009 - def _StopDebugger(self):
1010 1011 if self.thread.isAlive(): 1012 UnixGdb.quit.set() 1013 UnixGdb.started.clear() 1014 self.thread.gdb.quit() 1015 self.thread.join() 1016 time.sleep(0.25) # Take a breath
1017
1018 - def _IsDebuggerAlive(seld):
1019 return self.thread != None and self.thread.isAlive()
1020
1021 - def OnTestStarting(self):
1022 ''' 1023 Called right before start of test. 1024 ''' 1025 if not self._IsDebuggerAlive(): 1026 self._StartDebugger()
1027
1028 - def GetMonitorData(self):
1029 ''' 1030 Get any monitored data. 1031 ''' 1032 UnixGdb.lock.acquire() 1033 if UnixGdb.crashInfo != None: 1034 ret = UnixGdb.crashInfo 1035 UnixGdb.crashInfo = None 1036 _GdbEventHandler.buff = "" 1037 UnixGdb.lock.release() 1038 return ret 1039 1040 UnixGdb.lock.release() 1041 return None
1042
1043 - def DetectedFault(self):
1044 ''' 1045 Check if a fault was detected. 1046 ''' 1047 1048 time.sleep(0.15) 1049 1050 if not UnixGdb.handlingFault.isSet(): 1051 return False 1052 1053 UnixGdb.handledFault.wait() 1054 UnixGdb.lock.acquire() 1055 1056 if UnixGdb.fault or not self.thread.isAlive(): 1057 print ">>>>>> RETURNING FAULT <<<<<<<<<" 1058 UnixGdb.fault = False 1059 UnixGdb.lock.release() 1060 return True 1061 1062 UnixGdb.lock.release() 1063 return False
1064
1065 - def OnFault(self):
1066 ''' 1067 Called when a fault was detected. 1068 ''' 1069 self._StopDebugger()
1070
1071 - def OnShutdown(self):
1072 ''' 1073 Called when Agent is shutting down. 1074 ''' 1075 self._StopDebugger()
1076 1077 except: 1078 pass 1079 1080 # end 1081