Python-ELMO is a Python library which offers an encapsulation of the binary tool ELMO, in order to manipulate it easily in Python and SageMath script.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

manage.py 9.5KB

  1. import os, shutil
  2. import re
  3. import inspect
  4. import subprocess
  5. import sys
  6. from os.path import join as pjoin
  7. from .config import (
  8. TEMPLATE_REPOSITORY,
  9. PROJECTS_REPOSITORY,
  10. ELMO_TOOL_REPOSITORY,
  11. ELMO_EXECUTABLE_NAME,
  12. MODULE_PATH,
  13. ELMO_OUTPUT_ENCODING,
  14. SEARCH_EXCLUSION_TAG,
  15. )
  16. from .project_base import SimulationProject
  17. from .utils import Color
  18. ############ GETTERS ############
  19. def search_simulations_in_repository(repository, criteria=lambda x:True):
  20. """ Search simulation classes in the 'repository' verifying the 'criteria'
  21. Return a list of 'SimulationProject' subclasses
  22. :repository: Repository of the searched simulation classes (string)
  23. :criteria: Boolean function with 'SimulationProject' subclasses for input
  24. """
  25. projects = {}
  26. from .utils import write
  27. for root, repositories, files in os.walk(repository):
  28. for filename in files:
  29. if re.fullmatch(r'.*project.*\.py', filename):
  30. complete_filename = pjoin(root, filename)
  31. # Encapsulate the project
  32. globals = {
  33. #'__builtins__': {'__build_class__': __build_class__},
  34. 'SimulationProject': SimulationProject,
  35. 'write': write,
  36. }
  37. locals = {}
  38. # Read the project code
  39. with open(complete_filename, 'r') as _file:
  40. project = ''.join(_file.read())
  41. if ('SimulationProject' not in project) or (SEARCH_EXCLUSION_TAG in project):
  42. continue # Exclude this file
  43. exec(project, globals, locals)
  44. # Extract the simulations
  45. for key, obj in locals.items():
  46. if inspect.isclass(obj) and issubclass(obj, SimulationProject):
  47. if criteria(obj):
  48. if key in projects:
  49. print(Color.WARNING + \
  50. 'Warning! Many simulations with the same name. ' + \
  51. 'Simulation ignored: {} in {}'.format(
  52. key, complete_filename[len(repository)+1:]) + \
  53. Color.ENDC
  54. )
  55. else:
  56. obj.set_project_directory(os.path.abspath(root))
  57. projects[key] = obj
  58. return projects
  59. def search_simulations_in_module(criteria=lambda x:True):
  60. """ Search simulation classes among the module projects verifying the 'criteria'
  61. Return a list of 'SimulationProject' subclasses
  62. :criteria: Boolean function with 'SimulationProject' subclasses for input
  63. """
  64. projects_path = pjoin(MODULE_PATH, PROJECTS_REPOSITORY)
  65. return search_simulations_in_repository(projects_path, criteria)
  66. def search_simulations(repository='.', criteria=lambda x:True):
  67. """ Search simulation classes in the 'repository' and among
  68. the module projects verifying the 'criteria'
  69. Return a list of 'SimulationProject' subclasses
  70. :repository: Repository of the searched simulation classes (string)
  71. :criteria: Boolean function with 'SimulationProject' subclasses for input
  72. """
  73. projects = search_simulations_in_repository(repository, criteria)
  74. module_projects = search_simulations_in_module(criteria)
  75. for key, project in module_projects.items():
  76. if key not in projects:
  77. projects[key] = project
  78. return projects
  79. class SimulationNotFoundError(Exception):
  80. pass
  81. class TooManySimulationsError(Exception):
  82. pass
  83. def get_simulation(classname=None, repository='.'):
  84. """ Get a simulation class in the 'repository' with the specific 'classname'
  85. Return a subclass of 'SimulationProject' class
  86. :classname: Name of the searched simulation class (string, optional)
  87. :repository: Repository of the searched simulation class (string, optional)
  88. """
  89. criteria = lambda x: True
  90. if classname is not None:
  91. criteria = lambda x: x.__name__ == classname.strip()
  92. projects = search_simulations(repository, criteria)
  93. if len(projects) == 1:
  94. return list(projects.values())[0]
  95. elif len(projects) < 1:
  96. raise SimulationNotFoundError()
  97. else:
  98. raise TooManySimulationsError()
  99. ############ SETTERS ############
  100. def create_simulation(repository, classname, is_a_module_project=False):
  101. """ Create a Simulation Project.
  102. It builds a repository with the standard content for the project.
  103. Return the absolute path of the created repository
  104. :repository: Name of repository which will be created (string)
  105. :classname: Name of the Python class to manage the project (string)
  106. :is_a_module_project: If True and :repository: is a relative path,
  107. create the project among the module projects.
  108. """
  109. # Update the repository, if necessary
  110. if is_a_module_project and (not os.path.isabs(repository)):
  111. repository = pjoin(
  112. MODULE_PATH,
  113. PROJECTS_REPOSITORY,
  114. repository,
  115. )
  116. # Build the project repository
  117. try:
  118. os.makedirs(repository, exist_ok=False)
  119. except FileExistsError as err:
  120. raise FileExistsError('Error, a project with this repository already exists!') from err
  121. elmo_path = pjoin(MODULE_PATH, ELMO_TOOL_REPOSITORY)
  122. template_path = pjoin(MODULE_PATH, TEMPLATE_REPOSITORY)
  123. project_path = repository
  124. # Add standard content in the project
  125. files_from_ELMO = [
  126. pjoin('Examples', 'elmoasmfunctions.o'),
  127. pjoin('Examples', 'elmoasmfunctions.s'),
  128. pjoin('Examples', 'elmoasmfunctionsdef.h'),
  129. pjoin('Examples', 'DPATraces', 'MBedAES', 'vector.o'),
  130. ]
  131. files_from_templates = [
  132. 'elmoasmfunctionsdef-extension.h',
  133. 'Makefile',
  134. 'project.c'
  135. ]
  136. for filename in files_from_ELMO:
  137. shutil.copy(pjoin(elmo_path, filename), project_path)
  138. for filename in files_from_templates:
  139. shutil.copy(pjoin(template_path, filename), project_path)
  140. shutil.copy(
  141. pjoin(elmo_path, 'Examples', 'DPATraces', 'MBedAES', 'MBedAES.ld'),
  142. pjoin(project_path, 'project.ld')
  143. )
  144. # Create the project class
  145. with open(pjoin(template_path, 'projectclass.txt')) as _source:
  146. code = ''.join(_source.readlines())
  147. code = code.replace('{{PROJECTCLASSNAME}}', classname)
  148. with open(pjoin(project_path, 'projectclass.py'), 'w') as _dest:
  149. _dest.write(code)
  150. # Return the path of the created repository
  151. return os.path.abspath(project_path)
  152. ############ USAGE FUNCTIONS ############
  153. class DontFindBinaryError(Exception):
  154. pass
  155. def execute_simulation(project):
  156. """ Execute a simulation of the power leakage using ELMO tool
  157. Return the output and the errors of the execution of ELMO tool
  158. :project: Subclass of 'SimulationProject' defining all the parameters of the simulation
  159. """
  160. elmo_path = pjoin(MODULE_PATH, ELMO_TOOL_REPOSITORY)
  161. # Some checking before executing
  162. if not isinstance(project, SimulationProject):
  163. raise TypeError('The project is not an instance of \'SimulationProject\' class.')
  164. leaking_binary_path = project.get_binary_path()
  165. if not os.path.isabs(leaking_binary_path):
  166. leaking_binary_path = pjoin(project.get_project_directory(), project.get_binary_path())
  167. if not os.path.isfile(leaking_binary_path):
  168. raise BinaryNotFoundError('Binary not found. Did you compile your project?')
  169. if not os.path.isfile(pjoin(elmo_path, ELMO_EXECUTABLE_NAME)):
  170. raise Exception('Installation Error: the executable of the ELMO tool is not found.')
  171. # Launch generation of the traces by launching ELMO
  172. command = '{} "{}"'.format(
  173. pjoin('.', ELMO_EXECUTABLE_NAME),
  174. leaking_binary_path,
  175. )
  176. process = subprocess.Popen(command, shell=True,
  177. cwd=elmo_path, executable='/bin/bash',
  178. stdout=subprocess.PIPE, stderr=subprocess.PIPE
  179. )
  180. # Follow the generation
  181. output, error = b'', b''
  182. num_trace = 0
  183. while True:
  184. output_line = process.stdout.readline()
  185. error_line = process.stderr.readline()
  186. if (not output_line) and (not error_line) and (process.poll() is not None):
  187. break
  188. if error_line:
  189. error += error_line
  190. if output_line:
  191. output += output_line
  192. if 'TRACE NO' in output_line.decode(ELMO_OUTPUT_ENCODING):
  193. num_trace += 1
  194. return_code = process.poll()
  195. # Treat data
  196. output = output.decode(ELMO_OUTPUT_ENCODING) if output else None
  197. error = error.decode(ELMO_OUTPUT_ENCODING) if error else None
  198. nb_traces = None
  199. nb_instructions = None
  200. if output:
  201. nb_traces = output.count('TRACE NO')
  202. nb_instructions_pattern = re.compile(r'\s*instructions/cy..es\s+(\d+)\s*')
  203. for line in output.split('\n'):
  204. res = nb_instructions_pattern.fullmatch(line)
  205. if res:
  206. nb_instructions = res.group(1)
  207. # Return results
  208. return {
  209. 'nb_traces': nb_traces,
  210. 'nb_instructions': nb_instructions,
  211. 'output': output,
  212. 'error': error,
  213. }