Introduce "Program Memory Usage"

This commit is contained in:
Ivan Kravets
2018-06-04 14:09:48 +03:00
parent 2fb8128791
commit bfc94d36e3
5 changed files with 121 additions and 42 deletions

View File

@ -4,11 +4,16 @@ Release Notes
PlatformIO 3.0
--------------
3.5.4 (2018-??-??)
3.6.0 (2018-??-??)
~~~~~~~~~~~~~~~~~~
* Check maximum allowed firmware size for programming/uploading using `platformio run --target checkprogsize <http://docs.platformio.org/en/latest/userguide/cmd_run.html#cmdoption-platformio-run-t>`__
(`issue #1412 <https://github.com/platformio/platformio-core/issues/1412>`_)
* `Program Memory Usage <http://docs.platformio.org/en/latest/faq.html#program-memory-usage>`_
- Print human-readable memory usage information after a build and before uploading
- Print detailed memory usage information with "sections" and "addresses"
in `verbose mode <http://docs.platformio.org/en/latest/userguide/cmd_run.html#cmdoption-platformio-run-v>`__
- Check maximum allowed "program" and "data" sizes before uploading/programming
(`issue #1412 <https://github.com/platformio/platformio-core/issues/1412>`_)
3.5.3 (2018-06-01)
~~~~~~~~~~~~~~~~~~

2
docs

Submodule docs updated: 91eae26417...22c4d0109e

View File

@ -20,7 +20,7 @@ from os.path import expanduser, join
from time import time
from SCons.Script import (ARGUMENTS, COMMAND_LINE_TARGETS, DEFAULT_TARGETS,
Action, AllowSubstExceptions, AlwaysBuild,
AllowSubstExceptions, AlwaysBuild, Default,
DefaultEnvironment, Variables)
from platformio import util
@ -164,15 +164,34 @@ for item in env.GetExtraScripts("pre"):
env.SConscript("$BUILD_SCRIPT")
AlwaysBuild(env.Alias("__debug", DEFAULT_TARGETS + ["size"]))
AlwaysBuild(env.Alias("__test", DEFAULT_TARGETS + ["size"]))
if "UPLOAD_FLAGS" in env:
env.Prepend(UPLOADERFLAGS=["$UPLOAD_FLAGS"])
for item in env.GetExtraScripts("post"):
env.SConscript(item, exports="env")
##############################################################################
# Checking program size
if env.get("SIZETOOL") and "nobuild" not in COMMAND_LINE_TARGETS:
env.Depends(["upload", "program"], "checkprogsize")
# Replace platform's "size" target with our
_new_targets = [t for t in DEFAULT_TARGETS if str(t) != "size"]
Default(None)
Default(_new_targets)
Default("checkprogsize")
# Print configured protocols
env.AddPreAction(
["upload", "program"],
env.VerboseAction(lambda source, target, env: env.PrintUploadInfo(),
"Configuring upload protocol..."))
AlwaysBuild(env.Alias("__debug", DEFAULT_TARGETS))
AlwaysBuild(env.Alias("__test", DEFAULT_TARGETS))
##############################################################################
if "envdump" in COMMAND_LINE_TARGETS:
print env.Dump()
env.Exit(0)
@ -189,7 +208,3 @@ if "idedata" in COMMAND_LINE_TARGETS:
"See explanation in FAQ > Troubleshooting > Building\n"
"http://docs.platformio.org/page/faq.html\n\n")
env.Exit(1)
env.AddPreAction(["upload", "program"],
Action(lambda source, target, env: env.PrintUploadInfo(),
"Configuring upload protocol..."))

View File

@ -14,6 +14,7 @@
from __future__ import absolute_import
import re
import sys
from fnmatch import fnmatch
from os import environ
@ -21,11 +22,13 @@ from os.path import isfile, join
from shutil import copyfile
from time import sleep
from SCons.Node.Alias import Alias
from SCons.Script import ARGUMENTS
from serial import Serial, SerialException
from platformio import util
# pylint: disable=unused-argument
def FlushSerialBuffer(env, port):
s = Serial(env.subst(port))
@ -45,7 +48,7 @@ def TouchSerialPort(env, port, baudrate):
s = Serial(port=port, baudrate=baudrate)
s.setDTR(False)
s.close()
except: # pylint: disable=W0702
except: # pylint: disable=bare-except
pass
sleep(0.4) # DO NOT REMOVE THAT (required by SAM-BA based boards)
@ -88,7 +91,7 @@ def WaitForNewSerialPort(env, before):
return new_port
def AutodetectUploadPort(*args, **kwargs): # pylint: disable=unused-argument
def AutodetectUploadPort(*args, **kwargs):
env = args[0]
def _get_pattern():
@ -173,7 +176,7 @@ def AutodetectUploadPort(*args, **kwargs): # pylint: disable=unused-argument
env.Exit(1)
def UploadToDisk(_, target, source, env): # pylint: disable=W0613,W0621
def UploadToDisk(_, target, source, env):
assert "UPLOAD_PORT" in env
progname = env.subst("$PROGNAME")
for ext in ("bin", "hex"):
@ -186,32 +189,87 @@ def UploadToDisk(_, target, source, env): # pylint: disable=W0613,W0621
"(Some boards may require manual hard reset)"
def CheckUploadSize(_, target, source, env): # pylint: disable=W0613,W0621
if "BOARD" not in env:
return
max_size = int(env.BoardConfig().get("upload.maximum_size", 0))
if max_size == 0 or "SIZETOOL" not in env:
return
sysenv = environ.copy()
sysenv['PATH'] = str(env['ENV']['PATH'])
cmd = [
env.subst("$SIZETOOL"), "-B",
str(source[0] if isinstance(target[0], Alias) else target[0])
def CheckUploadSize(_, target, source, env):
check_conditions = [
env.get("BOARD"),
env.get("SIZETOOL") or env.get("SIZECHECKCMD")
]
result = util.exec_command(cmd, env=sysenv)
if result['returncode'] != 0:
if not all(check_conditions):
return
program_max_size = int(env.BoardConfig().get("upload.maximum_size", 0))
data_max_size = int(env.BoardConfig().get("upload.maximum_ram_size", 0))
if program_max_size == 0:
return
print result['out'].strip()
line = result['out'].strip().splitlines()[1]
values = [v.strip() for v in line.split("\t")]
used_size = int(values[0]) + int(values[1])
def _configure_defaults():
env.Replace(
SIZECHECKCMD="$SIZETOOL -B -d $SOURCES",
SIZEPROGREGEXP=r"^(\d+)\s+(\d+)\s+\d+\s",
SIZEDATAREGEXP=r"^\d+\s+(\d+)\s+(\d+)\s+\d+")
if used_size > max_size:
def _get_size_output():
cmd = env.get("SIZECHECKCMD")
if not cmd:
return None
if not isinstance(cmd, list):
cmd = cmd.split()
cmd = [arg.replace("$SOURCES", str(source[0])) for arg in cmd if arg]
sysenv = environ.copy()
sysenv['PATH'] = str(env['ENV']['PATH'])
result = util.exec_command(env.subst(cmd), env=sysenv)
if result['returncode'] != 0:
return None
return result['out'].strip()
def _calculate_size(output, pattern):
if not output or not pattern:
return -1
size = 0
regexp = re.compile(pattern)
for line in output.split("\n"):
line = line.strip()
if not line:
continue
match = regexp.search(line)
if not match:
continue
size += sum(int(value) for value in match.groups())
return size
def _format_availale_bytes(value, total):
percent_raw = float(value) / float(total)
blocks_per_progress = 10
used_blocks = int(round(blocks_per_progress * percent_raw))
if used_blocks > blocks_per_progress:
used_blocks = blocks_per_progress
return "[{:{}}] {: 6.1%} (used {:d} bytes from {:d} bytes)".format(
"=" * used_blocks, blocks_per_progress, percent_raw, value, total)
if not env.get("SIZECHECKCMD") and not env.get("SIZEPROGREGEXP"):
_configure_defaults()
output = _get_size_output()
program_size = _calculate_size(output, env.get("SIZEPROGREGEXP"))
data_size = _calculate_size(output, env.get("SIZEDATAREGEXP"))
print "Memory Usage -> http://bit.ly/pio-memory-usage"
if data_max_size and data_size > -1:
print "DATA: %s" % _format_availale_bytes(data_size, data_max_size)
if program_size > -1:
print "PROGRAM: %s" % _format_availale_bytes(program_size,
program_max_size)
if int(ARGUMENTS.get("PIOVERBOSE", 0)):
print output
# raise error
if data_max_size and data_size > data_max_size:
sys.stderr.write(
"Error: The data size (%d bytes) is greater "
"than maximum allowed (%s bytes)\n" % (data_size, data_max_size))
env.Exit(1)
if program_size > program_max_size:
sys.stderr.write("Error: The program size (%d bytes) is greater "
"than maximum allowed (%s bytes)\n" % (used_size,
max_size))
"than maximum allowed (%s bytes)\n" %
(program_size, program_max_size))
env.Exit(1)

View File

@ -107,12 +107,13 @@ def BuildProgram(env):
program = env.Program(
join("$BUILD_DIR", env.subst("$PROGNAME")), env['PIOBUILDFILES'])
env.Replace(PIOMAINPROG=program)
checksize_action = env.VerboseAction(env.CheckUploadSize,
"Checking program size")
AlwaysBuild(env.Alias("checkprogsize", program, checksize_action))
if set(["upload", "program"]) & set(COMMAND_LINE_TARGETS):
env.AddPostAction(program, checksize_action)
AlwaysBuild(
env.Alias(
"checkprogsize", program,
env.VerboseAction(env.CheckUploadSize,
"Checking size $PIOMAINPROG")))
return program