123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- #!/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 Computer, 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()
|