Bug 957742 - Logging for CPOWs (r=mrbkap)
authorTom Schuster <evilpies@gmail.com>
Wed, 28 May 2014 18:05:22 -0700
changeset 204748 38d3672ab1923988b1225913134bbdd6a73f9390
parent 204747 9bb48a3dc7e7b9ed439f297b6851cab04f0e7d1b
child 204749 8f40db8873daa3158c09b911bc43917b2764480b
push id3741
push userasasaki@mozilla.com
push dateMon, 21 Jul 2014 20:25:18 +0000
treeherdermozilla-beta@4d6f46f5af68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmrbkap
bugs957742
milestone32.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 957742 - Logging for CPOWs (r=mrbkap)
js/ipc/JavaScriptChild.h
js/ipc/JavaScriptLogging.h
js/ipc/JavaScriptParent.h
js/ipc/JavaScriptShared.cpp
js/ipc/JavaScriptShared.h
js/ipc/WrapperAnswer.cpp
js/ipc/WrapperOwner.cpp
--- a/js/ipc/JavaScriptChild.h
+++ b/js/ipc/JavaScriptChild.h
@@ -20,16 +20,19 @@ class JavaScriptChild : public JavaScrip
     JavaScriptChild(JSRuntime *rt);
     virtual ~JavaScriptChild();
 
     bool init();
     void finalize(JSFreeOp *fop);
 
     void drop(JSObject *obj);
 
+  protected:
+    virtual bool isParent() { return false; }
+
   private:
     bool fail(JSContext *cx, ReturnStatus *rs);
     bool ok(ReturnStatus *rs);
 };
 
 } // mozilla
 } // jsipc
 
new file mode 100644
--- /dev/null
+++ b/js/ipc/JavaScriptLogging.h
@@ -0,0 +1,194 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=80:
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_jsipc_JavaScriptLogging__
+#define mozilla_jsipc_JavaScriptLogging__
+
+#include "nsString.h"
+#include "nsPrintfCString.h"
+#include "js/OldDebugAPI.h"
+
+namespace mozilla {
+namespace jsipc {
+
+#define LOG(...)						               \
+    PR_BEGIN_MACRO                                                             \
+    if (LoggingEnabled()) {                                                    \
+	Logging log(this, cx);					               \
+	log.print(__VA_ARGS__);					               \
+    }                                                                          \
+    PR_END_MACRO
+
+#define LOG_STACK()		                                               \
+    PR_BEGIN_MACRO                                                             \
+    if (StackLoggingEnabled()) {                                               \
+        js_DumpBacktrace(cx);	                                               \
+    }                                                                          \
+    PR_END_MACRO
+
+struct ReceiverObj
+{
+    ObjectId id;
+    ReceiverObj(ObjectId id) : id(id) {}
+};
+
+struct InVariant
+{
+    JSVariant variant;
+    InVariant(const JSVariant &variant) : variant(variant) {}
+};
+
+struct OutVariant
+{
+    JSVariant variant;
+    OutVariant(const JSVariant &variant) : variant(variant) {}
+};
+
+class Logging
+{
+  public:
+    Logging(JavaScriptShared *shared, JSContext *cx) : shared(shared), cx(cx) {}
+
+    void print(const nsCString &str) {
+        const char *side = shared->isParent() ? "from child" : "from parent";
+        printf("CPOW %s: %s\n", side, str.get());
+    }
+
+    void print(const char *str) {
+        print(nsCString(str));
+    }
+    template<typename T1>
+    void print(const char *fmt, const T1 &a1) {
+        nsAutoCString tmp1;
+        format(a1, tmp1);
+        print(nsPrintfCString(fmt, tmp1.get()));
+    }
+    template<typename T1, typename T2>
+    void print(const char *fmt, const T1 &a1, const T2 &a2) {
+        nsAutoCString tmp1;
+        nsAutoCString tmp2;
+        format(a1, tmp1);
+        format(a2, tmp2);
+        print(nsPrintfCString(fmt, tmp1.get(), tmp2.get()));
+    }
+    template<typename T1, typename T2, typename T3>
+    void print(const char *fmt, const T1 &a1, const T2 &a2, const T3 &a3) {
+        nsAutoCString tmp1;
+        nsAutoCString tmp2;
+        nsAutoCString tmp3;
+        format(a1, tmp1);
+        format(a2, tmp2);
+        format(a3, tmp3);
+        print(nsPrintfCString(fmt, tmp1.get(), tmp2.get(), tmp3.get()));
+    }
+
+    void format(const nsString &str, nsCString &out) {
+        out = NS_ConvertUTF16toUTF8(str);
+    }
+
+    void formatObject(bool incoming, bool local, ObjectId id, nsCString &out) {
+        const char *side, *objDesc;
+
+        if (local == incoming) {
+            RootedObject obj(cx);
+            obj = shared->findObjectById(id);
+            if (obj) {
+                JSAutoCompartment ac(cx, obj);
+                objDesc = js_ObjectClassName(cx, obj);
+            } else {
+                objDesc = "<dead object>";
+            }
+
+            side = shared->isParent() ? "parent" : "child";
+        } else {
+            objDesc = "<cpow>";
+            side = shared->isParent() ? "child" : "parent";
+        }
+
+        out = nsPrintfCString("<%s %s:%d>", side, objDesc, id);
+    }
+
+
+    void format(const ReceiverObj &obj, nsCString &out) {
+        formatObject(true, true, obj.id, out);
+    }
+
+    void format(const nsTArray<JSParam> &values, nsCString &out) {
+        nsAutoCString tmp;
+        out.Truncate();
+        for (size_t i = 0; i < values.Length(); i++) {
+            if (i)
+                out.AppendLiteral(", ");
+            if (values[i].type() == JSParam::Tvoid_t) {
+                out.AppendLiteral("<void>");
+            } else {
+                format(InVariant(values[i].get_JSVariant()), tmp);
+                out += tmp;
+            }
+        }
+    }
+
+    void format(const InVariant &value, nsCString &out) {
+        format(true, value.variant, out);
+    }
+
+    void format(const OutVariant &value, nsCString &out) {
+        format(false, value.variant, out);
+    }
+
+    void format(bool incoming, const JSVariant &value, nsCString &out) {
+        switch (value.type()) {
+          case JSVariant::TUndefinedVariant: {
+              out = "undefined";
+              break;
+          }
+          case JSVariant::TNullVariant: {
+              out = "null";
+              break;
+          }
+          case JSVariant::TnsString: {
+              nsAutoCString tmp;
+              format(value.get_nsString(), tmp);
+              out = nsPrintfCString("\"%s\"", tmp.get());
+              break;
+          }
+          case JSVariant::TObjectVariant: {
+              const ObjectVariant &ovar = value.get_ObjectVariant();
+              if (ovar.type() == ObjectVariant::TLocalObject)
+                  formatObject(incoming, true, ovar.get_LocalObject().id(), out);
+              else
+                  formatObject(incoming, false, ovar.get_RemoteObject().id(), out);
+              break;
+          }
+          case JSVariant::Tdouble: {
+              out = nsPrintfCString("%.0f", value.get_double());
+              break;
+          }
+          case JSVariant::Tbool: {
+              out = value.get_bool() ? "true" : "false";
+              break;
+          }
+          case JSVariant::TJSIID: {
+              out = "<JSIID>";
+              break;
+          }
+          default: {
+              out = "<JSIID>";
+              break;
+          }
+        }
+    }
+
+  private:
+    JavaScriptShared *shared;
+    JSContext *cx;
+};
+
+}
+}
+
+#endif
--- a/js/ipc/JavaScriptParent.h
+++ b/js/ipc/JavaScriptParent.h
@@ -22,15 +22,18 @@ class JavaScriptParent : public JavaScri
 
     bool init();
     void trace(JSTracer *trc);
 
     void drop(JSObject *obj);
 
     mozilla::ipc::IProtocol*
     CloneProtocol(Channel* aChannel, ProtocolCloneContext* aCtx) MOZ_OVERRIDE;
+
+  protected:
+    virtual bool isParent() { return true; }
 };
 
 } // jsipc
 } // mozilla
 
 #endif // mozilla_jsipc_JavaScriptWrapper_h__
 
--- a/js/ipc/JavaScriptShared.cpp
+++ b/js/ipc/JavaScriptShared.cpp
@@ -5,16 +5,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "JavaScriptShared.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/TabChild.h"
 #include "jsfriendapi.h"
 #include "xpcprivate.h"
+#include "mozilla/Preferences.h"
 
 using namespace js;
 using namespace JS;
 using namespace mozilla;
 using namespace mozilla::jsipc;
 
 IdToObjectMap::IdToObjectMap()
   : table_(SystemAllocPolicy())
@@ -139,21 +140,32 @@ ObjectToIdMap::keyMarkCallback(JSTracer 
 }
 
 void
 ObjectToIdMap::remove(JSObject *obj)
 {
     table_->remove(obj);
 }
 
+bool JavaScriptShared::sLoggingInitialized;
+bool JavaScriptShared::sLoggingEnabled;
+bool JavaScriptShared::sStackLoggingEnabled;
+
 JavaScriptShared::JavaScriptShared(JSRuntime *rt)
   : rt_(rt),
     refcount_(1),
     lastId_(0)
 {
+    if (!sLoggingInitialized) {
+        sLoggingInitialized = true;
+        Preferences::AddBoolVarCache(&sLoggingEnabled,
+                                     "dom.ipc.cpows.log.enabled", false);
+        Preferences::AddBoolVarCache(&sStackLoggingEnabled,
+                                     "dom.ipc.cpows.log.stack", false);
+    }
 }
 
 bool
 JavaScriptShared::init()
 {
     if (!objects_.init())
         return false;
     if (!cpows_.init())
--- a/js/ipc/JavaScriptShared.h
+++ b/js/ipc/JavaScriptShared.h
@@ -76,16 +76,18 @@ class ObjectToIdMap
     void remove(JSObject *obj);
 
   private:
     static void keyMarkCallback(JSTracer *trc, JSObject *key, void *data);
 
     Table *table_;
 };
 
+class Logging;
+
 class JavaScriptShared
 {
   public:
     JavaScriptShared(JSRuntime *rt);
     virtual ~JavaScriptShared() {}
 
     bool init();
 
@@ -119,25 +121,36 @@ class JavaScriptShared
     JSObject *findCPOWById(uint32_t objId) {
         return cpows_.find(objId);
     }
     JSObject *findObjectById(uint32_t objId) {
         return objects_.find(objId);
     }
     JSObject *findObjectById(JSContext *cx, uint32_t objId);
 
+    static bool LoggingEnabled() { return sLoggingEnabled; }
+    static bool StackLoggingEnabled() { return sStackLoggingEnabled; }
+
+    friend class Logging;
+
+    virtual bool isParent() = 0;
+
   protected:
     JSRuntime *rt_;
     uintptr_t refcount_;
 
     IdToObjectMap objects_;
     IdToObjectMap cpows_;
 
     ObjectId lastId_;
     ObjectToIdMap objectIds_;
+
+    static bool sLoggingInitialized;
+    static bool sLoggingEnabled;
+    static bool sStackLoggingEnabled;
 };
 
 // Use 47 at most, to be safe, since jsval privates are encoded as doubles.
 static const uint64_t MAX_CPOW_IDS = (uint64_t(1) << 47) - 1;
 
 } // namespace jsipc
 } // namespace mozilla
 
--- a/js/ipc/WrapperAnswer.cpp
+++ b/js/ipc/WrapperAnswer.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=4 sw=4 et tw=80:
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WrapperAnswer.h"
+#include "JavaScriptLogging.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "nsContentUtils.h"
 #include "xpcprivate.h"
 #include "jsfriendapi.h"
 #include "nsCxPusher.h"
 
 using namespace JS;
@@ -66,16 +67,18 @@ WrapperAnswer::AnswerPreventExtensions(c
     RootedObject obj(cx, findObjectById(cx, objId));
     if (!obj)
         return fail(cx, rs);
 
     JSAutoCompartment comp(cx, obj);
     if (!JS_PreventExtensions(cx, obj))
         return fail(cx, rs);
 
+    LOG("%s.preventExtensions()", ReceiverObj(objId));
+
     return ok(rs);
 }
 
 static void
 EmptyDesc(PPropertyDescriptor *desc)
 {
     desc->obj() = LocalObject(0);
     desc->attrs() = 0;
@@ -94,16 +97,18 @@ WrapperAnswer::AnswerGetPropertyDescript
     EmptyDesc(out);
 
     RootedObject obj(cx, findObjectById(cx, objId));
     if (!obj)
         return fail(cx, rs);
 
     JSAutoCompartment comp(cx, obj);
 
+    LOG("%s.getPropertyDescriptor(%s)", ReceiverObj(objId), id);
+
     RootedId internedId(cx);
     if (!convertGeckoStringToId(cx, id, &internedId))
         return fail(cx, rs);
 
     Rooted<JSPropertyDescriptor> desc(cx);
     if (!JS_GetPropertyDescriptorById(cx, obj, internedId, &desc))
         return fail(cx, rs);
 
@@ -126,16 +131,18 @@ WrapperAnswer::AnswerGetOwnPropertyDescr
     EmptyDesc(out);
 
     RootedObject obj(cx, findObjectById(cx, objId));
     if (!obj)
         return fail(cx, rs);
 
     JSAutoCompartment comp(cx, obj);
 
+    LOG("%s.getOwnPropertyDescriptor(%s)", ReceiverObj(objId), id);
+
     RootedId internedId(cx);
     if (!convertGeckoStringToId(cx, id, &internedId))
         return fail(cx, rs);
 
     Rooted<JSPropertyDescriptor> desc(cx);
     if (!JS_GetPropertyDescriptorById(cx, obj, internedId, &desc))
         return fail(cx, rs);
 
@@ -156,16 +163,18 @@ WrapperAnswer::AnswerDefineProperty(cons
     JSAutoRequest request(cx);
 
     RootedObject obj(cx, findObjectById(cx, objId));
     if (!obj)
         return fail(cx, rs);
 
     JSAutoCompartment comp(cx, obj);
 
+    LOG("define %s[%s]", ReceiverObj(objId), id);
+
     RootedId internedId(cx);
     if (!convertGeckoStringToId(cx, id, &internedId))
         return fail(cx, rs);
 
     Rooted<JSPropertyDescriptor> desc(cx);
     if (!toDescriptor(cx, descriptor, &desc))
         return fail(cx, rs);
 
@@ -194,16 +203,18 @@ WrapperAnswer::AnswerDelete(const Object
     *success = false;
 
     RootedObject obj(cx, findObjectById(cx, objId));
     if (!obj)
         return fail(cx, rs);
 
     JSAutoCompartment comp(cx, obj);
 
+    LOG("delete %s[%s]", ReceiverObj(objId), id);
+
     RootedId internedId(cx);
     if (!convertGeckoStringToId(cx, id, &internedId))
         return fail(cx, rs);
 
     if (!JS_DeletePropertyById2(cx, obj, internedId, success))
         return fail(cx, rs);
 
     return ok(rs);
@@ -218,16 +229,18 @@ WrapperAnswer::AnswerHas(const ObjectId 
     *bp = false;
 
     RootedObject obj(cx, findObjectById(cx, objId));
     if (!obj)
         return fail(cx, rs);
 
     JSAutoCompartment comp(cx, obj);
 
+    LOG("%s.has(%s)", ReceiverObj(objId), id);
+
     RootedId internedId(cx);
     if (!convertGeckoStringToId(cx, id, &internedId))
         return fail(cx, rs);
 
     bool found;
     if (!JS_HasPropertyById(cx, obj, internedId, &found))
         return fail(cx, rs);
     *bp = !!found;
@@ -244,16 +257,18 @@ WrapperAnswer::AnswerHasOwn(const Object
     *bp = false;
 
     RootedObject obj(cx, findObjectById(cx, objId));
     if (!obj)
         return fail(cx, rs);
 
     JSAutoCompartment comp(cx, obj);
 
+    LOG("%s.hasOwn(%s)", ReceiverObj(objId), id);
+
     RootedId internedId(cx);
     if (!convertGeckoStringToId(cx, id, &internedId))
         return fail(cx, rs);
 
     Rooted<JSPropertyDescriptor> desc(cx);
     if (!JS_GetPropertyDescriptorById(cx, obj, internedId, &desc))
         return fail(cx, rs);
     *bp = (desc.object() == obj);
@@ -288,16 +303,18 @@ WrapperAnswer::AnswerGet(const ObjectId 
 
     JS::RootedValue val(cx);
     if (!JS_ForwardGetPropertyTo(cx, obj, internedId, receiver, &val))
         return fail(cx, rs);
 
     if (!toVariant(cx, val, result))
         return fail(cx, rs);
 
+    LOG("get %s.%s = %s", ReceiverObj(objId), id, OutVariant(*result));
+
     return ok(rs);
 }
 
 bool
 WrapperAnswer::AnswerSet(const ObjectId &objId, const ObjectId &receiverId, const nsString &id,
 			 const bool &strict, const JSVariant &value, ReturnStatus *rs,
 			 JSVariant *result)
 {
@@ -313,16 +330,18 @@ WrapperAnswer::AnswerSet(const ObjectId 
         return fail(cx, rs);
 
     RootedObject receiver(cx, findObjectById(cx, receiverId));
     if (!receiver)
         return fail(cx, rs);
 
     JSAutoCompartment comp(cx, obj);
 
+    LOG("set %s[%s] = %s", ReceiverObj(objId), id, InVariant(value));
+
     RootedId internedId(cx);
     if (!convertGeckoStringToId(cx, id, &internedId))
         return fail(cx, rs);
 
     MOZ_ASSERT(obj == receiver);
 
     RootedValue val(cx);
     if (!fromVariant(cx, value, &val))
@@ -346,16 +365,18 @@ WrapperAnswer::AnswerIsExtensible(const 
     *result = false;
 
     RootedObject obj(cx, findObjectById(cx, objId));
     if (!obj)
         return fail(cx, rs);
 
     JSAutoCompartment comp(cx, obj);
 
+    LOG("%s.isExtensible()", ReceiverObj(objId));
+
     bool extensible;
     if (!JS_IsExtensible(cx, obj, &extensible))
         return fail(cx, rs);
 
     *result = !!extensible;
     return ok(rs);
 }
 
@@ -448,16 +469,18 @@ WrapperAnswer::AnswerCall(const ObjectId
     // treat this as the outparam never having been set.
     for (size_t i = 0; i < vals.length(); i++) {
         JSVariant variant;
         if (!toVariant(cx, vals[i], &variant))
             return fail(cx, rs);
         outparams->ReplaceElementAt(i, JSParam(variant));
     }
 
+    LOG("%s.call(%s) = %s", ReceiverObj(objId), argv, OutVariant(*result));
+
     return ok(rs);
 }
 
 bool
 WrapperAnswer::AnswerObjectClassIs(const ObjectId &objId, const uint32_t &classValue,
 				   bool *result)
 {
     AutoSafeJSContext cx;
@@ -467,16 +490,18 @@ WrapperAnswer::AnswerObjectClassIs(const
     if (!obj) {
         // This is very unfortunate, but we have no choice.
         *result = false;
         return true;
     }
 
     JSAutoCompartment comp(cx, obj);
 
+    LOG("%s.objectClassIs()", ReceiverObj(objId));
+
     *result = js_ObjectClassIs(cx, obj, (js::ESClassValue)classValue);
     return true;
 }
 
 bool
 WrapperAnswer::AnswerClassName(const ObjectId &objId, nsString *name)
 {
     AutoSafeJSContext cx;
@@ -485,16 +510,18 @@ WrapperAnswer::AnswerClassName(const Obj
     RootedObject obj(cx, findObjectById(cx, objId));
     if (!obj) {
         // This is very unfortunate, but we have no choice.
         return "<dead CPOW>";
     }
 
     JSAutoCompartment comp(cx, obj);
 
+    LOG("%s.className()", ReceiverObj(objId));
+
     *name = NS_ConvertASCIItoUTF16(js_ObjectClassName(cx, obj));
     return true;
 }
 
 bool
 WrapperAnswer::AnswerGetPropertyNames(const ObjectId &objId, const uint32_t &flags,
 				      ReturnStatus *rs, nsTArray<nsString> *names)
 {
@@ -502,16 +529,18 @@ WrapperAnswer::AnswerGetPropertyNames(co
     JSAutoRequest request(cx);
 
     RootedObject obj(cx, findObjectById(cx, objId));
     if (!obj)
         return fail(cx, rs);
 
     JSAutoCompartment comp(cx, obj);
 
+    LOG("%s.getPropertyNames()", ReceiverObj(objId));
+
     AutoIdVector props(cx);
     if (!js::GetPropertyNames(cx, obj, flags, &props))
         return fail(cx, rs);
 
     for (size_t i = 0; i < props.length(); i++) {
         nsString name;
         if (!convertIdToGeckoString(cx, props[i], &name))
             return fail(cx, rs);
@@ -532,16 +561,18 @@ WrapperAnswer::AnswerInstanceOf(const Ob
     *instanceof = false;
 
     RootedObject obj(cx, findObjectById(cx, objId));
     if (!obj)
         return fail(cx, rs);
 
     JSAutoCompartment comp(cx, obj);
 
+    LOG("%s.instanceOf()", ReceiverObj(objId));
+
     nsID nsiid;
     ConvertID(iid, &nsiid);
 
     nsresult rv = xpc::HasInstance(cx, obj, &nsiid, instanceof);
     if (rv != NS_OK)
         return fail(cx, rs);
 
     return ok(rs);
@@ -558,16 +589,18 @@ WrapperAnswer::AnswerDOMInstanceOf(const
     *instanceof = false;
 
     RootedObject obj(cx, findObjectById(cx, objId));
     if (!obj)
         return fail(cx, rs);
 
     JSAutoCompartment comp(cx, obj);
 
+    LOG("%s.domInstanceOf()", ReceiverObj(objId));
+
     bool tmp;
     if (!mozilla::dom::InterfaceHasInstance(cx, prototypeID, depth, obj, &tmp))
         return fail(cx, rs);
     *instanceof = tmp;
 
     return ok(rs);
 }
 
--- a/js/ipc/WrapperOwner.cpp
+++ b/js/ipc/WrapperOwner.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=4 sw=4 et tw=80:
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WrapperOwner.h"
+#include "JavaScriptLogging.h"
 #include "mozilla/unused.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "jsfriendapi.h"
 #include "xpcprivate.h"
 
 using namespace js;
 using namespace JS;
 using namespace mozilla;
@@ -106,16 +107,18 @@ bool
 WrapperOwner::preventExtensions(JSContext *cx, HandleObject proxy)
 {
     ObjectId objId = idOf(proxy);
 
     ReturnStatus status;
     if (!CallPreventExtensions(objId, &status))
         return ipcfail(cx);
 
+    LOG_STACK();
+
     return ok(cx, status);
 }
 
 bool
 CPOWProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                         MutableHandle<JSPropertyDescriptor> desc)
 {
     FORWARD(getPropertyDescriptor, (cx, proxy, id, desc));
@@ -130,16 +133,19 @@ WrapperOwner::getPropertyDescriptor(JSCo
     nsString idstr;
     if (!convertIdToGeckoString(cx, id, &idstr))
         return false;
 
     ReturnStatus status;
     PPropertyDescriptor result;
     if (!CallGetPropertyDescriptor(objId, idstr, &status, &result))
         return ipcfail(cx);
+
+    LOG_STACK();
+
     if (!ok(cx, status))
         return false;
 
     return toDescriptor(cx, result, desc);
 }
 
 bool
 CPOWProxyHandler::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
@@ -157,16 +163,19 @@ WrapperOwner::getOwnPropertyDescriptor(J
     nsString idstr;
     if (!convertIdToGeckoString(cx, id, &idstr))
         return false;
 
     ReturnStatus status;
     PPropertyDescriptor result;
     if (!CallGetOwnPropertyDescriptor(objId, idstr, &status, &result))
         return ipcfail(cx);
+
+    LOG_STACK();
+
     if (!ok(cx, status))
         return false;
 
     return toDescriptor(cx, result, desc);
 }
 
 bool
 CPOWProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
@@ -188,16 +197,18 @@ WrapperOwner::defineProperty(JSContext *
     PPropertyDescriptor descriptor;
     if (!fromDescriptor(cx, desc, &descriptor))
         return false;
 
     ReturnStatus status;
     if (!CallDefineProperty(objId, idstr, descriptor, &status))
         return ipcfail(cx);
 
+    LOG_STACK();
+
     return ok(cx, status);
 }
 
 bool
 CPOWProxyHandler::getOwnPropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props)
 {
     FORWARD(getOwnPropertyNames, (cx, proxy, props));
 }
@@ -222,16 +233,18 @@ WrapperOwner::delete_(JSContext *cx, Han
     nsString idstr;
     if (!convertIdToGeckoString(cx, id, &idstr))
         return false;
 
     ReturnStatus status;
     if (!CallDelete(objId, idstr, &status, bp))
         return ipcfail(cx);
 
+    LOG_STACK();
+
     return ok(cx, status);
 }
 
 bool
 CPOWProxyHandler::enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props)
 {
     FORWARD(enumerate, (cx, proxy, props));
 }
@@ -256,16 +269,18 @@ WrapperOwner::has(JSContext *cx, HandleO
     nsString idstr;
     if (!convertIdToGeckoString(cx, id, &idstr))
         return false;
 
     ReturnStatus status;
     if (!CallHas(objId, idstr, &status, bp))
         return ipcfail(cx);
 
+    LOG_STACK();
+
     return ok(cx, status);
 }
 
 bool
 CPOWProxyHandler::hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
 {
     FORWARD(hasOwn, (cx, proxy, id, bp));
 }
@@ -278,16 +293,18 @@ WrapperOwner::hasOwn(JSContext *cx, Hand
     nsString idstr;
     if (!convertIdToGeckoString(cx, id, &idstr))
         return false;
 
     ReturnStatus status;
     if (!CallHasOwn(objId, idstr, &status, bp))
         return ipcfail(cx);
 
+    LOG_STACK();
+
     return !!ok(cx, status);
 }
 
 bool
 CPOWProxyHandler::get(JSContext *cx, HandleObject proxy, HandleObject receiver,
                       HandleId id, MutableHandleValue vp)
 {
     FORWARD(get, (cx, proxy, receiver, id, vp));
@@ -304,16 +321,18 @@ WrapperOwner::get(JSContext *cx, HandleO
     if (!convertIdToGeckoString(cx, id, &idstr))
         return false;
 
     JSVariant val;
     ReturnStatus status;
     if (!CallGet(objId, receiverId, idstr, &status, &val))
         return ipcfail(cx);
 
+    LOG_STACK();
+
     if (!ok(cx, status))
         return false;
 
     return fromVariant(cx, val, vp);
 }
 
 bool
 CPOWProxyHandler::set(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiver,
@@ -337,16 +356,18 @@ WrapperOwner::set(JSContext *cx, JS::Han
     if (!toVariant(cx, vp, &val))
         return false;
 
     ReturnStatus status;
     JSVariant result;
     if (!CallSet(objId, receiverId, idstr, strict, val, &status, &result))
         return ipcfail(cx);
 
+    LOG_STACK();
+
     if (!ok(cx, status))
         return false;
 
     return fromVariant(cx, result, vp);
 }
 
 bool
 CPOWProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props)
@@ -370,16 +391,18 @@ bool
 WrapperOwner::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible)
 {
     ObjectId objId = idOf(proxy);
 
     ReturnStatus status;
     if (!CallIsExtensible(objId, &status, extensible))
         return ipcfail(cx);
 
+    LOG_STACK();
+
     return ok(cx, status);
 }
 
 bool
 CPOWProxyHandler::call(JSContext *cx, HandleObject proxy, const CallArgs &args)
 {
     FORWARD(call, (cx, proxy, args));
 }
@@ -419,16 +442,19 @@ WrapperOwner::call(JSContext *cx, Handle
         vals.AppendElement(JSParam(val));
     }
 
     JSVariant result;
     ReturnStatus status;
     InfallibleTArray<JSParam> outparams;
     if (!CallCall(objId, vals, &status, &result, &outparams))
         return ipcfail(cx);
+
+    LOG_STACK();
+
     if (!ok(cx, status))
         return false;
 
     if (outparams.Length() != outobjects.length())
         return ipcfail(cx);
 
     RootedObject obj(cx);
     for (size_t i = 0; i < outparams.Length(); i++) {
@@ -465,16 +491,18 @@ WrapperOwner::objectClassIs(JSContext *c
     ObjectId objId = idOf(proxy);
 
     // This function is assumed infallible, so we just return false if the IPC
     // channel fails.
     bool result;
     if (!CallObjectClassIs(objId, classValue, &result))
         return false;
 
+    LOG_STACK();
+
     return result;
 }
 
 const char *
 CPOWProxyHandler::className(JSContext *cx, HandleObject proxy)
 {
     WrapperOwner *parent = OwnerOf(proxy);
     if (!parent->active())
@@ -486,16 +514,18 @@ const char *
 WrapperOwner::className(JSContext *cx, HandleObject proxy)
 {
     ObjectId objId = idOf(proxy);
 
     nsString name;
     if (!CallClassName(objId, &name))
         return "<error>";
 
+    LOG_STACK();
+
     return ToNewCString(name);
 }
 
 void
 CPOWProxyHandler::finalize(JSFreeOp *fop, JSObject *proxy)
 {
     OwnerOf(proxy)->drop(proxy);
 }
@@ -524,16 +554,19 @@ bool
 WrapperOwner::getPropertyNames(JSContext *cx, HandleObject proxy, uint32_t flags, AutoIdVector &props)
 {
     ObjectId objId = idOf(proxy);
 
     ReturnStatus status;
     InfallibleTArray<nsString> names;
     if (!CallGetPropertyNames(objId, flags, &status, &names))
         return ipcfail(cx);
+
+    LOG_STACK();
+
     if (!ok(cx, status))
         return false;
 
     for (size_t i = 0; i < names.Length(); i++) {
         RootedId name(cx);
         if (!convertGeckoStringToId(cx, names[i], &name))
             return false;
         if (!props.append(name))
@@ -592,16 +625,18 @@ bool
 WrapperOwner::domInstanceOf(JSContext *cx, JSObject *obj, int prototypeID, int depth, bool *bp)
 {
     ObjectId objId = idOf(obj);
 
     ReturnStatus status;
     if (!CallDOMInstanceOf(objId, prototypeID, depth, &status, bp))
         return ipcfail(cx);
 
+    LOG_STACK();
+
     return ok(cx, status);
 }
 
 void
 WrapperOwner::ActorDestroy(ActorDestroyReason why)
 {
     inactive_ = true;
 }