177 lines
7.2 KiB
Python
177 lines
7.2 KiB
Python
from collections import defaultdict
|
|
|
|
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.interruption_managers.base import InterruptionManager
|
|
from ScEpTIC.emulator.intermittent_executor.profiling import ProfilingReset, RunInfo, ProfilingLog
|
|
from ScEpTIC.emulator.io.input import InputManager, InputSkeleton
|
|
from ScEpTIC.emulator.io.output import OutputManager, OutputSkeleton
|
|
from ScEpTIC.exceptions import RuntimeException, StopException, MemoryException
|
|
|
|
|
|
class ProfilingInterruptionManager(InterruptionManager):
|
|
"""
|
|
Interruption manager for profiling intermittent executions
|
|
"""
|
|
|
|
requires_static_checkpoint = False
|
|
|
|
def __init__(self, vmstate, state_retention_manager):
|
|
super().__init__(vmstate, state_retention_manager)
|
|
self.output_functions = []
|
|
self.input_functions = []
|
|
|
|
self.is_dynamic = not self.state_retention.static_placement
|
|
|
|
for key in OutputSkeleton.output_functions:
|
|
function_name = '{}{}'.format(self.vmstate.ir_function_prefix, OutputSkeleton.output_functions[key])
|
|
self.output_functions.append(function_name)
|
|
|
|
for key in InputSkeleton.input_functions:
|
|
function_name = '{}{}'.format(self.vmstate.ir_function_prefix, InputSkeleton.input_functions[key])
|
|
self.input_functions.append(function_name)
|
|
|
|
def _execution_condition(self):
|
|
"""
|
|
Checks if we are inside the current checkpoint interval
|
|
"""
|
|
|
|
if self.vmstate.program_end_reached:
|
|
return False
|
|
|
|
current_instruction = self.vmstate.current_instruction
|
|
|
|
if isinstance(current_instruction, CallOperation) and current_instruction.name == self.state_retention.routine_names['save']:
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
def intermittent_execution_required(self):
|
|
return True
|
|
|
|
|
|
def _is_call_to_output(self):
|
|
current_instruction = self.vmstate.current_instruction
|
|
return isinstance(current_instruction, CallOperation) and current_instruction.name in self.output_functions
|
|
|
|
|
|
def run_with_intermittent_execution(self):
|
|
logging.info('[ProfilingInterruptionManager] Running {} in intermittent execution scenario.'.format(self.vmstate.register_file.pc))
|
|
|
|
# could be also the first instruction of the program
|
|
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)
|
|
|
|
# Only after first instruction of the program
|
|
if self.vmstate.global_clock > 0:
|
|
current_instruction = self.vmstate.current_instruction
|
|
|
|
if not isinstance(current_instruction, CallOperation) or current_instruction.name != self.state_retention.routine_names['save']:
|
|
raise RuntimeException('[ProfilingInterruptionManager] Checkpoint operation required. {} given.'.format(current_instruction.__class__.__name__))
|
|
|
|
# PC -> checkpoint. skip PC, since the simulator executes it.
|
|
self.vmstate.register_file.pc.increment_pc()
|
|
self.vmstate.global_clock += 1
|
|
|
|
# do checkpoint
|
|
self.state_retention.execute_state_save()
|
|
self.state_retention.save_dump()
|
|
|
|
force_stop = False
|
|
|
|
power_failures = 0
|
|
|
|
tracking = defaultdict(list)
|
|
|
|
ProfilingLog.tracking = tracking
|
|
ProfilingLog.power_failures = power_failures
|
|
|
|
while self._execution_condition():
|
|
|
|
current_instruction = self.vmstate.current_instruction
|
|
|
|
# if it is a call operation, can be an input/output operation
|
|
if isinstance(current_instruction, CallOperation):
|
|
|
|
io_type = None
|
|
|
|
if current_instruction.name in self.output_functions:
|
|
io_type = 'OUTPUT'
|
|
|
|
elif current_instruction.name in self.input_functions:
|
|
io_type = 'INPUT'
|
|
|
|
# if is an actual I/O operation, track it.
|
|
if io_type is not None:
|
|
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.name.replace(self.vmstate.ir_function_prefix, '')
|
|
|
|
if io_type == 'OUTPUT':
|
|
io_name = OutputSkeleton.output_names[function_name]
|
|
io_value = OutputManager.output_table[io_name]
|
|
|
|
else:
|
|
io_name = InputSkeleton.input_names[function_name]
|
|
io_value = InputManager.input_table[io_name]
|
|
|
|
# create tracking information
|
|
runinfo = RunInfo(io_type, pc, global_clock, power_failures, function_name, io_name, io_value)
|
|
|
|
# append tracking information
|
|
tracking[power_failures].append(runinfo)
|
|
|
|
# update variable
|
|
current_instruction = self.vmstate.current_instruction
|
|
|
|
# if it is a profiling reset
|
|
if isinstance(current_instruction, ProfilingReset) and current_instruction.evaluate_reset_condition(power_failures):
|
|
power_failures += 1
|
|
|
|
# Support for dynamic checkpoint mechanisms / Just In Time
|
|
# Custom routine can be emulated using execute_before / execute_after functions
|
|
if self.is_dynamic:
|
|
self.state_retention.execute_state_save()
|
|
self.state_retention.save_dump()
|
|
|
|
ProfilingLog.power_failures = power_failures
|
|
|
|
self.state_retention.execute_state_restore()
|
|
continue
|
|
|
|
# run the operation. If an exception occurs, sign it and stop tracking
|
|
try:
|
|
self.vmstate.run_step()
|
|
|
|
# Skip to next checkpoint interval if a stack activation record anomaly was found
|
|
if self.has_stack_activation_record_anomalies(checkpoint_pc, False):
|
|
tracking[power_failures].append('Unable to continue the analysis due to a stack anomaly.')
|
|
self.state_retention.restore_dump()
|
|
force_stop = True
|
|
break
|
|
|
|
except MemoryException as e:
|
|
tracking[power_failures].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()
|