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.
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

  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. }