# This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/
# The content of this file comes orginally from automationutils.py # and *should* be revised.
class ShutdownLeaks(object): """
Parses the mochitest run log when running a debug build, assigns all leaked
DOM windows (that are still around after test suite shutdown, despite running
the GC) to the tests that created them and prints leak statistics. """
# Remove 'log' when clipboard is gone and/or structured. if action in ("log", "process_output"):
line = message["message"] if action == "log"else message["data"]
m = RE_DOMWINDOW.search(line) if m:
self._logWindow(line, m.group(1) == "++") return
m = RE_DOCSHELL.search(line) if m:
self._logDocShell(line, m.group(1) == "++") return
if line.startswith("Completed ShutdownLeaks collections in process"):
pid = int(line.split()[-1])
self.seenShutdown.add(pid) elif action == "test_start":
fileName = message["test"].replace( "chrome://mochitests/content/browser/", ""
)
self.currentTest = { "fileName": fileName, "windows": set(), "docShells": set(),
} elif action == "test_end": # don't track a test if no windows or docShells leaked if self.currentTest and (
self.currentTest["windows"] or self.currentTest["docShells"]
):
self.tests.append(self.currentTest)
self.currentTest = None
def process(self):
failures = 0
ifnot self.seenShutdown:
self.logger.error( "TEST-UNEXPECTED-FAIL | ShutdownLeaks | process() called before end of test suite"
)
failures += 1
if (
self.numDocShellCreatedLogsSeen == 0 or self.numDocShellDestroyedLogsSeen == 0
):
self.logger.error( "TEST-UNEXPECTED-FAIL | did not see DOCSHELL log strings." " this occurs if the DOCSHELL logging gets disabled by" " something. %d created seen %d destroyed seen"
% (self.numDocShellCreatedLogsSeen, self.numDocShellDestroyedLogsSeen)
)
failures += 1 else:
self.logger.info( "TEST-INFO | Confirming we saw %d DOCSHELL created and %d destroyed log" " strings."
% (self.numDocShellCreatedLogsSeen, self.numDocShellDestroyedLogsSeen)
)
if (
self.numDomWindowCreatedLogsSeen == 0 or self.numDomWindowDestroyedLogsSeen == 0
):
self.logger.error( "TEST-UNEXPECTED-FAIL | did not see DOMWINDOW log strings." " this occurs if the DOMWINDOW logging gets disabled by" " something%d created seen %d destroyed seen"
% (self.numDomWindowCreatedLogsSeen, self.numDomWindowDestroyedLogsSeen)
)
failures += 1 else:
self.logger.info( "TEST-INFO | Confirming we saw %d DOMWINDOW created and %d destroyed log" " strings."
% (self.numDomWindowCreatedLogsSeen, self.numDomWindowDestroyedLogsSeen)
)
errors = [] for test in self._parseLeakingTests(): for url, count in self._zipLeakedWindows(test["leakedWindows"]):
errors.append(
{ "test": test["fileName"], "msg": "leaked %d window(s) until shutdown [url = %s]"
% (count, url),
}
)
failures += 1
if test["leakedDocShells"]:
errors.append(
{ "test": test["fileName"], "msg": "leaked %d docShell(s) until shutdown"
% (len(test["leakedDocShells"])),
}
)
failures += 1
self.logger.info( "TEST-INFO | %s | docShell(s) leaked: %s"
% (
test["fileName"], ", ".join(
[ "[pid = %s] [id = %s]" % x for x in test["leakedDocShells"]
]
),
)
)
if test["hiddenWindowsCount"] > 0: # Note: to figure out how many hidden windows were created, we divide # this number by 2, because 1 hidden window creation implies in # 1 outer window + 1 inner window. # pylint --py3k W1619
self.logger.info( "TEST-INFO | %s | This test created %d hidden window(s)"
% (test["fileName"], test["hiddenWindowsCount"] / 2)
)
if test["hiddenDocShellsCount"] > 0:
self.logger.info( "TEST-INFO | %s | This test created %d hidden docshell(s)"
% (test["fileName"], test["hiddenDocShellsCount"])
)
return failures, errors
def _logWindow(self, line, created):
pid = self._parseValue(line, "pid")
serial = self._parseValue(line, "serial")
self.numDomWindowCreatedLogsSeen += 1 if created else 0
self.numDomWindowDestroyedLogsSeen += 0 if created else 1
# log line has invalid format ifnot pid ornot serial:
self.logger.error( "TEST-UNEXPECTED-FAIL | ShutdownLeaks | failed to parse line"
)
self.logger.error("TEST-INFO | ShutdownLeaks | Unparsable line <%s>" % line) return
key = (pid, serial)
if self.currentTest:
windows = self.currentTest["windows"] if created:
windows.add(key) else:
windows.discard(key) elif int(pid) in self.seenShutdown andnot created:
url = self._parseValue(line, "url") ifnot self._isHiddenWindowURL(url):
self.leakedWindows[key] = url else:
self.hiddenWindowsCount += 1
def _logDocShell(self, line, created):
pid = self._parseValue(line, "pid")
id = self._parseValue(line, "id")
self.numDocShellCreatedLogsSeen += 1 if created else 0
self.numDocShellDestroyedLogsSeen += 0 if created else 1
# log line has invalid format ifnot pid ornot id:
self.logger.error( "TEST-UNEXPECTED-FAIL | ShutdownLeaks | failed to parse line"
)
self.logger.error("TEST-INFO | ShutdownLeaks | Unparsable line <%s>" % line) return
key = (pid, id)
if self.currentTest:
docShells = self.currentTest["docShells"] if created:
docShells.add(key) else:
docShells.discard(key) elif int(pid) in self.seenShutdown andnot created:
url = self._parseValue(line, "url") ifnot self._isHiddenWindowURL(url):
self.leakedDocShells.add(key) else:
self.hiddenDocShellsCount += 1
def _parseValue(self, line, name):
match = re.search(r"\[%s = (.+?)\]" % name, line) if match: return match.group(1) returnNone
def _parseLeakingTests(self):
leakingTests = []
for test in self.tests:
leakedWindows = [id for id in test["windows"] if id in self.leakedWindows]
test["leakedWindows"] = [self.leakedWindows[id] for id in leakedWindows]
test["hiddenWindowsCount"] = self.hiddenWindowsCount
test["leakedWindowsString"] = ", ".join(
["[pid = %s] [serial = %s]" % x for x in leakedWindows]
)
test["leakedDocShells"] = [
id for id in test["docShells"] if id in self.leakedDocShells
]
test["hiddenDocShellsCount"] = self.hiddenDocShellsCount
test["leakCount"] = len(test["leakedWindows"]) + len(
test["leakedDocShells"]
)
if (
test["leakCount"] or test["hiddenWindowsCount"] or test["hiddenDocShellsCount"]
):
leakingTests.append(test)
class LSANLeaks(object): """
Parses the log when running an LSAN build, looking for interesting stack frames in allocation stacks, and prints out reports. """
# Don't various allocation-related stack frames, as they do not help much to # distinguish different leaks.
unescapedSkipList = [ "malloc", "js_malloc", "js_arena_malloc", "malloc_", "__interceptor_malloc", "moz_xmalloc", "calloc", "js_calloc", "js_arena_calloc", "calloc_", "__interceptor_calloc", "moz_xcalloc", "realloc", "js_realloc", "js_arena_realloc", "realloc_", "__interceptor_realloc", "moz_xrealloc", "new", "js::MallocProvider",
]
self.skipListRegExp = re.compile( "^" + "|".join([re.escape(f) for f in unescapedSkipList]) + "$"
)
if re.match(self.fatalErrorRegExp, line):
self.fatalError = True return
if re.match(self.symbolizerOomRegExp, line):
self.symbolizerError = True return
ifnot self.inReport: return
if line.startswith("Direct leak") or line.startswith("Indirect leak"):
self._finishStack(path)
self.recordMoreFrames = True
self.currStack = [] return
if line.startswith("SUMMARY: AddressSanitizer"):
self._finishStack(path)
self.inReport = False return
ifnot self.recordMoreFrames: return
stackFrame = re.match(self.stackFrameRegExp, line) if stackFrame: # Split the frame to remove any return types.
frame = stackFrame.group(1).split()[-1] ifnot re.match(self.skipListRegExp, frame):
self._recordFrame(frame) return
sysLibStackFrame = re.match(self.sysLibStackFrameRegExp, line) if sysLibStackFrame: # System library stack frames will never match the skip list, # so don't bother checking if they do.
self._recordFrame(sysLibStackFrame.group(1))
# If we don't match either of these, just ignore the frame. # We'll end up with "unknown stack" if everything is ignored.
def process(self):
failures = 0
if self.fatalError:
self.logger.error( "TEST-UNEXPECTED-FAIL | LeakSanitizer | LeakSanitizer " "has encountered a fatal error."
)
failures += 1
if self.symbolizerError:
self.logger.error( "TEST-UNEXPECTED-FAIL | LeakSanitizer | LLVMSymbolizer " "was unable to allocate memory."
)
failures += 1
self.logger.info( "TEST-INFO | LeakSanitizer | This will cause leaks that " "should be ignored to instead be reported as an error"
)
if self.foundFrames:
self.logger.info( "TEST-INFO | LeakSanitizer | To show the " "addresses of leaked objects add report_objects=1 to LSAN_OPTIONS"
)
self.logger.info( "TEST-INFO | LeakSanitizer | This can be done " "in testing/mozbase/mozrunner/mozrunner/utils.py"
)
frames = list(self.foundFrames)
frames.sort() for f in frames: if self.scope:
f = "%s | %s" % (f, self.scope)
self.logger.error("TEST-UNEXPECTED-FAIL | LeakSanitizer leak at " + f)
failures += 1
return failures
def _finishStack(self, path=""): if self.recordMoreFrames and len(self.currStack) == 0:
self.currStack = ["unknown stack"] if self.currStack:
self.foundFrames.add(", ".join(self.currStack))
self.currStack = None
self.scope = path
self.recordMoreFrames = False
self.numRecordedFrames = 0
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.