mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-08 16:59:50 +03:00
removed old scripts, so mem optimizations
This commit is contained in:
@@ -6,7 +6,8 @@
|
|||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
- optimized MQTT for HA to reduce mem fragmentation issues
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
- old scripts
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
from subprocess import call
|
|
||||||
import os
|
|
||||||
|
|
||||||
# example stackdmp.txt would contain text like below copied & pasted from a 'crash dump' command:
|
|
||||||
|
|
||||||
# >>>stack>>>
|
|
||||||
# 3fffff20: 3fff32f0 00000003 3fff3028 402101b2
|
|
||||||
# 3fffff30: 3fffdad0 3fff3280 0000000d 402148aa
|
|
||||||
# 3fffff40: 3fffdad0 3fff3280 3fff326c 3fff32f0
|
|
||||||
# 3fffff50: 0000000d 3fff326c 3fff3028 402103bd
|
|
||||||
# 3fffff60: 0000000d 3fff34cc 40211de4 3fff34cc
|
|
||||||
# 3fffff70: 3fff3028 3fff14c4 3fff301c 3fff34cc
|
|
||||||
# 3fffff80: 3fffdad0 3fff14c4 3fff3028 40210493
|
|
||||||
# 3fffff90: 3fffdad0 00000000 3fff14c4 4020a738
|
|
||||||
# 3fffffa0: 3fffdad0 00000000 3fff349c 40211e90
|
|
||||||
# 3fffffb0: feefeffe feefeffe 3ffe8558 40100b01
|
|
||||||
# <<<stack<<<
|
|
||||||
|
|
||||||
call(['python', 'scripts/decoder.py ', '-s', '-e', os.getcwd()+"/.pio/build/esp12e/firmware.elf", 'scripts/stackdmp.txt'])
|
|
||||||
|
|
||||||
# example for linux:
|
|
||||||
# % cd EMS-ESP
|
|
||||||
# % python scripts/decoder_linux.py -s -e .pio/build/esp12e/firmware.elf scripts/stackdmp.txt
|
|
||||||
|
|
||||||
# python decoder_linux.py -s -e ../.pio/build/esp8266-debug/firmware.elf stackdmp.txt
|
|
||||||
169
scripts/build.sh
169
scripts/build.sh
@@ -1,169 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
### Functions
|
|
||||||
|
|
||||||
is_git() {
|
|
||||||
command -v git >/dev/null 2>&1 || return 1
|
|
||||||
command git rev-parse >/dev/null 2>&1 || return 1
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
stat_bytes() {
|
|
||||||
filesize=`du -k "$1" | cut -f1;`
|
|
||||||
echo 'size:' $filesize 'bytes'
|
|
||||||
}
|
|
||||||
|
|
||||||
# Available environments
|
|
||||||
list_envs() {
|
|
||||||
grep env: platformio.ini | sed 's/\[env:\(.*\)\]/\1/g'
|
|
||||||
}
|
|
||||||
|
|
||||||
print_available() {
|
|
||||||
echo "--------------------------------------------------------------"
|
|
||||||
echo "Available environments:"
|
|
||||||
for environment in $available; do
|
|
||||||
echo "-> $environment"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
print_environments() {
|
|
||||||
echo "--------------------------------------------------------------"
|
|
||||||
echo "Current environments:"
|
|
||||||
for environment in $environments; do
|
|
||||||
echo "-> $environment"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
set_default_environments() {
|
|
||||||
# Hook to build in parallel when using travis
|
|
||||||
if [[ "${TRAVIS_BUILD_STAGE_NAME}" = "Release" ]] && ${par_build}; then
|
|
||||||
environments=$(echo ${available} | \
|
|
||||||
awk -v par_thread=${par_thread} -v par_total_threads=${par_total_threads} \
|
|
||||||
'{ for (i = 1; i <= NF; i++) if (++j % par_total_threads == par_thread ) print $i; }')
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Only build travis target
|
|
||||||
if [[ "${TRAVIS_BUILD_STAGE_NAME}" = "Test" ]]; then
|
|
||||||
environments=$travis
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Fallback to all available environments
|
|
||||||
environments=$available
|
|
||||||
}
|
|
||||||
|
|
||||||
build_environments() {
|
|
||||||
echo "--------------------------------------------------------------"
|
|
||||||
echo "Building firmware images..."
|
|
||||||
# don't move to firmware folder until Travis fixed (see https://github.com/travis-ci/dpl/issues/846#issuecomment-547157406)
|
|
||||||
# mkdir -p $destination
|
|
||||||
|
|
||||||
for environment in $environments; do
|
|
||||||
echo "* EMS-ESP-$version-$environment.bin"
|
|
||||||
platformio run --silent --environment $environment || exit 1
|
|
||||||
stat_bytes .pio/build/$environment/firmware.bin
|
|
||||||
# mv .pio/build/$environment/firmware.bin $destination/EMS-ESP-$version-$environment.bin
|
|
||||||
# mv .pio/build/$environment/firmware.bin EMS-ESP-$version-$environment.bin
|
|
||||||
mv .pio/build/$environment/firmware.bin EMS-ESP-dev-$environment.bin
|
|
||||||
done
|
|
||||||
echo "--------------------------------------------------------------"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
####### MAIN
|
|
||||||
|
|
||||||
destination=firmware
|
|
||||||
version_file=./src/version.h
|
|
||||||
version=$(grep -E '^#define EMSESP_APP_VERSION' $version_file | awk '{print $3}' | sed 's/"//g')
|
|
||||||
|
|
||||||
if ${TRAVIS:-false}; then
|
|
||||||
git_revision=${TRAVIS_COMMIT::7}
|
|
||||||
git_tag=${TRAVIS_TAG}
|
|
||||||
elif is_git; then
|
|
||||||
git_revision=$(git rev-parse --short HEAD)
|
|
||||||
git_tag=$(git tag --contains HEAD)
|
|
||||||
else
|
|
||||||
git_revision=unknown
|
|
||||||
git_tag=
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo $git_tag
|
|
||||||
|
|
||||||
if [[ -n $git_tag ]]; then
|
|
||||||
new_version=${version/-*}
|
|
||||||
sed -i -e "s@$version@$new_version@" $version_file
|
|
||||||
version=$new_version
|
|
||||||
trap "git checkout -- $version_file" EXIT
|
|
||||||
fi
|
|
||||||
|
|
||||||
par_build=false
|
|
||||||
par_thread=${BUILDER_THREAD:-0}
|
|
||||||
par_total_threads=${BUILDER_TOTAL_THREADS:-4}
|
|
||||||
if [ ${par_thread} -ne ${par_thread} -o \
|
|
||||||
${par_total_threads} -ne ${par_total_threads} ]; then
|
|
||||||
echo "Parallel threads should be a number."
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
if [ ${par_thread} -ge ${par_total_threads} ]; then
|
|
||||||
echo "Current thread is greater than total threads. Doesn't make sense"
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
# travis platformio target is used for nightly Test
|
|
||||||
travis=$(list_envs | grep travis | sort)
|
|
||||||
|
|
||||||
# get all taregts, excluding travis and debug
|
|
||||||
available=$(list_envs | grep -Ev -- 'travis|debug|release' | sort)
|
|
||||||
|
|
||||||
export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS}"
|
|
||||||
|
|
||||||
# get command line Parameters
|
|
||||||
# l prints environments
|
|
||||||
# 2 does parallel builds
|
|
||||||
# d uses next arg as destination folder
|
|
||||||
while getopts "lpd:" opt; do
|
|
||||||
case $opt in
|
|
||||||
l)
|
|
||||||
print_available
|
|
||||||
exit
|
|
||||||
;;
|
|
||||||
p)
|
|
||||||
par_build=true
|
|
||||||
;;
|
|
||||||
d)
|
|
||||||
destination=$OPTARG
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
shift $((OPTIND-1))
|
|
||||||
|
|
||||||
# Welcome message
|
|
||||||
echo "--------------------------------------------------------------"
|
|
||||||
echo "EMS-ESP FIRMWARE BUILDER"
|
|
||||||
echo "Building for version ${version}" ${git_revision:+($git_revision)}
|
|
||||||
|
|
||||||
# Environments to build
|
|
||||||
environments=$@
|
|
||||||
|
|
||||||
if [ $# -eq 0 ]; then
|
|
||||||
set_default_environments
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ${CI:-false}; then
|
|
||||||
print_environments
|
|
||||||
fi
|
|
||||||
|
|
||||||
# for debugging
|
|
||||||
echo "* git_revision = $git_revision"
|
|
||||||
echo "* git_tag = $git_tag"
|
|
||||||
echo "* TRAVIS_COMMIT = $TRAVIS_COMMIT"
|
|
||||||
echo "* TRAVIS_TAG = $TRAVIS_TAG"
|
|
||||||
echo "* TRAVIS_BRANCH = $TRAVIS_BRANCH"
|
|
||||||
echo "* TRAVIS_BUILD_STAGE_NAME = $TRAVIS_BUILD_STAGE_NAME"
|
|
||||||
|
|
||||||
build_environments
|
|
||||||
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
from subprocess import call
|
|
||||||
import os
|
|
||||||
Import("env")
|
|
||||||
|
|
||||||
def clean(source, target, env):
|
|
||||||
print("\n** Starting clean...")
|
|
||||||
call(["pio", "run", "-t", "erase"])
|
|
||||||
call(["esptool.py", "-p COM6", "write_flash 0x00000", os.getcwd()+"../firmware/*.bin"])
|
|
||||||
print("\n** Finished clean.")
|
|
||||||
|
|
||||||
# built in targets: (buildprog, size, upload, program, buildfs, uploadfs, uploadfsota)
|
|
||||||
env.AddPreAction("buildprog", clean)
|
|
||||||
|
|
||||||
@@ -1,307 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
"""ESP Exception Decoder
|
|
||||||
|
|
||||||
github: https://github.com/janLo/EspArduinoExceptionDecoder
|
|
||||||
license: GPL v3
|
|
||||||
author: Jan Losinski
|
|
||||||
"""
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import re
|
|
||||||
import subprocess
|
|
||||||
from collections import namedtuple
|
|
||||||
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
EXCEPTIONS = [
|
|
||||||
"Illegal instruction",
|
|
||||||
"SYSCALL instruction",
|
|
||||||
"InstructionFetchError: Processor internal physical address or data error during instruction fetch",
|
|
||||||
"LoadStoreError: Processor internal physical address or data error during load or store",
|
|
||||||
"Level1Interrupt: Level-1 interrupt as indicated by set level-1 bits in the INTERRUPT register",
|
|
||||||
"Alloca: MOVSP instruction, if caller's registers are not in the register file",
|
|
||||||
"IntegerDivideByZero: QUOS, QUOU, REMS, or REMU divisor operand is zero",
|
|
||||||
"reserved",
|
|
||||||
"Privileged: Attempt to execute a privileged operation when CRING ? 0",
|
|
||||||
"LoadStoreAlignmentCause: Load or store to an unaligned address",
|
|
||||||
"reserved",
|
|
||||||
"reserved",
|
|
||||||
"InstrPIFDataError: PIF data error during instruction fetch",
|
|
||||||
"LoadStorePIFDataError: Synchronous PIF data error during LoadStore access",
|
|
||||||
"InstrPIFAddrError: PIF address error during instruction fetch",
|
|
||||||
"LoadStorePIFAddrError: Synchronous PIF address error during LoadStore access",
|
|
||||||
"InstTLBMiss: Error during Instruction TLB refill",
|
|
||||||
"InstTLBMultiHit: Multiple instruction TLB entries matched",
|
|
||||||
"InstFetchPrivilege: An instruction fetch referenced a virtual address at a ring level less than CRING",
|
|
||||||
"reserved",
|
|
||||||
"InstFetchProhibited: An instruction fetch referenced a page mapped with an attribute that does not permit instruction fetch",
|
|
||||||
"reserved",
|
|
||||||
"reserved",
|
|
||||||
"reserved",
|
|
||||||
"LoadStoreTLBMiss: Error during TLB refill for a load or store",
|
|
||||||
"LoadStoreTLBMultiHit: Multiple TLB entries matched for a load or store",
|
|
||||||
"LoadStorePrivilege: A load or store referenced a virtual address at a ring level less than CRING",
|
|
||||||
"reserved",
|
|
||||||
"LoadProhibited: A load referenced a page mapped with an attribute that does not permit loads",
|
|
||||||
"StoreProhibited: A store referenced a page mapped with an attribute that does not permit stores"
|
|
||||||
]
|
|
||||||
|
|
||||||
PLATFORMS = {
|
|
||||||
"ESP8266": "lx106",
|
|
||||||
"ESP32": "esp32"
|
|
||||||
}
|
|
||||||
|
|
||||||
EXCEPTION_REGEX = re.compile("^Exception \\((?P<exc>[0-9]*)\\):$")
|
|
||||||
COUNTER_REGEX = re.compile('^epc1=(?P<epc1>0x[0-9a-f]+) epc2=(?P<epc2>0x[0-9a-f]+) epc3=(?P<epc3>0x[0-9a-f]+) '
|
|
||||||
'excvaddr=(?P<excvaddr>0x[0-9a-f]+) depc=(?P<depc>0x[0-9a-f]+)$')
|
|
||||||
CTX_REGEX = re.compile("^ctx: (?P<ctx>.+)$")
|
|
||||||
POINTER_REGEX = re.compile('^sp: (?P<sp>[0-9a-f]+) end: (?P<end>[0-9a-f]+) offset: (?P<offset>[0-9a-f]+)$')
|
|
||||||
STACK_BEGIN = '>>>stack>>>'
|
|
||||||
STACK_END = '<<<stack<<<'
|
|
||||||
STACK_REGEX = re.compile(
|
|
||||||
'^(?P<off>[0-9a-f]+):\W+(?P<c1>[0-9a-f]+) (?P<c2>[0-9a-f]+) (?P<c3>[0-9a-f]+) (?P<c4>[0-9a-f]+)(\W.*)?$')
|
|
||||||
|
|
||||||
StackLine = namedtuple("StackLine", ["offset", "content"])
|
|
||||||
|
|
||||||
|
|
||||||
class ExceptionDataParser(object):
|
|
||||||
def __init__(self):
|
|
||||||
self.exception = None
|
|
||||||
|
|
||||||
self.epc1 = None
|
|
||||||
self.epc2 = None
|
|
||||||
self.epc3 = None
|
|
||||||
self.excvaddr = None
|
|
||||||
self.depc = None
|
|
||||||
|
|
||||||
self.ctx = None
|
|
||||||
|
|
||||||
self.sp = None
|
|
||||||
self.end = None
|
|
||||||
self.offset = None
|
|
||||||
|
|
||||||
self.stack = []
|
|
||||||
|
|
||||||
def _parse_exception(self, line):
|
|
||||||
match = EXCEPTION_REGEX.match(line)
|
|
||||||
if match is not None:
|
|
||||||
self.exception = int(match.group('exc'))
|
|
||||||
return self._parse_counters
|
|
||||||
return self._parse_exception
|
|
||||||
|
|
||||||
def _parse_counters(self, line):
|
|
||||||
match = COUNTER_REGEX.match(line)
|
|
||||||
if match is not None:
|
|
||||||
self.epc1 = match.group("epc1")
|
|
||||||
self.epc2 = match.group("epc2")
|
|
||||||
self.epc3 = match.group("epc3")
|
|
||||||
self.excvaddr = match.group("excvaddr")
|
|
||||||
self.depc = match.group("depc")
|
|
||||||
return self._parse_ctx
|
|
||||||
return self._parse_counters
|
|
||||||
|
|
||||||
def _parse_ctx(self, line):
|
|
||||||
match = CTX_REGEX.match(line)
|
|
||||||
if match is not None:
|
|
||||||
self.ctx = match.group("ctx")
|
|
||||||
return self._parse_pointers
|
|
||||||
return self._parse_ctx
|
|
||||||
|
|
||||||
def _parse_pointers(self, line):
|
|
||||||
match = POINTER_REGEX.match(line)
|
|
||||||
if match is not None:
|
|
||||||
self.sp = match.group("sp")
|
|
||||||
self.end = match.group("end")
|
|
||||||
self.offset = match.group("offset")
|
|
||||||
return self._parse_stack_begin
|
|
||||||
return self._parse_pointers
|
|
||||||
|
|
||||||
def _parse_stack_begin(self, line):
|
|
||||||
if line == STACK_BEGIN:
|
|
||||||
return self._parse_stack_line
|
|
||||||
return self._parse_stack_begin
|
|
||||||
|
|
||||||
def _parse_stack_line(self, line):
|
|
||||||
if line != STACK_END:
|
|
||||||
match = STACK_REGEX.match(line)
|
|
||||||
if match is not None:
|
|
||||||
self.stack.append(StackLine(offset=match.group("off"),
|
|
||||||
content=(match.group("c1"), match.group("c2"), match.group("c3"),
|
|
||||||
match.group("c4"))))
|
|
||||||
return self._parse_stack_line
|
|
||||||
return None
|
|
||||||
|
|
||||||
def parse_file(self, file, stack_only=False):
|
|
||||||
func = self._parse_exception
|
|
||||||
if stack_only:
|
|
||||||
func = self._parse_stack_begin
|
|
||||||
|
|
||||||
for line in file:
|
|
||||||
func = func(line.strip())
|
|
||||||
if func is None:
|
|
||||||
break
|
|
||||||
|
|
||||||
if func is not None:
|
|
||||||
print("ERROR: Parser not complete!")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
class AddressResolver(object):
|
|
||||||
def __init__(self, tool_path, elf_path):
|
|
||||||
self._tool = tool_path
|
|
||||||
self._elf = elf_path
|
|
||||||
self._address_map = {}
|
|
||||||
|
|
||||||
def _lookup(self, addresses):
|
|
||||||
cmd = [self._tool, "-aipfC", "-e", self._elf] + [addr for addr in addresses if addr is not None]
|
|
||||||
|
|
||||||
if sys.version_info[0] < 3:
|
|
||||||
output = subprocess.check_output(cmd)
|
|
||||||
else:
|
|
||||||
output = subprocess.check_output(cmd, encoding="utf-8")
|
|
||||||
|
|
||||||
line_regex = re.compile("^(?P<addr>[0-9a-fx]+): (?P<result>.+)$")
|
|
||||||
|
|
||||||
last = None
|
|
||||||
for line in output.splitlines():
|
|
||||||
line = line.strip()
|
|
||||||
match = line_regex.match(line)
|
|
||||||
|
|
||||||
if match is None:
|
|
||||||
if last is not None and line.startswith('(inlined by)'):
|
|
||||||
line = line [12:].strip()
|
|
||||||
self._address_map[last] += ("\n \-> inlined by: " + line)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if match.group("result") == '?? ??:0':
|
|
||||||
continue
|
|
||||||
|
|
||||||
self._address_map[match.group("addr")] = match.group("result")
|
|
||||||
last = match.group("addr")
|
|
||||||
|
|
||||||
def fill(self, parser):
|
|
||||||
addresses = [parser.epc1, parser.epc2, parser.epc3, parser.excvaddr, parser.sp, parser.end, parser.offset]
|
|
||||||
for line in parser.stack:
|
|
||||||
addresses.extend(line.content)
|
|
||||||
|
|
||||||
self._lookup(addresses)
|
|
||||||
|
|
||||||
def _sanitize_addr(self, addr):
|
|
||||||
if addr.startswith("0x"):
|
|
||||||
addr = addr[2:]
|
|
||||||
|
|
||||||
fill = "0" * (8 - len(addr))
|
|
||||||
return "0x" + fill + addr
|
|
||||||
|
|
||||||
def resolve_addr(self, addr):
|
|
||||||
out = self._sanitize_addr(addr)
|
|
||||||
|
|
||||||
if out in self._address_map:
|
|
||||||
out += ": " + self._address_map[out]
|
|
||||||
|
|
||||||
return out
|
|
||||||
|
|
||||||
def resolve_stack_addr(self, addr, full=True):
|
|
||||||
addr = self._sanitize_addr(addr)
|
|
||||||
if addr in self._address_map:
|
|
||||||
return addr + ": " + self._address_map[addr]
|
|
||||||
|
|
||||||
if full:
|
|
||||||
return "[DATA (0x" + addr + ")]"
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def print_addr(name, value, resolver):
|
|
||||||
print("{}:{} {}".format(name, " " * (8 - len(name)), resolver.resolve_addr(value)))
|
|
||||||
|
|
||||||
|
|
||||||
def print_stack_full(lines, resolver):
|
|
||||||
print("stack:")
|
|
||||||
for line in lines:
|
|
||||||
print(line.offset + ":")
|
|
||||||
for content in line.content:
|
|
||||||
print(" " + resolver.resolve_stack_addr(content))
|
|
||||||
|
|
||||||
|
|
||||||
def print_stack(lines, resolver):
|
|
||||||
print("stack:")
|
|
||||||
for line in lines:
|
|
||||||
for content in line.content:
|
|
||||||
out = resolver.resolve_stack_addr(content, full=False)
|
|
||||||
if out is None:
|
|
||||||
continue
|
|
||||||
print(out)
|
|
||||||
|
|
||||||
|
|
||||||
def print_result(parser, resolver, full=True, stack_only=False):
|
|
||||||
if not stack_only:
|
|
||||||
print('Exception: {} ({})'.format(parser.exception, EXCEPTIONS[parser.exception]))
|
|
||||||
|
|
||||||
print("")
|
|
||||||
print_addr("epc1", parser.epc1, resolver)
|
|
||||||
print_addr("epc2", parser.epc2, resolver)
|
|
||||||
print_addr("epc3", parser.epc3, resolver)
|
|
||||||
print_addr("excvaddr", parser.excvaddr, resolver)
|
|
||||||
print_addr("depc", parser.depc, resolver)
|
|
||||||
|
|
||||||
print("")
|
|
||||||
print("ctx: " + parser.ctx)
|
|
||||||
|
|
||||||
print("")
|
|
||||||
print_addr("sp", parser.sp, resolver)
|
|
||||||
print_addr("end", parser.end, resolver)
|
|
||||||
print_addr("offset", parser.offset, resolver)
|
|
||||||
|
|
||||||
print("")
|
|
||||||
if full:
|
|
||||||
print_stack_full(parser.stack, resolver)
|
|
||||||
else:
|
|
||||||
print_stack(parser.stack, resolver)
|
|
||||||
|
|
||||||
|
|
||||||
def parse_args():
|
|
||||||
parser = argparse.ArgumentParser(description="decode ESP Stacktraces.")
|
|
||||||
|
|
||||||
parser.add_argument("-p", "--platform", help="The platform to decode from", choices=PLATFORMS.keys(),
|
|
||||||
default="ESP8266")
|
|
||||||
parser.add_argument("-t", "--tool", help="Path to the xtensa toolchain",
|
|
||||||
default="~/.platformio/packages/toolchain-xtensa/")
|
|
||||||
parser.add_argument("-e", "--elf", help="path to elf file", required=True)
|
|
||||||
parser.add_argument("-f", "--full", help="Print full stack dump", action="store_true")
|
|
||||||
parser.add_argument("-s", "--stack_only", help="Decode only a stractrace", action="store_true")
|
|
||||||
parser.add_argument("file", help="The file to read the exception data from ('-' for STDIN)", default="-")
|
|
||||||
|
|
||||||
return parser.parse_args()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
args = parse_args()
|
|
||||||
|
|
||||||
if args.file == "-":
|
|
||||||
file = sys.stdin
|
|
||||||
else:
|
|
||||||
if not os.path.exists(args.file):
|
|
||||||
print("ERROR: file " + args.file + " not found")
|
|
||||||
sys.exit(1)
|
|
||||||
file = open(args.file, "r")
|
|
||||||
|
|
||||||
addr2line = os.path.join(os.path.abspath(os.path.expanduser(args.tool)),
|
|
||||||
"bin/xtensa-" + PLATFORMS[args.platform] + "-elf-addr2line.exe")
|
|
||||||
if not os.path.exists(addr2line):
|
|
||||||
print("ERROR: addr2line not found (" + addr2line + ")")
|
|
||||||
|
|
||||||
elf_file = os.path.abspath(os.path.expanduser(args.elf))
|
|
||||||
if not os.path.exists(elf_file):
|
|
||||||
print("ERROR: elf file not found (" + elf_file + ")")
|
|
||||||
|
|
||||||
parser = ExceptionDataParser()
|
|
||||||
resolver = AddressResolver(addr2line, elf_file)
|
|
||||||
|
|
||||||
parser.parse_file(file, args.stack_only)
|
|
||||||
resolver.fill(parser)
|
|
||||||
|
|
||||||
print_result(parser, resolver, args.full, args.stack_only)
|
|
||||||
@@ -1,307 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
"""ESP Exception Decoder
|
|
||||||
|
|
||||||
github: https://github.com/janLo/EspArduinoExceptionDecoder
|
|
||||||
license: GPL v3
|
|
||||||
author: Jan Losinski
|
|
||||||
"""
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import re
|
|
||||||
import subprocess
|
|
||||||
from collections import namedtuple
|
|
||||||
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
EXCEPTIONS = [
|
|
||||||
"Illegal instruction",
|
|
||||||
"SYSCALL instruction",
|
|
||||||
"InstructionFetchError: Processor internal physical address or data error during instruction fetch",
|
|
||||||
"LoadStoreError: Processor internal physical address or data error during load or store",
|
|
||||||
"Level1Interrupt: Level-1 interrupt as indicated by set level-1 bits in the INTERRUPT register",
|
|
||||||
"Alloca: MOVSP instruction, if caller's registers are not in the register file",
|
|
||||||
"IntegerDivideByZero: QUOS, QUOU, REMS, or REMU divisor operand is zero",
|
|
||||||
"reserved",
|
|
||||||
"Privileged: Attempt to execute a privileged operation when CRING ? 0",
|
|
||||||
"LoadStoreAlignmentCause: Load or store to an unaligned address",
|
|
||||||
"reserved",
|
|
||||||
"reserved",
|
|
||||||
"InstrPIFDataError: PIF data error during instruction fetch",
|
|
||||||
"LoadStorePIFDataError: Synchronous PIF data error during LoadStore access",
|
|
||||||
"InstrPIFAddrError: PIF address error during instruction fetch",
|
|
||||||
"LoadStorePIFAddrError: Synchronous PIF address error during LoadStore access",
|
|
||||||
"InstTLBMiss: Error during Instruction TLB refill",
|
|
||||||
"InstTLBMultiHit: Multiple instruction TLB entries matched",
|
|
||||||
"InstFetchPrivilege: An instruction fetch referenced a virtual address at a ring level less than CRING",
|
|
||||||
"reserved",
|
|
||||||
"InstFetchProhibited: An instruction fetch referenced a page mapped with an attribute that does not permit instruction fetch",
|
|
||||||
"reserved",
|
|
||||||
"reserved",
|
|
||||||
"reserved",
|
|
||||||
"LoadStoreTLBMiss: Error during TLB refill for a load or store",
|
|
||||||
"LoadStoreTLBMultiHit: Multiple TLB entries matched for a load or store",
|
|
||||||
"LoadStorePrivilege: A load or store referenced a virtual address at a ring level less than CRING",
|
|
||||||
"reserved",
|
|
||||||
"LoadProhibited: A load referenced a page mapped with an attribute that does not permit loads",
|
|
||||||
"StoreProhibited: A store referenced a page mapped with an attribute that does not permit stores"
|
|
||||||
]
|
|
||||||
|
|
||||||
PLATFORMS = {
|
|
||||||
"ESP8266": "lx106",
|
|
||||||
"ESP32": "esp32"
|
|
||||||
}
|
|
||||||
|
|
||||||
EXCEPTION_REGEX = re.compile("^Exception \\((?P<exc>[0-9]*)\\):$")
|
|
||||||
COUNTER_REGEX = re.compile('^epc1=(?P<epc1>0x[0-9a-f]+) epc2=(?P<epc2>0x[0-9a-f]+) epc3=(?P<epc3>0x[0-9a-f]+) '
|
|
||||||
'excvaddr=(?P<excvaddr>0x[0-9a-f]+) depc=(?P<depc>0x[0-9a-f]+)$')
|
|
||||||
CTX_REGEX = re.compile("^ctx: (?P<ctx>.+)$")
|
|
||||||
POINTER_REGEX = re.compile('^sp: (?P<sp>[0-9a-f]+) end: (?P<end>[0-9a-f]+) offset: (?P<offset>[0-9a-f]+)$')
|
|
||||||
STACK_BEGIN = '>>>stack>>>'
|
|
||||||
STACK_END = '<<<stack<<<'
|
|
||||||
STACK_REGEX = re.compile(
|
|
||||||
'^(?P<off>[0-9a-f]+):\W+(?P<c1>[0-9a-f]+) (?P<c2>[0-9a-f]+) (?P<c3>[0-9a-f]+) (?P<c4>[0-9a-f]+)(\W.*)?$')
|
|
||||||
|
|
||||||
StackLine = namedtuple("StackLine", ["offset", "content"])
|
|
||||||
|
|
||||||
|
|
||||||
class ExceptionDataParser(object):
|
|
||||||
def __init__(self):
|
|
||||||
self.exception = None
|
|
||||||
|
|
||||||
self.epc1 = None
|
|
||||||
self.epc2 = None
|
|
||||||
self.epc3 = None
|
|
||||||
self.excvaddr = None
|
|
||||||
self.depc = None
|
|
||||||
|
|
||||||
self.ctx = None
|
|
||||||
|
|
||||||
self.sp = None
|
|
||||||
self.end = None
|
|
||||||
self.offset = None
|
|
||||||
|
|
||||||
self.stack = []
|
|
||||||
|
|
||||||
def _parse_exception(self, line):
|
|
||||||
match = EXCEPTION_REGEX.match(line)
|
|
||||||
if match is not None:
|
|
||||||
self.exception = int(match.group('exc'))
|
|
||||||
return self._parse_counters
|
|
||||||
return self._parse_exception
|
|
||||||
|
|
||||||
def _parse_counters(self, line):
|
|
||||||
match = COUNTER_REGEX.match(line)
|
|
||||||
if match is not None:
|
|
||||||
self.epc1 = match.group("epc1")
|
|
||||||
self.epc2 = match.group("epc2")
|
|
||||||
self.epc3 = match.group("epc3")
|
|
||||||
self.excvaddr = match.group("excvaddr")
|
|
||||||
self.depc = match.group("depc")
|
|
||||||
return self._parse_ctx
|
|
||||||
return self._parse_counters
|
|
||||||
|
|
||||||
def _parse_ctx(self, line):
|
|
||||||
match = CTX_REGEX.match(line)
|
|
||||||
if match is not None:
|
|
||||||
self.ctx = match.group("ctx")
|
|
||||||
return self._parse_pointers
|
|
||||||
return self._parse_ctx
|
|
||||||
|
|
||||||
def _parse_pointers(self, line):
|
|
||||||
match = POINTER_REGEX.match(line)
|
|
||||||
if match is not None:
|
|
||||||
self.sp = match.group("sp")
|
|
||||||
self.end = match.group("end")
|
|
||||||
self.offset = match.group("offset")
|
|
||||||
return self._parse_stack_begin
|
|
||||||
return self._parse_pointers
|
|
||||||
|
|
||||||
def _parse_stack_begin(self, line):
|
|
||||||
if line == STACK_BEGIN:
|
|
||||||
return self._parse_stack_line
|
|
||||||
return self._parse_stack_begin
|
|
||||||
|
|
||||||
def _parse_stack_line(self, line):
|
|
||||||
if line != STACK_END:
|
|
||||||
match = STACK_REGEX.match(line)
|
|
||||||
if match is not None:
|
|
||||||
self.stack.append(StackLine(offset=match.group("off"),
|
|
||||||
content=(match.group("c1"), match.group("c2"), match.group("c3"),
|
|
||||||
match.group("c4"))))
|
|
||||||
return self._parse_stack_line
|
|
||||||
return None
|
|
||||||
|
|
||||||
def parse_file(self, file, stack_only=False):
|
|
||||||
func = self._parse_exception
|
|
||||||
if stack_only:
|
|
||||||
func = self._parse_stack_begin
|
|
||||||
|
|
||||||
for line in file:
|
|
||||||
func = func(line.strip())
|
|
||||||
if func is None:
|
|
||||||
break
|
|
||||||
|
|
||||||
if func is not None:
|
|
||||||
print("ERROR: Parser not complete!")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
class AddressResolver(object):
|
|
||||||
def __init__(self, tool_path, elf_path):
|
|
||||||
self._tool = tool_path
|
|
||||||
self._elf = elf_path
|
|
||||||
self._address_map = {}
|
|
||||||
|
|
||||||
def _lookup(self, addresses):
|
|
||||||
cmd = [self._tool, "-aipfC", "-e", self._elf] + [addr for addr in addresses if addr is not None]
|
|
||||||
|
|
||||||
if sys.version_info[0] < 3:
|
|
||||||
output = subprocess.check_output(cmd)
|
|
||||||
else:
|
|
||||||
output = subprocess.check_output(cmd, encoding="utf-8")
|
|
||||||
|
|
||||||
line_regex = re.compile("^(?P<addr>[0-9a-fx]+): (?P<result>.+)$")
|
|
||||||
|
|
||||||
last = None
|
|
||||||
for line in output.splitlines():
|
|
||||||
line = line.strip()
|
|
||||||
match = line_regex.match(line)
|
|
||||||
|
|
||||||
if match is None:
|
|
||||||
if last is not None and line.startswith('(inlined by)'):
|
|
||||||
line = line [12:].strip()
|
|
||||||
self._address_map[last] += ("\n \-> inlined by: " + line)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if match.group("result") == '?? ??:0':
|
|
||||||
continue
|
|
||||||
|
|
||||||
self._address_map[match.group("addr")] = match.group("result")
|
|
||||||
last = match.group("addr")
|
|
||||||
|
|
||||||
def fill(self, parser):
|
|
||||||
addresses = [parser.epc1, parser.epc2, parser.epc3, parser.excvaddr, parser.sp, parser.end, parser.offset]
|
|
||||||
for line in parser.stack:
|
|
||||||
addresses.extend(line.content)
|
|
||||||
|
|
||||||
self._lookup(addresses)
|
|
||||||
|
|
||||||
def _sanitize_addr(self, addr):
|
|
||||||
if addr.startswith("0x"):
|
|
||||||
addr = addr[2:]
|
|
||||||
|
|
||||||
fill = "0" * (8 - len(addr))
|
|
||||||
return "0x" + fill + addr
|
|
||||||
|
|
||||||
def resolve_addr(self, addr):
|
|
||||||
out = self._sanitize_addr(addr)
|
|
||||||
|
|
||||||
if out in self._address_map:
|
|
||||||
out += ": " + self._address_map[out]
|
|
||||||
|
|
||||||
return out
|
|
||||||
|
|
||||||
def resolve_stack_addr(self, addr, full=True):
|
|
||||||
addr = self._sanitize_addr(addr)
|
|
||||||
if addr in self._address_map:
|
|
||||||
return addr + ": " + self._address_map[addr]
|
|
||||||
|
|
||||||
if full:
|
|
||||||
return "[DATA (0x" + addr + ")]"
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def print_addr(name, value, resolver):
|
|
||||||
print("{}:{} {}".format(name, " " * (8 - len(name)), resolver.resolve_addr(value)))
|
|
||||||
|
|
||||||
|
|
||||||
def print_stack_full(lines, resolver):
|
|
||||||
print("stack:")
|
|
||||||
for line in lines:
|
|
||||||
print(line.offset + ":")
|
|
||||||
for content in line.content:
|
|
||||||
print(" " + resolver.resolve_stack_addr(content))
|
|
||||||
|
|
||||||
|
|
||||||
def print_stack(lines, resolver):
|
|
||||||
print("stack:")
|
|
||||||
for line in lines:
|
|
||||||
for content in line.content:
|
|
||||||
out = resolver.resolve_stack_addr(content, full=False)
|
|
||||||
if out is None:
|
|
||||||
continue
|
|
||||||
print(out)
|
|
||||||
|
|
||||||
|
|
||||||
def print_result(parser, resolver, full=True, stack_only=False):
|
|
||||||
if not stack_only:
|
|
||||||
print('Exception: {} ({})'.format(parser.exception, EXCEPTIONS[parser.exception]))
|
|
||||||
|
|
||||||
print("")
|
|
||||||
print_addr("epc1", parser.epc1, resolver)
|
|
||||||
print_addr("epc2", parser.epc2, resolver)
|
|
||||||
print_addr("epc3", parser.epc3, resolver)
|
|
||||||
print_addr("excvaddr", parser.excvaddr, resolver)
|
|
||||||
print_addr("depc", parser.depc, resolver)
|
|
||||||
|
|
||||||
print("")
|
|
||||||
print("ctx: " + parser.ctx)
|
|
||||||
|
|
||||||
print("")
|
|
||||||
print_addr("sp", parser.sp, resolver)
|
|
||||||
print_addr("end", parser.end, resolver)
|
|
||||||
print_addr("offset", parser.offset, resolver)
|
|
||||||
|
|
||||||
print("")
|
|
||||||
if full:
|
|
||||||
print_stack_full(parser.stack, resolver)
|
|
||||||
else:
|
|
||||||
print_stack(parser.stack, resolver)
|
|
||||||
|
|
||||||
|
|
||||||
def parse_args():
|
|
||||||
parser = argparse.ArgumentParser(description="decode ESP Stacktraces.")
|
|
||||||
|
|
||||||
parser.add_argument("-p", "--platform", help="The platform to decode from", choices=PLATFORMS.keys(),
|
|
||||||
default="ESP8266")
|
|
||||||
parser.add_argument("-t", "--tool", help="Path to the xtensa toolchain",
|
|
||||||
default="~/.platformio/packages/toolchain-xtensa/")
|
|
||||||
parser.add_argument("-e", "--elf", help="path to elf file", required=True)
|
|
||||||
parser.add_argument("-f", "--full", help="Print full stack dump", action="store_true")
|
|
||||||
parser.add_argument("-s", "--stack_only", help="Decode only a stractrace", action="store_true")
|
|
||||||
parser.add_argument("file", help="The file to read the exception data from ('-' for STDIN)", default="-")
|
|
||||||
|
|
||||||
return parser.parse_args()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
args = parse_args()
|
|
||||||
|
|
||||||
if args.file == "-":
|
|
||||||
file = sys.stdin
|
|
||||||
else:
|
|
||||||
if not os.path.exists(args.file):
|
|
||||||
print("ERROR: file " + args.file + " not found")
|
|
||||||
sys.exit(1)
|
|
||||||
file = open(args.file, "r")
|
|
||||||
|
|
||||||
addr2line = os.path.join(os.path.abspath(os.path.expanduser(args.tool)),
|
|
||||||
"bin/xtensa-" + PLATFORMS[args.platform] + "-elf-addr2line")
|
|
||||||
if not os.path.exists(addr2line):
|
|
||||||
print("ERROR: addr2line not found (" + addr2line + ")")
|
|
||||||
|
|
||||||
elf_file = os.path.abspath(os.path.expanduser(args.elf))
|
|
||||||
if not os.path.exists(elf_file):
|
|
||||||
print("ERROR: elf file not found (" + elf_file + ")")
|
|
||||||
|
|
||||||
parser = ExceptionDataParser()
|
|
||||||
resolver = AddressResolver(addr2line, elf_file)
|
|
||||||
|
|
||||||
parser.parse_file(file, args.stack_only)
|
|
||||||
resolver.fill(parser)
|
|
||||||
|
|
||||||
print_result(parser, resolver, args.full, args.stack_only)
|
|
||||||
@@ -1,383 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# pylint: disable=C0301,C0114,C0116,W0511
|
|
||||||
# coding=utf-8
|
|
||||||
# -------------------------------------------------------------------------------
|
|
||||||
# based on ESPurna module memory analyser by xose.perez@gmail.com
|
|
||||||
#
|
|
||||||
# Rewritten for python-3 and changed to use "size" instead of "objdump"
|
|
||||||
# Based on https://github.com/esp8266/Arduino/pull/6525
|
|
||||||
# by Maxim Prokhorov <prokhorov.max@outlook.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
|
|
||||||
#
|
|
||||||
# -------------------------------------------------------------------------------
|
|
||||||
#
|
|
||||||
# When using Windows with non-default installation at the C:\.platformio,
|
|
||||||
# you would need to specify toolchain path manually. For example:
|
|
||||||
#
|
|
||||||
# $ py -3 scripts\memanalyzer.py --toolchain-prefix C:\.platformio\packages\toolchain-xtensa\bin\xtensa-lx106-elf- <args>
|
|
||||||
#
|
|
||||||
# You could also change the path to platformio binary in a similar fashion:
|
|
||||||
# $ py -3 scripts\memanalyzer.py --platformio-prefix C:\Users\Max\platformio-penv\Scripts\
|
|
||||||
#
|
|
||||||
# -------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
from collections import OrderedDict
|
|
||||||
from subprocess import getstatusoutput
|
|
||||||
|
|
||||||
__version__ = (0, 3)
|
|
||||||
|
|
||||||
# -------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
TOTAL_IRAM = 32786
|
|
||||||
TOTAL_DRAM = 81920
|
|
||||||
|
|
||||||
DEFAULT_ENV = "esp12e"
|
|
||||||
TOOLCHAIN_PREFIX = "~/.platformio/packages/toolchain-xtensa/bin/xtensa-lx106-elf-"
|
|
||||||
PLATFORMIO_PREFIX = ""
|
|
||||||
SECTIONS = OrderedDict(
|
|
||||||
[
|
|
||||||
(".data", "Initialized Data (RAM)"),
|
|
||||||
(".rodata", "ReadOnly Data (RAM)"),
|
|
||||||
(".bss", "Uninitialized Data (RAM)"),
|
|
||||||
(".text", "Cached Code (IRAM)"),
|
|
||||||
(".irom0.text", "Uncached Code (SPI)"),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
DESCRIPTION = "Memory Analyzer v{}".format(
|
|
||||||
".".join(str(x) for x in __version__)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# -------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
def size_binary_path(prefix):
|
|
||||||
return "{}size".format(os.path.expanduser(prefix))
|
|
||||||
|
|
||||||
|
|
||||||
def file_size(file):
|
|
||||||
try:
|
|
||||||
return os.stat(file).st_size
|
|
||||||
except OSError:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def analyse_memory(size, elf_file):
|
|
||||||
proc = subprocess.Popen(
|
|
||||||
[size, "-A", elf_file], stdout=subprocess.PIPE, universal_newlines=True
|
|
||||||
)
|
|
||||||
lines = proc.stdout.readlines()
|
|
||||||
|
|
||||||
values = {}
|
|
||||||
|
|
||||||
for line in lines:
|
|
||||||
words = line.split()
|
|
||||||
for name in SECTIONS.keys():
|
|
||||||
if line.startswith(name):
|
|
||||||
value = values.setdefault(name, 0)
|
|
||||||
value += int(words[1])
|
|
||||||
values[name] = value
|
|
||||||
break
|
|
||||||
|
|
||||||
return values
|
|
||||||
|
|
||||||
|
|
||||||
def run(prefix, env, modules, debug):
|
|
||||||
flags = " ".join("-D{}_SUPPORT={:d}".format(k, v) for k, v in modules.items())
|
|
||||||
|
|
||||||
os_env = os.environ.copy()
|
|
||||||
os_env["PLATFORMIO_SRC_BUILD_FLAGS"] = flags
|
|
||||||
os_env["PLATFORMIO_BUILD_CACHE_DIR"] = "test/pio_cache"
|
|
||||||
|
|
||||||
if debug:
|
|
||||||
print("Selected flags: {}".format(flags))
|
|
||||||
|
|
||||||
command = [os.path.join(prefix, "platformio"), "run"]
|
|
||||||
if not debug:
|
|
||||||
command.append("--silent")
|
|
||||||
command.extend(["--environment", env])
|
|
||||||
|
|
||||||
output = None if debug else subprocess.DEVNULL
|
|
||||||
|
|
||||||
|
|
||||||
try:
|
|
||||||
subprocess.check_call(
|
|
||||||
command, shell=False, env=os_env, stdout=output, stderr=output
|
|
||||||
)
|
|
||||||
except subprocess.CalledProcessError:
|
|
||||||
print(" - Command failed: {}".format(command))
|
|
||||||
print(" - Selected flags: {}".format(flags))
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
def get_available_modules():
|
|
||||||
modules = []
|
|
||||||
for line in open("lib/framework/services.h"):
|
|
||||||
match = re.search(r"(\w*)_SUPPORT", line)
|
|
||||||
if match:
|
|
||||||
modules.append((match.group(1), 0))
|
|
||||||
modules.sort(key=lambda item: item[0])
|
|
||||||
|
|
||||||
return OrderedDict(modules)
|
|
||||||
|
|
||||||
|
|
||||||
def parse_commandline_args():
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
description=DESCRIPTION, formatter_class=argparse.ArgumentDefaultsHelpFormatter
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"-e", "--environment", help="platformio environment to use", default=DEFAULT_ENV
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--toolchain-prefix",
|
|
||||||
help="where to find the xtensa toolchain binaries",
|
|
||||||
default=TOOLCHAIN_PREFIX,
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--platformio-prefix",
|
|
||||||
help="where to find the platformio executable",
|
|
||||||
default=PLATFORMIO_PREFIX,
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"-c",
|
|
||||||
"--core",
|
|
||||||
help="use core as base configuration instead of default",
|
|
||||||
action="store_true",
|
|
||||||
default=False,
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"-l",
|
|
||||||
"--list",
|
|
||||||
help="list available modules",
|
|
||||||
action="store_true",
|
|
||||||
default=False,
|
|
||||||
)
|
|
||||||
parser.add_argument("-d", "--debug", action="store_true", default=False)
|
|
||||||
parser.add_argument(
|
|
||||||
"modules", nargs="*", help="Modules to test (use ALL to test them all)"
|
|
||||||
)
|
|
||||||
|
|
||||||
return parser.parse_args()
|
|
||||||
|
|
||||||
|
|
||||||
def size_binary_exists(args):
|
|
||||||
|
|
||||||
status, _ = getstatusoutput(size_binary_path(args.toolchain_prefix))
|
|
||||||
if status != 1:
|
|
||||||
print("size not found, please check that the --toolchain-prefix is correct")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
def get_modules(args):
|
|
||||||
|
|
||||||
# Load list of all modules
|
|
||||||
available_modules = get_available_modules()
|
|
||||||
if args.list:
|
|
||||||
print("List of available modules:\n")
|
|
||||||
for module in available_modules:
|
|
||||||
print("* " + module)
|
|
||||||
print()
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
modules = []
|
|
||||||
if args.modules:
|
|
||||||
if "ALL" in args.modules:
|
|
||||||
modules.extend(available_modules.keys())
|
|
||||||
else:
|
|
||||||
modules.extend(args.modules)
|
|
||||||
modules.sort()
|
|
||||||
|
|
||||||
# Check test modules exist
|
|
||||||
for module in modules:
|
|
||||||
if module not in available_modules:
|
|
||||||
print("Module {} not found".format(module))
|
|
||||||
sys.exit(2)
|
|
||||||
|
|
||||||
# Either use all of modules or specified subset
|
|
||||||
if args.core:
|
|
||||||
modules = available_modules
|
|
||||||
else:
|
|
||||||
modules = OrderedDict((x, 0) for x in modules)
|
|
||||||
|
|
||||||
configuration = "CORE" if args.core else "DEFAULT"
|
|
||||||
|
|
||||||
return configuration, modules
|
|
||||||
|
|
||||||
|
|
||||||
# -------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
class Analyser:
|
|
||||||
"""Run platformio and print info about the resulting binary."""
|
|
||||||
|
|
||||||
OUTPUT_FORMAT = "{:<20}|{:<15}|{:<15}|{:<15}|{:<15}|{:<15}|{:<15}|{:<15}"
|
|
||||||
DELIMETERS = OUTPUT_FORMAT.format(
|
|
||||||
"-" * 20, "-" * 15, "-" * 15, "-" * 15, "-" * 15, "-" * 15, "-" * 15, "-" * 15
|
|
||||||
)
|
|
||||||
FIRMWARE_FORMAT = ".pio/build/{env}/firmware.{suffix}"
|
|
||||||
|
|
||||||
class _Enable:
|
|
||||||
def __init__(self, analyser, module=None):
|
|
||||||
self.analyser = analyser
|
|
||||||
self.module = module
|
|
||||||
|
|
||||||
def __enter__(self):
|
|
||||||
if not self.module:
|
|
||||||
for name in self.analyser.modules:
|
|
||||||
self.analyser.modules[name] = 1
|
|
||||||
else:
|
|
||||||
self.analyser.modules[self.module] = 1
|
|
||||||
return self.analyser
|
|
||||||
|
|
||||||
def __exit__(self, *args, **kwargs):
|
|
||||||
if not self.module:
|
|
||||||
for name in self.analyser.modules:
|
|
||||||
self.analyser.modules[name] = 0
|
|
||||||
else:
|
|
||||||
self.analyser.modules[self.module] = 0
|
|
||||||
|
|
||||||
analyser = None
|
|
||||||
module = None
|
|
||||||
|
|
||||||
def __init__(self, args, modules):
|
|
||||||
self._debug = args.debug
|
|
||||||
self._platformio_prefix = args.platformio_prefix
|
|
||||||
self._toolchain_prefix = args.toolchain_prefix
|
|
||||||
self._environment = args.environment
|
|
||||||
self.modules = modules
|
|
||||||
self.baseline = None
|
|
||||||
|
|
||||||
def enable(self, module=None):
|
|
||||||
return self._Enable(self, module)
|
|
||||||
|
|
||||||
def print(self, *args):
|
|
||||||
print(self.OUTPUT_FORMAT.format(*args))
|
|
||||||
|
|
||||||
def print_delimiters(self):
|
|
||||||
print(self.DELIMETERS)
|
|
||||||
|
|
||||||
def begin(self, name):
|
|
||||||
self.print(
|
|
||||||
"Module",
|
|
||||||
"Cache IRAM",
|
|
||||||
"Init RAM",
|
|
||||||
"R.O. RAM",
|
|
||||||
"Uninit RAM",
|
|
||||||
"Available RAM",
|
|
||||||
"Flash ROM",
|
|
||||||
"Binary size",
|
|
||||||
)
|
|
||||||
self.print(
|
|
||||||
"",
|
|
||||||
".text + .text1",
|
|
||||||
".data",
|
|
||||||
".rodata",
|
|
||||||
".bss",
|
|
||||||
"heap + stack",
|
|
||||||
".irom0.text",
|
|
||||||
"",
|
|
||||||
)
|
|
||||||
self.print_delimiters()
|
|
||||||
self.baseline = self.run()
|
|
||||||
self.print_values(name, self.baseline)
|
|
||||||
|
|
||||||
def print_values(self, header, values):
|
|
||||||
self.print(
|
|
||||||
header,
|
|
||||||
values[".text"],
|
|
||||||
values[".data"],
|
|
||||||
values[".rodata"],
|
|
||||||
values[".bss"],
|
|
||||||
values["free"],
|
|
||||||
values[".irom0.text"],
|
|
||||||
values["size"],
|
|
||||||
)
|
|
||||||
|
|
||||||
def print_compare(self, header, values):
|
|
||||||
self.print(
|
|
||||||
header,
|
|
||||||
values[".text"] - self.baseline[".text"],
|
|
||||||
values[".data"] - self.baseline[".data"],
|
|
||||||
values[".rodata"] - self.baseline[".rodata"],
|
|
||||||
values[".bss"] - self.baseline[".bss"],
|
|
||||||
values["free"] - self.baseline["free"],
|
|
||||||
values[".irom0.text"] - self.baseline[".irom0.text"],
|
|
||||||
values["size"] - self.baseline["size"],
|
|
||||||
)
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
run(self._platformio_prefix, self._environment, self.modules, self._debug)
|
|
||||||
|
|
||||||
elf_path = self.FIRMWARE_FORMAT.format(env=self._environment, suffix="elf")
|
|
||||||
bin_path = self.FIRMWARE_FORMAT.format(env=self._environment, suffix="bin")
|
|
||||||
|
|
||||||
values = analyse_memory(
|
|
||||||
size_binary_path(self._toolchain_prefix), elf_path
|
|
||||||
)
|
|
||||||
|
|
||||||
free = 80 * 1024 - values[".data"] - values[".rodata"] - values[".bss"]
|
|
||||||
free = free + (16 - free % 16)
|
|
||||||
values["free"] = free
|
|
||||||
|
|
||||||
values["size"] = file_size(bin_path)
|
|
||||||
|
|
||||||
return values
|
|
||||||
|
|
||||||
|
|
||||||
def main(args):
|
|
||||||
|
|
||||||
# Check xtensa-lx106-elf-size is in the path
|
|
||||||
size_binary_exists(args)
|
|
||||||
|
|
||||||
# Which modules to test?
|
|
||||||
configuration, modules = get_modules(args)
|
|
||||||
|
|
||||||
# print_values init message
|
|
||||||
print('Selected environment "{}"'.format(args.environment), end="")
|
|
||||||
if modules:
|
|
||||||
print(" with modules: {}".format(" ".join(modules.keys())))
|
|
||||||
else:
|
|
||||||
print()
|
|
||||||
|
|
||||||
print()
|
|
||||||
print("Analyzing {} configuration".format(configuration))
|
|
||||||
print()
|
|
||||||
|
|
||||||
# Build the core without any modules to get base memory usage
|
|
||||||
analyser = Analyser(args, modules)
|
|
||||||
analyser.begin(configuration)
|
|
||||||
|
|
||||||
# Test each module separately
|
|
||||||
results = {}
|
|
||||||
for module in analyser.modules:
|
|
||||||
with analyser.enable(module):
|
|
||||||
results[module] = analyser.run()
|
|
||||||
analyser.print_compare(module, results[module])
|
|
||||||
|
|
||||||
# Test all modules
|
|
||||||
if analyser.modules:
|
|
||||||
|
|
||||||
with analyser.enable():
|
|
||||||
total = analyser.run()
|
|
||||||
|
|
||||||
analyser.print_delimiters()
|
|
||||||
if len(analyser.modules) > 1:
|
|
||||||
analyser.print_compare("ALL MODULES", total)
|
|
||||||
|
|
||||||
analyser.print_values("TOTAL", total)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main(parse_commandline_args())
|
|
||||||
@@ -1 +1 @@
|
|||||||
#define EMSESP_APP_VERSION "2.1.1b0"
|
#define EMSESP_APP_VERSION "2.1.1b1"
|
||||||
|
|||||||
Reference in New Issue
Block a user