"""
Breakpoint Objects
"""
# Copyright (C) 2007 Invisigoth - See LICENSE file for details
import time
import vtrace
class Breakpoint:
[docs] """
Breakpoints in Vtrace are platform independant objects that
use the underlying trace objects to get things like the
program counter and the break instruction. As long as
platfforms are completely implemented, all breakpoint
objects should be portable.
"""
bpcodeobj = {} # Cache compiled code objects on the class def
def __init__(self, address, expression=None):
self.saved = None
self.resonce = False
self.address = address
self.breakinst = None
self.enabled = True
self.active = False
self.fastbreak = False
self.id = -1
self.vte = None
self.bpcode = None
if expression:
self.vte = expression
def getAddress(self):
[docs] """
This will return the address for this breakpoint. If the return'd
address is None, this is a deferred breakpoint which needs to have
resolveAddress() called to attempt to set the address.
"""
return self.address
def getId(self):
[docs] return self.id
def getName(self):
[docs] if self.vte:
return str(self.vte)
return "0x%.8x" % self.address
def __repr__(self):
if self.address == None:
addr = "unresolved"
else:
addr = "0x%.8x" % self.address
return "[%d] %s %s: %s" % (self.id, addr, self.__class__.__name__, self.getName())
def inittrace(self, trace):
[docs] '''
A callback to do housekeeping at the time the breakpoint is
added to the tracer object. This should be used instead of activate
for initialization time infoz to save on time per activate call...
'''
self.breakinst = trace.archGetBreakInstr()
def resolvedaddr(self, trace, addr):
[docs] '''
An initialization callback which will be executed when the
actual address for this breakpoint has been resolved.
'''
self.saved = trace.readMemory(addr, len(self.breakinst))
def activate(self, trace):
[docs] """
Actually store off and replace memory for this process. This
is caried out by the trace object itself when it begins
running or stops. You probably never need to call this
(see isEnabled() setEnabled() for boolean enable/disablle)
"""
trace.requireAttached()
if not self.active:
if self.address != None:
trace.writeMemory(self.address, self.breakinst)
self.active = True
return self.active
def deactivate(self, trace):
[docs] """
Repair the process for continued execution. this does NOT
make a breakpoint *inactive*, but removes it's "0xcc" from mem
(see isEnabled() setEnabled() for boolean enable/dissable)
"""
trace.requireAttached()
if self.active:
self.active = False
trace.writeMemory(self.address, self.saved)
return self.active
def resolveAddress(self, trace):
[docs] """
Try to resolve the address for this break. If this is a statically
addressed break, just return the address. If it has an "expression"
use that to resolve the address...
"""
if self.address == None and self.vte:
try:
self.address = trace.parseExpression(self.vte)
except Exception, e:
self.address == None
# If we resolved, lets get our saved code...
if self.address != None and not self.resonce:
self.resonce = True
self.resolvedaddr(trace, self.address)
return self.address
def isEnabled(self):
[docs] """
Is this breakpoint "enabled"?
"""
return self.enabled
def setEnabled(self, enabled=True):
[docs] """
Set this breakpoints "enabled" status
"""
self.enabled = enabled
def setBreakpointCode(self, pystr):
[docs] """
Use this method to set custom python code to run when this
breakpoint gets hit. The code will have the following objects
mapped into it's namespace when run:
trace - the tracer
vtrace - the vtrace module
bp - the breakpoint
"""
self.bpcode = pystr
Breakpoint.bpcodeobj.pop(self.id, None)
def getBreakpointCode(self):
[docs] """
Return the current python string that will be run when this break is hit.
"""
return self.bpcode
def notify(self, event, trace):
[docs] """
Breakpoints may also extend and implement "notify" which will be
called whenever they are hit. If you want to continue the ability
for this breakpoint to have bpcode, you must call this method from
your override.
"""
if self.bpcode != None:
cobj = Breakpoint.bpcodeobj.get(self.id, None)
if cobj == None:
fname = "BP:%d (0x%.8x)" % (self.id, self.address)
cobj = compile(self.bpcode, fname, "exec")
Breakpoint.bpcodeobj[self.id] = cobj
d = vtrace.VtraceExpressionLocals(trace)
d['bp'] = self
exec(cobj, None, d)
class TrackerBreak(Breakpoint):
[docs] """
A breakpoint which will record how many times it was hit
(by the address it was at) as metadata for the tracer.
"""
def notify(self, event, trace):
[docs] tb = trace.getMeta("TrackerBreak", None)
if tb == None:
tb = {}
trace.setMeta("TrackerBreak", tb)
tb[self.address] = (tb.get(self.address,0) + 1)
Breakpoint.notify(self, event, trace)
class OneTimeBreak(Breakpoint):
[docs] """
This type of breakpoint is exclusivly for marking
and code-coverage stuff. It removes itself.
(most frequently used with a continued trace)
"""
def notify(self, event, trace):
[docs] trace.removeBreakpoint(self.id)
Breakpoint.notify(self, event, trace)
class StopRunForeverBreak(Breakpoint):
[docs] """
This breakpoint will turn off RunForever mode
on the tracer object when hit. it's a good way
to let things run on and on processing exceptions
but stop when you get to this one thing.
"""
def notify(self, event, trace):
[docs] trace.setMode("RunForever", False)
Breakpoint.notify(self, event, trace)
class StopAndRemoveBreak(Breakpoint):
[docs] """
When hit, take the tracer out of run-forever mode and
remove this breakpoint.
"""
def notify(self, event, trace):
[docs] trace.setMode("RunForever", False)
trace.removeBreakpoint(self.id)
Breakpoint.notify(self, event, trace)
class CallBreak(Breakpoint):
[docs] """
A special breakpoint which will restore process
state (registers in particular) when it gets hit.
This is primarily used by the call method inside
the trace object to restore original state
after a successful "call" method call.
Additionally, the endregs dict will be filled in
with the regs at the time it was hit and kept until
we get garbage collected...
"""
def __init__(self, address, saved_regs):
Breakpoint.__init__(self, address)
self.endregs = None # Filled in when we get hit
self.saved_regs = saved_regs
def notify(self, event, trace):
[docs] self.endregs = trace.getRegisters()
trace.removeBreakpoint(self.id)
trace.setRegisters(self.saved_regs)
trace.setMeta("PendingSignal", None)
class SnapshotBreak(Breakpoint):
[docs] """
A special breakpoint type which will produce vtrace snapshots
for the target process when hit. The snapshots will be saved
to a default name of <exename>-<timestamp>.vsnap. This is not
recommended for use in heavily hit breakpoints as taking a
snapshot is processor intensive.
"""
def notify(self, event, trace):
[docs] exe = trace.getExe()
snap = trace.takeSnapshot()
snap.saveToFile("%s-%d.vsnap" % (exe,time.time()))
Breakpoint.notify(self, event, trace)