168 lines
6.7 KiB
Python
168 lines
6.7 KiB
Python
import copy
|
|
import logging
|
|
|
|
from ScEpTIC.AST.elements.instructions.other_operations import CallOperation
|
|
from ScEpTIC.AST.elements.instructions.termination_instructions import ReturnOperation
|
|
from ScEpTIC.emulator.intermittent_executor.anomalies import OutputAnomaly
|
|
from ScEpTIC.emulator.intermittent_executor.interruption_managers.base import InterruptionManager
|
|
from ScEpTIC.emulator.intermittent_executor.profiling import RunInfo
|
|
from ScEpTIC.emulator.io.output import OutputManager, OutputSkeleton
|
|
from ScEpTIC.exceptions import RuntimeException, StopException, MemoryException
|
|
|
|
|
|
class OutputInterruptionManager(InterruptionManager):
|
|
"""
|
|
Interruption manager for analyzing output interactions
|
|
"""
|
|
|
|
requires_static_checkpoint = True
|
|
|
|
def __init__(self, vmstate, state_retention_manager):
|
|
super().__init__(vmstate, state_retention_manager)
|
|
self.output_functions = []
|
|
self.ignore_pc = []
|
|
|
|
for key in OutputManager.output_idempotency_table:
|
|
function_name = '{}{}'.format(self.vmstate.ir_function_prefix, OutputSkeleton.output_functions[key])
|
|
self.output_functions.append(function_name)
|
|
|
|
|
|
def intermittent_execution_required(self):
|
|
if self.vmstate.register_file.pc in self.ignore_pc:
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
def _intermittent_condition(self):
|
|
"""
|
|
Returns if the intermittent execution test must continue.
|
|
"""
|
|
|
|
if self.vmstate.program_end_reached:
|
|
return False
|
|
|
|
current_instruction = self.vmstate.current_instruction
|
|
|
|
# if static checkpoint, stop only when another checkpoint is reached.
|
|
if isinstance(current_instruction, CallOperation) and current_instruction.resolve_function_name() == self.state_retention.routine_names['save']:
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
def run_with_intermittent_execution(self):
|
|
logging.info('[OutputInterruptionManager] Running {} in intermittent execution scenario.'.format(self.vmstate.register_file.pc))
|
|
|
|
force_checkpoint = False
|
|
|
|
current_instruction = self.vmstate.current_instruction
|
|
|
|
# check constraint
|
|
if not isinstance(current_instruction, CallOperation) or current_instruction.resolve_function_name() != self.state_retention.routine_names['save']:
|
|
if self.vmstate.global_clock == 0:
|
|
force_checkpoint = True
|
|
else:
|
|
raise RuntimeException('[OutputInterruptionManager] Invalid operation {} for starting intermittent execution. Checkpoint operation required.'.format(current_instruction.__class__.__name__))
|
|
|
|
# save checkpoint pc
|
|
checkpoint_pc = copy.deepcopy(self.vmstate.register_file.pc)
|
|
checkpoint_clock = self.vmstate.global_clock
|
|
self.vmstate.state_save_clock_pc_maps[checkpoint_clock] = copy.deepcopy(checkpoint_pc)
|
|
|
|
# do checkpoint
|
|
if not force_checkpoint:
|
|
self.vmstate.register_file.pc.increment_pc()
|
|
self.vmstate.global_clock += 1
|
|
|
|
self.state_retention.execute_state_save()
|
|
self.state_retention.save_dump()
|
|
|
|
# initialize tracking dict
|
|
tracking = {}
|
|
reset_no = {}
|
|
out_vals = {}
|
|
|
|
for i in self.output_functions:
|
|
tracking[i] = []
|
|
reset_no[i] = 0
|
|
|
|
resetting_out = None
|
|
|
|
force_stop = False
|
|
|
|
# run until the required number of runs is reached
|
|
while self._intermittent_condition():
|
|
|
|
if self.has_stack_activation_record_anomalies(checkpoint_pc, False):
|
|
self.ignore_pc.append(checkpoint_pc)
|
|
tracking[resetting_out].append('Unable to continue the analysis due to a stack anomaly.')
|
|
self.state_retention.restore_dump()
|
|
break
|
|
|
|
current_instruction = self.vmstate.current_instruction
|
|
|
|
# if is a call operation, can be an input/output operation
|
|
if isinstance(current_instruction, CallOperation) and current_instruction.resolve_function_name() in self.output_functions and reset_no[current_instruction.resolve_function_name()] < 2:
|
|
resetting_out = current_instruction.resolve_function_name()
|
|
|
|
pc = copy.deepcopy(self.vmstate.register_file.pc)
|
|
global_clock = self.vmstate.global_clock
|
|
|
|
# run i/o function
|
|
while not isinstance(self.vmstate.current_instruction, ReturnOperation):
|
|
self.vmstate.run_step()
|
|
|
|
# return from i/o function
|
|
self.vmstate.run_step()
|
|
|
|
function_name = current_instruction.resolve_function_name().replace(self.vmstate.ir_function_prefix, '')
|
|
|
|
io_name = OutputSkeleton.output_names[function_name]
|
|
io_value = OutputManager.output_table[io_name]
|
|
|
|
if io_name not in out_vals:
|
|
out_vals[io_name] = io_value
|
|
else:
|
|
idempotency = OutputManager.IDEMPOTENT if io_value == out_vals[io_name] else OutputManager.NON_IDEMPOTENT
|
|
required_idempotency = OutputManager.output_idempotency_table[io_name]
|
|
OutputManager.measure_idempotency(io_name, idempotency)
|
|
|
|
if idempotency != required_idempotency:
|
|
anomaly = OutputAnomaly(checkpoint_pc, checkpoint_clock, self.vmstate.register_file.pc, self.vmstate.global_clock, io_name, idempotency, required_idempotency)
|
|
if anomaly not in self.vmstate.anomalies:
|
|
self.vmstate.anomalies.append(anomaly)
|
|
|
|
|
|
# create tracking information
|
|
runinfo = RunInfo('OUTPUT', pc, global_clock, reset_no[resetting_out], function_name, io_name, io_value)
|
|
|
|
# append tracking information
|
|
tracking[resetting_out].append(runinfo)
|
|
|
|
reset_no[resetting_out] += 1
|
|
|
|
if reset_no[resetting_out] == 1:
|
|
self.vmstate.reset()
|
|
self.state_retention.execute_state_restore()
|
|
|
|
else:
|
|
self.state_retention.restore_dump()
|
|
|
|
# run the operation. If an exception occurs, sign it and skip tracking (cannot continue)
|
|
try:
|
|
self.vmstate.run_step()
|
|
|
|
except MemoryException as e:
|
|
self.ignore_pc.append(checkpoint_pc)
|
|
tracking[resetting_out].append('Unable to continue the analysis due to a MemoryException: {}'.format(e))
|
|
self.state_retention.restore_dump()
|
|
force_stop = True
|
|
break
|
|
|
|
# append to global tracking info
|
|
self.vmstate.profiling[checkpoint_clock] = tracking
|
|
|
|
if force_stop:
|
|
raise StopException('')
|