forked from mirrors/gecko-dev
Bug 1865826 - Add a way to get CC logs without GC logs. r=smaug
This adds a new option MOZ_CC_DISABLE_GC_LOG which will stop shutdown CC logging from producing a GC log. Other ways to produce a CC log will be unaffected. This can be useful when a leak involves some kind of race that is sensitive to timing, as GC logging can be slow, or simply to have less logs clogging up your hard drive. Differential Revision: https://phabricator.services.mozilla.com/D202881
This commit is contained in:
parent
b3ca18ec8a
commit
d51dcd9005
4 changed files with 64 additions and 30 deletions
|
|
@ -159,6 +159,7 @@
|
||||||
#include "mozilla/HashFunctions.h"
|
#include "mozilla/HashFunctions.h"
|
||||||
#include "mozilla/HashTable.h"
|
#include "mozilla/HashTable.h"
|
||||||
#include "mozilla/HoldDropJSObjects.h"
|
#include "mozilla/HoldDropJSObjects.h"
|
||||||
|
#include "mozilla/Maybe.h"
|
||||||
/* This must occur *after* base/process_util.h to avoid typedefs conflicts. */
|
/* This must occur *after* base/process_util.h to avoid typedefs conflicts. */
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
@ -269,6 +270,11 @@ static void SuspectUsingNurseryPurpleBuffer(
|
||||||
// MOZ_CC_LOG_DIRECTORY: The directory in which logs are placed (such as
|
// MOZ_CC_LOG_DIRECTORY: The directory in which logs are placed (such as
|
||||||
// logs from MOZ_CC_LOG_ALL and MOZ_CC_LOG_SHUTDOWN, or other uses
|
// logs from MOZ_CC_LOG_ALL and MOZ_CC_LOG_SHUTDOWN, or other uses
|
||||||
// of nsICycleCollectorListener)
|
// of nsICycleCollectorListener)
|
||||||
|
//
|
||||||
|
// MOZ_CC_DISABLE_GC_LOG: If defined, don't make a GC log whenever we make a
|
||||||
|
// cycle collector log. This can be useful for leaks that go away when shutdown
|
||||||
|
// gets slower, when the JS heap is not involved in the leak. The default is to
|
||||||
|
// make the GC log.
|
||||||
|
|
||||||
// Various parameters of this collector can be tuned using environment
|
// Various parameters of this collector can be tuned using environment
|
||||||
// variables.
|
// variables.
|
||||||
|
|
@ -279,13 +285,15 @@ struct nsCycleCollectorParams {
|
||||||
bool mAllTracesAll;
|
bool mAllTracesAll;
|
||||||
bool mAllTracesShutdown;
|
bool mAllTracesShutdown;
|
||||||
bool mLogThisThread;
|
bool mLogThisThread;
|
||||||
|
bool mLogGC;
|
||||||
int32_t mLogShutdownSkip = 0;
|
int32_t mLogShutdownSkip = 0;
|
||||||
|
|
||||||
nsCycleCollectorParams()
|
nsCycleCollectorParams()
|
||||||
: mLogAll(PR_GetEnv("MOZ_CC_LOG_ALL") != nullptr),
|
: mLogAll(PR_GetEnv("MOZ_CC_LOG_ALL") != nullptr),
|
||||||
mLogShutdown(PR_GetEnv("MOZ_CC_LOG_SHUTDOWN") != nullptr),
|
mLogShutdown(PR_GetEnv("MOZ_CC_LOG_SHUTDOWN") != nullptr),
|
||||||
mAllTracesAll(false),
|
mAllTracesAll(false),
|
||||||
mAllTracesShutdown(false) {
|
mAllTracesShutdown(false),
|
||||||
|
mLogGC(!PR_GetEnv("MOZ_CC_DISABLE_GC_LOG")) {
|
||||||
if (const char* lssEnv = PR_GetEnv("MOZ_CC_LOG_SHUTDOWN_SKIP")) {
|
if (const char* lssEnv = PR_GetEnv("MOZ_CC_LOG_SHUTDOWN_SKIP")) {
|
||||||
mLogShutdown = true;
|
mLogShutdown = true;
|
||||||
nsDependentCString lssString(lssEnv);
|
nsDependentCString lssString(lssEnv);
|
||||||
|
|
@ -352,6 +360,8 @@ struct nsCycleCollectorParams {
|
||||||
bool AllTracesThisCC(bool aIsShutdown) {
|
bool AllTracesThisCC(bool aIsShutdown) {
|
||||||
return mAllTracesAll || (aIsShutdown && mAllTracesShutdown);
|
return mAllTracesAll || (aIsShutdown && mAllTracesShutdown);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool LogThisGC() const { return mLogGC; }
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef COLLECT_TIME_DEBUG
|
#ifdef COLLECT_TIME_DEBUG
|
||||||
|
|
@ -1376,10 +1386,12 @@ class nsCycleCollectorLogSinkToFile final : public nsICycleCollectorLogSink {
|
||||||
public:
|
public:
|
||||||
NS_DECL_ISUPPORTS
|
NS_DECL_ISUPPORTS
|
||||||
|
|
||||||
nsCycleCollectorLogSinkToFile()
|
explicit nsCycleCollectorLogSinkToFile(bool aLogGC)
|
||||||
: mProcessIdentifier(base::GetCurrentProcId()),
|
: mProcessIdentifier(base::GetCurrentProcId()), mCCLog("cc-edges") {
|
||||||
mGCLog("gc-edges"),
|
if (aLogGC) {
|
||||||
mCCLog("cc-edges") {}
|
mGCLog.emplace("gc-edges");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NS_IMETHOD GetFilenameIdentifier(nsAString& aIdentifier) override {
|
NS_IMETHOD GetFilenameIdentifier(nsAString& aIdentifier) override {
|
||||||
aIdentifier = mFilenameIdentifier;
|
aIdentifier = mFilenameIdentifier;
|
||||||
|
|
@ -1402,7 +1414,10 @@ class nsCycleCollectorLogSinkToFile final : public nsICycleCollectorLogSink {
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHOD GetGcLog(nsIFile** aPath) override {
|
NS_IMETHOD GetGcLog(nsIFile** aPath) override {
|
||||||
NS_IF_ADDREF(*aPath = mGCLog.mFile);
|
if (mGCLog.isNothing()) {
|
||||||
|
return NS_ERROR_UNEXPECTED;
|
||||||
|
}
|
||||||
|
NS_IF_ADDREF(*aPath = mGCLog.ref().mFile);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1414,13 +1429,21 @@ class nsCycleCollectorLogSinkToFile final : public nsICycleCollectorLogSink {
|
||||||
NS_IMETHOD Open(FILE** aGCLog, FILE** aCCLog) override {
|
NS_IMETHOD Open(FILE** aGCLog, FILE** aCCLog) override {
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
|
|
||||||
if (mGCLog.mStream || mCCLog.mStream) {
|
if (mCCLog.mStream) {
|
||||||
return NS_ERROR_UNEXPECTED;
|
return NS_ERROR_UNEXPECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
rv = OpenLog(&mGCLog);
|
if (mGCLog.isSome()) {
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
if (mGCLog.ref().mStream) {
|
||||||
*aGCLog = mGCLog.mStream;
|
return NS_ERROR_UNEXPECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = OpenLog(&mGCLog.ref());
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
*aGCLog = mGCLog.ref().mStream;
|
||||||
|
} else {
|
||||||
|
*aGCLog = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
rv = OpenLog(&mCCLog);
|
rv = OpenLog(&mCCLog);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
@ -1430,10 +1453,13 @@ class nsCycleCollectorLogSinkToFile final : public nsICycleCollectorLogSink {
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHOD CloseGCLog() override {
|
NS_IMETHOD CloseGCLog() override {
|
||||||
if (!mGCLog.mStream) {
|
if (mGCLog.isNothing()) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
if (!mGCLog.ref().mStream) {
|
||||||
return NS_ERROR_UNEXPECTED;
|
return NS_ERROR_UNEXPECTED;
|
||||||
}
|
}
|
||||||
CloseLog(&mGCLog, u"Garbage"_ns);
|
CloseLog(&mGCLog.ref(), u"Garbage"_ns);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1447,9 +1473,9 @@ class nsCycleCollectorLogSinkToFile final : public nsICycleCollectorLogSink {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
~nsCycleCollectorLogSinkToFile() {
|
~nsCycleCollectorLogSinkToFile() {
|
||||||
if (mGCLog.mStream) {
|
if (mGCLog.isSome() && mGCLog.ref().mStream) {
|
||||||
MozillaUnRegisterDebugFILE(mGCLog.mStream);
|
MozillaUnRegisterDebugFILE(mGCLog.ref().mStream);
|
||||||
fclose(mGCLog.mStream);
|
fclose(mGCLog.ref().mStream);
|
||||||
}
|
}
|
||||||
if (mCCLog.mStream) {
|
if (mCCLog.mStream) {
|
||||||
MozillaUnRegisterDebugFILE(mCCLog.mStream);
|
MozillaUnRegisterDebugFILE(mCCLog.mStream);
|
||||||
|
|
@ -1566,7 +1592,7 @@ class nsCycleCollectorLogSinkToFile final : public nsICycleCollectorLogSink {
|
||||||
|
|
||||||
int32_t mProcessIdentifier;
|
int32_t mProcessIdentifier;
|
||||||
nsString mFilenameIdentifier;
|
nsString mFilenameIdentifier;
|
||||||
FileInfo mGCLog;
|
Maybe<FileInfo> mGCLog;
|
||||||
FileInfo mCCLog;
|
FileInfo mCCLog;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1576,8 +1602,8 @@ class nsCycleCollectorLogger final : public nsICycleCollectorListener {
|
||||||
~nsCycleCollectorLogger() { ClearDescribers(); }
|
~nsCycleCollectorLogger() { ClearDescribers(); }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
nsCycleCollectorLogger()
|
explicit nsCycleCollectorLogger(bool aLogGC)
|
||||||
: mLogSink(nsCycleCollector_createLogSink()),
|
: mLogSink(nsCycleCollector_createLogSink(aLogGC)),
|
||||||
mWantAllTraces(false),
|
mWantAllTraces(false),
|
||||||
mDisableLog(false),
|
mDisableLog(false),
|
||||||
mWantAfterProcessing(false),
|
mWantAfterProcessing(false),
|
||||||
|
|
@ -1646,13 +1672,14 @@ class nsCycleCollectorLogger final : public nsICycleCollectorListener {
|
||||||
rv = mLogSink->Open(&gcLog, &mCCLog);
|
rv = mLogSink->Open(&gcLog, &mCCLog);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
// Dump the JS heap.
|
// Dump the JS heap.
|
||||||
CollectorData* data = sCollectorData.get();
|
if (gcLog) {
|
||||||
if (data && data->mContext) {
|
CollectorData* data = sCollectorData.get();
|
||||||
data->mContext->Runtime()->DumpJSHeap(gcLog);
|
if (data && data->mContext) {
|
||||||
|
data->mContext->Runtime()->DumpJSHeap(gcLog);
|
||||||
|
}
|
||||||
|
rv = mLogSink->CloseGCLog();
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
}
|
}
|
||||||
rv = mLogSink->CloseGCLog();
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
fprintf(mCCLog, "# WantAllTraces=%s\n", mWantAllTraces ? "true" : "false");
|
fprintf(mCCLog, "# WantAllTraces=%s\n", mWantAllTraces ? "true" : "false");
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
@ -1822,7 +1849,8 @@ class nsCycleCollectorLogger final : public nsICycleCollectorListener {
|
||||||
NS_IMPL_ISUPPORTS(nsCycleCollectorLogger, nsICycleCollectorListener)
|
NS_IMPL_ISUPPORTS(nsCycleCollectorLogger, nsICycleCollectorListener)
|
||||||
|
|
||||||
already_AddRefed<nsICycleCollectorListener> nsCycleCollector_createLogger() {
|
already_AddRefed<nsICycleCollectorListener> nsCycleCollector_createLogger() {
|
||||||
nsCOMPtr<nsICycleCollectorListener> logger = new nsCycleCollectorLogger();
|
nsCOMPtr<nsICycleCollectorListener> logger =
|
||||||
|
new nsCycleCollectorLogger(/* aLogGC = */ true);
|
||||||
return logger.forget();
|
return logger.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3628,7 +3656,7 @@ void nsCycleCollector::BeginCollection(
|
||||||
|
|
||||||
aManualListener = nullptr;
|
aManualListener = nullptr;
|
||||||
if (!mLogger && mParams.LogThisCC(mShutdownCount)) {
|
if (!mLogger && mParams.LogThisCC(mShutdownCount)) {
|
||||||
mLogger = new nsCycleCollectorLogger();
|
mLogger = new nsCycleCollectorLogger(mParams.LogThisGC());
|
||||||
if (mParams.AllTracesThisCC(isShutdown)) {
|
if (mParams.AllTracesThisCC(isShutdown)) {
|
||||||
mLogger->SetAllTraces();
|
mLogger->SetAllTraces();
|
||||||
}
|
}
|
||||||
|
|
@ -3963,8 +3991,10 @@ bool nsCycleCollector_doDeferredDeletionWithBudget(js::SliceBudget& aBudget) {
|
||||||
return data->mCollector->FreeSnowWhiteWithBudget(aBudget);
|
return data->mCollector->FreeSnowWhiteWithBudget(aBudget);
|
||||||
}
|
}
|
||||||
|
|
||||||
already_AddRefed<nsICycleCollectorLogSink> nsCycleCollector_createLogSink() {
|
already_AddRefed<nsICycleCollectorLogSink> nsCycleCollector_createLogSink(
|
||||||
nsCOMPtr<nsICycleCollectorLogSink> sink = new nsCycleCollectorLogSinkToFile();
|
bool aLogGC) {
|
||||||
|
nsCOMPtr<nsICycleCollectorLogSink> sink =
|
||||||
|
new nsCycleCollectorLogSinkToFile(aLogGC);
|
||||||
return sink.forget();
|
return sink.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,8 @@ void nsCycleCollector_dispatchDeferredDeletion(bool aContinuation = false,
|
||||||
bool nsCycleCollector_doDeferredDeletion();
|
bool nsCycleCollector_doDeferredDeletion();
|
||||||
bool nsCycleCollector_doDeferredDeletionWithBudget(js::SliceBudget& aBudget);
|
bool nsCycleCollector_doDeferredDeletionWithBudget(js::SliceBudget& aBudget);
|
||||||
|
|
||||||
already_AddRefed<nsICycleCollectorLogSink> nsCycleCollector_createLogSink();
|
already_AddRefed<nsICycleCollectorLogSink> nsCycleCollector_createLogSink(
|
||||||
|
bool aLogGC);
|
||||||
already_AddRefed<nsICycleCollectorListener> nsCycleCollector_createLogger();
|
already_AddRefed<nsICycleCollectorListener> nsCycleCollector_createLogger();
|
||||||
|
|
||||||
// Run a cycle collection and return whether anything was collected.
|
// Run a cycle collection and return whether anything was collected.
|
||||||
|
|
|
||||||
|
|
@ -72,8 +72,11 @@ interface nsICycleCollectorHandler : nsISupports
|
||||||
[scriptable, builtinclass, uuid(3ad9875f-d0e4-4ac2-87e3-f127f6c02ce1)]
|
[scriptable, builtinclass, uuid(3ad9875f-d0e4-4ac2-87e3-f127f6c02ce1)]
|
||||||
interface nsICycleCollectorLogSink : nsISupports
|
interface nsICycleCollectorLogSink : nsISupports
|
||||||
{
|
{
|
||||||
|
// aGCLog can be null, if the sink was not configured to create a GC log.
|
||||||
[noscript] void open(out FILE aGCLog, out FILE aCCLog);
|
[noscript] void open(out FILE aGCLog, out FILE aCCLog);
|
||||||
|
|
||||||
void closeGCLog();
|
void closeGCLog();
|
||||||
|
|
||||||
void closeCCLog();
|
void closeCCLog();
|
||||||
|
|
||||||
// This string will appear somewhere in the log's filename.
|
// This string will appear somewhere in the log's filename.
|
||||||
|
|
|
||||||
|
|
@ -323,7 +323,7 @@ nsMemoryInfoDumper::DumpGCAndCCLogsToFile(
|
||||||
for (uint32_t i = 0; i < children.Length(); i++) {
|
for (uint32_t i = 0; i < children.Length(); i++) {
|
||||||
ContentParent* cp = children[i];
|
ContentParent* cp = children[i];
|
||||||
nsCOMPtr<nsICycleCollectorLogSink> logSink =
|
nsCOMPtr<nsICycleCollectorLogSink> logSink =
|
||||||
nsCycleCollector_createLogSink();
|
nsCycleCollector_createLogSink(/* aLogGC = */ true);
|
||||||
|
|
||||||
logSink->SetFilenameIdentifier(identifier);
|
logSink->SetFilenameIdentifier(identifier);
|
||||||
logSink->SetProcessIdentifier(cp->Pid());
|
logSink->SetProcessIdentifier(cp->Pid());
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue