Skip to content

Instantly share code, notes, and snippets.

@alessandrod
Created March 4, 2014 11:15
Show Gist options
  • Save alessandrod/9344589 to your computer and use it in GitHub Desktop.
Save alessandrod/9344589 to your computer and use it in GitHub Desktop.
from argparse import ArgumentParser
import re
import os
PARSER_STATE_NONE = 0
PARSER_STATE_FILES = 1
PARSER_STATE_SYMBOLS = 2
def format_size(num):
for x in ['bytes','KB','MB','GB']:
if num < 1024.0 and num > -1024.0:
return "%3.2f%s" % (num, x)
num /= 1024.0
return "%3.1f%s" % (num, 'TB')
class VoidParser(object):
state = PARSER_STATE_NONE
def __init__(self, parser):
pass
def match(self, line):
return True
def parse(self, line):
pass
class File(object):
def __init__(self, filename):
self.filename = filename
self.symbols = []
self.size = None
def add_symbol(self, symbol):
self.symbols.append(symbol)
self.size = None
def __str__(self):
return '<object file "{filename}">'.format(filename=self.filename)
def __repr__(self):
return self.__str__()
def get_size(self):
if self.size is not None:
return self.size
self.size = 0
for symbol in self.symbols:
self.size += symbol.size
return self.size
class FileParser(object):
state = PARSER_STATE_FILES
def __init__(self, parser):
self.parser = parser
self.files = {}
def match(self, line):
return line == '# Object files:'
def parse(self, line):
index_end = line.find(']')
index = int(line[1:index_end].strip())
filename = line[index_end + 1:].strip()
assert index not in self.files
self.files[index] = File(filename)
class Symbol(object):
def __init__(self, address, size, name):
self.address = address
self.size = size
self.name = name
def __str__(self):
return '<symbol {name}>'.format(name=self.name)
def __repr__(self):
return self.__str__()
class SymbolParser(object):
state = PARSER_STATE_SYMBOLS
def __init__(self, parser):
self.parser = parser
self.files = {}
self.pattern = re.compile('(?P<address>0x[0-9A-Z]+)\s+(?P<size>0x[0-9A-Z]+)\s+\[\s*(?P<index>\d+)\]\s+(?P<symbol>\S+)')
def match(self, line):
return line == '# Symbols:'
def parse(self, line):
match = self.pattern.match(line)
if match is None:
return
address = match.group('address')
size = int(match.group('size'), 16)
index = int(match.group('index'))
name = match.group('symbol')
symbol = Symbol(address, size, name)
self.parser.get_file(index).add_symbol(symbol)
class Parser(object):
def __init__(self, filename):
self.filename = filename
self.parse_state = PARSER_STATE_NONE
self.section_parsers = [FileParser(self), SymbolParser(self),
VoidParser(self)]
self.section_parsers_map = {parser.state: parser for parser in
self.section_parsers}
def parse(self):
with open(self.filename) as map_file:
for line in map_file:
line = line.strip()
if not line:
continue
self.parse_line(line)
def parse_section(self, line):
for parser in self.section_parsers:
if parser.match(line):
self.parse_state = parser.state
break
def parse_line(self, line):
if line[0] == '#' and ':' in line:
self.parse_section(line)
return
self.section_parsers_map[self.parse_state].parse(line)
def get_file(self, index):
return self.section_parsers_map[PARSER_STATE_FILES].files[index]
def get_files(self):
return self.section_parsers_map[PARSER_STATE_FILES].files.values()
class FileSizeReporter(object):
def __init__(self):
self.prefixes = [0, {}, 0]
def add_object_file(self, object_file):
prefix = self.prefixes
name = object_file.filename
components = name.split('/')
for depth, component in enumerate(components):
prefix[0] += object_file.get_size()
prefix = prefix[1].setdefault(component, [0, {}, depth])
prefix[0] += object_file.get_size()
def get_sizes(self, prefix=None, max_depth=None):
prefixes = list(self.prefixes)
if prefix is not None:
components = prefix.split('/')
while components:
prefixes = prefixes[1][components.pop(0)]
starting_depth = prefix.count('/')
dirs = [[prefix, prefixes]]
results = []
while dirs:
prefix, (size, subdirs, depth) = dirs.pop(0)
depth -= starting_depth
within_depth = max_depth is None or depth < max_depth
if subdirs and within_depth:
for subdir in subdirs:
dirs.append((os.path.join(prefix, subdir),
subdirs[subdir]))
if within_depth:
results.append((prefix, size))
return results
def main():
cmd = ArgumentParser()
cmd.add_argument('--prefix')
cmd.add_argument('--depth', default=None, type=int)
cmd.add_argument('filename')
args = cmd.parse_args()
parser = Parser(args.filename)
parser.parse()
reporter = FileSizeReporter()
for object_file in parser.get_files():
reporter.add_object_file(object_file)
for name, size in sorted(reporter.get_sizes(args.prefix, args.depth),
key=lambda r: r[0]):
print name, format_size(size)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment