Bug 765065 - Annotation for crash reports: "Are we GCing?" (r=bsmedberg,a=lsblakk)
authorBill McCloskey <wmccloskey@mozilla.com>
Wed, 15 Aug 2012 10:47:51 -0700
changeset 102420 08e55f351f6d24fccbfdf2ff21362f4ab622378d
parent 102419 034e993e79f82b01b3e091c20c8822aa0c102df0
child 102421 4baf783709af4c45d2a8f89da5164dd8f6f24a80
push id1921
push userwmccloskey@mozilla.com
push dateThu, 16 Aug 2012 21:39:35 +0000
treeherdermozilla-aurora@08e55f351f6d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbsmedberg, lsblakk
bugs765065
milestone16.0a2
Bug 765065 - Annotation for crash reports: "Are we GCing?" (r=bsmedberg,a=lsblakk)
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/src/xpcprivate.h
toolkit/crashreporter/nsExceptionHandler.cpp
toolkit/crashreporter/nsExceptionHandler.h
--- 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);