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.

227 lines
6.9 KiB

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