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 4 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
174 changes: 117 additions & 57 deletions tools/sizes.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,74 +17,134 @@
# 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
import contextlib

HINTS = {
"ICACHE": "reserved space for flash instruction cache",
"IRAM": "code in IRAM (IRAM_ATTR, ICACHE_RAM_ATTR)",
"IROM": "code in flash (default, ICACHE_FLASH_ATTR)",
"DATA": "initialized variables (global, static)",
"RODATA": "constants (global, static)",
"BSS": "zeroed variables (global, static)",
}


def get_segment_sizes(elf, path, mmu):
iram_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)

sizes = [
["Variables and constants in RAM", [{
"DATA": 0,
"RODATA": 0,
"BSS": 0,
}, 80192]],
["Instruction RAM", [{
"ICACHE": icache_size,
"IRAM": 0,
}, 65536]],
["Code in flash", [{
"IROM": 0
}, 1048576]],
]

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 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

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])
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)))


def prefix(n, segments):
if n == len(segments):
out = "└──"
else:
out = "├──"

return out


def safe_prefix(n, segments):
if n == len(segments):
out = "|__"
else:
out = "|--"

return out


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)

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))
for group, (segments, total) in sizes:
used = sum(segments.values())
print(
f". {group:<8}, total {total} bytes, used {used} bytes ({percentage(used, total)})"
)
for n, (segment, size) in enumerate(segments.items(), start=1):
try:
print(
f"{prefix(n, segments)} {segment:<8} {size:<8} {HINTS[segment]:<16}"
)
except UnicodeEncodeError:
print(
f"{safe_prefix(n, segments)} {segment:<8} {size:<8} {HINTS[segment]:<16}"
)

return 0

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