#!/usr/bin/env python2 # # Generate a size report from a Duktape library / executable. # Write out useful information about function sizes in a variety # of forms. # import os import sys import re import subprocess #000000000040d200 : # 40d200: 55 push %rbp # 40d201: 89 f5 mov %esi,%ebp re_funcstart = re.compile(r'^[0-9a-fA-F]+\s<(.*?)>:$') re_codeline = re.compile(r'^\s*([0-9a-fA-F]+):\s+((?:[0-9a-fA-F][0-9a-fA-F] )*[0-9a-fA-F][0-9a-fA-F])\s+(.*?)\s*$') def objdump(filename): proc = subprocess.Popen(['objdump', '-D', filename], stdout=subprocess.PIPE) curr_func = None func_start = None func_end = None ret = {} def storeFunc(): if curr_func is None or func_start is None or func_end is None: return ret[curr_func] = { 'name': curr_func, 'start': func_start, 'end': func_end, # exclusive 'length': func_end - func_start } for line in proc.stdout: line = line.strip() m = re_funcstart.match(line) if m is not None: if curr_func is not None: storeFunc() curr_func = m.group(1) func_start = None func_end = None m = re_codeline.match(line) if m is not None: func_addr = long(m.group(1), 16) func_bytes = m.group(2) func_nbytes = len(func_bytes.split(' ')) func_instr = m.group(3) if func_start is None: func_start = func_addr func_end = func_addr + func_nbytes storeFunc() return ret def filterFuncs(funcs): todo = [] # avoid mutation while iterating def accept(fun): n = fun['name'] if n in [ '.comment', '.dynstr', '.dynsym', '.eh_frame_hdr', '.interp', '.rela.dyn', '.rela.plt', '_DYNAMIC', '_GLOBAL_OFFSET_TABLE_', '_IO_stdin_used', '__CTOR_LIST__', '__DTOR_LIST__', '_fini', '_init', '_start', '' ]: return False for pfx in [ '.debug', '.gnu', '.note', '__FRAME_', '__' ]: if n.startswith(pfx): return False return True for k in funcs.keys(): if not accept(funcs[k]): todo.append(k) for k in todo: del funcs[k] def main(): funcs = objdump(sys.argv[1]) filterFuncs(funcs) funcs_keys = funcs.keys() funcs_keys.sort() combined_size_all = 0 combined_size_duk = 0 for k in funcs_keys: fun = funcs[k] combined_size_all += fun['length'] if fun['name'].startswith('duk_'): combined_size_duk += fun['length'] f = sys.stdout f.write('') f.write('') f.write('Size dump for %s' % sys.argv[1]) f.write("""\ """) f.write('') f.write('') f.write('

Summary

') f.write('') f.write('' % len(funcs_keys)) f.write('' % combined_size_all) f.write('' % combined_size_duk) f.write('
Entries%d
Combined size (all)%d
Combined size (duk_*)%d
') f.write('

Sorted by function name

') f.write('') f.write('') funcs_keys = funcs.keys() funcs_keys.sort() for k in funcs_keys: fun = funcs[k] f.write('' % (fun['name'], fun['length'])) f.write('
NameBytes
%s%d
') f.write('

Sorted by size

') f.write('') f.write('') funcs_keys = funcs.keys() def cmpSize(a,b): return cmp(funcs[a]['length'], funcs[b]['length']) funcs_keys.sort(cmp=cmpSize) for k in funcs_keys: fun = funcs[k] f.write('' % (fun['name'], fun['length'])) f.write('
NameBytes
%s%d
') f.write('') f.write('') if __name__ == '__main__': main()