Bug 1420975 - Add a environment variable to record JS stack for leaks. r=froydnj,mrbkap

This patch adds a new environment variable XPCOM_MEM_LOG_JS_STACK that
changes XPCOM leakchecking to record a JS stack for all objects, in
addition to a C++ stack. This is useful when a C++ object is being
leaked due to JS. The JS stack will be printed if the object leaks, if
it is used in combination with XPCOM_MEM_BLOAT_LOG=1 and
XPCOM_MEM_LOG_CLASSES=nsFoo, if nsFoo is the class of interest.

This patch moves a few XPConnect functions for recording the stack
into xpcpublic.h so they can be called from nsTraceRefcnt.cpp.

MozReview-Commit-ID: FX2QVCSXz4f

--HG--
extra : rebase_source : 5bd4e341072f4cf7d3be774b63d2107479fe9985
This commit is contained in:
Andrew McCreight 2018-01-10 14:02:45 -08:00
parent f6ce1f10e0
commit 9d063bfac8
3 changed files with 50 additions and 12 deletions

View File

@ -2353,18 +2353,6 @@ xpc_JSObjectToID(JSContext* cx, JSObject* obj);
extern bool
xpc_JSObjectIsID(JSContext* cx, JSObject* obj);
/***************************************************************************/
// in XPCDebug.cpp
extern bool
xpc_DumpJSStack(bool showArgs, bool showLocals, bool showThisProps);
// Return a newly-allocated string containing a representation of the
// current JS stack.
extern JS::UniqueChars
xpc_PrintJSStack(JSContext* cx, bool showArgs, bool showLocals,
bool showThisProps);
/******************************************************************************
* Handles pre/post script processing.
*/

View File

@ -254,6 +254,17 @@ xpc_TryUnmarkWrappedGrayObject(nsISupports* aWrappedJS);
extern void
xpc_UnmarkSkippableJSHolders();
// Defined in XPCDebug.cpp.
extern bool
xpc_DumpJSStack(bool showArgs, bool showLocals, bool showThisProps);
// Return a newly-allocated string containing a representation of the
// current JS stack. Defined in XPCDebug.cpp.
extern JS::UniqueChars
xpc_PrintJSStack(JSContext* cx, bool showArgs, bool showLocals,
bool showThisProps);
// readable string conversions, static methods and members only
class XPCStringConvert
{

View File

@ -34,6 +34,7 @@
#include "mozilla/AutoRestore.h"
#include "mozilla/BlockingResourceBase.h"
#include "mozilla/PoisonIOInterposer.h"
#include "mozilla/UniquePtr.h"
#include <string>
#include <vector>
@ -83,6 +84,7 @@ static PLHashTable* gObjectsToLog;
static PLHashTable* gSerialNumbers;
static intptr_t gNextSerialNumber;
static bool gDumpedStatistics = false;
static bool gLogJSStacks = false;
// By default, debug builds only do bloat logging. Bloat logging
// only tries to record when an object is created or destroyed, so we
@ -133,6 +135,27 @@ struct SerialNumberRecord
// XPCOM equivalents do leak-checking, and if you try to leak-check while
// leak-checking, you're gonna have a bad time.
std::vector<void*> allocationStack;
mozilla::UniquePtr<char[]> jsStack;
void SaveJSStack() {
// If this thread isn't running JS, there's nothing to do.
if (!CycleCollectedJSContext::Get()) {
return;
}
JSContext* cx = nsContentUtils::GetCurrentJSContextForThread();
if (!cx) {
return;
}
JS::UniqueChars chars = xpc_PrintJSStack(cx,
/*showArgs=*/ false,
/*showLocals=*/ false,
/*showThisProps=*/ false);
size_t len = strlen(chars.get());
jsStack = MakeUnique<char[]>(len + 1);
memcpy(jsStack.get(), chars.get(), len + 1);
}
};
struct nsTraceRefcntStats
@ -472,6 +495,15 @@ DumpSerialNumbers(PLHashEntry* aHashEntry, int aIndex, void* aClosure)
fprintf(outputFile, "%s\n", buf);
}
}
if (gLogJSStacks) {
if (record->jsStack) {
fprintf(outputFile, "JS allocation stack:\n%s\n", record->jsStack.get());
} else {
fprintf(outputFile, "There is no JS context on the stack.\n");
}
}
return HT_ENUMERATE_NEXT;
}
@ -587,6 +619,9 @@ GetSerialNumber(void* aPtr, bool aCreate)
WalkTheStackSavingLocations(record->allocationStack);
PL_HashTableRawAdd(gSerialNumbers, hep, HashNumber(aPtr),
aPtr, static_cast<void*>(record));
if (gLogJSStacks) {
record->SaveJSStack();
}
return gNextSerialNumber;
}
@ -829,6 +864,10 @@ InitTraceLog()
}
}
if (getenv("XPCOM_MEM_LOG_JS_STACK")) {
fprintf(stdout, "### XPCOM_MEM_LOG_JS_STACK defined\n");
gLogJSStacks = true;
}
if (gBloatLog) {
gLogging = OnlyBloatLogging;