Bug 1515214 - Add CallbackObject::GetDescription and TimeoutHandler::GetDescription. r=bzbarsky
authorMarkus Stange <mstange@themasta.com>
Wed, 10 Jul 2019 20:48:14 +0000
changeset 482276 a6ad66ea6ddd7dd14eafc95f5128ee15c40cae18
parent 482275 5d2091f9acb34a4fd0733e01e5ce005e643c13b8
child 482277 835a18c6213d447af34dfa1fa19bcb8ae6dade86
push id89670
push usermstange@themasta.com
push dateWed, 10 Jul 2019 20:49:14 +0000
treeherderautoland@442fa46b63e9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky
bugs1515214
milestone70.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 1515214 - Add CallbackObject::GetDescription and TimeoutHandler::GetDescription. r=bzbarsky This allows us to create profiler markers whose description contains the name of the function and its file / line number. This allows the profiler UI to match up setTimeout callbacks for multiple instances of the same page load, in order to create meaningful profile comparisons based on markers. Differential Revision: https://phabricator.services.mozilla.com/D19192
dom/bindings/CallbackObject.cpp
dom/bindings/CallbackObject.h
--- a/dom/bindings/CallbackObject.cpp
+++ b/dom/bindings/CallbackObject.cpp
@@ -126,16 +126,74 @@ JSObject* CallbackObject::Callback(JSCon
   if (!callback) {
     callback = JS_NewDeadWrapper(aCx);
   }
 
   MOZ_DIAGNOSTIC_ASSERT(callback);
   return callback;
 }
 
+void CallbackObject::GetDescription(nsACString& aOutString) {
+  JSObject* wrappedCallback = CallbackOrNull();
+  if (!wrappedCallback) {
+    aOutString.Append("<callback from a nuked compartment>");
+    return;
+  }
+
+  JS::Rooted<JSObject*> unwrappedCallback(
+      RootingCx(), js::CheckedUnwrapStatic(wrappedCallback));
+  if (!unwrappedCallback) {
+    aOutString.Append("<not a function>");
+    return;
+  }
+
+  AutoJSAPI jsapi;
+  jsapi.Init();
+  JSContext* cx = jsapi.cx();
+
+  JS::RootedObject rootedCallback(cx, unwrappedCallback);
+  JSAutoRealm ar(cx, rootedCallback);
+
+  JS::Rooted<JSFunction*> rootedFunction(cx,
+                                         JS_GetObjectFunction(rootedCallback));
+  if (!rootedFunction) {
+    aOutString.Append("<not a function>");
+    return;
+  }
+
+  JS::Rooted<JSString*> displayId(cx, JS_GetFunctionDisplayId(rootedFunction));
+  if (displayId) {
+    nsAutoJSString funcNameStr;
+    if (funcNameStr.init(cx, displayId)) {
+      if (funcNameStr.IsEmpty()) {
+        aOutString.Append("<empty name>");
+      } else {
+        AppendUTF16toUTF8(funcNameStr, aOutString);
+      }
+    } else {
+      aOutString.Append("<function name string failed to materialize>");
+      jsapi.ClearException();
+    }
+  } else {
+    aOutString.Append("<anonymous>");
+  }
+
+  JS::Rooted<JSScript*> rootedScript(cx,
+                                     JS_GetFunctionScript(cx, rootedFunction));
+  if (!rootedScript) {
+    return;
+  }
+
+  aOutString.Append(" (");
+  aOutString.Append(JS_GetScriptFilename(rootedScript));
+  aOutString.Append(":");
+  aOutString.AppendInt(JS_GetScriptBaseLineNumber(cx, rootedScript));
+  aOutString.Append(")");
+}
+
 CallbackObject::CallSetup::CallSetup(CallbackObject* aCallback,
                                      ErrorResult& aRv,
                                      const char* aExecutionReason,
                                      ExceptionHandling aExceptionHandling,
                                      JS::Realm* aRealm,
                                      bool aIsJSImplementedWebIDL)
     : mCx(nullptr),
       mRealm(aRealm),
--- a/dom/bindings/CallbackObject.h
+++ b/dom/bindings/CallbackObject.h
@@ -157,16 +157,23 @@ class CallbackObject : public nsISupport
     // report it.
     eRethrowContentExceptions,
     // Throw exceptions to the caller code, unless the caller realm is
     // provided, the exception is not a DOMException from the caller
     // realm, and the caller realm does not subsume our unwrapped callback.
     eRethrowExceptions
   };
 
+  // Append a UTF-8 string to aOutString that describes the callback function,
+  // for use in logging or profiler markers.
+  // The string contains the function name and its source location, if
+  // available, in the following format:
+  // "<functionName> (<sourceURL>:<lineNumber>)"
+  void GetDescription(nsACString& aOutString);
+
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
     return aMallocSizeOf(this);
   }
 
   // Used for cycle collection optimization.  Should return true only if all our
   // outgoing edges are to known-live objects.  In that case, there's no point
   // traversing our edges to them, because we know they can't be collected
   // anyway.