blob: abaed62cec2be3377db67b1cd7a85fb139acb8a9 [file] [log] [blame]
#!/usr/bin/env python
#
# Copyright (C) 2011 Apple Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. Neither the name of Apple Inc. ("Apple") nor the names of
# its contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import sys
import getopt
from optparse import OptionParser
oneK = 1024
oneM = 1024 * 1024
oneG = 1024 * 1024 * 1024
hotspot = False
scaleSize = True
showBars = True
def byteString(bytes):
if scaleSize:
format = ' %4d '
val = bytes
if bytes >= oneG:
format = '%8.1fG'
val = float(bytes) / oneG
elif bytes >= oneM:
format = '%8.1fM'
val = float(bytes) / oneM
elif bytes >= oneK:
format = '%8.1fK'
val = float(bytes) / oneK
return format % val
if hotspot:
return '%d' % bytes
return '%12d' % bytes
class Node:
def __init__(self, name, level = 0, bytes = 0):
self.name = name
self.level = level
self.children = {}
self.totalBytes = bytes
def hasChildren(self):
return len(self.children) > 0
def getChild(self, name):
if not name in self.children:
newChild = Node(name, self.level + 1)
self.children[name] = newChild
return self.children[name]
def getBytes(self):
return self.totalBytes
def addBytes(self, bytes):
self.totalBytes = self.totalBytes + bytes
def processLine(self, bytes, line):
sep = line.find('|')
if sep < 0:
childName = line.strip()
line = ''
else:
childName = line[:sep].strip()
line = line[sep+1:]
child = self.getChild(childName)
child.addBytes(bytes)
if len(line) > 0:
child.processLine(bytes, line)
def printNode(self, prefix = ' '):
global hotspot
global scaleSize
global showBars
if self.hasChildren():
byteStr = byteString(self.totalBytes)
if hotspot:
print(' %s%s %s' % (self.level * ' ', byteString(self.totalBytes), self.name))
else:
print('%s %s%s' % (byteString(self.totalBytes), prefix[:-1], self.name))
sortedChildren = sorted(self.children.values(), key=sortKeyByBytes, reverse=True)
if showBars and len(self.children) > 1:
newPrefix = prefix + '|'
else:
newPrefix = prefix + ' '
childrenLeft = len(sortedChildren)
for child in sortedChildren:
if childrenLeft <= 1:
newPrefix = prefix + ' '
else:
childrenLeft = childrenLeft - 1
child.printNode(newPrefix)
else:
byteStr = byteString(self.totalBytes)
if hotspot:
print(' %s%s %s' % (self.level * ' ', byteString(self.totalBytes), self.name))
else:
print('%s %s%s' % (byteString(self.totalBytes), prefix[:-1], self.name))
def sortKeyByBytes(node):
return node.getBytes();
def main():
global hotspot
global scaleSize
global showBars
# parse command line options
parser = OptionParser(usage='malloc-tree [options] [malloc_history-file]',
description='Format malloc_history output as a nested tree',
epilog='stdin used if malloc_history-file is missing')
parser.add_option('-n', '--nobars', action='store_false', dest='showBars',
default=True, help='don\'t show bars lining up siblings in tree');
parser.add_option('-b', '--size-in-bytes', action='store_false', dest='scaleSize',
default=None, help='show sizes in bytes');
parser.add_option('-s', '--size-scale', action='store_true', dest='scaleSize',
default=None, help='show sizes with appropriate scale suffix [K,M,G]');
parser.add_option('-t', '--hotspot', action='store_true', dest='hotspot',
default=False, help='output in HotSpotFinder format, implies -b');
(options, args) = parser.parse_args()
hotspot = options.hotspot
if options.scaleSize is None:
if hotspot:
scaleSize = False
else:
scaleSize = True
else:
scaleSize = options.scaleSize
showBars = options.showBars
if len(args) < 1:
inputFile = sys.stdin
else:
inputFile = open(args[0], "r")
line = inputFile.readline()
rootNodes = {}
while line:
firstSep = line.find('|')
if firstSep > 0:
firstPart = line[:firstSep].strip()
lineRemain = line[firstSep+1:]
bytesSep = firstPart.find('bytes:')
if bytesSep >= 0:
name = firstPart[bytesSep+7:]
stats = firstPart.split(' ')
bytes = int(stats[3].replace(',', ''))
if not name in rootNodes:
node = Node(name, 0, bytes);
rootNodes[name] = node
else:
node = rootNodes[name]
node.addBytes(bytes)
node.processLine(bytes, lineRemain)
line = inputFile.readline()
sortedRootNodes = sorted(rootNodes.values(), key=sortKeyByBytes, reverse=True)
print 'Call graph:'
try:
for node in sortedRootNodes:
node.printNode()
print
except:
pass
if __name__ == "__main__":
main()