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.

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]