Bug 765065 - Annotation for crash reports: "Are we GCing?" (r=bsmedberg,a=lsblakk)
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -579,16 +579,34 @@ DoDeferredRelease(nsTArray<T> &array)
}
T wrapper = array[count-1];
array.RemoveElementAt(count-1);
NS_RELEASE(wrapper);
}
}
/* static */ void
+XPCJSRuntime::GCSliceCallback(JSRuntime *rt,
+ js::GCProgress progress,
+ const js::GCDescription &desc)
+{
+ XPCJSRuntime *self = nsXPConnect::GetRuntimeInstance();
+ if (!self)
+ return;
+
+#ifdef MOZ_CRASHREPORTER
+ CrashReporter::SetGarbageCollecting(progress == js::GC_CYCLE_BEGIN ||
+ progress == js::GC_SLICE_BEGIN);
+#endif
+
+ if (self->mPrevGCSliceCallback)
+ (*self->mPrevGCSliceCallback)(rt, progress, desc);
+}
+
+/* static */ void
XPCJSRuntime::GCCallback(JSRuntime *rt, JSGCStatus status)
{
XPCJSRuntime* self = nsXPConnect::GetRuntimeInstance();
if (!self)
return;
switch (status) {
case JSGC_BEGIN:
@@ -991,16 +1009,18 @@ XPCJSRuntime::GetJSCycleCollectionContex
if (!mJSCycleCollectionContext)
return nsnull;
}
return mJSCycleCollectionContext;
}
XPCJSRuntime::~XPCJSRuntime()
{
+ js::SetGCSliceCallback(mJSRuntime, mPrevGCSliceCallback);
+
if (mWatchdogWakeup) {
// If the watchdog thread is running, tell it to terminate waking it
// up if necessary and wait until it signals that it finished. As we
// must release the lock before calling PR_DestroyCondVar, we use an
// extra block here.
{
AutoLockWatchdog lock(this);
if (mWatchdogThread) {
@@ -2051,16 +2071,17 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect*
// ASan requires more stack space due to redzones
JS_SetNativeStackQuota(mJSRuntime, 2 * 128 * sizeof(size_t) * 1024);
#else
JS_SetNativeStackQuota(mJSRuntime, 128 * sizeof(size_t) * 1024);
#endif
JS_SetContextCallback(mJSRuntime, ContextCallback);
JS_SetDestroyCompartmentCallback(mJSRuntime, CompartmentDestroyedCallback);
JS_SetGCCallback(mJSRuntime, GCCallback);
+ mPrevGCSliceCallback = js::SetGCSliceCallback(mJSRuntime, GCSliceCallback);
JS_SetFinalizeCallback(mJSRuntime, FinalizeCallback);
JS_SetExtraGCRootsTracer(mJSRuntime, TraceBlackJS, this);
JS_SetGrayGCRootsTracer(mJSRuntime, TraceGrayJS, this);
JS_SetWrapObjectCallbacks(mJSRuntime,
xpc::WrapperFactory::Rewrap,
xpc::WrapperFactory::WrapForSameCompartment,
xpc::WrapperFactory::PrepareForWrapping);
js::SetPreserveWrapperCallback(mJSRuntime, PreserveWrapper);
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -750,16 +750,19 @@ public:
static void TraceBlackJS(JSTracer* trc, void* data);
static void TraceGrayJS(JSTracer* trc, void* data);
void TraceXPConnectRoots(JSTracer *trc);
void AddXPConnectRoots(nsCycleCollectionTraversalCallback& cb);
void UnmarkSkippableJSHolders();
static void GCCallback(JSRuntime *rt, JSGCStatus status);
+ static void GCSliceCallback(JSRuntime *rt,
+ js::GCProgress progress,
+ const js::GCDescription &desc);
static void FinalizeCallback(JSFreeOp *fop, JSFinalizeStatus status, JSBool isCompartmentGC);
inline void AddVariantRoot(XPCTraceableVariant* variant);
inline void AddWrappedJSRoot(nsXPCWrappedJS* wrappedJS);
inline void AddObjectHolderRoot(XPCJSObjectHolder* holder);
nsresult AddJSHolder(void* aHolder, nsScriptObjectTracer* aTracer);
nsresult RemoveJSHolder(void* aHolder);
@@ -901,16 +904,17 @@ private:
XPCRootSetElem *mObjectHolderRoots;
JSDHashTable mJSHolders;
PRLock *mWatchdogLock;
PRCondVar *mWatchdogWakeup;
PRThread *mWatchdogThread;
nsTArray<JSGCCallback> extraGCCallbacks;
bool mWatchdogHibernating;
PRTime mLastActiveTime; // -1 if active NOW
+ js::GCSliceCallback mPrevGCSliceCallback;
nsCOMPtr<nsIException> mPendingException;
nsCOMPtr<nsIExceptionManager> mExceptionManager;
bool mExceptionManagerNotAvailable;
friend class AutoLockWatchdog;
};
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -192,22 +192,27 @@ static const int kOOMAllocationSizeParam
static const char kAvailablePageFileParameter[] = "AvailablePageFile=";
static const int kAvailablePageFileParameterLen =
sizeof(kAvailablePageFileParameter)-1;
static const char kAvailablePhysicalMemoryParameter[] = "AvailablePhysicalMemory=";
static const int kAvailablePhysicalMemoryParameterLen =
sizeof(kAvailablePhysicalMemoryParameter)-1;
+static const char kIsGarbageCollectingParameter[] = "IsGarbageCollecting=";
+static const int kIsGarbageCollectingParameterLen =
+ sizeof(kIsGarbageCollectingParameter)-1;
+
// this holds additional data sent via the API
static Mutex* crashReporterAPILock;
static Mutex* notesFieldLock;
static AnnotationTable* crashReporterAPIData_Hash;
static nsCString* crashReporterAPIData = nsnull;
static nsCString* notesField = nsnull;
+static bool isGarbageCollecting;
// OOP crash reporting
static CrashGenerationServer* crashServer; // chrome process has this
# if defined(XP_WIN) || defined(XP_MACOSX)
// If crash reporting is disabled, we hand out this "null" pipe to the
// child process and don't attempt to connect to a parent server.
static const char kNullNotifyPipe[] = "-";
@@ -494,16 +499,22 @@ bool MinidumpCallback(const XP_CHAR* dum
WriteFile(hFile, "\n", 1, &nBytes, NULL);
if (timeSinceLastCrash != 0) {
WriteFile(hFile, kTimeSinceLastCrashParameter,
kTimeSinceLastCrashParameterLen, &nBytes, NULL);
WriteFile(hFile, timeSinceLastCrashString, timeSinceLastCrashStringLen,
&nBytes, NULL);
WriteFile(hFile, "\n", 1, &nBytes, NULL);
}
+ if (isGarbageCollecting) {
+ WriteFile(hFile, kIsGarbageCollectingParameter, kIsGarbageCollectingParameterLen,
+ &nBytes, NULL);
+ WriteFile(hFile, isGarbageCollecting ? "1" : "0", 1, &nBytes, NULL);
+ WriteFile(hFile, "\n", 1, &nBytes, NULL);
+ }
// Try to get some information about memory.
MEMORYSTATUSEX statex;
statex.dwLength = sizeof(statex);
if (GlobalMemoryStatusEx(&statex)) {
char buffer[128];
int bufferLen;
@@ -571,16 +582,21 @@ bool MinidumpCallback(const XP_CHAR* dum
ignored = sys_write(fd, "\n", 1);
if (timeSinceLastCrash != 0) {
ignored = sys_write(fd, kTimeSinceLastCrashParameter,
kTimeSinceLastCrashParameterLen);
ignored = sys_write(fd, timeSinceLastCrashString,
timeSinceLastCrashStringLen);
ignored = sys_write(fd, "\n", 1);
}
+ if (isGarbageCollecting) {
+ ignored = sys_write(fd, kIsGarbageCollectingParameter, kIsGarbageCollectingParameterLen);
+ ignored = sys_write(fd, isGarbageCollecting ? "1" : "0", 1);
+ ignored = sys_write(fd, "\n", 1);
+ }
if (oomAllocationSizeBufferLen) {
sys_write(fd, kOOMAllocationSizeParameter,
kOOMAllocationSizeParameterLen);
sys_write(fd, oomAllocationSizeBuffer, oomAllocationSizeBufferLen);
sys_write(fd, "\n", 1);
}
sys_close(fd);
}
@@ -1306,16 +1322,26 @@ nsresult AnnotateCrashReport(const nsACS
// now rebuild the file contents
crashReporterAPIData->Truncate(0);
crashReporterAPIData_Hash->EnumerateRead(EnumerateEntries,
crashReporterAPIData);
return NS_OK;
}
+nsresult SetGarbageCollecting(bool collecting)
+{
+ if (!GetEnabled())
+ return NS_ERROR_NOT_INITIALIZED;
+
+ isGarbageCollecting = collecting;
+
+ return NS_OK;
+}
+
nsresult AppendAppNotesToCrashReport(const nsACString& data)
{
if (!GetEnabled())
return NS_ERROR_NOT_INITIALIZED;
if (DoFindInReadable(data, NS_LITERAL_CSTRING("\0")))
return NS_ERROR_INVALID_ARG;
--- a/toolkit/crashreporter/nsExceptionHandler.h
+++ b/toolkit/crashreporter/nsExceptionHandler.h
@@ -35,16 +35,18 @@ nsresult SetMinidumpPath(const nsAString
// AnnotateCrashReport and AppendAppNotesToCrashReport may be called from any
// thread in a chrome process, but may only be called from the main thread in
// a content process.
nsresult AnnotateCrashReport(const nsACString& key, const nsACString& data);
nsresult AppendAppNotesToCrashReport(const nsACString& data);
+nsresult SetGarbageCollecting(bool collecting);
+
nsresult SetRestartArgs(int argc, char** argv);
nsresult SetupExtraData(nsIFile* aAppDataDirectory,
const nsACString& aBuildID);
// Registers an additional memory region to be included in the minidump
nsresult RegisterAppMemory(void* ptr, size_t length);
nsresult UnregisterAppMemory(void* ptr);