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.

project_base.py 9.4KB

  1. import os, re
  2. import numpy as np
  3. from .config import MODULE_PATH, ELMO_TOOL_REPOSITORY
  4. from .utils import write
  5. class SimulationProject:
  6. # TAG: EXCLUDE-FROM-SIMULATION-SEARCH
  7. """ Class to manage a simultion
  8. It contains all the parameters of the simulation and has method to use it
  9. """
  10. _nb_bits_for_nb_challenges = 16
  11. _project_directory = None
  12. ### Define the project
  13. @classmethod
  14. def get_project_directory(cl):
  15. """ """
  16. if cl._project_directory:
  17. return cl._project_directory
  18. else:
  19. raise NotImplementedError()
  20. @classmethod
  21. def set_project_directory(cl, project_directory):
  22. cl._project_directory = project_directory
  23. @classmethod
  24. def get_project_label(cl):
  25. return cl.get_project_directory()
  26. @classmethod
  27. def get_make_directory(cl):
  28. return ''
  29. @classmethod
  30. def get_binary_path(cl):
  31. raise NotImplementedError()
  32. @classmethod
  33. def get_parameters_names(cl):
  34. return set()
  35. def get_challenge_format(self):
  36. raise NotImplementedError()
  37. ### Tools to realize the simulation of the project
  38. def __init__(self, challenges=None):
  39. self.elmo_folder = os.path.join(MODULE_PATH, ELMO_TOOL_REPOSITORY)
  40. self.challenges = challenges
  41. self.reset()
  42. def reset(self):
  43. self.is_executed = False
  44. self.has_been_online = False
  45. self._complete_asmtrace = None
  46. self._complete_results = None
  47. self._complete_printed_data = None
  48. def get_test_challenges(self):
  49. raise NotImplementedError()
  50. def get_random_challenges(self, nb_challenges):
  51. raise NotImplementedError()
  52. def set_challenges(self, challenges):
  53. self.challenges = challenges
  54. def get_writable_input_file(self):
  55. return open('{}/input.txt'.format(self.elmo_folder), 'w')
  56. def set_input_for_each_challenge(self, input, challenge):
  57. format = self.get_challenge_format()
  58. def aux(sizes, data):
  59. if len(sizes) == 0:
  60. write(input, data)
  61. else:
  62. assert len(data) == sizes[0], 'Incorrect format for challenge. Get {} instead of {}'.format(len(data), sizes[0])
  63. for i in range(sizes[0]):
  64. aux(sizes[1:], data[i])
  65. for num_part in range(len(format)):
  66. aux(format[num_part], challenge[num_part])
  67. def set_input(self, input):
  68. if self.challenges:
  69. assert len(self.challenges) < (1 << self._nb_bits_for_nb_challenges), \
  70. 'The number of challenges must be strictly lower than {}. Currently, there are {} challenges.'.format(
  71. 1 << self._nb_bits_for_nb_challenges,
  72. len(self.challenges),
  73. )
  74. write(input, len(self.challenges), nb_bits=self._nb_bits_for_nb_challenges)
  75. for challenge in self.challenges:
  76. self.set_input_for_each_challenge(input, challenge)
  77. def run(self):
  78. self.reset()
  79. with open('{}/input.txt'.format(self.elmo_folder), 'w') as _input:
  80. self.set_input(_input)
  81. from .manage import execute_simulation
  82. res = execute_simulation(self)
  83. self.is_executed = True
  84. self.has_been_online = False
  85. return res
  86. def run_online(self, host='localhost', port=5000):
  87. from .server.protocol import SocketTool
  88. import socket
  89. class TempInput:
  90. def __init__(self):
  91. self._buffer = ''
  92. def write(self, data):
  93. self._buffer += data
  94. def get_string(self):
  95. return self._buffer
  96. self.reset()
  97. input = TempInput()
  98. self.set_input(input)
  99. try:
  100. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  101. s.connect((host, port))
  102. SocketTool.send_data(s, {
  103. 'input': input.get_string(),
  104. })
  105. if not SocketTool.get_ack(s):
  106. raise RuntimeError("NACK received: The request has been refused !")
  107. else:
  108. SocketTool.send_file(s, '{}/{}'.format(self.get_project_directory(), self.get_binary()))
  109. data = SocketTool.get_data(s)
  110. if data['error']:
  111. raise Exception("The simulation returned an error: {}".format(data['error']))
  112. s.close()
  113. except IOError as err:
  114. raise RuntimeError("The connection refused. Has the ELMO server been switch on ?") from err
  115. self.is_executed = True
  116. self.has_been_online = True
  117. self._complete_asmtrace = data['asmtrace']
  118. self._complete_results = data['results']
  119. self._complete_printed_data = data['printed_data']
  120. ### Manipulate the ASM trace
  121. def get_asmtrace_filename(self):
  122. return '{}/output/asmoutput/asmtrace00001.txt'.format(self.elmo_folder)
  123. def get_asmtrace(self):
  124. if self._complete_asmtrace is None:
  125. with open(self.get_asmtrace_filename(), 'r') as _file:
  126. self._complete_asmtrace = ''.join(_file.readlines())
  127. return self._complete_asmtrace.split('\n')
  128. def get_indexes_of(self, condition):
  129. return [i for i, instr in enumerate(self.get_asmtrace()) if condition(instr)]
  130. ### Manipulate the results
  131. def get_number_of_traces(self):
  132. return len(self.challenges)
  133. def get_results(self, only_filenames=False, reorganise=None, indexes=None):
  134. assert self.is_executed
  135. nb_traces = self.get_number_of_traces()
  136. if only_filenames and self.has_been_online:
  137. raise Exception('Impossible to get the filenames for an online execution')
  138. if only_filenames or self._complete_results is None:
  139. trace_filenames = []
  140. for filename in os.listdir('{}/output/traces/'.format(self.elmo_folder)):
  141. if re.search(r'^trace\d+\.trc$', filename):
  142. trace_filenames.append('{}/output/traces/{}'.format(self.elmo_folder, filename))
  143. if len(trace_filenames) >= nb_traces:
  144. break
  145. assert len(trace_filenames) == nb_traces
  146. if only_filenames:
  147. return reorganise(trace_filenames) if reorganise is not None else trace_filenames
  148. self._complete_results = []
  149. for filename in trace_filenames:
  150. with open(filename, 'r') as _file:
  151. self._complete_results.append(list(map(float, _file.readlines())))
  152. results = self._complete_results
  153. if indexes is not None:
  154. for i in range(len(self._complete_results)):
  155. results[i] = results[i][indexes]
  156. if reorganise is not None:
  157. results = reorganise(results)
  158. return results
  159. def get_traces(self, reorganise=None, indexes=None):
  160. results = self.get_results(only_filenames=False, reorganise=reorganise, indexes=indexes)
  161. nb_traces = self.get_number_of_traces()
  162. trace_length = len(results[0])
  163. traces = np.zeros((nb_traces, trace_length))
  164. for i in range(nb_traces):
  165. traces[i,:] = results[i]
  166. if reorganise is not None:
  167. traces = reorganise(traces)
  168. return traces
  169. ### Manipulate the Printed Data
  170. def get_printed_data(self):
  171. if self._complete_printed_data is None:
  172. with open('{}/output/printdata.txt'.format(self.elmo_folder), 'r') as _file:
  173. self._complete_printed_data = list(map(lambda x: int(x, 16), _file.readlines()))
  174. data = self._complete_printed_data
  175. nb_traces = self.get_number_of_traces()
  176. nb_data_per_trace = len(data) // nb_traces
  177. return [data[nb_data_per_trace*i:nb_data_per_trace*(i+1)] for i in range(nb_traces)]
  178. ### Other
  179. def analyse_operands(self, num_line, num_trace=1):
  180. num_str = str(num_trace)
  181. num_str = '0'*(5-len(num_str)) + num_str
  182. operands_filename = self.elmo_folder + '/output/operands/operands{}.txt'.format(num_str)
  183. trace_filename = self.elmo_folder + '/output/traces/trace0000{}.trc'.format(num_trace)
  184. is_multiple = (type(num_line) is list)
  185. if not is_multiple:
  186. num_line = [num_line]
  187. output = [{}]*len(num_line)
  188. with open(operands_filename, 'r') as _file:
  189. lines = _file.readlines()
  190. for i, num in enumerate(num_line):
  191. line = lines[num].split()
  192. data = list(map(int, line[0:7]))
  193. output[i]['previous'] = data[0:2]
  194. output[i]['current'] = data[2:4]
  195. output[i]['triplet'] = data[4:7]
  196. output[i]['other'] = list(map(float, line[7:]))
  197. with open(trace_filename, 'r') as _file:
  198. lines = _file.readlines()
  199. for i, num in enumerate(num_line):
  200. line = lines[num]
  201. output[i]['power'] = float(line)
  202. return output if is_multiple else output[0]