mirror of
https://github.com/google/pebble.git
synced 2025-03-15 08:41:21 +00:00
200 lines
5.3 KiB
Python
200 lines
5.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 sh
|
|
import sys
|
|
|
|
|
|
total_alloc_size = 0
|
|
alloc_count = 0
|
|
total_free_size = 0
|
|
free_count = 0
|
|
largest_free_block = 0
|
|
|
|
per_file_dict = {}
|
|
alloc_list = []
|
|
|
|
root_path = os.path.join(os.path.dirname(sys.argv[0]), '..')
|
|
|
|
min_addr = 0
|
|
max_addr = 0
|
|
current_size = 0
|
|
high_water_mark = 0
|
|
|
|
def get_filename_linenumber(addr_str):
|
|
try:
|
|
line = sh.arm_none_eabi_addr2line(addr_str, exe=elf_path)
|
|
except:
|
|
return ("?", 0)
|
|
|
|
line = line.strip()
|
|
|
|
index = line.rfind(':')
|
|
filename = line[:index]
|
|
linenumber = line[index + 1:]
|
|
if ':' not in filename:
|
|
filename = os.path.relpath(filename, root_path)
|
|
|
|
return (filename, linenumber)
|
|
|
|
def handle_line(line, verbose):
|
|
parts = line.split(' ')
|
|
|
|
# Skip gdb continuation prompts
|
|
if parts[0].startswith('---'):
|
|
return;
|
|
|
|
if parts[0] == 'Heap':
|
|
metadata_name = ' '.join(parts[1:-1])
|
|
if metadata_name == 'start':
|
|
global min_addr
|
|
min_addr = int(parts[-1].strip(), 16)
|
|
elif metadata_name == 'end':
|
|
global max_addr
|
|
max_addr = int(parts[-1].strip(), 16)
|
|
elif metadata_name == 'current size':
|
|
global current_size
|
|
current_size = int(parts[-1].strip())
|
|
elif metadata_name == 'high water mark':
|
|
global high_water_mark
|
|
high_water_mark = int(parts[-1].strip())
|
|
return
|
|
|
|
def get_part_value(index):
|
|
return parts[index].split(':')[1]
|
|
|
|
pc = get_part_value(0)
|
|
addr = get_part_value(1)
|
|
actual_size = int(get_part_value(2))
|
|
|
|
filename, linenumber = get_filename_linenumber(pc)
|
|
|
|
is_free = (int(pc, 0) == 0)
|
|
if verbose:
|
|
if is_free:
|
|
print "Size: %6u, Addr: 0x%08x PC: 0x%08x FREE" % (actual_size, int(addr, 0), int(pc, 0))
|
|
else:
|
|
print "Size: %6u, Addr: 0x%08x PC: 0x%08x %s:%s" % (actual_size, int(addr, 0), int(pc, 0), filename,
|
|
linenumber)
|
|
|
|
global total_alloc_size
|
|
global alloc_count
|
|
global total_free_size
|
|
global free_count
|
|
global largest_free_block
|
|
|
|
# Capture largest free block size
|
|
if is_free:
|
|
total_free_size += actual_size
|
|
free_count += 1
|
|
if actual_size > largest_free_block:
|
|
largest_free_block = actual_size
|
|
return
|
|
|
|
total_alloc_size += actual_size
|
|
alloc_count += 1
|
|
|
|
filename = filename
|
|
try:
|
|
per_file_dict[filename] += actual_size
|
|
except:
|
|
per_file_dict[filename] = actual_size
|
|
|
|
alloc_list.append([int(addr, base=16), actual_size])
|
|
|
|
def draw_image():
|
|
import cairo
|
|
|
|
WIDTH, HEIGHT = 2048, 256
|
|
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, WIDTH, HEIGHT)
|
|
ctx = cairo.Context(surface)
|
|
|
|
ctx.set_source_rgb(0.8, 0.8, 0.8)
|
|
ctx.set_line_width(0)
|
|
ctx.rectangle(0, 0, WIDTH, HEIGHT)
|
|
ctx.fill()
|
|
|
|
def translate_to_pixels(addr):
|
|
return (float(addr - min_addr) / float(max_addr - min_addr)) * WIDTH
|
|
|
|
def draw_section(start, end):
|
|
ctx.set_source_rgb(0, 0, 0)
|
|
ctx.set_line_width(0)
|
|
ctx.rectangle(start, 0, end - start, HEIGHT)
|
|
ctx.fill()
|
|
|
|
for alloc in alloc_list:
|
|
addr = alloc[0]
|
|
size = alloc[1]
|
|
pixel_start = translate_to_pixels(addr)
|
|
pixel_end = translate_to_pixels(addr + size)
|
|
draw_section(pixel_start, pixel_end)
|
|
|
|
ctx.stroke()
|
|
surface.write_to_png("dump_malloc.png")
|
|
print "Drew image to dump_malloc.png"
|
|
|
|
if __name__ == '__main__':
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument('--image', action='store_true')
|
|
parser.add_argument('--verbose', action='store_true')
|
|
parser.add_argument('--elf_file', required=True)
|
|
parser.add_argument('in_file')
|
|
|
|
args = parser.parse_args()
|
|
|
|
verbose = False
|
|
if args.verbose is not None:
|
|
verbose = args.verbose
|
|
|
|
elf_path = args.elf_file
|
|
|
|
with open(args.in_file, 'r') as f:
|
|
infile = f.readlines()
|
|
|
|
for line in infile:
|
|
line = line.strip()
|
|
if len(line) > 0:
|
|
handle_line(line, verbose)
|
|
|
|
if min_addr == 0 or max_addr == 0:
|
|
min_addr = alloc_list[0][0]
|
|
max_addr = alloc_list[0][0] + alloc_list[0][1]
|
|
for alloc in alloc_list:
|
|
low_addr = alloc[0]
|
|
if low_addr < min_addr: min_addr = low_addr
|
|
high_addr = low_addr + alloc[1]
|
|
if high_addr > max_addr: max_addr = high_addr
|
|
|
|
if verbose:
|
|
print ""
|
|
print "Heap start: 0x%x" % min_addr
|
|
print "Heap end: 0x%x" % max_addr
|
|
print "Heap size: %u bytes" % (max_addr - min_addr)
|
|
print "Total allocated: %u bytes, %u blocks" % (total_alloc_size, alloc_count);
|
|
print "High water mark: %u" % high_water_mark
|
|
print "Total free: %u bytes, %u blocks" % (total_free_size, free_count);
|
|
print "Largest free block: %u" % largest_free_block
|
|
print
|
|
|
|
per_file_list = [[k, v] for k, v in per_file_dict.iteritems()]
|
|
sorted_per_file_list = sorted(per_file_list, key=lambda v: v[1], reverse=True)
|
|
for k, v in sorted_per_file_list:
|
|
print "%s: %u bytes" % (k, v)
|
|
|
|
if args.image:
|
|
draw_image()
|
|
|