1 """A tool to inspect the binary size of a built binary file. 3 This script prints out a tree of symbols and their corresponding sizes, using 4 Linux's nm functionality. 8 python binary_size.py -- \ 9 --target=/path/to/your/target/binary \ 10 [--nm_command=/path/to/your/custom/nm] \ 11 [--max_depth=10] [--min_size=1024] \ 14 To assist visualization, pass in '--color' to make the symbols color coded to 15 green, assuming that you have a xterm connection that supports color. 18 from __future__
import absolute_import
19 from __future__
import division
20 from __future__
import print_function
21 from __future__
import unicode_literals
28 """A simple class that represents a Trie.""" 31 """Initializes a Trie object.""" 38 """Gets a symbol trie with the passed in target. 41 target: the target binary to inspect. 42 nm_command: the command to run nm. 43 max_depth: the maximum depth to create the trie. 46 proc = subprocess.Popen(
47 [nm_command,
'--radix=d',
'--size-sort',
'--print-size', target],
48 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
49 nm_out, _ = proc.communicate()
50 if proc.returncode != 0:
51 print(
'NM command failed. Output is as follows:')
55 proc = subprocess.Popen([
'c++filt'],
56 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
57 stderr=subprocess.STDOUT)
58 out, _ = proc.communicate(input=nm_out)
59 if proc.returncode != 0:
60 print(
'c++filt failed. Output is as follows:')
65 for line
in out.split(
'\n'):
67 content = line.split(
' ')
71 data.append([int(content[1]),
' '.join(content[3:])])
72 symbol_trie =
Trie(
'')
73 for size, name
in data:
76 if c
not in curr.dictionary:
77 curr.dictionary[c] =
Trie(curr.name + c)
78 curr = curr.dictionary[c]
80 if len(curr.name) > max_depth:
82 symbol_trie.size = sum(t.size
for t
in symbol_trie.dictionary.values())
87 """Wrap the input string to the xterm green color, if color is set. 90 return '\033[92m{0}\033[0m'.format(s)
96 """Get a human-readable size.""" 97 for unit
in [
'B',
'KB',
'MB',
'GB']:
98 if abs(num) <= 1024.0:
99 return '%3.2f%s' % (num, unit)
101 return '%.1f TB' % (num,)
106 def PrintTrie(trie, prefix, max_depth, min_size, color):
107 """Prints the symbol trie in a readable manner. 109 if len(trie.name) == max_depth
or not trie.dictionary.keys():
112 if trie.size > min_size:
113 print(
'{0}{1} {2}'.format(
117 elif len(trie.dictionary.keys()) == 1:
121 trie.dictionary.values()[0], prefix, max_depth, min_size, color)
122 elif trie.size > min_size:
123 print(
'{0}{1} {2}'.format(
128 (k, trie.dictionary[k].size)
for k
in trie.dictionary.keys()]
129 keys_with_sizes.sort(key=
lambda x: x[1])
130 for k, _
in keys_with_sizes[::-1]:
132 trie.dictionary[k], prefix +
' |', max_depth, min_size, color)
136 if not sys.platform.startswith(
'linux'):
137 raise RuntimeError(
'Currently this tool only supports Linux.')
138 parser = argparse.ArgumentParser(
139 description=
"Tool to inspect binary size.")
141 '--max_depth', type=int, default=10,
142 help=
'The maximum depth to print the symbol tree.')
144 '--min_size', type=int, default=1024,
145 help=
'The mininum symbol size to print.')
147 '--nm_command', type=str, default=
'nm',
148 help=
'The path to the nm command that the tool needs.')
150 '--color', action=
'store_true',
151 help=
'If set, use ascii color for output.')
153 '--target', type=str,
154 help=
'The binary target to inspect.')
155 args = parser.parse_args(argv)
157 raise RuntimeError(
'You must specify a target to inspect.')
159 args.target, args.nm_command, args.max_depth)
160 PrintTrie(symbol_trie,
'', args.max_depth, args.min_size, args.color)
163 if __name__ ==
'__main__':
def GetSymbolTrie(target, nm_command, max_depth)
def PrintTrie(trie, prefix, max_depth, min_size, color)
def MaybeAddColor(s, color)