find_leakers.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. #!/usr/bin/python
  2. #
  3. # This Source Code Form is subject to the terms of the Mozilla Public
  4. # License, v. 2.0. If a copy of the MPL was not distributed with this
  5. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. # This script processes a `refcount' log, and finds out if any object leaked.
  7. # It simply goes through the log, finds `AddRef' or `Ctor' lines, and then
  8. # sees if they `Release' or `Dtor'. If not, it reports them as leaks.
  9. # Please see README file in the same directory.
  10. import sys
  11. def print_output(allocation, obj_to_class):
  12. '''Formats and prints output.'''
  13. items = []
  14. for obj, count, in allocation.iteritems():
  15. # Adding items to a list, so we can sort them.
  16. items.append((obj, count))
  17. # Sorting by count.
  18. items.sort(key=lambda item: item[1])
  19. for obj, count, in items:
  20. print "{obj} ({count}) @ {class_name}".format(obj=obj,
  21. count=count,
  22. class_name=obj_to_class[obj])
  23. def process_log(log_lines):
  24. '''Process through the log lines, and print out the result.
  25. @param log_lines: List of strings.
  26. '''
  27. allocation = {}
  28. class_count = {}
  29. obj_to_class = {}
  30. for log_line in log_lines:
  31. if not log_line.startswith('<'):
  32. continue
  33. (class_name,
  34. obj,
  35. ignore,
  36. operation,
  37. count,) = log_line.strip('\r\n').split(' ')[:5]
  38. # for AddRef/Release `count' is the refcount,
  39. # for Ctor/Dtor it's the size.
  40. if ((operation == 'AddRef' and count == '1') or
  41. operation == 'Ctor'):
  42. # Examples:
  43. # <nsStringBuffer> 0x01AFD3B8 1 AddRef 1
  44. # <PStreamNotifyParent> 0x08880BD0 8 Ctor (20)
  45. class_count[class_name] = class_count.setdefault(class_name, 0) + 1
  46. allocation[obj] = class_count[class_name]
  47. obj_to_class[obj] = class_name
  48. elif ((operation == 'Release' and count == '0') or
  49. operation == 'Dtor'):
  50. # Examples:
  51. # <nsStringBuffer> 0x01AFD3B8 1 Release 0
  52. # <PStreamNotifyParent> 0x08880BD0 8 Dtor (20)
  53. if obj not in allocation:
  54. print "An object was released that wasn't allocated!",
  55. print obj, "@", class_name
  56. else:
  57. allocation.pop(obj)
  58. obj_to_class.pop(obj)
  59. # Printing out the result.
  60. print_output(allocation, obj_to_class)
  61. def print_usage():
  62. print
  63. print "Usage: find-leakers.py [log-file]"
  64. print
  65. print "If `log-file' provided, it will read that as the input log."
  66. print "Else, it will read the stdin as the input log."
  67. print
  68. def main():
  69. '''Main method of the script.'''
  70. if len(sys.argv) == 1:
  71. # Reading log from stdin.
  72. process_log(sys.stdin.readlines())
  73. elif len(sys.argv) == 2:
  74. # Reading log from file.
  75. with open(sys.argv[1], 'r') as log_file:
  76. log_lines = log_file.readlines()
  77. process_log(log_lines)
  78. else:
  79. print 'ERROR: Invalid number of arguments'
  80. print_usage()
  81. if __name__ == '__main__':
  82. main()