Fork of the espurna firmware for `mhsw` switches
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.

254 lines
8.0 KiB

  1. #!/usr/bin/env python
  2. # coding=utf-8
  3. # -------------------------------------------------------------------------------
  4. # ESPurna module memory analyser
  5. # xose.perez@gmail.com
  6. #
  7. # Based on:
  8. # https://github.com/letscontrolit/ESPEasy/blob/mega/memanalyzer.py
  9. # by psy0rz <edwin@datux.nl>
  10. # https://raw.githubusercontent.com/SmingHub/Sming/develop/tools/memanalyzer.py
  11. # by Slavey Karadzhov <slav@attachix.com>
  12. # https://github.com/Sermus/ESP8266_memory_analyzer
  13. # by Andrey Filimonov
  14. #
  15. # -------------------------------------------------------------------------------
  16. from __future__ import print_function
  17. import argparse
  18. import os
  19. import re
  20. import shlex
  21. import sys
  22. from collections import OrderedDict
  23. from sortedcontainers import SortedDict
  24. import subprocess
  25. if (sys.version_info > (3, 0)):
  26. from subprocess import getstatusoutput as getstatusoutput
  27. else:
  28. from commands import getstatusoutput as getstatusoutput
  29. # -------------------------------------------------------------------------------
  30. TOTAL_IRAM = 32786
  31. TOTAL_DRAM = 81920
  32. env = "esp8266-4m-ota"
  33. objdump_binary = "xtensa-lx106-elf-objdump"
  34. sections = OrderedDict([
  35. ("data", "Initialized Data (RAM)"),
  36. ("rodata", "ReadOnly Data (RAM)"),
  37. ("bss", "Uninitialized Data (RAM)"),
  38. ("text", "Cached Code (IRAM)"),
  39. ("irom0_text", "Uncached Code (SPI)")
  40. ])
  41. description = "ESPurna Memory Analyzer v0.1"
  42. # -------------------------------------------------------------------------------
  43. def file_size(file):
  44. try:
  45. return os.stat(file).st_size
  46. except OSError:
  47. return 0
  48. def analyse_memory(elf_file):
  49. command = "%s -t '%s'" % (objdump_binary, elf_file)
  50. response = subprocess.check_output(shlex.split(command))
  51. if isinstance(response, bytes):
  52. response = response.decode('utf-8')
  53. lines = response.split('\n')
  54. # print("{0: >10}|{1: >30}|{2: >12}|{3: >12}|{4: >8}".format("Section", "Description", "Start (hex)", "End (hex)", "Used space"));
  55. # print("------------------------------------------------------------------------------");
  56. ret = {}
  57. for (id_, _) in list(sections.items()):
  58. section_start_token = " _%s_start" % id_
  59. section_end_token = " _%s_end" % id_
  60. section_start = -1
  61. section_end = -1
  62. for line in lines:
  63. if section_start_token in line:
  64. data = line.split(' ')
  65. section_start = int(data[0], 16)
  66. if section_end_token in line:
  67. data = line.split(' ')
  68. section_end = int(data[0], 16)
  69. if section_start != -1 and section_end != -1:
  70. break
  71. section_length = section_end - section_start
  72. # if i < 3:
  73. # usedRAM += section_length
  74. # if i == 3:
  75. # usedIRAM = TOTAL_IRAM - section_length;
  76. ret[id_] = section_length
  77. # print("{0: >10}|{1: >30}|{2:12X}|{3:12X}|{4:8}".format(id_, descr, section_start, section_end, section_length))
  78. # i += 1
  79. # print("Total Used RAM : %d" % usedRAM)
  80. # print("Free RAM : %d" % (TOTAL_DRAM - usedRAM))
  81. # print("Free IRam : %d" % usedIRAM)
  82. return ret
  83. def run(env_, modules_):
  84. flags = ""
  85. for item in modules_.items():
  86. flags += "-D%s_SUPPORT=%d " % item
  87. command = "ESPURNA_BOARD=\"WEMOS_D1_MINI_RELAYSHIELD\" ESPURNA_FLAGS=\"%s\" platformio run --silent --environment %s 2>/dev/null" % (flags, env_)
  88. subprocess.check_call(command, shell=True)
  89. def modules_get():
  90. modules_ = SortedDict()
  91. for line in open("espurna/config/arduino.h"):
  92. m = re.search(r'(\w*)_SUPPORT', line)
  93. if m:
  94. modules_[m.group(1)] = 0
  95. del modules_['LLMNR']
  96. del modules_['NETBIOS']
  97. return modules_
  98. try:
  99. # Parse command line options
  100. parser = argparse.ArgumentParser(description=description)
  101. parser.add_argument("modules", nargs='*', help="Modules to test (use ALL to test them all)")
  102. parser.add_argument("-c", "--core", help="use core as base configuration instead of default", default=0, action='count')
  103. parser.add_argument("-l", "--list", help="list available modules", default=0, action='count')
  104. args = parser.parse_args()
  105. # Hello
  106. print()
  107. print(description)
  108. print()
  109. # Check xtensa-lx106-elf-objdump is in the path
  110. status, result = getstatusoutput(objdump_binary)
  111. if status != 2 and status != 512:
  112. print("xtensa-lx106-elf-objdump not found, please check it is in your PATH")
  113. sys.exit(1)
  114. # Load list of all modules
  115. available_modules = modules_get()
  116. if args.list > 0:
  117. print("List of available modules:\n")
  118. for key, value in available_modules.items():
  119. print("* " + key)
  120. print()
  121. sys.exit(0)
  122. # Which modules to test?
  123. test_modules = []
  124. if len(args.modules) > 0:
  125. if "ALL" in args.modules:
  126. test_modules = available_modules.keys()
  127. else:
  128. test_modules = args.modules
  129. # Check test modules exist
  130. for module in test_modules:
  131. if module not in available_modules:
  132. print("Module %s not found" % module)
  133. sys.exit(2)
  134. # Define base configuration
  135. if args.core == 0:
  136. modules = SortedDict()
  137. for m in test_modules:
  138. modules[m] = 0
  139. else:
  140. modules = available_modules
  141. # Show init message
  142. if len(test_modules) > 0:
  143. print("Analyzing module(s) %s on top of %s configuration\n" % (", ".join(test_modules), "CORE" if args.core > 0 else "DEFAULT"))
  144. else:
  145. print("Analyzing %s configuration\n" % ("CORE" if args.core > 0 else "DEFAULT"))
  146. output_format = "{:<20}|{:<11}|{:<11}|{:<11}|{:<11}|{:<11}|{:<12}"
  147. print(output_format.format(
  148. "Module",
  149. "Cache IRAM",
  150. "Init RAM",
  151. "R.O. RAM",
  152. "Uninit RAM",
  153. "Flash ROM",
  154. "Binary size"
  155. ))
  156. # Build the core without modules to get base memory usage
  157. run(env, modules)
  158. base = analyse_memory(".pioenvs/%s/firmware.elf" % env)
  159. base['size'] = file_size(".pioenvs/%s/firmware.bin" % env)
  160. print(output_format.format(
  161. "CORE" if args.core == 1 else "DEFAULT",
  162. base['text'],
  163. base['data'],
  164. base['rodata'],
  165. base['bss'],
  166. base['irom0_text'],
  167. base['size'],
  168. ))
  169. # Test each module
  170. results = {}
  171. for module in test_modules:
  172. modules[module] = 1
  173. run(env, modules)
  174. results[module] = analyse_memory(".pioenvs/%s/firmware.elf" % env)
  175. results[module]['size'] = file_size(".pioenvs/%s/firmware.bin" % env)
  176. modules[module] = 0
  177. print(output_format.format(
  178. module,
  179. results[module]['text'] - base['text'],
  180. results[module]['data'] - base['data'],
  181. results[module]['rodata'] - base['rodata'],
  182. results[module]['bss'] - base['bss'],
  183. results[module]['irom0_text'] - base['irom0_text'],
  184. results[module]['size'] - base['size'],
  185. ))
  186. # Test all modules
  187. if len(test_modules) > 0:
  188. for module in test_modules:
  189. modules[module] = 1
  190. run(env, modules)
  191. total = analyse_memory(".pioenvs/%s/firmware.elf" % env)
  192. total['size'] = file_size(".pioenvs/%s/firmware.bin" % env)
  193. if len(test_modules) > 1:
  194. print(output_format.format(
  195. "ALL MODULES",
  196. total['text'] - base['text'],
  197. total['data'] - base['data'],
  198. total['rodata'] - base['rodata'],
  199. total['bss'] - base['bss'],
  200. total['irom0_text'] - base['irom0_text'],
  201. total['size'] - base['size'],
  202. ))
  203. print(output_format.format(
  204. "TOTAL",
  205. total['text'],
  206. total['data'],
  207. total['rodata'],
  208. total['bss'],
  209. total['irom0_text'],
  210. total['size'],
  211. ))
  212. except:
  213. raise
  214. print("\n")