Package vtrace :: Package platforms :: Module base
[hide private]
[frames] | no frames]

Source Code for Module vtrace.platforms.base

  1  """ 
  2  Tracer Platform Base 
  3  """ 
  4  # Copyright (C) 2007 Invisigoth - See LICENSE file for details 
  5  import os 
  6  import struct 
  7  import vtrace 
  8  import traceback 
  9  import platform 
 10   
 11  from Queue import Queue 
 12  from threading import Thread,currentThread,Lock 
 13   
 14  import envi 
 15  import envi.memory as e_mem 
 16  import envi.threads as e_threads 
 17  import envi.resolver as e_resolv 
 18   
 19  import vstruct.builder as vs_builder 
 20   
21 -class TracerBase(vtrace.Notifier):
22 """ 23 The basis for a tracer's internals. All platformFoo/archFoo 24 functions are defaulted, and internal state is initialized. 25 Additionally, a number of internal utilities are housed here. 26 """
27 - def __init__(self):
28 """ 29 The routine to initialize a tracer's initial internal state. This 30 is used by the initial creation routines, AND on attaches/executes 31 to re-fresh the state of the tracer. 32 WARNING: This will erase all metadata/symbols (modes/notifiers are kept) 33 """ 34 vtrace.Notifier.__init__(self) 35 36 self.pid = 0 # Attached pid (also used to know if attached) 37 self.exited = False 38 self.breakpoints = {} 39 self.newbreaks = [] 40 self.bpbyid = {} 41 self.bpid = 0 42 self.curbp = None 43 self.bplock = Lock() 44 self.deferred = [] 45 self.running = False 46 self.runagain = False 47 self.attached = False 48 # A cache for memory maps and fd listings 49 self.mapcache = None 50 self.thread = None # our proxy thread... 51 self.threadcache = None 52 self.fds = None 53 self.signal_ignores = [] 54 self.localvars = {} 55 56 # Set if we are RunForever until a thread exit... 57 self._join_thread = None 58 self._break_after_bp = True # Do we stop on the instruction *after* the bp? 59 60 self.vsbuilder = vs_builder.VStructBuilder() 61 62 self.psize = self.getPointerSize() # From the envi arch mod... 63 64 # Track which libraries are parsed, and their 65 # normame to full path mappings 66 self.libloaded = {} # True if the library has been loaded already 67 self.libpaths = {} # normname->filename and filename->normname lookup 68 69 # Set up some globally expected metadata 70 self.setMeta('PendingSignal', None) 71 self.setMeta('SignalInfo', None) 72 self.setMeta("IgnoredSignals",[]) 73 self.setMeta("LibraryBases", {}) # name -> base address mappings for binaries 74 self.setMeta("LibraryPaths", {}) # base -> path mappings for binaries 75 self.setMeta("ThreadId", 0) # If you *can* have a thread id put it here 76 plat = platform.system() 77 rel = platform.release() 78 self.setMeta("Platform", plat) 79 self.setMeta("Release", rel) 80 81 # Use this if we are *expecting* a break 82 # which is caused by us (so we remove the 83 # SIGBREAK from pending_signal 84 self.setMeta("ShouldBreak", False)
85
86 - def nextBpId(self):
87 self.bplock.acquire() 88 x = self.bpid 89 self.bpid += 1 90 self.bplock.release() 91 return x
92
93 - def _justAttached(self, pid):
94 # Used by the top level Trace after platform routines 95 self.pid = pid 96 self.attached = True 97 self.breakpoints = {} 98 self.bpbyid = {} 99 self.setMeta("PendingSignal", None) 100 self.setMeta("ExitCode", 0) 101 self.exited = False
102
103 - def getResolverForFile(self, filename):
104 res = self.resbynorm.get(filename, None) 105 if res: return res 106 res = self.resbyfile.get(filename, None) 107 if res: return res 108 return None
109
110 - def steploop(self):
111 """ 112 Continue stepi'ing in a loop until shouldRunAgain() 113 returns false (like RunForever mode or something) 114 """ 115 if self.getMode("NonBlocking", False): 116 e_threads.firethread(self.doStepLoop)() 117 else: 118 self.doStepLoop()
119
120 - def doStepLoop(self):
121 go = True 122 while go: 123 self.stepi() 124 go = self.shouldRunAgain()
125
126 - def _doRun(self):
127 # Exists to avoid recursion from loop in doWait 128 self.requireAttached() 129 self.requireNotRunning() 130 self.requireNotExited() 131 132 fastbreak = False 133 if self.curbp: 134 fastbreak = self.curbp.fastbreak 135 136 # If we are on a breakpoint, and it's a fastbreak 137 # we don't want to fire a "continue" event. 138 if not fastbreak: 139 self.fireNotifiers(vtrace.NOTIFY_CONTINUE) 140 141 # Step past a breakpoint if we are on one. 142 self._checkForBreak() 143 144 # Throw down and activate breakpoints... 145 if not fastbreak: 146 self._throwdownBreaks() 147 148 self.running = True 149 self.runagain = False 150 self._syncRegs() # Must be basically last... 151 self.platformContinue() 152 self.setMeta("PendingSignal", None)
153
154 - def wait(self):
155 """ 156 Wait for the trace target to have 157 something happen... If the trace is in 158 NonBlocking mode, this will fire a thread 159 to wait for you and return control immediately. 160 """ 161 if self.getMode("NonBlocking"): 162 e_threads.firethread(self._doWait)() 163 else: 164 self._doWait()
165
166 - def _doWait(self):
167 doit = True 168 while doit: 169 # A wrapper method for wait() and the wait thread to use 170 self.setMeta('SignalInfo', None) 171 self.setMeta('PendingSignal', None) 172 event = self.platformWait() 173 self.running = False 174 self.platformProcessEvent(event) 175 doit = self.shouldRunAgain() 176 if doit: 177 self._doRun()
178
179 - def _fireSignal(self, signo, siginfo=None):
180 self.setMeta('PendingSignal', signo) 181 self.setMeta('SignalInfo', siginfo) 182 self.fireNotifiers(vtrace.NOTIFY_SIGNAL)
183
184 - def _fireExit(self, ecode):
185 self.setMeta('ExitCode', ecode) 186 self.fireNotifiers(vtrace.NOTIFY_EXIT)
187
188 - def _fireExitThread(self, threadid, ecode):
189 self.setMeta('ExitThread', threadid) 190 self.setMeta('ExitCode', ecode) 191 self.fireNotifiers(vtrace.NOTIFY_EXIT_THREAD)
192
193 - def _activateBreak(self, bp):
194 # NOTE: This is special cased by hardware debuggers etc... 195 if bp.isEnabled(): 196 try: 197 bp.activate(self) 198 except Exception, e: 199 traceback.print_exc() 200 print "WARNING: bpid %d activate failed (deferring): %s" % (bp.id, e) 201 self.deferred.append(bp)
202
203 - def _throwdownBreaks(self):
204 """ 205 Run through the breakpoints and setup 206 the ones that are enabled. 207 208 NOTE: This should *not* get called when continuing 209 from a fastbreak... 210 """ 211 212 # Resolve deferred breaks 213 for bp in self.deferred: 214 addr = bp.resolveAddress(self) 215 if addr != None: 216 self.deferred.remove(bp) 217 self.breakpoints[addr] = bp 218 219 for bp in self.breakpoints.values(): 220 self._activateBreak(bp)
221
222 - def _syncRegs(self):
223 """ 224 Sync the reg-cache into the target process 225 """ 226 if self.regcache != None: 227 for tid, ctx in self.regcache.items(): 228 if ctx.isDirty(): 229 self.platformSetRegCtx(tid, ctx) 230 self.regcache = None
231
232 - def _cacheRegs(self, threadid):
233 """ 234 Make sure the reg-cache is populated 235 """ 236 if self.regcache == None: 237 self.regcache = {} 238 ret = self.regcache.get(threadid) 239 if ret == None: 240 ret = self.platformGetRegCtx(threadid) 241 ret.setIsDirty(False) 242 self.regcache[threadid] = ret 243 return ret
244
245 - def _checkForBreak(self):
246 """ 247 Check to see if we've landed on a breakpoint, and if so 248 deactivate and step us past it. 249 250 WARNING: Unfortunatly, cause this is used immidiatly before 251 a call to run/wait, we must block briefly even for the GUI 252 """ 253 # Steal a reference because the step should 254 # clear curbp... 255 bp = self.curbp 256 if bp != None and bp.isEnabled(): 257 if bp.active: 258 bp.deactivate(self) 259 orig = self.getMode("FastStep") 260 self.setMode("FastStep", True) 261 self.stepi() 262 self.setMode("FastStep", orig) 263 bp.activate(self) 264 self.curbp = None
265
266 - def shouldRunAgain(self):
267 """ 268 A unified place for the test as to weather this trace 269 should be told to run again after reaching some stopping 270 condition. 271 """ 272 if not self.attached: 273 return False 274 275 if self.exited: 276 return False 277 278 if self.getMode("RunForever"): 279 return True 280 281 if self.runagain: 282 return True 283 284 return False
285
286 - def __repr__(self):
287 run = "stopped" 288 exe = "None" 289 if self.isRunning(): 290 run = "running" 291 elif self.exited: 292 run = "exited" 293 exe = self.getMeta("ExeName") 294 return "[%d]\t- %s <%s>" % (self.pid, exe, run)
295
296 - def initMode(self, name, value, descr):
297 """ 298 Initialize a mode, this should ONLY be called 299 during setup routines for the trace! It determines 300 the available mode setings. 301 """ 302 self.modes[name] = bool(value) 303 self.modedocs[name] = descr
304
305 - def release(self):
306 """ 307 Do cleanup when we're done. This is mostly necissary 308 because of the thread proxy holding a reference to this 309 tracer... We need to let him die off and try to get 310 garbage collected. 311 """ 312 if self.thread: 313 self.thread.go = False
314
315 - def _cleanupResources(self):
316 self._tellThreadExit()
317
318 - def _tellThreadExit(self):
319 if self.thread != None: 320 self.thread.queue.put(None) 321 self.thread.join(timeout=2) 322 self.thread = None
323
324 - def __del__(self):
325 if not self._released: 326 print 'Warning! tracer del w/o release()!'
327
328 - def fireTracerThread(self):
329 # Fire the threadwrap proxy thread for this tracer 330 # (if it hasnt been fired...) 331 if self.thread == None: 332 self.thread = TracerThread()
333
334 - def fireNotifiers(self, event):
335 """ 336 Fire the registered notifiers for the NOTIFY_* event. 337 """ 338 if event == vtrace.NOTIFY_SIGNAL: 339 signo = self.getCurrentSignal() 340 if signo in self.getMeta("IgnoredSignals", []): 341 if vtrace.verbose: print "Ignoring",signo 342 self.runAgain() 343 return 344 345 alllist = self.getNotifiers(vtrace.NOTIFY_ALL) 346 nlist = self.getNotifiers(event) 347 348 trace = self 349 # if the trace has a proxy it's notifiers 350 # need that, cause we can't be pickled ;) 351 if self.proxy: 352 trace = self.proxy 353 354 # First we notify ourself.... 355 self.handleEvent(event, self) 356 357 # The "NOTIFY_ALL" guys get priority 358 for notifier in alllist: 359 try: 360 notifier.handleEvent(event,trace) 361 except: 362 print "WARNING: Notifier exception for",repr(notifier) 363 traceback.print_exc() 364 365 for notifier in nlist: 366 try: 367 notifier.handleEvent(event,trace) 368 except: 369 print "WARNING: Notifier exception for",repr(notifier) 370 traceback.print_exc()
371
372 - def _cleanupBreakpoints(self):
373 ''' 374 Cleanup all breakpoints (if the current bp is "fastbreak" this routine 375 will not be called... 376 ''' 377 for bp in self.breakpoints.itervalues(): 378 bp.deactivate(self)
379
380 - def _fireStep(self):
381 if self.getMode('FastStep', False): 382 return 383 self.fireNotifiers(vtrace.NOTIFY_STEP)
384
385 - def _fireBreakpoint(self, bp):
386 387 self.curbp = bp 388 389 # A breakpoint should be inactive when fired 390 # (even fastbreaks, we'll need to deactivate for stepi anyway) 391 bp.deactivate(self) 392 393 try: 394 bp.notify(vtrace.NOTIFY_BREAK, self) 395 except Exception, msg: 396 traceback.print_exc() 397 print "Breakpoint Exception 0x%.8x : %s" % (bp.address,msg) 398 399 if not bp.fastbreak: 400 self.fireNotifiers(vtrace.NOTIFY_BREAK) 401 402 else: 403 # fastbreak's are basically always "run again" 404 self.runagain = True
405
406 - def checkPageWatchpoints(self):
407 """ 408 Check if the given memory fault was part of a valid 409 MapWatchpoint. 410 """ 411 faultaddr,faultperm = self.platformGetMemFault() 412 413 #FIXME this is some AWESOME but intel specific nonsense 414 if faultaddr == None: return False 415 faultpage = faultaddr & 0xfffff000 416 417 wp = self.breakpoints.get(faultpage, None) 418 if wp == None: 419 return False 420 421 self._fireBreakpoint(wp) 422 423 return True
424
425 - def checkWatchpoints(self):
426 # Check for hardware watchpoints 427 waddr = self.archCheckWatchpoints() 428 if waddr != None: 429 wp = self.breakpoints.get(waddr, None) 430 self._fireBreakpoint(wp) 431 return True
432
433 - def checkBreakpoints(self):
434 """ 435 This is mostly for systems (like linux) where you can't tell 436 the difference between some SIGSTOP/SIGBREAK conditions and 437 an actual breakpoint instruction. 438 439 This method will return true if either the breakpoint 440 subsystem or the sendBreak (via ShouldBreak meta) is true 441 (and it will have handled firing events for the bp) 442 """ 443 pc = self.getProgramCounter() 444 if self._break_after_bp: 445 bi = self.archGetBreakInstr() 446 pc -= len(bi) 447 448 bp = self.breakpoints.get(pc, None) 449 450 if bp: 451 addr = bp.getAddress() 452 # Step back one instruction to account break 453 self.setProgramCounter(addr) 454 self._fireBreakpoint(bp) 455 return True 456 457 if self.getMeta("ShouldBreak"): 458 self.setMeta("ShouldBreak", False) 459 self.fireNotifiers(vtrace.NOTIFY_BREAK) 460 return True 461 462 return False
463
464 - def notify(self, event, trace):
465 """ 466 We are frequently a notifier for ourselves, so we can do things 467 like handle events on attach and on break in a unified fashion. 468 """ 469 self.threadcache = None 470 self.mapcache = None 471 self.fds = None 472 self.running = False 473 474 if event in self.auto_continue: 475 self.runAgain() 476 477 # For thread exits, make sure the tid 478 # isn't in 479 if event == vtrace.NOTIFY_EXIT_THREAD: 480 tid = self.getMeta("ThreadId") 481 self.sus_threads.pop(tid, None) 482 # Check if this is a thread we were waiting on. 483 if tid == self._join_thread: 484 self._join_thread = None 485 # Turn off the RunForever in joinThread() 486 self.setMode('RunForever', False) 487 # Either way, we don't want to run again... 488 self.runAgain(False) 489 490 # Do the stuff we do for detach/exit or 491 # cleanup breaks etc... 492 if event == vtrace.NOTIFY_ATTACH: 493 pass 494 495 elif event == vtrace.NOTIFY_DETACH: 496 for tid in self.sus_threads.keys(): 497 self.resumeThread(tid) 498 self._cleanupBreakpoints() 499 500 elif event == vtrace.NOTIFY_EXIT: 501 self.setMode("RunForever", False) 502 self.exited = True 503 self.attached = False 504 505 elif event == vtrace.NOTIFY_CONTINUE: 506 self.runagain = False 507 508 else: 509 self._cleanupBreakpoints()
510
511 - def delLibraryBase(self, baseaddr):
512 513 libname = self.getMeta("LibraryPaths").get(baseaddr, "unknown") 514 normname = self.normFileName(libname) 515 516 sym = self.getSymByName(normname) 517 518 self.setMeta("LatestLibrary", libname) 519 self.setMeta("LatestLibraryNorm", normname) 520 521 self.fireNotifiers(vtrace.NOTIFY_UNLOAD_LIBRARY) 522 523 self.getMeta("LibraryBases").pop(normname, None) 524 self.getMeta("LibraryPaths").pop(baseaddr, None) 525 if sym != None: 526 self.delSymbol(sym)
527
528 - def addLibraryBase(self, libname, address, always=False):
529 """ 530 This should be used *at load time* to setup the library 531 event metadata. 532 533 This *must* be called from a context where it's safe to 534 fire notifiers, because it will fire a notifier to alert 535 about a LOAD_LIBRARY. (This means *not* from inside another 536 notifer) 537 """ 538 539 self.setMeta("LatestLibrary", None) 540 self.setMeta("LatestLibraryNorm", None) 541 542 normname = self.normFileName(libname) 543 if self.getSymByName(normname) != None: 544 normname = "%s_%.8x" % (normname,address) 545 546 # Only actually do library work with a file or force 547 if os.path.exists(libname) or always: 548 549 self.getMeta("LibraryPaths")[address] = libname 550 self.getMeta("LibraryBases")[normname] = address 551 self.setMeta("LatestLibrary", libname) 552 self.setMeta("LatestLibraryNorm", normname) 553 554 width = self.arch.getPointerSize() 555 sym = e_resolv.FileSymbol(normname, address, 0, width=width) 556 sym.casesens = self.casesens 557 self.addSymbol(sym) 558 559 self.libpaths[normname] = libname 560 561 self.fireNotifiers(vtrace.NOTIFY_LOAD_LIBRARY)
562
563 - def normFileName(self, libname):
564 basename = os.path.basename(libname) 565 return basename.split(".")[0].split("-")[0].lower()
566
567 - def _findLibraryMaps(self, magic, always=False):
568 # A utility for platforms which lack library load 569 # notification through the operating system 570 done = {} 571 mlen = len(magic) 572 573 for addr,size,perms,fname in self.getMemoryMaps(): 574 575 if not fname: 576 continue 577 578 if done.get(fname): 579 continue 580 581 try: 582 583 if self.readMemory(addr, mlen) == magic: 584 done[fname] = True 585 self.addLibraryBase(fname, addr, always=always) 586 587 except: 588 pass # *never* do this... except this once...
589
590 - def _loadBinaryNorm(self, normname):
591 if not self.libloaded.get(normname, False): 592 fname = self.libpaths.get(normname) 593 if fname != None: 594 self._loadBinary(fname) 595 return True 596 return False
597
598 - def _loadBinary(self, filename):
599 """ 600 Check if a filename has yet to be parsed. If it has NOT 601 been parsed, parse it and return True, otherwise, return False 602 """ 603 normname = self.normFileName(filename) 604 if not self.libloaded.get(normname, False): 605 address = self.getMeta("LibraryBases").get(normname) 606 if address != None: 607 self.platformParseBinary(filename, address, normname) 608 self.libloaded[normname] = True 609 return True 610 return False
611
612 - def _simpleCreateThreads(self):
613 ''' 614 Fire a thread event for each of the current threads. 615 (for use by tracers which don't get help from the OS) 616 ''' 617 initid = self.getMeta('ThreadId') 618 for tid in self.platformGetThreads().keys(): 619 self.setMeta('ThreadId', tid) 620 self.fireNotifiers(vtrace.NOTIFY_CREATE_THREAD) 621 self.setMeta('ThreadId', initid)
622 623 ####################################################################### 624 # 625 # NOTE: all platform/arch defaults are populated here. 626 #
627 - def platformGetThreads(self):
628 """ 629 Return a dictionary of <threadid>:<tinfo> pairs where tinfo is either 630 the stack top, or the teb for win32 631 """ 632 raise Exception("Platform must implement platformGetThreads()")
633
634 - def platformSelectThread(self, thrid):
635 """ 636 Platform implementers are encouraged to use the metadata field "ThreadId" 637 as the identifier (int) for which thread has "focus". Additionally, the 638 field "StoppedThreadId" should be used in instances (like win32) where you 639 must specify the ORIGINALLY STOPPED thread-id in the continue. 640 """ 641 self.setMeta("ThreadId",thrid)
642
643 - def platformSuspendThread(self, thrid):
644 raise Exception("Platform must implement platformSuspendThread()")
645
646 - def platformResumeThread(self, thrid):
647 raise Exception("Platform must implement platformResumeThread()")
648
649 - def platformInjectThread(self, pc, arg=0):
650 raise Exception("Platform must implement platformInjectThread()")
651
652 - def platformKill(self):
653 raise Exception("Platform must implement platformKill()")
654
655 - def platformExec(self, cmdline):
656 """ 657 Platform exec will execute the process specified in cmdline 658 and return the PID 659 """ 660 raise Exception("Platmform must implement platformExec")
661
662 - def platformInjectSo(self, filename):
663 raise Exception("Platform must implement injectso()")
664
665 - def platformGetFds(self):
666 """ 667 Return what getFds() wants for this particular platform 668 """ 669 raise Exception("Platform must implement platformGetFds()")
670
671 - def platformGetSignal(self):
672 ''' 673 Return the currently posted exception/signal.... 674 ''' 675 # Default to the thing they all should do... 676 return self.getMeta('PendingSignal', None)
677
678 - def platformSetSignal(self, sig=None):
679 ''' 680 Set the current signal to deliver to the process on cont. 681 (Use None for no signal delivery. 682 ''' 683 self.setMeta('PendingSignal', sig)
684
685 - def platformGetMaps(self):
686 """ 687 Return a list of the memory maps where each element has 688 the following structure: 689 (address, length, perms, file="") 690 NOTE: By Default this list is available as Trace.maps 691 because the default implementation attempts to populate 692 them on every break/stop/etc... 693 """ 694 raise Exception("Platform must implement GetMaps")
695
696 - def platformPs(self):
697 """ 698 Actually return a list of tuples in the format 699 (pid, name) for this platform 700 """ 701 raise Exception("Platform must implement Ps")
702
703 - def archGetStackTrace(self):
704 raise Exception("Architecure must implement argGetStackTrace()!")
705
706 - def archAddWatchpoint(self, address, size=4, perms="rw"):
707 """ 708 Add a watchpoint for the given address. Raise if the platform 709 doesn't support, or too many are active... 710 """ 711 raise Exception("Architecture doesn't implement watchpoints!")
712
713 - def archRemWatchpoint(self, address):
714 raise Exception("Architecture doesn't implement watchpoints!")
715
716 - def archCheckWatchpoints(self):
717 """ 718 If the current register state indicates that a watchpoint was hit, 719 return the address of the watchpoint and clear the event. Otherwise 720 return None 721 """ 722 pass
723
724 - def archGetRegCtx(self):
725 """ 726 Return a new empty envi.registers.RegisterContext object for this 727 trace. 728 """ 729 raise Exception("Platform must implement archGetRegCtx()")
730
731 - def getStackTrace(self):
732 """ 733 Return a list of the stack frames for this process 734 (currently Intel/ebp based only). Each element of the 735 "frames list" consists of another list which is (eip,ebp) 736 """ 737 raise Exception("Platform must implement getStackTrace()")
738
739 - def getExe(self):
740 """ 741 Get the full path to the main executable for this 742 *attached* Trace 743 """ 744 return self.getMeta("ExeName","Unknown")
745
746 - def platformAttach(self, pid):
747 """ 748 Actually carry out attaching to a target process. Like 749 platformStepi this is expected to be ATOMIC and not return 750 until a complete attach. 751 """ 752 raise Exception("Platform must implement platformAttach()")
753
754 - def platformContinue(self):
755 raise Exception("Platform must implement platformContinue()")
756
757 - def platformDetach(self):
758 """ 759 Actually perform the detach for this type 760 """ 761 raise Exception("Platform must implement platformDetach()")
762
763 - def platformStepi(self):
764 """ 765 PlatformStepi should be ATOMIC, meaning it gets called, and 766 by the time it returns, you're one step further. This is completely 767 regardless of blocking/nonblocking/whatever. 768 """ 769 raise Exception("Platform must implement platformStepi!")
770
771 - def platformCall(self, address, args, convention=None):
772 """ 773 Platform call takes an address, and an array of args 774 (string types will be mapped and located for you) 775 776 platformCall is expected to return a dicionary of the 777 current register values at the point where the call 778 has returned... 779 """ 780 raise Exception("Platform must implement platformCall")
781
782 - def platformGetRegCtx(self, threadid):
783 raise Exception("Platform must implement platformGetRegCtx!")
784
785 - def platformSetRegCtx(self, threadid, ctx):
786 raise Exception("Platform must implement platformSetRegCtx!")
787
788 - def platformProtectMemory(self, va, size, perms):
789 raise Exception("Plaform does not implement protect memory")
790
791 - def platformAllocateMemory(self, size, perms=e_mem.MM_RWX, suggestaddr=0):
792 raise Exception("Plaform does not implement allocate memory")
793
794 - def platformReadMemory(self, address, size):
795 raise Exception("Platform must implement platformReadMemory!")
796
797 - def platformWriteMemory(self, address, bytes):
798 raise Exception("Platform must implement platformWriteMemory!")
799
800 - def platformGetMemFault(self):
801 """ 802 Return the addr of the current memory fault 803 or None 804 """ 805 #NOTE: This is used by the PageWatchpoint subsystem 806 # (and is still considered experimental) 807 return None,None
808
809 - def platformWait(self):
810 """ 811 Wait for something interesting to occur and return a 812 *platform specific* representation of what happened. 813 814 This will then be passed to the platformProcessEvent() 815 method which will be responsible for doing things like 816 firing notifiers. Because the platformWait() method needs 817 to be commonly @threadwrap and you can't fire notifiers 818 from within a threadwrapped function... 819 """ 820 raise Exception("Platform must implement platformWait!")
821
822 - def platformProcessEvent(self, event):
823 """ 824 This method processes the event data provided by platformWait() 825 826 This method is responsible for firing ALL notifiers *except*: 827 828 vtrace.NOTIFY_CONTINUE - This is handled by the run api (and isn't the result of an event) 829 """ 830 raise Exception("Platform must implement platformProcessEvent")
831
832 - def platformOpenFile(self, filename):
833 # Open a file for reading 834 return file(filename, 'rb')
835
836 - def platformParseBinary(self, filename, baseaddr, normname):
837 """ 838 Platforms must parse the given binary file and load any symbols 839 into the internal SymbolResolver using self.addSymbol() 840 """ 841 raise Exception("Platform must implement platformParseBinary")
842
843 - def platformRelease(self):
844 ''' 845 Called back on release. 846 ''' 847 pass
848 849 import threading
850 -def threadwrap(func):
851 def trfunc(self, *args, **kwargs): 852 if threading.currentThread().__class__ == TracerThread: 853 return func(self, *args, **kwargs) 854 # Proxy the call through a single thread 855 q = Queue() 856 # FIXME change calling convention! 857 args = (self, ) + args 858 self.thread.queue.put((func, args, kwargs, q)) 859 ret = q.get() 860 if issubclass(ret.__class__, Exception): 861 raise ret 862 return ret
863 return trfunc 864
865 -class TracerThread(Thread):
866 """ 867 Ok... so here's the catch... most debug APIs do *not* allow 868 one thread to do the attach and another to do continue and another 869 to do wait... they just dont. So there. I have to make a thread 870 per-tracer (on most platforms) and proxy requests (for *some* trace 871 API methods) to it for actual execution. SUCK! 872 873 However, this lets async things like GUIs and threaded things like 874 cobra not have to be aware of which one is allowed and not allowed 875 to make particular calls and on what platforms... YAY! 876 """
877 - def __init__(self):
878 Thread.__init__(self) 879 self.queue = Queue() 880 self.setDaemon(True) 881 self.start()
882
883 - def run(self):
884 """ 885 Run in a circle getting requests from our queue and 886 executing them based on the thread. 887 """ 888 while True: 889 try: 890 qobj = self.queue.get() 891 if qobj == None: 892 break 893 meth, args, kwargs, queue = qobj 894 try: 895 queue.put(meth(*args, **kwargs)) 896 except Exception,e: 897 queue.put(e) 898 if vtrace.verbose: 899 traceback.print_exc() 900 continue 901 except: 902 if vtrace.verbose: 903 traceback.print_exc()
904