Bug 1100498 - Report function names for addon exceptions. r=billm
☠☠ backed out by 8cdf0d931ed8 ☠ ☠
authorGabor Krizsanits <gkrizsanits@mozilla.com>
Mon, 15 Jun 2015 14:59:49 +0200
changeset 279637 2cba58b2e1b402536eeb4fdbc70bf5cfc14b9495
parent 279636 35e83cf9f460fe6487b7d1867c273e6f2195e0b1
child 279638 1d67d747b3eba11d8a9ba766abb19eb2ebcd0676
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-beta@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbillm
bugs1100498
milestone41.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1100498 - Report function names for addon exceptions. r=billm
js/src/jsexn.cpp
js/src/jsfriendapi.h
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -676,52 +676,93 @@ ErrorReport::~ErrorReport()
         size_t i = 0;
         while (ownedReport.messageArgs[i])
             js_free(const_cast<char16_t*>(ownedReport.messageArgs[i++]));
         js_free(ownedReport.messageArgs);
     }
     js_free(const_cast<char16_t*>(ownedReport.ucmessage));
 }
 
+void
+ErrorReport::ReportAddonExceptionToTelementry(JSContext* cx)
+{
+    MOZ_ASSERT(exnObject);
+    RootedObject unwrapped(cx, UncheckedUnwrap(exnObject));
+    MOZ_ASSERT(unwrapped, "UncheckedUnwrap failed?");
+
+    // There is not much we can report if the exception is not an ErrorObject, let's ignore those.
+    if (!unwrapped->is<ErrorObject>())
+        return;
+
+    Rooted<ErrorObject*> errObj(cx, &unwrapped->as<ErrorObject>());
+    RootedObject stack(cx, errObj->stack());
+
+    // Let's ignore TOP level exceptions. For regular add-ons those will not be reported anyway,
+    // for SDK based once it should not be a valid case either.
+    // At this point the frame stack is unwound but the exception object stored the stack so let's
+    // use that for getting the function name.
+    if (!stack)
+        return;
+
+    JSCompartment* comp = stack->compartment();
+    JSAddonId* addonId = comp->addonId;
+
+    // We only want to send the report if the scope that just have thrown belongs to an add-on.
+    // Let's check the compartment of the youngest function on the stack, to determine that.
+    if (!addonId)
+        return;
+
+    RootedString funnameString(cx);
+    JS::SavedFrameResult result = GetSavedFrameFunctionDisplayName(cx, stack, &funnameString);
+    // AccessDenied should never be the case here for add-ons but let's not risk it.
+    JSAutoByteString bytes;
+    const char* funname = nullptr;
+    funname = result == JS::SavedFrameResult::AccessDenied ? "unknown"
+                                                           : AtomToPrintableString(cx,
+                                                                                   &funnameString->asAtom(),
+                                                                                   &bytes);
+
+    UniqueChars addonIdChars(JS_EncodeString(cx, addonId));
+
+    const char* filename = nullptr;
+    if (reportp && reportp->filename) {
+        filename = strrchr(reportp->filename, '/');
+        if (filename)
+            filename++;
+    }
+    if (!filename) {
+        filename = "FILE_NOT_FOUND";
+    }
+    char histogramKey[64];
+    JS_snprintf(histogramKey, sizeof(histogramKey),
+                "%s %s %s %u",
+                addonIdChars.get(),
+                funname,
+                filename,
+                (reportp ? reportp->lineno : 0) );
+    cx->runtime()->addTelemetry(JS_TELEMETRY_ADDON_EXCEPTIONS, 1, histogramKey);
+}
+
 bool
 ErrorReport::init(JSContext* cx, HandleValue exn)
 {
     MOZ_ASSERT(!cx->isExceptionPending());
 
-    /*
-     * Because ToString below could error and an exception object could become
-     * unrooted, we must root our exception object, if any.
-     */
     if (exn.isObject()) {
+        // Because ToString below could error and an exception object could become
+        // unrooted, we must root our exception object, if any.
         exnObject = &exn.toObject();
         reportp = ErrorFromException(cx, exnObject);
 
-        JSCompartment* comp = exnObject->compartment();
-        JSAddonId* addonId = comp->addonId;
-        if (addonId) {
-            UniqueChars addonIdChars(JS_EncodeString(cx, addonId));
+        // Let's see if the exception is from add-on code, if so, it should be reported
+        // to telementry.
+        ReportAddonExceptionToTelementry(cx);
+    }
 
-            const char* filename = nullptr;
-            if (reportp && reportp->filename) {
-                filename = strrchr(reportp->filename, '/');
-                if (filename)
-                    filename++;
-            }
-            if (!filename) {
-                filename = "FILE_NOT_FOUND";
-            }
-            char histogramKey[64];
-            JS_snprintf(histogramKey, sizeof(histogramKey),
-                        "%s %s %u",
-                        addonIdChars.get(),
-                        filename,
-                        (reportp ? reportp->lineno : 0) );
-            cx->runtime()->addTelemetry(JS_TELEMETRY_ADDON_EXCEPTIONS, 1, histogramKey);
-        }
-    }
+
     // Be careful not to invoke ToString if we've already successfully extracted
     // an error report, since the exception might be wrapped in a security
     // wrapper, and ToString-ing it might throw.
     if (reportp)
         str = ErrorReportToString(cx, reportp);
     else
         str = ToString<CanGC>(cx, exn);
 
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -1391,16 +1391,19 @@ struct MOZ_STACK_CLASS JS_FRIEND_API(Err
     // but fills in an ErrorReport instead of reporting it.  Uses varargs to
     // make it simpler to call js::ExpandErrorArgumentsVA.
     //
     // Returns false if we fail to actually populate the ErrorReport
     // for some reason (probably out of memory).
     bool populateUncaughtExceptionReport(JSContext* cx, ...);
     bool populateUncaughtExceptionReportVA(JSContext* cx, va_list ap);
 
+    // Reports exceptions from add-on scopes to telementry.
+    void ReportAddonExceptionToTelementry(JSContext* cx);
+
     // We may have a provided JSErrorReport, so need a way to represent that.
     JSErrorReport* reportp;
 
     // And we may have a message.
     const char* message_;
 
     // Or we may need to synthesize a JSErrorReport one of our own.
     JSErrorReport ownedReport;