Bug 1215148 - Object-count based leak checking for Mochitest. r=jgriffin
authorAndrew McCreight <continuation@gmail.com>
Thu, 22 Oct 2015 09:21:52 -0700
changeset 304154 f0134eb0b78f2145a778bcc790c416102225618b
parent 304153 0c702af0b7328dbeb050812f64df016efd375925
child 304155 97c0692a9ce5336fb5579aa52581fba8eb79c32a
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjgriffin
bugs1215148
milestone44.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1215148 - Object-count based leak checking for Mochitest. r=jgriffin
testing/mozbase/mozleak/mozleak/leaklog.py
--- a/testing/mozbase/mozleak/mozleak/leaklog.py
+++ b/testing/mozbase/mozleak/mozleak/leaklog.py
@@ -10,16 +10,65 @@ import sys
 import mozinfo
 import mozrunner.utils
 
 def _raw_log():
     import logging
     return logging.getLogger(__name__)
 
 
+# Do not add anything to this list, unless one of the existing leaks below
+# has started to leak additional objects. This function returns a dict
+# mapping the names of objects as reported to the XPCOM leak checker to an
+# upper bound on the number of leaked objects of that kind that are allowed
+# to appear in a content process leak report.
+def expectedTabProcessLeakCounts():
+    leaks = {}
+
+    def appendExpectedLeakCounts(leaks2):
+        for obj, count in leaks2.iteritems():
+            leaks[obj] = leaks.get(obj, 0) + count
+
+    # Bug 1117203 - ImageBridgeChild is not shut down in tab processes.
+    appendExpectedLeakCounts({
+        'AsyncTransactionTrackersHolder': 1,
+        'CondVar': 2,
+        'IPC::Channel': 1,
+        'MessagePump': 1,
+        'Mutex': 2,
+        'PImageBridgeChild': 1,
+        'RefCountedMonitor': 1,
+        'RefCountedTask': 2,
+        'StoreRef': 1,
+        'WaitableEventKernel': 1,
+        'WeakReference<MessageListener>': 1,
+        'base::Thread': 1,
+        'ipc::MessageChannel': 1,
+        'nsTArray_base': 7,
+        'nsThread': 1,
+    })
+
+    # Bug 1215265 - CompositorChild is not shut down.
+    appendExpectedLeakCounts({
+        'CompositorChild': 1,
+        'CondVar': 1,
+        'IPC::Channel': 1,
+        'Mutex': 1,
+        'PCompositorChild': 1,
+        'RefCountedMonitor': 1,
+        'RefCountedTask': 2,
+        'StoreRef': 1,
+        'WeakReference<MessageListener>': 1,
+        'ipc::MessageChannel': 1,
+        'nsTArray_base': 2,
+    })
+
+    return leaks
+
+
 def process_single_leak_file(leakLogFileName, processType, leakThreshold,
                              ignoreMissingLeaks, log=None,
                              stackFixer=None):
     """Process a single leak log.
     """
 
     #     |              |Per-Inst  Leaked|     Total  Rem|
     #   0 |TOTAL         |      17     192| 419115886    2|
@@ -29,16 +78,17 @@ def process_single_leak_file(leakLogFile
                         r"(?P<name>[^|]+)\|"
                         r"\s*(?P<size>-?\d+)\s+(?P<bytesLeaked>-?\d+)\s*\|"
                         r"\s*-?\d+\s+(?P<numLeaked>-?\d+)")
     # The class name can contain spaces. We remove trailing whitespace later.
 
     log = log or _raw_log()
 
     processString = "%s process:" % processType
+    expectedLeaks = expectedTabProcessLeakCounts() if processType == 'tab' else {}
     crashedOnPurpose = False
     totalBytesLeaked = None
     logAsWarning = False
     leakAnalysis = []
     leakedObjectAnalysis = []
     leakedObjectNames = []
     recordLeakedObjects = False
     with open(leakLogFileName, "r") as leaks:
@@ -81,18 +131,25 @@ def process_single_leak_file(leakLogFile
                     recordLeakedObjects = False
             if size < 0 or bytesLeaked < 0 or numLeaked < 0:
                 leakAnalysis.append("TEST-UNEXPECTED-FAIL | leakcheck | %s negative leaks caught!"
                                     % processString)
                 logAsWarning = True
                 continue
             if name != "TOTAL" and numLeaked != 0 and recordLeakedObjects:
                 leakedObjectNames.append(name)
-                leakedObjectAnalysis.append("TEST-INFO | leakcheck | %s leaked %d %s (%s bytes)"
-                                            % (processString, numLeaked, name, bytesLeaked))
+
+                currExpectedLeak = expectedLeaks.get(name, 0)
+                if not expectedLeaks or numLeaked <= currExpectedLeak:
+                    leakedObjectAnalysis.append("TEST-INFO | leakcheck | %s leaked %d %s"
+                                                % (processString, numLeaked, name))
+                else:
+                    leakedObjectAnalysis.append("TEST-UNEXPECTED-FAIL | leakcheck | %s leaked too many %s (expected %d, got %d)"
+                                                % (processString, name, currExpectedLeak, numLeaked))
+
 
     leakAnalysis.extend(leakedObjectAnalysis)
     if logAsWarning:
         log.warning('\n'.join(leakAnalysis))
     else:
         log.info('\n'.join(leakAnalysis))
 
     logAsWarning = False