|
- import os, shutil
- import re
- import inspect
- import subprocess
- import sys
- from os.path import join as pjoin
-
- from .config import (
- TEMPLATE_REPOSITORY,
- PROJECTS_REPOSITORY,
- ELMO_TOOL_REPOSITORY,
- ELMO_EXECUTABLE_NAME,
- MODULE_PATH,
- ELMO_OUTPUT_ENCODING,
- SEARCH_EXCLUSION_TAG,
- )
- from .project_base import SimulationProject
- from .utils import Color
-
- ############ GETTERS ############
-
- def search_simulations_in_repository(repository, criteria=lambda x:True):
- """ Search simulation classes in the 'repository' verifying the 'criteria'
- Return a list of 'SimulationProject' subclasses
- :repository: Repository of the searched simulation classes (string)
- :criteria: Boolean function with 'SimulationProject' subclasses for input
- """
-
- projects = {}
-
- from .utils import write
- for root, repositories, files in os.walk(repository):
- for filename in files:
- if re.fullmatch(r'.*project.*\.py', filename):
- complete_filename = pjoin(root, filename)
-
- # Encapsulate the project
- globals = {
- #'__builtins__': {'__build_class__': __build_class__},
- 'SimulationProject': SimulationProject,
- 'write': write,
- }
- locals = {}
-
- # Read the project code
- with open(complete_filename, 'r') as _file:
- project = ''.join(_file.read())
- if ('SimulationProject' not in project) or (SEARCH_EXCLUSION_TAG in project):
- continue # Exclude this file
- exec(project, globals, locals)
-
- # Extract the simulations
- for key, obj in locals.items():
- if inspect.isclass(obj) and issubclass(obj, SimulationProject):
- if criteria(obj):
- if key in projects:
- print(Color.WARNING + \
- 'Warning! Many simulations with the same name. ' + \
- 'Simulation ignored: {} in {}'.format(
- key, complete_filename[len(repository)+1:]) + \
- Color.ENDC
- )
- else:
- obj.set_project_directory(os.path.abspath(root))
- projects[key] = obj
-
- return projects
-
- def search_simulations_in_module(criteria=lambda x:True):
- """ Search simulation classes among the module projects verifying the 'criteria'
- Return a list of 'SimulationProject' subclasses
- :criteria: Boolean function with 'SimulationProject' subclasses for input
- """
- projects_path = pjoin(MODULE_PATH, PROJECTS_REPOSITORY)
- return search_simulations_in_repository(projects_path, criteria)
-
- def search_simulations(repository='.', criteria=lambda x:True):
- """ Search simulation classes in the 'repository' and among
- the module projects verifying the 'criteria'
- Return a list of 'SimulationProject' subclasses
- :repository: Repository of the searched simulation classes (string)
- :criteria: Boolean function with 'SimulationProject' subclasses for input
- """
- projects = search_simulations_in_repository(repository, criteria)
-
- module_projects = search_simulations_in_module(criteria)
- for key, project in module_projects.items():
- if key not in projects:
- projects[key] = project
-
- return projects
-
- class SimulationNotFoundError(Exception):
- pass
-
- class TooManySimulationsError(Exception):
- pass
-
- def get_simulation(classname=None, repository='.'):
- """ Get a simulation class in the 'repository' with the specific 'classname'
- Return a subclass of 'SimulationProject' class
- :classname: Name of the searched simulation class (string, optional)
- :repository: Repository of the searched simulation class (string, optional)
- """
- criteria = lambda x: True
- if classname is not None:
- criteria = lambda x: x.__name__ == classname.strip()
- projects = search_simulations(repository, criteria)
-
- if len(projects) == 1:
- return list(projects.values())[0]
- elif len(projects) < 1:
- raise SimulationNotFoundError()
- else:
- raise TooManySimulationsError()
-
-
- ############ SETTERS ############
-
- def create_simulation(repository, classname, is_a_module_project=False):
- """ Create a Simulation Project.
- It builds a repository with the standard content for the project.
- Return the absolute path of the created repository
- :repository: Name of repository which will be created (string)
- :classname: Name of the Python class to manage the project (string)
- :is_a_module_project: If True and :repository: is a relative path,
- create the project among the module projects.
- """
-
- # Update the repository, if necessary
- if is_a_module_project and (not os.path.isabs(repository)):
- repository = pjoin(
- MODULE_PATH,
- PROJECTS_REPOSITORY,
- repository,
- )
-
- # Build the project repository
- try:
- os.makedirs(repository, exist_ok=False)
- except FileExistsError as err:
- raise FileExistsError('Error, a project with this repository already exists!') from err
-
- elmo_path = pjoin(MODULE_PATH, ELMO_TOOL_REPOSITORY)
- template_path = pjoin(MODULE_PATH, TEMPLATE_REPOSITORY)
- project_path = repository
-
- # Add standard content in the project
- files_from_ELMO = [
- pjoin('Examples', 'elmoasmfunctions.o'),
- pjoin('Examples', 'elmoasmfunctions.s'),
- pjoin('Examples', 'elmoasmfunctionsdef.h'),
- pjoin('Examples', 'DPATraces', 'MBedAES', 'vector.o'),
- ]
- files_from_templates = [
- 'elmoasmfunctionsdef-extension.h',
- 'Makefile',
- 'project.c'
- ]
-
- for filename in files_from_ELMO:
- shutil.copy(pjoin(elmo_path, filename), project_path)
- for filename in files_from_templates:
- shutil.copy(pjoin(template_path, filename), project_path)
- shutil.copy(
- pjoin(elmo_path, 'Examples', 'DPATraces', 'MBedAES', 'MBedAES.ld'),
- pjoin(project_path, 'project.ld')
- )
-
- # Create the project class
- with open(pjoin(template_path, 'projectclass.txt')) as _source:
- code = ''.join(_source.readlines())
- code = code.replace('{{PROJECTCLASSNAME}}', classname)
- with open(pjoin(project_path, 'projectclass.py'), 'w') as _dest:
- _dest.write(code)
-
- # Return the path of the created repository
- return os.path.abspath(project_path)
-
-
- ############ USAGE FUNCTIONS ############
-
- class DontFindBinaryError(Exception):
- pass
-
- def execute_simulation(project):
- """ Execute a simulation of the power leakage using ELMO tool
- Return the output and the errors of the execution of ELMO tool
- :project: Subclass of 'SimulationProject' defining all the parameters of the simulation
- """
- elmo_path = pjoin(MODULE_PATH, ELMO_TOOL_REPOSITORY)
-
- # Some checking before executing
- if not isinstance(project, SimulationProject):
- raise TypeError('The project is not an instance of \'SimulationProject\' class.')
-
- leaking_binary_path = pjoin(project.get_project_directory(), project.get_binary_path())
- if not os.path.isfile(leaking_binary_path):
- raise BinaryNotFoundError('Binary not found. Did you compile your project?')
-
- if not os.path.isfile(pjoin(elmo_path, ELMO_EXECUTABLE_NAME)):
- raise Exception('Installation Error: the executable of the ELMO tool is not found.')
-
- # Launch generation of the traces by launching ELMO
- command = '{} "{}"'.format(
- pjoin('.', ELMO_EXECUTABLE_NAME),
- leaking_binary_path,
- )
- process = subprocess.Popen(command, shell=True,
- cwd=elmo_path, executable='/bin/bash',
- stdout=subprocess.PIPE, stderr=subprocess.PIPE
- )
-
- # Follow the generation
- output, error = b'', b''
- num_trace = 0
- while True:
- output_line = process.stdout.readline()
- error_line = process.stderr.readline()
- if (not output_line) and (not error_line) and (process.poll() is not None):
- break
- if error_line:
- error += error_line
- if output_line:
- output += output_line
- if 'TRACE NO' in output_line.decode(ELMO_OUTPUT_ENCODING):
- num_trace += 1
- return_code = process.poll()
-
- # Treat data
- output = output.decode(ELMO_OUTPUT_ENCODING) if output else None
- error = error.decode(ELMO_OUTPUT_ENCODING) if error else None
-
- nb_traces = None
- nb_instructions = None
-
- if output:
- nb_traces = output.count('TRACE NO')
- nb_instructions_pattern = re.compile(r'\s*instructions/cy..es\s+(\d+)\s*')
- for line in output.split('\n'):
- res = nb_instructions_pattern.fullmatch(line)
- if res:
- nb_instructions = res.group(1)
-
- # Return results
- return {
- 'nb_traces': nb_traces,
- 'nb_instructions': nb_instructions,
- 'output': output,
- 'error': error,
- }
-
|