mirror of
https://github.com/google/pebble.git
synced 2025-03-15 08:41:21 +00:00
207 lines
6.3 KiB
Python
207 lines
6.3 KiB
Python
|
# Copyright 2024 Google LLC
|
||
|
#
|
||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
# you may not use this file except in compliance with the License.
|
||
|
# You may obtain a copy of the License at
|
||
|
#
|
||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||
|
#
|
||
|
# Unless required by applicable law or agreed to in writing, software
|
||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
# See the License for the specific language governing permissions and
|
||
|
# limitations under the License.
|
||
|
|
||
|
import argparse
|
||
|
import os
|
||
|
import re
|
||
|
import sys
|
||
|
|
||
|
sys.path.append(os.path.dirname(__file__))
|
||
|
import analyze_static_memory_usage
|
||
|
from binutils import nm_generator, analyze_elf
|
||
|
|
||
|
def cleanup_path(f):
|
||
|
f = os.path.normpath(f)
|
||
|
|
||
|
# Check for .c.3.o style suffixes and strip them back to just .c
|
||
|
if len(f) > 6 and f[-6:-3] == '.c.' and f[-2:] == '.o':
|
||
|
f = f[:-4]
|
||
|
|
||
|
if f.startswith('src/'):
|
||
|
f = f[4:]
|
||
|
|
||
|
newlib_index = f.rfind('/newlib/libc')
|
||
|
if newlib_index != -1:
|
||
|
f = f[newlib_index+1:]
|
||
|
|
||
|
libgcc_index = f.rfind('/libgcc/')
|
||
|
if libgcc_index != -1:
|
||
|
f = f[libgcc_index+1:]
|
||
|
|
||
|
libc_index = f.rfind('/arm-none-eabi/lib/')
|
||
|
if libc_index != -1:
|
||
|
f = f[libc_index+1:]
|
||
|
|
||
|
tintin_index = f.rfind('/tintin/src/')
|
||
|
if tintin_index != -1:
|
||
|
f = f[tintin_index + len('/tintin/src/'):]
|
||
|
|
||
|
tintin_build_index = f.rfind('/tintin/build/src/')
|
||
|
if tintin_build_index != -1:
|
||
|
f = f[tintin_build_index + len('/tintin/'):]
|
||
|
|
||
|
return f
|
||
|
|
||
|
analyze_static_memory_usage.cleanup_path_func = cleanup_path
|
||
|
|
||
|
def analyze_map(map_file, sections):
|
||
|
# Now that we have a list of all the symbols listed in the nm output, we need to go back
|
||
|
# and dig through the map file to find filenames for the symbols with an "Unknown" filename
|
||
|
|
||
|
# We only care about the .text section here
|
||
|
if not 't' in sections:
|
||
|
return
|
||
|
|
||
|
text_section = sections['t']
|
||
|
|
||
|
def line_generator(map_file):
|
||
|
with open(map_file, 'r') as f:
|
||
|
for line in f:
|
||
|
yield line
|
||
|
|
||
|
lines = line_generator(map_file)
|
||
|
for line in lines:
|
||
|
if line.startswith('Linker script and memory map'):
|
||
|
break
|
||
|
|
||
|
for line in lines:
|
||
|
if line.startswith('.text'):
|
||
|
break
|
||
|
|
||
|
# We're looking for groups of lines like the following...
|
||
|
#
|
||
|
# .text.do_tap_handle
|
||
|
# 0x0000000008010e08 0x28 src/fw/applib/accel_service.c.3.o
|
||
|
|
||
|
symbol_pattern = re.compile(r""" \.?[^\.\s]*\.(\S+)""")
|
||
|
for line in lines:
|
||
|
match = symbol_pattern.match(line.rstrip())
|
||
|
if match is None:
|
||
|
continue
|
||
|
|
||
|
symbol = match.group(1)
|
||
|
|
||
|
line = lines.next()
|
||
|
|
||
|
cols = line.split()
|
||
|
if len(cols) < 3:
|
||
|
continue
|
||
|
|
||
|
filename = cols[2]
|
||
|
|
||
|
symbol_with_unknown_file = text_section.remove_unknown_entry(symbol)
|
||
|
if symbol_with_unknown_file is None:
|
||
|
continue
|
||
|
text_section.add_entry(symbol, filename, symbol_with_unknown_file.size)
|
||
|
|
||
|
def analyze_libs(root_directory, sections, use_fast):
|
||
|
def analyze_lib(lib_filename):
|
||
|
for (_, section, symbol_name, filename, line, size) in nm_generator(lib_filename, use_fast):
|
||
|
if not section in sections:
|
||
|
continue
|
||
|
|
||
|
section_info = sections[section]
|
||
|
|
||
|
symbol_with_unknown_file = section_info.remove_unknown_entry(symbol_name)
|
||
|
if symbol_with_unknown_file is None:
|
||
|
continue
|
||
|
|
||
|
section_info.add_entry(symbol_name, lib_filename, size)
|
||
|
|
||
|
for (dirpath, dirnames, filenames) in os.walk(root_directory):
|
||
|
for f in filenames:
|
||
|
if f.endswith('.a'):
|
||
|
analyze_lib(os.path.join(dirpath, f))
|
||
|
|
||
|
def print_groups(text_section, verbose):
|
||
|
mappings = [
|
||
|
('fw/vendor/FreeRTOS/', 'FreeRTOS'),
|
||
|
('core/vendor/STM32F2xx_StdPeriph_Lib_V1.0.0', 'STM32'),
|
||
|
('newlib/', 'newlib'),
|
||
|
('libgcc/', 'libgcc'),
|
||
|
('arm-none-eabi/lib/', 'libc'),
|
||
|
('fw/applib/', 'FW Applib'),
|
||
|
('fw/apps/', 'FW Apps'),
|
||
|
('fw/comm/ble/', 'FW Comm LE'),
|
||
|
('fw/comm/', 'FW Comm'),
|
||
|
('fw/kernel/services/', 'FW Kernel Services'),
|
||
|
('fw/', 'FW Other'),
|
||
|
('core/', 'FW Other'),
|
||
|
('build/src/fw', 'FW Other')
|
||
|
]
|
||
|
|
||
|
class Group(object):
|
||
|
def __init__(self, name):
|
||
|
self.name = name
|
||
|
self.total_size = 0
|
||
|
self.files = []
|
||
|
|
||
|
def add_file(self, f):
|
||
|
self.total_size += f.size
|
||
|
self.files.append(f)
|
||
|
|
||
|
group_sizes = {}
|
||
|
|
||
|
for f in text_section.get_files():
|
||
|
found = False
|
||
|
|
||
|
for prefix, value in mappings:
|
||
|
if f.filename.startswith(prefix):
|
||
|
if not value in group_sizes:
|
||
|
group_sizes[value] = Group(f.filename)
|
||
|
group_sizes[value].add_file(f)
|
||
|
found = True
|
||
|
break
|
||
|
|
||
|
if not found:
|
||
|
if not 'Unknown' in group_sizes:
|
||
|
group_sizes['Unknown'] = Group(f.filename)
|
||
|
group_sizes['Unknown'].add_file(f)
|
||
|
|
||
|
sorted_items = sorted(group_sizes.iteritems(), key=lambda x: -x[1].total_size)
|
||
|
for group_name, group in sorted_items:
|
||
|
print "%-20s %u" % (group_name, group.total_size)
|
||
|
if verbose:
|
||
|
sorted_files = sorted(group.files, key=lambda x: -x.size)
|
||
|
for f in sorted_files:
|
||
|
print " %6u %-20s" % (f.size, f.filename)
|
||
|
|
||
|
|
||
|
|
||
|
if (__name__ == '__main__'):
|
||
|
parser = argparse.ArgumentParser()
|
||
|
parser.add_argument('--text_groups', action='store_true')
|
||
|
parser.add_argument('--verbose', action='store_true')
|
||
|
parser.add_argument('--summary', action='store_true')
|
||
|
parser.add_argument('--sections', default='bdt')
|
||
|
parser.add_argument('--fast', action='store_true')
|
||
|
args = parser.parse_args()
|
||
|
|
||
|
if args.text_groups:
|
||
|
args.sections = 't'
|
||
|
|
||
|
tintin_dir = os.path.join(os.path.dirname(__file__), '..')
|
||
|
elf_path = os.path.join(tintin_dir, 'build', 'src', 'fw', 'tintin_fw.elf')
|
||
|
|
||
|
sections = analyze_elf(elf_path, args.sections, args.fast)
|
||
|
|
||
|
analyze_map(os.path.join(tintin_dir, 'build', 'tintin_fw.map'), sections)
|
||
|
|
||
|
if args.text_groups:
|
||
|
print_groups(sections['t'], args.verbose)
|
||
|
else:
|
||
|
for s in args.sections:
|
||
|
sections[s].pprint(args.summary, args.verbose)
|
||
|
|