3 # Copyright (C) 2011 Apple Inc. All rights reserved.
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions
9 # 1. Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 # 2. Redistributions in binary form must reproduce the above copyright
12 # notice, this list of conditions and the following disclaimer in the
13 # documentation and/or other materials provided with the distribution.
14 # 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15 # its contributors may be used to endorse or promote products derived
16 # from this software without specific prior written permission.
18 # THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 # DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 from optparse import OptionParser
35 oneG = 1024 * 1024 * 1024
41 def byteString(bytes):
48 val = float(bytes) / oneG
51 val = float(bytes) / oneM
54 val = float(bytes) / oneK
62 def __init__(self, name, level = 0, bytes = 0):
66 self.totalBytes = bytes
68 def hasChildren(self):
69 return len(self.children) > 0
71 def getChild(self, name):
72 if not name in self.children:
73 newChild = Node(name, self.level + 1)
74 self.children[name] = newChild
76 return self.children[name]
79 return self.totalBytes
81 def addBytes(self, bytes):
82 self.totalBytes = self.totalBytes + bytes
84 def processLine(self, bytes, line):
87 childName = line.strip()
90 childName = line[:sep].strip()
93 child = self.getChild(childName)
97 child.processLine(bytes, line)
99 def printNode(self, prefix = ' '):
104 if self.hasChildren():
105 byteStr = byteString(self.totalBytes)
108 print(' %s%s %s' % (self.level * ' ', byteString(self.totalBytes), self.name))
110 print('%s %s%s' % (byteString(self.totalBytes), prefix[:-1], self.name))
112 sortedChildren = sorted(self.children.values(), key=sortKeyByBytes, reverse=True)
114 if showBars and len(self.children) > 1:
115 newPrefix = prefix + '|'
117 newPrefix = prefix + ' '
119 childrenLeft = len(sortedChildren)
120 for child in sortedChildren:
121 if childrenLeft <= 1:
122 newPrefix = prefix + ' '
124 childrenLeft = childrenLeft - 1
125 child.printNode(newPrefix)
127 byteStr = byteString(self.totalBytes)
130 print(' %s%s %s' % (self.level * ' ', byteString(self.totalBytes), self.name))
132 print('%s %s%s' % (byteString(self.totalBytes), prefix[:-1], self.name))
134 def sortKeyByBytes(node):
135 return node.getBytes();
142 # parse command line options
143 parser = OptionParser(usage='malloc-tree [options] [malloc_history-file]',
144 description='Format malloc_history output as a nested tree',
145 epilog='stdin used if malloc_history-file is missing')
147 parser.add_option('-n', '--nobars', action='store_false', dest='showBars',
148 default=True, help='don\'t show bars lining up siblings in tree');
149 parser.add_option('-b', '--size-in-bytes', action='store_false', dest='scaleSize',
150 default=None, help='show sizes in bytes');
151 parser.add_option('-s', '--size-scale', action='store_true', dest='scaleSize',
152 default=None, help='show sizes with appropriate scale suffix [K,M,G]');
153 parser.add_option('-t', '--hotspot', action='store_true', dest='hotspot',
154 default=False, help='output in HotSpotFinder format, implies -b');
156 (options, args) = parser.parse_args()
158 hotspot = options.hotspot
159 if options.scaleSize is None:
165 scaleSize = options.scaleSize
166 showBars = options.showBars
169 inputFile = sys.stdin
171 inputFile = open(args[0], "r")
173 line = inputFile.readline()
178 firstSep = line.find('|')
180 firstPart = line[:firstSep].strip()
181 lineRemain = line[firstSep+1:]
182 bytesSep = firstPart.find('bytes:')
184 name = firstPart[bytesSep+7:]
185 stats = firstPart.split(' ')
186 bytes = int(stats[3].replace(',', ''))
188 if not name in rootNodes:
189 node = Node(name, 0, bytes);
190 rootNodes[name] = node
192 node = rootNodes[name]
195 node.processLine(bytes, lineRemain)
197 line = inputFile.readline()
199 sortedRootNodes = sorted(rootNodes.values(), key=sortKeyByBytes, reverse=True)
203 for node in sortedRootNodes:
209 if __name__ == "__main__":