malloc-tree 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  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 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.
  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. import sys
  29. import getopt
  30. from optparse import OptionParser
  31. oneK = 1024
  32. oneM = 1024 * 1024
  33. oneG = 1024 * 1024 * 1024
  34. hotspot = False
  35. scaleSize = True
  36. showBars = True
  37. def byteString(bytes):
  38. if scaleSize:
  39. format = ' %4d '
  40. val = bytes
  41. if bytes >= oneG:
  42. format = '%8.1fG'
  43. val = float(bytes) / oneG
  44. elif bytes >= oneM:
  45. format = '%8.1fM'
  46. val = float(bytes) / oneM
  47. elif bytes >= oneK:
  48. format = '%8.1fK'
  49. val = float(bytes) / oneK
  50. return format % val
  51. if hotspot:
  52. return '%d' % bytes
  53. return '%12d' % bytes
  54. class Node:
  55. def __init__(self, name, level = 0, bytes = 0):
  56. self.name = name
  57. self.level = level
  58. self.children = {}
  59. self.totalBytes = bytes
  60. def hasChildren(self):
  61. return len(self.children) > 0
  62. def getChild(self, name):
  63. if not name in self.children:
  64. newChild = Node(name, self.level + 1)
  65. self.children[name] = newChild
  66. return self.children[name]
  67. def getBytes(self):
  68. return self.totalBytes
  69. def addBytes(self, bytes):
  70. self.totalBytes = self.totalBytes + bytes
  71. def processLine(self, bytes, line):
  72. sep = line.find('|')
  73. if sep < 0:
  74. childName = line.strip()
  75. line = ''
  76. else:
  77. childName = line[:sep].strip()
  78. line = line[sep+1:]
  79. child = self.getChild(childName)
  80. child.addBytes(bytes)
  81. if len(line) > 0:
  82. child.processLine(bytes, line)
  83. def printNode(self, prefix = ' '):
  84. global hotspot
  85. global scaleSize
  86. global showBars
  87. if self.hasChildren():
  88. byteStr = byteString(self.totalBytes)
  89. if hotspot:
  90. print(' %s%s %s' % (self.level * ' ', byteString(self.totalBytes), self.name))
  91. else:
  92. print('%s %s%s' % (byteString(self.totalBytes), prefix[:-1], self.name))
  93. sortedChildren = sorted(self.children.values(), key=sortKeyByBytes, reverse=True)
  94. if showBars and len(self.children) > 1:
  95. newPrefix = prefix + '|'
  96. else:
  97. newPrefix = prefix + ' '
  98. childrenLeft = len(sortedChildren)
  99. for child in sortedChildren:
  100. if childrenLeft <= 1:
  101. newPrefix = prefix + ' '
  102. else:
  103. childrenLeft = childrenLeft - 1
  104. child.printNode(newPrefix)
  105. else:
  106. byteStr = byteString(self.totalBytes)
  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. def sortKeyByBytes(node):
  112. return node.getBytes();
  113. def main():
  114. global hotspot
  115. global scaleSize
  116. global showBars
  117. # parse command line options
  118. parser = OptionParser(usage='malloc-tree [options] [malloc_history-file]',
  119. description='Format malloc_history output as a nested tree',
  120. epilog='stdin used if malloc_history-file is missing')
  121. parser.add_option('-n', '--nobars', action='store_false', dest='showBars',
  122. default=True, help='don\'t show bars lining up siblings in tree');
  123. parser.add_option('-b', '--size-in-bytes', action='store_false', dest='scaleSize',
  124. default=None, help='show sizes in bytes');
  125. parser.add_option('-s', '--size-scale', action='store_true', dest='scaleSize',
  126. default=None, help='show sizes with appropriate scale suffix [K,M,G]');
  127. parser.add_option('-t', '--hotspot', action='store_true', dest='hotspot',
  128. default=False, help='output in HotSpotFinder format, implies -b');
  129. (options, args) = parser.parse_args()
  130. hotspot = options.hotspot
  131. if options.scaleSize is None:
  132. if hotspot:
  133. scaleSize = False
  134. else:
  135. scaleSize = True
  136. else:
  137. scaleSize = options.scaleSize
  138. showBars = options.showBars
  139. if len(args) < 1:
  140. inputFile = sys.stdin
  141. else:
  142. inputFile = open(args[0], "r")
  143. line = inputFile.readline()
  144. rootNodes = {}
  145. while line:
  146. firstSep = line.find('|')
  147. if firstSep > 0:
  148. firstPart = line[:firstSep].strip()
  149. lineRemain = line[firstSep+1:]
  150. bytesSep = firstPart.find('bytes:')
  151. if bytesSep >= 0:
  152. name = firstPart[bytesSep+7:]
  153. stats = firstPart.split(' ')
  154. bytes = int(stats[3].replace(',', ''))
  155. if not name in rootNodes:
  156. node = Node(name, 0, bytes);
  157. rootNodes[name] = node
  158. else:
  159. node = rootNodes[name]
  160. node.addBytes(bytes)
  161. node.processLine(bytes, lineRemain)
  162. line = inputFile.readline()
  163. sortedRootNodes = sorted(rootNodes.values(), key=sortKeyByBytes, reverse=True)
  164. print 'Call graph:'
  165. try:
  166. for node in sortedRootNodes:
  167. node.printNode()
  168. print
  169. except:
  170. pass
  171. if __name__ == "__main__":
  172. main()