Bug 519570. Log leaks for child processes in separate files, and handle them in automation.py. r=bsmedberg

This commit is contained in:
Jonathan Griffin 2009-11-10 11:16:12 -08:00
Родитель d515b14aeb
Коммит e57f832609
3 изменённых файлов: 88 добавлений и 26 удалений

Просмотреть файл

@ -200,17 +200,11 @@ def dumpLeakLog(leakLogFile, filter = False):
# Simply copy the log. # Simply copy the log.
log.info(leakReport.rstrip("\n")) log.info(leakReport.rstrip("\n"))
def processLeakLog(leakLogFile, leakThreshold = 0): def processSingleLeakFile(leakLogFileName, PID, processType, leakThreshold):
"""Process the leak log, parsing it. """Process a single leak log, corresponding to the specified
process PID and type.
Use this function if you want an additional PASS/FAIL summary.
It must be used with the |XPCOM_MEM_BLOAT_LOG| environment variable.
""" """
if not os.path.exists(leakLogFile):
log.info("WARNING | automationutils.processLeakLog() | refcount logging is off, so leaks can't be detected!")
return
# Per-Inst Leaked Total Rem ... # Per-Inst Leaked Total Rem ...
# 0 TOTAL 17 192 419115886 2 ... # 0 TOTAL 17 192 419115886 2 ...
# 833 nsTimerImpl 60 120 24726 2 ... # 833 nsTimerImpl 60 120 24726 2 ...
@ -218,7 +212,10 @@ def processLeakLog(leakLogFile, leakThreshold = 0):
r"(?P<size>-?\d+)\s+(?P<bytesLeaked>-?\d+)\s+" r"(?P<size>-?\d+)\s+(?P<bytesLeaked>-?\d+)\s+"
r"-?\d+\s+(?P<numLeaked>-?\d+)") r"-?\d+\s+(?P<numLeaked>-?\d+)")
leaks = open(leakLogFile, "r") processString = ""
if PID and processType:
processString = "| %s process %s " % (processType, PID)
leaks = open(leakLogFileName, "r")
for line in leaks: for line in leaks:
matches = lineRe.match(line) matches = lineRe.match(line)
if (matches and if (matches and
@ -228,7 +225,7 @@ def processLeakLog(leakLogFile, leakThreshold = 0):
log.info(line.rstrip()) log.info(line.rstrip())
leaks.close() leaks.close()
leaks = open(leakLogFile, "r") leaks = open(leakLogFileName, "r")
seenTotal = False seenTotal = False
prefix = "TEST-PASS" prefix = "TEST-PASS"
for line in leaks: for line in leaks:
@ -240,7 +237,8 @@ def processLeakLog(leakLogFile, leakThreshold = 0):
bytesLeaked = int(matches.group("bytesLeaked")) bytesLeaked = int(matches.group("bytesLeaked"))
numLeaked = int(matches.group("numLeaked")) numLeaked = int(matches.group("numLeaked"))
if size < 0 or bytesLeaked < 0 or numLeaked < 0: if size < 0 or bytesLeaked < 0 or numLeaked < 0:
log.info("TEST-UNEXPECTED-FAIL | automationutils.processLeakLog() | negative leaks caught!") log.info("TEST-UNEXPECTED-FAIL %s| automationutils.processLeakLog() | negative leaks caught!" %
processString)
if name == "TOTAL": if name == "TOTAL":
seenTotal = True seenTotal = True
elif name == "TOTAL": elif name == "TOTAL":
@ -248,13 +246,14 @@ def processLeakLog(leakLogFile, leakThreshold = 0):
# Check for leaks. # Check for leaks.
if bytesLeaked < 0 or bytesLeaked > leakThreshold: if bytesLeaked < 0 or bytesLeaked > leakThreshold:
prefix = "TEST-UNEXPECTED-FAIL" prefix = "TEST-UNEXPECTED-FAIL"
leakLog = "TEST-UNEXPECTED-FAIL | automationutils.processLeakLog() | leaked" \ leakLog = "TEST-UNEXPECTED-FAIL %s| automationutils.processLeakLog() | leaked" \
" %d bytes during test execution" % bytesLeaked " %d bytes during test execution" % (processString, bytesLeaked)
elif bytesLeaked > 0: elif bytesLeaked > 0:
leakLog = "TEST-PASS | automationutils.processLeakLog() | WARNING leaked" \ leakLog = "TEST-PASS %s| automationutils.processLeakLog() | WARNING leaked" \
" %d bytes during test execution" % bytesLeaked " %d bytes during test execution" % (processString, bytesLeaked)
else: else:
leakLog = "TEST-PASS | automationutils.processLeakLog() | no leaks detected!" leakLog = "TEST-PASS %s| automationutils.processLeakLog() | no leaks detected!" \
% processString
# Remind the threshold if it is not 0, which is the default/goal. # Remind the threshold if it is not 0, which is the default/goal.
if leakThreshold != 0: if leakThreshold != 0:
leakLog += " (threshold set at %d bytes)" % leakThreshold leakLog += " (threshold set at %d bytes)" % leakThreshold
@ -268,14 +267,46 @@ def processLeakLog(leakLogFile, leakThreshold = 0):
else: else:
instance = "instance" instance = "instance"
rest = "" rest = ""
log.info("%(prefix)s | automationutils.processLeakLog() | leaked %(numLeaked)d %(instance)s of %(name)s " log.info("%(prefix)s %(process)s| automationutils.processLeakLog() | leaked %(numLeaked)d %(instance)s of %(name)s "
"with size %(size)s bytes%(rest)s" % "with size %(size)s bytes%(rest)s" %
{ "prefix": prefix, { "prefix": prefix,
"process": processString,
"numLeaked": numLeaked, "numLeaked": numLeaked,
"instance": instance, "instance": instance,
"name": name, "name": name,
"size": matches.group("size"), "size": matches.group("size"),
"rest": rest }) "rest": rest })
if not seenTotal: if not seenTotal:
log.info("TEST-UNEXPECTED-FAIL | automationutils.processLeakLog() | missing output line for total leaks!") log.info("TEST-UNEXPECTED-FAIL %s| automationutils.processLeakLog() | missing output line for total leaks!" %
processString)
leaks.close() leaks.close()
def processLeakLog(leakLogFile, leakThreshold = 0):
"""Process the leak log, including separate leak logs created
by child processes.
Use this function if you want an additional PASS/FAIL summary.
It must be used with the |XPCOM_MEM_BLOAT_LOG| environment variable.
"""
if not os.path.exists(leakLogFile):
log.info("WARNING | automationutils.processLeakLog() | refcount logging is off, so leaks can't be detected!")
return
(leakLogFileDir, leakFileBase) = os.path.split(leakLogFile)
pidRegExp = re.compile(r".*?_([a-z]*)_pid(\d*)$")
if leakFileBase[-4:] == ".log":
leakFileBase = leakFileBase[:-4]
pidRegExp = re.compile(r".*?_([a-z]*)_pid(\d*).log$")
for fileName in os.listdir(leakLogFileDir):
if fileName.find(leakFileBase) != -1:
thisFile = os.path.join(leakLogFileDir, fileName)
processPID = 0
processType = None
m = pidRegExp.search(fileName)
if m:
processType = m.group(1)
processPID = m.group(2)
processSingleLeakFile(thisFile, processPID, processType, leakThreshold)

Просмотреть файл

@ -256,6 +256,8 @@ XRE_InitChildProcess(int aArgc,
NS_ENSURE_ARG_POINTER(aArgv); NS_ENSURE_ARG_POINTER(aArgv);
NS_ENSURE_ARG_POINTER(aArgv[0]); NS_ENSURE_ARG_POINTER(aArgv[0]);
sChildProcessType = aProcess;
#if defined(MOZ_WIDGET_GTK2) #if defined(MOZ_WIDGET_GTK2)
g_thread_init(NULL); g_thread_init(NULL);
#endif #endif
@ -319,7 +321,6 @@ XRE_InitChildProcess(int aArgc,
NS_RUNTIMEABORT("Unknown main thread class"); NS_RUNTIMEABORT("Unknown main thread class");
} }
sChildProcessType = aProcess;
ChildProcess process(mainThread); ChildProcess process(mainThread);
// Do IPC event loop // Do IPC event loop

Просмотреть файл

@ -50,6 +50,17 @@
#include <math.h> #include <math.h>
#include "nsStackWalk.h" #include "nsStackWalk.h"
#ifdef MOZ_IPC
#include "nsXULAppAPI.h"
#include "nsString.h"
#ifdef XP_WIN
#include <process.h>
#define getpid _getpid
#else
#include <unistd.h>
#endif
#endif
#ifdef HAVE_LIBDL #ifdef HAVE_LIBDL
#include <dlfcn.h> #include <dlfcn.h>
#endif #endif
@ -318,8 +329,12 @@ public:
} }
PRBool PrintDumpHeader(FILE* out, const char* msg, nsTraceRefcntImpl::StatisticsType type) { PRBool PrintDumpHeader(FILE* out, const char* msg, nsTraceRefcntImpl::StatisticsType type) {
#ifdef MOZ_IPC
fprintf(out, "\n== BloatView: %s, %s process %d\n", msg,
XRE_ChildProcessTypeToString(XRE_GetProcessType()), getpid());
#else
fprintf(out, "\n== BloatView: %s\n", msg); fprintf(out, "\n== BloatView: %s\n", msg);
#endif
nsTraceRefcntStats& stats = nsTraceRefcntStats& stats =
(type == nsTraceRefcntImpl::NEW_STATS) ? mNewStats : mAllStats; (type == nsTraceRefcntImpl::NEW_STATS) ? mNewStats : mAllStats;
if (gLogLeaksOnly && !HaveLeaks(&stats)) if (gLogLeaksOnly && !HaveLeaks(&stats))
@ -618,18 +633,33 @@ static PRBool InitLog(const char* envVar, const char* msg, FILE* *result)
return PR_TRUE; return PR_TRUE;
} }
else { else {
FILE *stream = ::fopen(value, "w"); FILE *stream;
nsCAutoString fname(value);
#ifdef MOZ_IPC
if (XRE_GetProcessType() != GeckoProcessType_Default) {
bool hasLogExtension =
fname.RFind(".log", PR_TRUE, -1, 4) == kNotFound ? false : true;
if (hasLogExtension)
fname.Cut(fname.Length() - 4, 4);
fname.AppendLiteral("_");
fname.Append((char*)XRE_ChildProcessTypeToString(XRE_GetProcessType()));
fname.AppendLiteral("_pid");
fname.AppendInt(getpid());
if (hasLogExtension)
fname.AppendLiteral(".log");
}
#endif
stream = ::fopen(fname.get(), "w");
if (stream != NULL) { if (stream != NULL) {
*result = stream; *result = stream;
fprintf(stdout, "### %s defined -- logging %s to %s\n", fprintf(stdout, "### %s defined -- logging %s to %s\n",
envVar, msg, value); envVar, msg, fname.get());
return PR_TRUE;
} }
else { else {
fprintf(stdout, "### %s defined -- unable to log %s to %s\n", fprintf(stdout, "### %s defined -- unable to log %s to %s\n",
envVar, msg, value); envVar, msg, fname.get());
return PR_FALSE;
} }
return stream != NULL;
} }
} }
return PR_FALSE; return PR_FALSE;