pytest is not correctly auto-installed
[WebKit-https.git] / Tools / Scripts / malloc-tree
1 #!/usr/bin/env python
2 #
3 # Copyright (C) 2011 Apple Inc. All rights reserved.
4 #
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions
7 # are met:
8 #
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 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.
17 #
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.
28
29 import sys
30 import getopt
31 from optparse import OptionParser
32
33 oneK = 1024
34 oneM = 1024 * 1024
35 oneG = 1024 * 1024 * 1024
36
37 hotspot = False
38 scaleSize = True
39 showBars = True
40
41 def byteString(bytes):
42     if scaleSize:
43         format = '  %4d   '
44         val = bytes
45
46         if bytes >= oneG:
47             format = '%8.1fG'
48             val = float(bytes) / oneG
49         elif bytes >= oneM:
50             format = '%8.1fM'
51             val = float(bytes) / oneM
52         elif bytes >= oneK:
53             format = '%8.1fK'
54             val = float(bytes) / oneK
55
56         return format % val
57     if hotspot:
58         return '%d' % bytes
59     return '%12d' % bytes
60
61 class Node:
62     def __init__(self, name, level = 0, bytes = 0):
63         self.name = name
64         self.level = level
65         self.children = {}
66         self.totalBytes = bytes
67
68     def hasChildren(self):
69         return len(self.children) > 0
70
71     def getChild(self, name):
72         if not name in self.children:
73             newChild = Node(name, self.level + 1)
74             self.children[name] = newChild
75
76         return self.children[name]
77
78     def getBytes(self):
79         return self.totalBytes
80
81     def addBytes(self, bytes):
82         self.totalBytes = self.totalBytes + bytes
83
84     def processLine(self, bytes, line):
85         sep = line.find('|')
86         if sep < 0:
87             childName = line.strip()
88             line = ''
89         else:
90             childName = line[:sep].strip()
91             line = line[sep+1:]
92
93         child = self.getChild(childName)
94         child.addBytes(bytes)
95
96         if len(line) > 0:
97             child.processLine(bytes, line)
98
99     def printNode(self, prefix = ' '):
100         global hotspot
101         global scaleSize
102         global showBars
103
104         if self.hasChildren():
105             byteStr = byteString(self.totalBytes)
106
107             if hotspot:
108                 print('    %s%s %s' % (self.level * ' ', byteString(self.totalBytes), self.name))
109             else:
110                 print('%s %s%s' % (byteString(self.totalBytes), prefix[:-1], self.name))
111
112             sortedChildren = sorted(self.children.values(), key=sortKeyByBytes, reverse=True)
113
114             if showBars and len(self.children) > 1:
115                 newPrefix = prefix + '|'
116             else:
117                 newPrefix = prefix + ' '
118
119             childrenLeft = len(sortedChildren)
120             for child in sortedChildren:
121                 if childrenLeft <= 1:
122                     newPrefix = prefix + ' '
123                 else:
124                     childrenLeft = childrenLeft - 1
125                 child.printNode(newPrefix)
126         else:
127             byteStr = byteString(self.totalBytes)
128
129             if hotspot:
130                 print('    %s%s %s' % (self.level * ' ', byteString(self.totalBytes), self.name))
131             else:
132                 print('%s %s%s' % (byteString(self.totalBytes), prefix[:-1], self.name))
133
134 def sortKeyByBytes(node):
135     return node.getBytes();
136
137 def main():
138     global hotspot
139     global scaleSize
140     global showBars
141
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')
146
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');
155
156     (options, args) = parser.parse_args()
157
158     hotspot = options.hotspot
159     if options.scaleSize is None:
160         if hotspot:
161             scaleSize = False
162         else:
163             scaleSize = True
164     else:
165         scaleSize = options.scaleSize
166     showBars = options.showBars
167
168     if len(args) < 1:
169         inputFile = sys.stdin
170     else:
171         inputFile = open(args[0], "r")
172
173     line = inputFile.readline()
174  
175     rootNodes = {}
176
177     while line:
178         firstSep = line.find('|')
179         if firstSep > 0:
180             firstPart = line[:firstSep].strip()
181             lineRemain = line[firstSep+1:]
182             bytesSep = firstPart.find('bytes:')
183             if bytesSep >= 0:
184                 name = firstPart[bytesSep+7:]
185                 stats = firstPart.split(' ')
186                 bytes = int(stats[3].replace(',', ''))
187
188                 if not name in rootNodes:
189                     node = Node(name, 0, bytes);
190                     rootNodes[name] = node
191                 else:
192                     node = rootNodes[name]
193                     node.addBytes(bytes)
194
195                 node.processLine(bytes, lineRemain)
196
197         line = inputFile.readline()
198
199     sortedRootNodes = sorted(rootNodes.values(), key=sortKeyByBytes, reverse=True)
200
201     print 'Call graph:'
202     try:
203         for node in sortedRootNodes:
204             node.printNode()
205             print 
206     except:
207         pass
208
209 if __name__ == "__main__":
210     main()