#!/usr/bin/python3 import os import sys import pandas as pd import matplotlib.pyplot as plt import matplotlib from elftools.elf.elffile import ELFFile print('Usage: {} orig_file shrunk_file'.format(sys.argv[0])) fd_orig = open(sys.argv[1], 'rb') elf_orig = ELFFile(fd_orig) fd_shrunk = open(sys.argv[2], 'rb') elf_shrunk = ELFFile(fd_shrunk) outfile = os.path.basename(sys.argv[1]) + '_section_sizes.pdf' lst = [] for index, section in enumerate(elf_orig.iter_sections()): size_orig = section['sh_size'] if size_orig == 0: continue section_shrunk = elf_shrunk.get_section_by_name(section.name) if not section_shrunk: break size_shrunk = section_shrunk['sh_size'] print('[{}] {}: {} -> {} (-{:.3f}%)'.format(index, section.name, size_orig, size_shrunk, ((size_orig - size_shrunk) / size_orig) * 100)) lst.append({'section': section.name, 'old size': size_orig, 'new size': size_shrunk}) last_n_equal = 0 for d in reversed(lst): if d['new size'] != d['old size']: break last_n_equal += 1 last_n_equal = 0 print('cutting {} last sections with matching sizes'.format(last_n_equal)) df = pd.DataFrame(lst[:len(lst) - last_n_equal], columns=['section', 'old size', 'new size']) print(df) fig = plt.figure() ax1 = fig.add_subplot() max_x = df['old size'].max() max_x = round(max_x * 1.15) ax3 = df.plot.barh(x='section', # y=['code size after', 'code size before'], y=['old size', 'new size'], figsize=(7,10), xlim=(0,max_x), alpha=.7, width=.8, ax=ax1, legend=True) handles, labels = ax3.get_legend_handles_labels() ax3.set_xlabel('Size of section in bytes') ax3.set_ylabel('Name of ELF section') ax3.bar_label(handles[0], padding=10, fmt='%d') ax3.bar_label(handles[1], padding=10, fmt='%d') ax1.invert_yaxis() # Don't use scientific notation on the x axis ax3.ticklabel_format(style='plain', axis='x') ax3.get_xaxis().set_major_formatter(matplotlib.ticker.FuncFormatter(lambda x, p: '{:,}'.format(int(x)).replace(",", u"\N{thin space}"))) # Write the plot out plt.tight_layout() plt.savefig(outfile)