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.6KB

  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. SocketTool.send_file(s, '{}/{}'.format(self.get_project_directory(), self.get_binary_path()))
  108. if not SocketTool.get_ack(s):
  109. raise RuntimeError("NACK received: The binary file has been refused!")
  110. data = SocketTool.get_data(s)
  111. if data['error']:
  112. raise Exception("The simulation returned an error: {}".format(data['error']))
  113. s.close()
  114. except IOError as err:
  115. raise RuntimeError("The connection refused. Has the ELMO server been switch on?") from err
  116. self.is_executed = True
  117. self.has_been_online = True
  118. self._complete_asmtrace = data['asmtrace']
  119. self._complete_results = data['results']
  120. self._complete_printed_data = data['printed_data']
  121. return { key: value
  122. for key, value in data.items()
  123. if key not in ['results', 'asmtrace', 'printed_data']
  124. }
  125. ### Manipulate the ASM trace
  126. def get_asmtrace_filename(self):
  127. return '{}/output/asmoutput/asmtrace00001.txt'.format(self.elmo_folder)
  128. def get_asmtrace(self):
  129. if self._complete_asmtrace is None:
  130. with open(self.get_asmtrace_filename(), 'r') as _file:
  131. self._complete_asmtrace = ''.join(_file.readlines())
  132. return self._complete_asmtrace.split('\n')
  133. def get_indexes_of(self, condition):
  134. return [i for i, instr in enumerate(self.get_asmtrace()) if condition(instr)]
  135. ### Manipulate the results
  136. def get_number_of_traces(self):
  137. return len(self.challenges)
  138. def get_results(self, only_filenames=False, reorganise=None, indexes=None):
  139. assert self.is_executed
  140. nb_traces = self.get_number_of_traces()
  141. if only_filenames and self.has_been_online:
  142. raise Exception('Impossible to get the filenames for an online execution')
  143. if only_filenames or self._complete_results is None:
  144. trace_filenames = []
  145. for filename in os.listdir('{}/output/traces/'.format(self.elmo_folder)):
  146. if re.search(r'^trace\d+\.trc$', filename):
  147. trace_filenames.append('{}/output/traces/{}'.format(self.elmo_folder, filename))
  148. if len(trace_filenames) >= nb_traces:
  149. break
  150. assert len(trace_filenames) == nb_traces
  151. if only_filenames:
  152. return reorganise(trace_filenames) if reorganise is not None else trace_filenames
  153. self._complete_results = []
  154. for filename in trace_filenames:
  155. with open(filename, 'r') as _file:
  156. self._complete_results.append(list(map(float, _file.readlines())))
  157. results = self._complete_results
  158. if indexes is not None:
  159. for i in range(len(self._complete_results)):
  160. results[i] = results[i][indexes]
  161. if reorganise is not None:
  162. results = reorganise(results)
  163. return results
  164. def get_traces(self, reorganise=None, indexes=None):
  165. results = self.get_results(only_filenames=False, reorganise=reorganise, indexes=indexes)
  166. nb_traces = self.get_number_of_traces()
  167. trace_length = len(results[0])
  168. traces = np.zeros((nb_traces, trace_length))
  169. for i in range(nb_traces):
  170. traces[i,:] = results[i]
  171. if reorganise is not None:
  172. traces = reorganise(traces)
  173. return traces
  174. ### Manipulate the Printed Data
  175. def get_printed_data(self):
  176. if self._complete_printed_data is None:
  177. with open('{}/output/printdata.txt'.format(self.elmo_folder), 'r') as _file:
  178. self._complete_printed_data = list(map(lambda x: int(x, 16), _file.readlines()))
  179. data = self._complete_printed_data
  180. nb_traces = self.get_number_of_traces()
  181. nb_data_per_trace = len(data) // nb_traces
  182. return [data[nb_data_per_trace*i:nb_data_per_trace*(i+1)] for i in range(nb_traces)]
  183. ### Other
  184. def analyse_operands(self, num_line, num_trace=1):
  185. num_str = str(num_trace)
  186. num_str = '0'*(5-len(num_str)) + num_str
  187. operands_filename = self.elmo_folder + '/output/operands/operands{}.txt'.format(num_str)
  188. trace_filename = self.elmo_folder + '/output/traces/trace0000{}.trc'.format(num_trace)
  189. is_multiple = (type(num_line) is list)
  190. if not is_multiple:
  191. num_line = [num_line]
  192. output = [{}]*len(num_line)
  193. with open(operands_filename, 'r') as _file:
  194. lines = _file.readlines()
  195. for i, num in enumerate(num_line):
  196. line = lines[num].split()
  197. data = list(map(int, line[0:7]))
  198. output[i]['previous'] = data[0:2]
  199. output[i]['current'] = data[2:4]
  200. output[i]['triplet'] = data[4:7]
  201. output[i]['other'] = list(map(float, line[7:]))
  202. with open(trace_filename, 'r') as _file:
  203. lines = _file.readlines()
  204. for i, num in enumerate(num_line):
  205. line = lines[num]
  206. output[i]['power'] = float(line)
  207. return output if is_multiple else output[0]