Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Table output for segment size script #8551

Merged
merged 7 commits into from
May 11, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 141 additions & 59 deletions tools/sizes.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,74 +17,156 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

from __future__ import print_function
import argparse
import os
import subprocess
import sys

def get_segment_hints(iram):
hints = {}
hints['ICACHE'] = ' - flash instruction cache'
hints['IROM'] = ' - code in flash (default or ICACHE_FLASH_ATTR)'
hints['IRAM'] = ' / ' + str(iram) + ' - code in IRAM (IRAM_ATTR, ISRs...)'
hints['DATA'] = ') - initialized variables (global, static) in RAM/HEAP'
hints['RODATA'] = ') / 81920 - constants (global, static) in RAM/HEAP'
hints['BSS'] = ') - zeroed variables (global, static) in RAM/HEAP'
return hints

def get_segment_sizes(elf, path):
sizes = {}
sizes['ICACHE'] = 0
sizes['IROM'] = 0
sizes['IRAM'] = 0
sizes['DATA'] = 0
sizes['RODATA'] = 0
sizes['BSS'] = 0
p = subprocess.Popen([path + "/xtensa-lx106-elf-size", '-A', elf], stdout=subprocess.PIPE, universal_newlines=True )
lines = p.stdout.readlines()
for line in lines:
words = line.split()
if line.startswith('.irom0.text'):
sizes['IROM'] = sizes['IROM'] + int(words[1])
elif line.startswith('.text'): # Gets .text and .text1
sizes['IRAM'] = sizes['IRAM'] + int(words[1])
elif line.startswith('.data'): # Gets .text and .text1
sizes['DATA'] = sizes['DATA'] + int(words[1])
elif line.startswith('.rodata'): # Gets .text and .text1
sizes['RODATA'] = sizes['RODATA'] + int(words[1])
elif line.startswith('.bss'): # Gets .text and .text1
sizes['BSS'] = sizes['BSS'] + int(words[1])


def get_segment_sizes(elf, path, mmu):
iram_size = 0
iheap_size = 0
icache_size = 32168

for line in mmu.split():
words = line.split("=")
if line.startswith("-DMMU_IRAM_SIZE"):
iram_size = int(words[1], 16)
elif line.startswith("-DMMU_ICACHE_SIZE"):
icache_size = int(words[1], 16)
elif line.startswith("-DMMU_SEC_HEAP_SIZE"):
iheap_size = int(words[1], 16)

sizes = [
[
"Variables and constants in RAM (global, static)",
[
{
"DATA": 0,
"RODATA": 0,
"BSS": 0,
},
80192,
],
],
[
"Instruction RAM (IRAM_ATTR, ICACHE_RAM_ATTR)",
[
{
"ICACHE": icache_size,
"IHEAP": iheap_size,
"IRAM": 0,
},
65536,
],
],
["Code in flash (default, ICACHE_FLASH_ATTR)", [{"IROM": 0}, 1048576]],
]

section_mapping = (
(".irom0.text", "IROM"),
(".text", "IRAM"),
(".data", "DATA"),
(".rodata", "RODATA"),
(".bss", "BSS"),
)

cmd = [os.path.join(path, "xtensa-lx106-elf-size"), "-A", elf]
with subprocess.Popen(cmd, stdout=subprocess.PIPE, universal_newlines=True) as proc:
lines = proc.stdout.readlines()
for line in lines:
words = line.split()
for section, target in section_mapping:
if not line.startswith(section):
continue
for group, (segments, total) in sizes:
if target in segments:
segments[target] += int(words[1])
assert segments[target] <= total

return sizes

def get_mmu_sizes(mmu, sizes):
iram = 0x8000
sizes['ICACHE'] = 0x8000
lines = mmu.split(' ')
for line in lines:
words = line.split('=')
if line.startswith('-DMMU_IRAM_SIZE'):
iram = int(words[1], 16)
elif line.startswith('-DMMU_ICACHE_SIZE'):
sizes['ICACHE'] = int(words[1], 16)
return [iram, sizes]

def percentage(lhs, rhs):
return "{}%".format(int(100.0 * float(lhs) / float(rhs)))


HINTS = {
"ICACHE": "reserved space for flash instruction cache",
"IRAM": "code in IRAM",
"IHEAP": "secondary heap space",
"IROM": "code in flash",
"DATA": "initialized variables",
"RODATA": "constants",
"BSS": "zeroed variables",
}


def safe_prefix(n, length):
if n == length:
return "`--"

return "|--"


def prefix(n, length):
if n == length:
return "└──"

return "├──"


def filter_segments(segments):
used = 0
number = 0
available = []

for (segment, size) in segments.items():
if not size:
continue
used += size
number += 1
available.append((number, segment, size))

return (number, used, available)


def main():
parser = argparse.ArgumentParser(description='Report the different segment sizes of a compiled ELF file')
parser.add_argument('-e', '--elf', action='store', required=True, help='Path to the Arduino sketch ELF')
parser.add_argument('-p', '--path', action='store', required=True, help='Path to Xtensa toolchain binaries')
parser.add_argument('-i', '--mmu', action='store', required=False, help='MMU build options')
parser = argparse.ArgumentParser(
description="Report the different segment sizes of a compiled ELF file"
)
parser.add_argument(
"-e",
"--elf",
action="store",
required=True,
help="Path to the Arduino sketch ELF",
)
parser.add_argument(
"-p",
"--path",
action="store",
required=True,
help="Path to Xtensa toolchain binaries",
)
parser.add_argument(
"-i", "--mmu", action="store", required=False, help="MMU build options"
)

args = parser.parse_args()
sizes = get_segment_sizes(args.elf, args.path)
[iram, sizes] = get_mmu_sizes(args.mmu, sizes)
hints = get_segment_hints(iram)
sizes = get_segment_sizes(args.elf, args.path, args.mmu)

for group, (segments, total) in sizes:
number, used, segments = filter_segments(segments)

sys.stderr.write("Executable segment sizes:" + os.linesep)
for k in sizes.keys():
sys.stderr.write("%-7s: %-5d %s %s" % (k, sizes[k], hints[k], os.linesep))
print(f". {group:<8}, used {used} / {total} bytes ({percentage(used, total)})")
print("| SEGMENT BYTES DESCRIPTION")
for n, segment, size in segments:
try:
print(f"{prefix(n, number)} ", end="")
except UnicodeEncodeError:
print(f"{safe_prefix(n, number)} ", end="")
print(f"{segment:<8} {size:<8} {HINTS[segment]:<16}")

return 0

if __name__ == '__main__':
sys.exit(main())
if __name__ == "__main__":
main()