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.
 
 
 
 
 
 

303 lines
9.2 KiB

#!/usr/bin/env python
# coding=utf-8
# -------------------------------------------------------------------------------
# ESPurna module memory analyser
# xose.perez@gmail.com
#
# Based on:
# https://github.com/letscontrolit/ESPEasy/blob/mega/memanalyzer.py
# by psy0rz <edwin@datux.nl>
# https://raw.githubusercontent.com/SmingHub/Sming/develop/tools/memanalyzer.py
# by Slavey Karadzhov <slav@attachix.com>
# https://github.com/Sermus/ESP8266_memory_analyzer
# by Andrey Filimonov
#
# -------------------------------------------------------------------------------
from __future__ import print_function
import argparse
import os
import re
import shlex
import subprocess
import sys
from collections import OrderedDict
from sortedcontainers import SortedDict
if sys.version_info > (3, 0):
from subprocess import getstatusoutput
else:
from commands import getstatusoutput
# -------------------------------------------------------------------------------
TOTAL_IRAM = 32786
TOTAL_DRAM = 81920
env = "esp8266-4m-ota"
objdump_binary = "xtensa-lx106-elf-objdump"
sections = OrderedDict([
("data", "Initialized Data (RAM)"),
("rodata", "ReadOnly Data (RAM)"),
("bss", "Uninitialized Data (RAM)"),
("text", "Cached Code (IRAM)"),
("irom0_text", "Uncached Code (SPI)")
])
description = "ESPurna Memory Analyzer v0.1"
# -------------------------------------------------------------------------------
def file_size(file):
try:
return os.stat(file).st_size
except OSError:
return 0
def analyse_memory(elf_file):
command = "{} -t '{}'".format(objdump_binary, elf_file)
response = subprocess.check_output(shlex.split(command))
if isinstance(response, bytes):
response = response.decode('utf-8')
lines = response.split('\n')
# print("{0: >10}|{1: >30}|{2: >12}|{3: >12}|{4: >8}".format("Section", "Description", "Start (hex)", "End (hex)", "Used space"));
# print("------------------------------------------------------------------------------");
ret = {}
for (id_, _) in list(sections.items()):
section_start_token = " _{}_start".format(id_)
section_end_token = " _{}_end".format(id_)
section_start = -1
section_end = -1
for line in lines:
if section_start_token in line:
data = line.split(' ')
section_start = int(data[0], 16)
if section_end_token in line:
data = line.split(' ')
section_end = int(data[0], 16)
if section_start != -1 and section_end != -1:
break
section_length = section_end - section_start
# if i < 3:
# usedRAM += section_length
# if i == 3:
# usedIRAM = TOTAL_IRAM - section_length;
ret[id_] = section_length
# print("{0: >10}|{1: >30}|{2:12X}|{3:12X}|{4:8}".format(id_, descr, section_start, section_end, section_length))
# i += 1
# print("Total Used RAM : {:d}".format(usedRAM))
# print("Free RAM : {:d}".format(TOTAL_DRAM - usedRAM))
# print("Free IRam : {:d}".format(usedIRAM))
return ret
def run(env_, modules_):
flags = ""
for k, v in modules_.items():
flags += "-D{}_SUPPORT={:d} ".format(k, v)
os_env = os.environ.copy()
os_env["ESPURNA_BOARD"] = "WEMOS_D1_MINI_RELAYSHIELD"
os_env["ESPURNA_FLAGS"] = flags
os_env["ESPURNA_PIO_SHARED_LIBRARIES"] = "y"
command = "platformio run --silent --environment {} 2>/dev/null".format(env_)
subprocess.check_call(command, shell=True)
def calc_free(module):
free = 80 * 1024 - module['data'] - module['rodata'] - module['bss']
free = free + (16 - free % 16)
module['free'] = free
def modules_get():
modules_ = SortedDict()
for line in open("espurna/config/arduino.h"):
m = re.search(r'(\w*)_SUPPORT', line)
if m:
modules_[m.group(1)] = 0
del modules_['LLMNR']
del modules_['NETBIOS']
return modules_
if __name__ == '__main__':
# Parse command line options
parser = argparse.ArgumentParser(description=description)
parser.add_argument("modules", nargs='*', help="Modules to test (use ALL to test them all)")
parser.add_argument("-c", "--core", help="use core as base configuration instead of default", default=0, action='count')
parser.add_argument("-l", "--list", help="list available modules", default=0, action='count')
args = parser.parse_args()
# Hello
print()
print(description)
print()
# Check xtensa-lx106-elf-objdump is in the path
status, result = getstatusoutput(objdump_binary)
if status != 2 and status != 512:
print("xtensa-lx106-elf-objdump not found, please check it is in your PATH")
sys.exit(1)
# Load list of all modules
available_modules = modules_get()
if args.list > 0:
print("List of available modules:\n")
for key, value in available_modules.items():
print("* " + key)
print()
sys.exit(0)
# Which modules to test?
test_modules = []
if len(args.modules) > 0:
if "ALL" in args.modules:
test_modules = available_modules.keys()
else:
test_modules = args.modules
# Check test modules exist
for module in test_modules:
if module not in available_modules:
print("Module {} not found".format(module))
sys.exit(2)
# Define base configuration
if args.core == 0:
modules = SortedDict()
for m in test_modules:
modules[m] = 0
else:
modules = available_modules
# Show init message
if len(test_modules) > 0:
print("Analyzing module(s) {} on top of {} configuration\n".format(", ".join(test_modules), "CORE" if args.core > 0 else "DEFAULT"))
else:
print("Analyzing {} configuration\n".format("CORE" if args.core > 0 else "DEFAULT"))
output_format = "{:<20}|{:<15}|{:<15}|{:<15}|{:<15}|{:<15}|{:<15}|{:<15}"
print(output_format.format(
"Module",
"Cache IRAM",
"Init RAM",
"R.O. RAM",
"Uninit RAM",
"Available RAM",
"Flash ROM",
"Binary size"
))
print(output_format.replace("<", ">").format(
"",
".text",
".data",
".rodata",
".bss",
"heap + stack",
".irom0.text",
""
))
print(output_format.format(
"-" * 20,
"-" * 15,
"-" * 15,
"-" * 15,
"-" * 15,
"-" * 15,
"-" * 15,
"-" * 15
))
# Build the core without modules to get base memory usage
run(env, modules)
base = analyse_memory(".pio/build/{}/firmware.elf".format(env))
base['size'] = file_size(".pio/build/{}/firmware.bin".format(env))
calc_free(base)
print(output_format.format(
"CORE" if args.core == 1 else "DEFAULT",
base['text'],
base['data'],
base['rodata'],
base['bss'],
base['free'],
base['irom0_text'],
base['size'],
))
# Test each module
results = {}
for module in test_modules:
modules[module] = 1
run(env, modules)
results[module] = analyse_memory(".pio/build/{}/firmware.elf".format(env))
results[module]['size'] = file_size(".pio/build/{}/firmware.bin".format(env))
calc_free(results[module])
modules[module] = 0
print(output_format.format(
module,
results[module]['text'] - base['text'],
results[module]['data'] - base['data'],
results[module]['rodata'] - base['rodata'],
results[module]['bss'] - base['bss'],
results[module]['free'] - base['free'],
results[module]['irom0_text'] - base['irom0_text'],
results[module]['size'] - base['size'],
))
# Test all modules
if len(test_modules) > 0:
for module in test_modules:
modules[module] = 1
run(env, modules)
total = analyse_memory(".pio/build/{}/firmware.elf".format(env))
total['size'] = file_size(".pio/build/{}/firmware.bin".format(env))
calc_free(total)
print(output_format.format(
"-" * 20,
"-" * 15,
"-" * 15,
"-" * 15,
"-" * 15,
"-" * 15,
"-" * 15,
"-" * 15
))
if len(test_modules) > 1:
print(output_format.format(
"ALL MODULES",
total['text'] - base['text'],
total['data'] - base['data'],
total['rodata'] - base['rodata'],
total['bss'] - base['bss'],
total['free'] - base['free'],
total['irom0_text'] - base['irom0_text'],
total['size'] - base['size'],
))
print(output_format.format(
"TOTAL",
total['text'],
total['data'],
total['rodata'],
total['bss'],
total['free'],
total['irom0_text'],
total['size'],
))
print("\n")