|
|
@ -1,5 +1,6 @@ |
|
|
|
#!/usr/bin/env python |
|
|
|
#------------------------------------------------------------------------------- |
|
|
|
# coding=utf-8 |
|
|
|
# ------------------------------------------------------------------------------- |
|
|
|
# ESPurna module memory analyser |
|
|
|
# xose.perez@gmail.com |
|
|
|
# |
|
|
@ -11,23 +12,24 @@ |
|
|
|
# https://github.com/Sermus/ESP8266_memory_analyzer |
|
|
|
# by Andrey Filimonov |
|
|
|
# |
|
|
|
#------------------------------------------------------------------------------- |
|
|
|
# ------------------------------------------------------------------------------- |
|
|
|
from __future__ import print_function |
|
|
|
|
|
|
|
from collections import OrderedDict |
|
|
|
from sortedcontainers import SortedDict |
|
|
|
import argparse |
|
|
|
import os |
|
|
|
import re |
|
|
|
import shlex |
|
|
|
import commands |
|
|
|
import subprocess |
|
|
|
import sys |
|
|
|
import os |
|
|
|
import re |
|
|
|
import argparse |
|
|
|
from collections import OrderedDict |
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
|
|
|
from sortedcontainers import SortedDict |
|
|
|
|
|
|
|
TOTAL_IRAM = 32786; |
|
|
|
TOTAL_DRAM = 81920; |
|
|
|
env="esp8266-4m-ota" |
|
|
|
# ------------------------------------------------------------------------------- |
|
|
|
|
|
|
|
TOTAL_IRAM = 32786 |
|
|
|
TOTAL_DRAM = 81920 |
|
|
|
env = "esp8266-4m-ota" |
|
|
|
objdump_binary = "xtensa-lx106-elf-objdump" |
|
|
|
sections = OrderedDict([ |
|
|
|
("data", "Initialized Data (RAM)"), |
|
|
@ -38,7 +40,8 @@ sections = OrderedDict([ |
|
|
|
]) |
|
|
|
description = "ESPurna Memory Analyzer v0.1" |
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------- |
|
|
|
|
|
|
|
def file_size(file): |
|
|
|
try: |
|
|
@ -46,8 +49,8 @@ def file_size(file): |
|
|
|
except: |
|
|
|
return 0 |
|
|
|
|
|
|
|
def analyse_memory(elf_file): |
|
|
|
|
|
|
|
def analyse_memory(elf_file): |
|
|
|
command = "%s -t '%s' " % (objdump_binary, elf_file) |
|
|
|
response = subprocess.check_output(shlex.split(command)) |
|
|
|
if isinstance(response, bytes): |
|
|
@ -56,59 +59,59 @@ def analyse_memory(elf_file): |
|
|
|
|
|
|
|
# print("{0: >10}|{1: >30}|{2: >12}|{3: >12}|{4: >8}".format("Section", "Description", "Start (hex)", "End (hex)", "Used space")); |
|
|
|
# print("------------------------------------------------------------------------------"); |
|
|
|
ret={} |
|
|
|
usedRAM = 0 |
|
|
|
usedIRAM = 0 |
|
|
|
|
|
|
|
i = 0 |
|
|
|
for (id, descr) in list(sections.items()): |
|
|
|
sectionStartToken = " _%s_start" % id |
|
|
|
sectionEndToken = " _%s_end" % id |
|
|
|
sectionStart = -1 |
|
|
|
sectionEnd = -1 |
|
|
|
ret = {} |
|
|
|
|
|
|
|
for (id_, descr) in list(sections.items()): |
|
|
|
section_start_token = " _%s_start" % id_ |
|
|
|
section_end_token = " _%s_end" % id_ |
|
|
|
section_start = -1 |
|
|
|
section_end = -1 |
|
|
|
for line in lines: |
|
|
|
if sectionStartToken in line: |
|
|
|
if section_start_token in line: |
|
|
|
data = line.split(' ') |
|
|
|
sectionStart = int(data[0], 16) |
|
|
|
section_start = int(data[0], 16) |
|
|
|
|
|
|
|
if sectionEndToken in line: |
|
|
|
if section_end_token in line: |
|
|
|
data = line.split(' ') |
|
|
|
sectionEnd = int(data[0], 16) |
|
|
|
section_end = int(data[0], 16) |
|
|
|
|
|
|
|
if sectionStart != -1 and sectionEnd != -1: |
|
|
|
if section_start != -1 and section_end != -1: |
|
|
|
break |
|
|
|
|
|
|
|
sectionLength = sectionEnd - sectionStart |
|
|
|
section_length = section_end - section_start |
|
|
|
# if i < 3: |
|
|
|
# usedRAM += sectionLength |
|
|
|
# usedRAM += section_length |
|
|
|
# if i == 3: |
|
|
|
# usedIRAM = TOTAL_IRAM - sectionLength; |
|
|
|
# usedIRAM = TOTAL_IRAM - section_length; |
|
|
|
|
|
|
|
ret[id]=sectionLength |
|
|
|
# print("{0: >10}|{1: >30}|{2:12X}|{3:12X}|{4:8}".format(id, descr, sectionStart, sectionEnd, sectionLength)) |
|
|
|
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" % usedRAM) |
|
|
|
# print("Free RAM : %d" % (TOTAL_DRAM - usedRAM)) |
|
|
|
# print("Free IRam : %d" % usedIRAM) |
|
|
|
return(ret) |
|
|
|
return ret |
|
|
|
|
|
|
|
def run(env, modules): |
|
|
|
|
|
|
|
def run(env_, modules_): |
|
|
|
flags = "" |
|
|
|
for item in modules.items(): |
|
|
|
for item in modules_.items(): |
|
|
|
flags += "-D%s_SUPPORT=%d " % item |
|
|
|
command = "export ESPURNA_BOARD=\"WEMOS_D1_MINI_RELAYSHIELD\"; export ESPURNA_FLAGS=\"%s\"; platformio run --silent --environment %s" % (flags, env) |
|
|
|
command = "export ESPURNA_BOARD=\"WEMOS_D1_MINI_RELAYSHIELD\"; export ESPURNA_FLAGS=\"%s\"; platformio run --silent --environment %s" % (flags, env_) |
|
|
|
subprocess.check_call(command, shell=True) |
|
|
|
|
|
|
|
|
|
|
|
def modules_get(): |
|
|
|
modules = SortedDict() |
|
|
|
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 |
|
|
|
modules_[m.group(1)] = 0 |
|
|
|
del modules_['LLMNR'] |
|
|
|
del modules_['NETBIOS'] |
|
|
|
return modules_ |
|
|
|
|
|
|
|
|
|
|
|
try: |
|
|
|
|
|
|
@ -120,23 +123,22 @@ try: |
|
|
|
args = parser.parse_args() |
|
|
|
|
|
|
|
# Hello |
|
|
|
print |
|
|
|
print description |
|
|
|
print |
|
|
|
|
|
|
|
print() |
|
|
|
print(description) |
|
|
|
print() |
|
|
|
# Check xtensa-lx106-elf-objdump is in the path |
|
|
|
status, result = commands.getstatusoutput(objdump_binary) |
|
|
|
status, result = subprocess.getstatusoutput(objdump_binary) |
|
|
|
if status != 512: |
|
|
|
print "xtensa-lx106-elf-objdump not found, please check it is in your PATH" |
|
|
|
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" |
|
|
|
print("List of available modules:\n") |
|
|
|
for key, value in available_modules.items(): |
|
|
|
print "* " + key |
|
|
|
print |
|
|
|
print("* " + key) |
|
|
|
print() |
|
|
|
sys.exit(0) |
|
|
|
|
|
|
|
# Which modules to test? |
|
|
@ -150,7 +152,7 @@ try: |
|
|
|
# Check test modules exist |
|
|
|
for module in test_modules: |
|
|
|
if module not in available_modules: |
|
|
|
print "Module %s not found" % module |
|
|
|
print("Module %s not found" % module) |
|
|
|
sys.exit(2) |
|
|
|
|
|
|
|
# Define base configuration |
|
|
@ -163,19 +165,19 @@ try: |
|
|
|
|
|
|
|
# Show init message |
|
|
|
if len(test_modules) > 0: |
|
|
|
print "Analyzing module(s) %s on top of %s configuration\n" % (", ".join(test_modules), "CORE" if args.core > 0 else "DEFAULT") |
|
|
|
print("Analyzing module(s) %s on top of %s configuration\n" % (", ".join(test_modules), "CORE" if args.core > 0 else "DEFAULT")) |
|
|
|
else: |
|
|
|
print "Analyzing %s configuration\n" % ("CORE" if args.core > 0 else "DEFAULT") |
|
|
|
print("Analyzing %s configuration\n" % ("CORE" if args.core > 0 else "DEFAULT")) |
|
|
|
|
|
|
|
output_format="{:<20}|{:<11}|{:<11}|{:<11}|{:<11}|{:<11}|{:<12}" |
|
|
|
output_format = "{:<20}|{:<11}|{:<11}|{:<11}|{:<11}|{:<11}|{:<12}" |
|
|
|
print(output_format.format( |
|
|
|
"Module", |
|
|
|
"Cache IRAM", |
|
|
|
"Init RAM", |
|
|
|
"R.O. RAM", |
|
|
|
"Uninit RAM", |
|
|
|
"Flash ROM", |
|
|
|
"Binary size" |
|
|
|
"Module", |
|
|
|
"Cache IRAM", |
|
|
|
"Init RAM", |
|
|
|
"R.O. RAM", |
|
|
|
"Uninit RAM", |
|
|
|
"Flash ROM", |
|
|
|
"Binary size" |
|
|
|
)) |
|
|
|
|
|
|
|
# Build the core without modules to get base memory usage |
|
|
@ -183,13 +185,13 @@ try: |
|
|
|
base = analyse_memory(".pioenvs/%s/firmware.elf" % env) |
|
|
|
base['size'] = file_size(".pioenvs/%s/firmware.bin" % env) |
|
|
|
print(output_format.format( |
|
|
|
"CORE" if args.core == 1 else "DEFAULT", |
|
|
|
base['text'], |
|
|
|
base['data'], |
|
|
|
base['rodata'], |
|
|
|
base['bss'], |
|
|
|
base['irom0_text'], |
|
|
|
base['size'], |
|
|
|
"CORE" if args.core == 1 else "DEFAULT", |
|
|
|
base['text'], |
|
|
|
base['data'], |
|
|
|
base['rodata'], |
|
|
|
base['bss'], |
|
|
|
base['irom0_text'], |
|
|
|
base['size'], |
|
|
|
)) |
|
|
|
|
|
|
|
# Test each module |
|
|
@ -198,18 +200,18 @@ try: |
|
|
|
|
|
|
|
modules[module] = 1 |
|
|
|
run(env, modules) |
|
|
|
results[module]=analyse_memory(".pioenvs/%s/firmware.elf" % env) |
|
|
|
results[module] = analyse_memory(".pioenvs/%s/firmware.elf" % env) |
|
|
|
results[module]['size'] = file_size(".pioenvs/%s/firmware.bin" % env) |
|
|
|
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]['irom0_text'] - base['irom0_text'], |
|
|
|
results[module]['size'] - base['size'], |
|
|
|
module, |
|
|
|
results[module]['text'] - base['text'], |
|
|
|
results[module]['data'] - base['data'], |
|
|
|
results[module]['rodata'] - base['rodata'], |
|
|
|
results[module]['bss'] - base['bss'], |
|
|
|
results[module]['irom0_text'] - base['irom0_text'], |
|
|
|
results[module]['size'] - base['size'], |
|
|
|
)) |
|
|
|
|
|
|
|
# Test all modules |
|
|
@ -223,23 +225,23 @@ try: |
|
|
|
|
|
|
|
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['irom0_text'] - base['irom0_text'], |
|
|
|
total['size'] - base['size'], |
|
|
|
"ALL MODULES", |
|
|
|
total['text'] - base['text'], |
|
|
|
total['data'] - base['data'], |
|
|
|
total['rodata'] - base['rodata'], |
|
|
|
total['bss'] - base['bss'], |
|
|
|
total['irom0_text'] - base['irom0_text'], |
|
|
|
total['size'] - base['size'], |
|
|
|
)) |
|
|
|
|
|
|
|
print(output_format.format( |
|
|
|
"TOTAL", |
|
|
|
total['text'], |
|
|
|
total['data'], |
|
|
|
total['rodata'], |
|
|
|
total['bss'], |
|
|
|
total['irom0_text'], |
|
|
|
total['size'], |
|
|
|
"TOTAL", |
|
|
|
total['text'], |
|
|
|
total['data'], |
|
|
|
total['rodata'], |
|
|
|
total['bss'], |
|
|
|
total['irom0_text'], |
|
|
|
total['size'], |
|
|
|
)) |
|
|
|
|
|
|
|
except: |
|
|
|