2026-07-10 10:38:57 +02:00

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('')