New Policy class (#368)
* New Policy class * Move visited to Policy - Fix contexts * Add BranchLimited * signals -> events for branchlimited
This commit is contained in:
parent
c6f8fd1291
commit
f9b02bd194
@ -45,33 +45,34 @@ class Policy(object):
|
|||||||
self._executor.subscribe('did_add_state', self._add_state_callback)
|
self._executor.subscribe('did_add_state', self._add_state_callback)
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def locked_context(self):
|
def locked_context(self, key=None, default=dict):
|
||||||
''' Policy shared context dictionary '''
|
''' Policy shared context dictionary '''
|
||||||
with self._executor.locked_context() as ctx:
|
keys = ['policy']
|
||||||
policy_context = ctx.get('policy', None)
|
if key is not None:
|
||||||
if policy_context is None:
|
keys.append(key)
|
||||||
policy_context = dict()
|
with self._executor.locked_context('.'.join(keys), default) as policy_context:
|
||||||
yield policy_context
|
yield policy_context
|
||||||
ctx['policy'] = policy_context
|
|
||||||
|
|
||||||
def _add_state_callback(self, state_id, state):
|
def _add_state_callback(self, state_id, state):
|
||||||
''' Save prepare(state) on policy shared context before
|
''' Save summarize(state) on policy shared context before
|
||||||
the state is stored
|
the state is stored
|
||||||
'''
|
'''
|
||||||
with self.locked_context() as ctx:
|
summary = self.summarize(state)
|
||||||
metric = self.prepare(state)
|
if summary is None:
|
||||||
if metric is not None:
|
return
|
||||||
ctx[state_id] = metric
|
with self.locked_context('summaries', dict) as ctx:
|
||||||
|
ctx[state_id] = summary
|
||||||
|
|
||||||
def prepare(self, state):
|
def summarize(self, state):
|
||||||
''' Process a state and keep enough data to later decide it's
|
'''
|
||||||
priority #fixme rephrase
|
Extract the relevant information from a state for later
|
||||||
|
prioritization
|
||||||
'''
|
'''
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def choice(self, state_ids):
|
def choice(self, state_ids):
|
||||||
''' Select a state id from states_id.
|
''' Select a state id from state_ids.
|
||||||
self.context has a dict mapping state_ids -> prepare(state)'''
|
self.context has a dict mapping state_ids -> summarize(state)'''
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
class Random(Policy):
|
class Random(Policy):
|
||||||
@ -87,9 +88,20 @@ class Uncovered(Policy):
|
|||||||
super(Uncovered, self).__init__(executor, *args, **kwargs)
|
super(Uncovered, self).__init__(executor, *args, **kwargs)
|
||||||
#hook on the necesary executor signals
|
#hook on the necesary executor signals
|
||||||
#on callbacks save data in executor.context['policy']
|
#on callbacks save data in executor.context['policy']
|
||||||
|
self._executor.subscribe('will_load_state', self._register)
|
||||||
|
|
||||||
def prepare(self, state):
|
def _register(self, *args):
|
||||||
''' this is what we need to save for choosing later '''
|
self._executor.subscribe('will_execute_instruction', self._visited_callback)
|
||||||
|
|
||||||
|
def _visited_callback(self, state, instr):
|
||||||
|
''' Maintain our own copy of the visited set
|
||||||
|
'''
|
||||||
|
pc = state.platform.current.PC
|
||||||
|
with self.locked_context('visited', set) as ctx:
|
||||||
|
ctx.add(pc)
|
||||||
|
|
||||||
|
def summarize(self, state):
|
||||||
|
''' Save the last pc before storing the state '''
|
||||||
return state.cpu.PC
|
return state.cpu.PC
|
||||||
|
|
||||||
def choice(self, state_ids):
|
def choice(self, state_ids):
|
||||||
@ -102,10 +114,46 @@ class Uncovered(Policy):
|
|||||||
for _id in state_ids:
|
for _id in state_ids:
|
||||||
if lastpc.get(_id, None) not in visited:
|
if lastpc.get(_id, None) not in visited:
|
||||||
interesting.add(_id)
|
interesting.add(_id)
|
||||||
if len(interesting) > 0:
|
|
||||||
return random.choice(tuple(interesting))
|
return random.choice(tuple(interesting))
|
||||||
|
|
||||||
|
class BranchLimited(Policy):
|
||||||
|
def __init__(self, executor, *args, **kwargs):
|
||||||
|
super(BranchLimited, self).__init__(executor, *args, **kwargs)
|
||||||
|
self._executor.subscribe('will_load_state', self._register)
|
||||||
|
self._limit = kwargs.get('limit', 5)
|
||||||
|
|
||||||
|
def _register(self, *args):
|
||||||
|
self._executor.subscribe('will_execute_instruction', self._visited_callback)
|
||||||
|
|
||||||
|
def _visited_callback(self, state, instr):
|
||||||
|
''' Maintain our own copy of the visited set
|
||||||
|
'''
|
||||||
|
pc = state.platform.current.PC
|
||||||
|
with self.locked_context('visited', dict) as ctx:
|
||||||
|
ctx[pc] = ctx.get(pc, 0) + 1
|
||||||
|
|
||||||
|
def summarize(self, state):
|
||||||
|
return state.cpu.PC
|
||||||
|
|
||||||
|
def choice(self, state_ids):
|
||||||
|
interesting = set(state_ids)
|
||||||
|
with self.locked_context() as policy_ctx:
|
||||||
|
visited = policy_ctx.get('visited', dict())
|
||||||
|
summaries = policy_ctx.get('summaries', dict())
|
||||||
|
lst = []
|
||||||
|
for id_, pc in summaries.items():
|
||||||
|
cnt = visited.get(pc, 0)
|
||||||
|
if id_ not in state_ids:
|
||||||
|
continue
|
||||||
|
if cnt <= self._limit:
|
||||||
|
lst.append((id_, visited.get(pc, 0)))
|
||||||
|
lst = sorted(lst, key=lambda x: x[1])
|
||||||
|
|
||||||
|
if lst:
|
||||||
|
return lst[0][0]
|
||||||
else:
|
else:
|
||||||
return random.choice(state_ids)
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Executor(Eventful):
|
class Executor(Eventful):
|
||||||
@ -146,7 +194,8 @@ class Executor(Eventful):
|
|||||||
#scheduling priority policy (wip)
|
#scheduling priority policy (wip)
|
||||||
#Set policy
|
#Set policy
|
||||||
policies = {'random': Random,
|
policies = {'random': Random,
|
||||||
'uncovered': Uncovered
|
'uncovered': Uncovered,
|
||||||
|
'branchlimited': BranchLimited,
|
||||||
}
|
}
|
||||||
self._policy = policies[policy](self)
|
self._policy = policies[policy](self)
|
||||||
assert isinstance(self._policy, Policy)
|
assert isinstance(self._policy, Policy)
|
||||||
@ -156,7 +205,7 @@ class Executor(Eventful):
|
|||||||
logger.error("Ignoring initial state")
|
logger.error("Ignoring initial state")
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def locked_context(self):
|
def locked_context(self, key=None, default=dict):
|
||||||
''' Executor context is a shared memory object. All workers share this.
|
''' Executor context is a shared memory object. All workers share this.
|
||||||
It needs a lock. Its used like this:
|
It needs a lock. Its used like this:
|
||||||
|
|
||||||
@ -165,8 +214,24 @@ class Executor(Eventful):
|
|||||||
visited.append(state.cpu.PC)
|
visited.append(state.cpu.PC)
|
||||||
context['visited'] = visited
|
context['visited'] = visited
|
||||||
'''
|
'''
|
||||||
|
assert default in (list, dict, set)
|
||||||
with self._lock:
|
with self._lock:
|
||||||
|
if key is None:
|
||||||
yield self._shared_context
|
yield self._shared_context
|
||||||
|
elif '.' in key:
|
||||||
|
keys = key.split('.')
|
||||||
|
with self.locked_context('.'.join(keys[:-1])) as sub_context:
|
||||||
|
sub_sub_context = sub_context.get(keys[-1], None)
|
||||||
|
if sub_sub_context is None:
|
||||||
|
sub_sub_context = default()
|
||||||
|
yield sub_sub_context
|
||||||
|
sub_context[keys[-1]] = sub_sub_context
|
||||||
|
else:
|
||||||
|
sub_context = self._shared_context.get(key, None)
|
||||||
|
if sub_context is None:
|
||||||
|
sub_context = default()
|
||||||
|
yield sub_context
|
||||||
|
self._shared_context[key] = sub_context
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -264,6 +329,8 @@ class Executor(Eventful):
|
|||||||
self._lock.wait()
|
self._lock.wait()
|
||||||
|
|
||||||
state_id = self._policy.choice(list(self._states))
|
state_id = self._policy.choice(list(self._states))
|
||||||
|
if state_id is None:
|
||||||
|
return None
|
||||||
del self._states[self._states.index(state_id)]
|
del self._states[self._states.index(state_id)]
|
||||||
return state_id
|
return state_id
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user