Blob Blame History Raw
From a44c356190f44f1532208f1a0612a878bbafce27 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Fri, 16 Feb 2018 10:51:37 -0500
Subject: [PATCH] object: only print stacktraces when debugging enabled

We have a bunch of corruption right now spamming the
log.

This commit gets rid of the spam unless G_MESSAGES_DEBUG
is set.
---
 gi/object.cpp | 86 +++++++++++++++++++++++++++++++--------------------
 1 file changed, 52 insertions(+), 34 deletions(-)

diff --git a/gi/object.cpp b/gi/object.cpp
index fe381ec3..bd09a75c 100644
--- a/gi/object.cpp
+++ b/gi/object.cpp
@@ -127,60 +127,83 @@ struct ObjectInstance {
        prototypes) */
     GTypeClass *klass;
 
     GjsListLink instance_link;
 
     unsigned js_object_finalized : 1;
     unsigned g_object_finalized  : 1;
 
     /* True if this object has visible JS state, and thus its lifecycle is
      * managed using toggle references. False if this object just keeps a
      * hard ref on the underlying GObject, and may be finalized at will. */
     bool uses_toggle_ref : 1;
 };
 
 static std::stack<JS::PersistentRootedObject> object_init_list;
 
 using ParamRef = std::unique_ptr<GParamSpec, decltype(&g_param_spec_unref)>;
 using ParamRefArray = std::vector<ParamRef>;
 static std::unordered_map<GType, ParamRefArray> class_init_properties;
 
 static bool context_weak_pointer_callback = false;
 static bool weak_pointer_callback = false;
 ObjectInstance *wrapped_gobject_list;
 
 extern struct JSClass gjs_object_instance_class;
 GJS_DEFINE_PRIV_FROM_JS(ObjectInstance, gjs_object_instance_class)
 
 static void            disassociate_js_gobject (GObject *gobj);
 static void ensure_uses_toggle_ref(JSContext *cx, ObjectInstance *priv);
 
+static void
+gjs_log_stacktrace(const char *format,
+                   ...)
+{
+    const char *domain;
+    va_list     args;
+
+    domain = g_getenv("G_MESSAGES_DEBUG");
+
+    if (!domain)
+        return;
+
+    if (!g_str_equal(domain, "all") &&
+        !strstr(domain, G_LOG_DOMAIN))
+        return;
+
+    va_start(args, format);
+    g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, format, args);
+    va_end(args);
+
+    gjs_dumpstack();
+}
+
 typedef enum {
     SOME_ERROR_OCCURRED = false,
     NO_SUCH_G_PROPERTY,
     VALUE_WAS_SET
 } ValueFromPropertyResult;
 
 static GQuark
 gjs_is_custom_type_quark (void)
 {
     static GQuark val = 0;
     if (!val)
         val = g_quark_from_static_string ("gjs::custom-type");
 
     return val;
 }
 
 static GQuark
 gjs_is_custom_property_quark (void)
 {
     static GQuark val = 0;
     if (!val)
         val = g_quark_from_static_string ("gjs::custom-property");
 
     return val;
 }
 
 static GQuark
 gjs_object_priv_quark (void)
 {
     static GQuark val = 0;
@@ -433,66 +456,65 @@ out:
 /* a hook on getting a property; set value_p to override property's value.
  * Return value is false on OOM/exception.
  */
 static bool
 object_instance_get_prop(JSContext              *context,
                          JS::HandleObject        obj,
                          JS::HandleId            id,
                          JS::MutableHandleValue  value_p)
 {
     ObjectInstance *priv;
     GjsAutoJSChar name;
 
     if (!gjs_get_string_id(context, id, &name))
         return true; /* not resolved, but no error */
 
     priv = priv_from_js(context, obj);
     gjs_debug_jsprop(GJS_DEBUG_GOBJECT,
                      "Get prop '%s' hook obj %p priv %p",
                      name.get(), obj.get(), priv);
 
     if (priv == nullptr)
         /* If we reach this point, either object_instance_new_resolve
          * did not throw (so name == "_init"), or the property actually
          * exists and it's not something we should be concerned with */
         return true;
 
     if (priv->gobj == NULL) /* prototype, not an instance. */
         return true;
 
     if (priv->g_object_finalized) {
-        g_critical("Object %s.%s (%p), has been already finalized. "
-                   "Impossible to get any property from it.",
-                   priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "",
-                   priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype),
-                   priv->gobj);
-        gjs_dumpstack();
+        gjs_log_stacktrace("Object %s.%s (%p), has been already finalized. "
+                           "Impossible to get any property from it.",
+                           priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "",
+                           priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype),
+                           priv->gobj);
         return true;
     }
 
     if (!get_prop_from_g_param(context, obj, priv, name, value_p))
         return false;
 
     if (!value_p.isUndefined())
         return true;
 
     /* Fall back to fields */
     return get_prop_from_field(context, obj, priv, name, value_p);
 }
 
 static bool
 set_g_param_from_prop(JSContext          *context,
                       ObjectInstance     *priv,
                       const char         *name,
                       bool&               was_set,
                       JS::HandleValue     value_p,
                       JS::ObjectOpResult& result)
 {
     GParameter param = { NULL, { 0, }};
     was_set = false;
 
     switch (init_g_param_from_property(context, name,
                                        value_p,
                                        G_TYPE_FROM_INSTANCE(priv->gobj),
                                        &param,
                                        false /* constructing */)) {
     case SOME_ERROR_OCCURRED:
@@ -549,66 +571,65 @@ check_set_field_from_prop(JSContext             *cx,
     value_p.setUndefined();
 out:
     g_base_info_unref((GIBaseInfo *) field);
     return retval;
 }
 
 /* a hook on setting a property; set value_p to override property value to
  * be set. Return value is false on OOM/exception.
  */
 static bool
 object_instance_set_prop(JSContext              *context,
                          JS::HandleObject        obj,
                          JS::HandleId            id,
                          JS::MutableHandleValue  value_p,
                          JS::ObjectOpResult&     result)
 {
     ObjectInstance *priv;
     GjsAutoJSChar name;
     bool ret = true;
     bool g_param_was_set = false;
 
     priv = priv_from_js(context, obj);
     if (priv == nullptr)
         /* see the comment in object_instance_get_prop() on this */
         return result.succeed();
 
     if (priv->gobj == NULL) /* prototype, not an instance. */
         return result.succeed();
 
     if (priv->g_object_finalized) {
-        g_critical("Object %s.%s (%p), has been already finalized. "
-                   "Impossible to set any property to it.",
-                   priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "",
-                   priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype),
-                   priv->gobj);
-        gjs_dumpstack();
+        gjs_log_stacktrace("Object %s.%s (%p), has been already finalized. "
+                           "Impossible to set any property to it.",
+                           priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "",
+                           priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype),
+                           priv->gobj);
         return result.succeed();
     }
 
     if (!gjs_get_string_id(context, id, &name)) {
         /* We need to keep the wrapper alive in order not to lose custom
          * "expando" properties. In this case if gjs_get_string_id() is false
          * then a number or symbol property was probably set. */
         ensure_uses_toggle_ref(context, priv);
         return result.succeed();  /* not resolved, but no error */
     }
 
     gjs_debug_jsprop(GJS_DEBUG_GOBJECT,
                      "Set prop '%s' hook obj %p priv %p",
                      name.get(), obj.get(), priv);
 
     ret = set_g_param_from_prop(context, priv, name, g_param_was_set, value_p, result);
     if (g_param_was_set || !ret)
         return ret;
 
     /* note that the prop will also have been set in JS, which I think
      * is OK, since we hook get and set so will always override that
      * value. We could also use JS_DefineProperty though and specify a
      * getter/setter maybe, don't know if that is better.
      */
     return check_set_field_from_prop(context, priv, name, value_p, result);
 }
 
 static bool
 is_vfunc_unchanged(GIVFuncInfo *info,
                    GType        gtype)
@@ -1780,67 +1801,66 @@ do_associate_closure(ObjectInstance *priv,
      * invalidated */
     priv->closures.insert(closure);
     g_closure_add_invalidate_notifier(closure, priv, closure_invalidated);
 }
 
 static bool
 real_connect_func(JSContext *context,
                   unsigned   argc,
                   JS::Value *vp,
                   bool       after)
 {
     GJS_GET_PRIV(context, argc, vp, argv, obj, ObjectInstance, priv);
     GClosure *closure;
     gulong id;
     guint signal_id;
     GQuark signal_detail;
 
     gjs_debug_gsignal("connect obj %p priv %p argc %d", obj.get(), priv, argc);
     if (priv == NULL) {
         throw_priv_is_null_error(context);
         return false; /* wrong class passed in */
     }
     if (priv->gobj == NULL) {
         /* prototype, not an instance. */
         gjs_throw(context, "Can't connect to signals on %s.%s.prototype; only on instances",
                   priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "",
                   priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype));
         return false;
     }
     if (priv->g_object_finalized) {
-        g_critical("Object %s.%s (%p), has been already deallocated - impossible to connect to signal. "
-                   "This might be caused by the fact that the object has been destroyed from C "
-                   "code using something such as destroy(), dispose(), or remove() vfuncs",
-                   priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "",
-                   priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype),
-                   priv->gobj);
-        gjs_dumpstack();
+        gjs_log_stacktrace("Object %s.%s (%p), has been already deallocated - impossible to connect to signal. "
+                           "This might be caused by the fact that the object has been destroyed from C "
+                           "code using something such as destroy(), dispose(), or remove() vfuncs",
+                           priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "",
+                           priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype),
+                           priv->gobj);
         return true;
     }
 
     ensure_uses_toggle_ref(context, priv);
 
     if (argc != 2 || !argv[0].isString() || !JS::IsCallable(&argv[1].toObject())) {
         gjs_throw(context, "connect() takes two args, the signal name and the callback");
         return false;
     }
 
     JS::RootedString signal_str(context, argv[0].toString());
     GjsAutoJSChar signal_name = JS_EncodeStringToUTF8(context, signal_str);
     if (!signal_name)
         return false;
 
     if (!g_signal_parse_name(signal_name,
                              G_OBJECT_TYPE(priv->gobj),
                              &signal_id,
                              &signal_detail,
                              true)) {
         gjs_throw(context, "No signal '%s' on object '%s'",
                   signal_name.get(),
                   g_type_name(G_OBJECT_TYPE(priv->gobj)));
         return false;
     }
 
     closure = gjs_closure_new_for_signal(context, &argv[1].toObject(), "signal callback", signal_id);
     if (closure == NULL)
         return false;
     do_associate_closure(priv, closure);
@@ -1875,67 +1895,66 @@ connect_func(JSContext *context,
 static bool
 emit_func(JSContext *context,
           unsigned   argc,
           JS::Value *vp)
 {
     GJS_GET_PRIV(context, argc, vp, argv, obj, ObjectInstance, priv);
     guint signal_id;
     GQuark signal_detail;
     GSignalQuery signal_query;
     GValue *instance_and_args;
     GValue rvalue = G_VALUE_INIT;
     unsigned int i;
     bool failed;
 
     gjs_debug_gsignal("emit obj %p priv %p argc %d", obj.get(), priv, argc);
 
     if (priv == NULL) {
         throw_priv_is_null_error(context);
         return false; /* wrong class passed in */
     }
 
     if (priv->gobj == NULL) {
         /* prototype, not an instance. */
         gjs_throw(context, "Can't emit signal on %s.%s.prototype; only on instances",
                   priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "",
                   priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype));
         return false;
     }
 
     if (priv->g_object_finalized) {
-        g_critical("Object %s.%s (%p), has been already deallocated - impossible to emit signal. "
-                   "This might be caused by the fact that the object has been destroyed from C "
-                   "code using something such as destroy(), dispose(), or remove() vfuncs",
-                   priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "",
-                   priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype),
-                   priv->gobj);
-        gjs_dumpstack();
+        gjs_log_stacktrace("Object %s.%s (%p), has been already deallocated - impossible to emit signal. "
+                           "This might be caused by the fact that the object has been destroyed from C "
+                           "code using something such as destroy(), dispose(), or remove() vfuncs",
+                           priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "",
+                           priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype),
+                           priv->gobj);
         return true;
     }
 
     if (argc < 1 || !argv[0].isString()) {
         gjs_throw(context, "emit() first arg is the signal name");
         return false;
     }
 
     JS::RootedString signal_str(context, argv[0].toString());
     GjsAutoJSChar signal_name = JS_EncodeStringToUTF8(context, signal_str);
     if (!signal_name)
         return false;
 
     if (!g_signal_parse_name(signal_name,
                              G_OBJECT_TYPE(priv->gobj),
                              &signal_id,
                              &signal_detail,
                              false)) {
         gjs_throw(context, "No signal '%s' on object '%s'",
                   signal_name.get(),
                   g_type_name(G_OBJECT_TYPE(priv->gobj)));
         return false;
     }
 
     g_signal_query(signal_id, &signal_query);
 
     if ((argc - 1) != signal_query.n_params) {
         gjs_throw(context, "Signal '%s' on %s requires %d args got %d",
                   signal_name.get(),
                   g_type_name(G_OBJECT_TYPE(priv->gobj)),
@@ -2250,68 +2269,67 @@ gjs_object_from_g_object(JSContext    *context,
             return nullptr;
 
         JS::RootedObject obj(context,
             JS_NewObjectWithGivenProto(context, JS_GetClass(proto), proto));
         if (!obj)
             return nullptr;
 
         priv = init_object_private(context, obj);
 
         g_object_ref_sink(gobj);
         associate_js_gobject(context, obj, gobj);
 
         g_assert(priv->keep_alive == obj.get());
     }
 
     return priv->keep_alive;
 }
 
 GObject*
 gjs_g_object_from_object(JSContext       *context,
                          JS::HandleObject obj)
 {
     ObjectInstance *priv;
 
     if (!obj)
         return NULL;
 
     priv = priv_from_js(context, obj);
 
     if (priv->g_object_finalized) {
-        g_critical("Object %s.%s (%p), has been already deallocated - "
-                   "impossible to access it. This might be caused by the "
-                   "object having been destroyed from C code using something "
-                   "such as destroy(), dispose(), or remove() vfuncs",
-                   priv->info ? g_base_info_get_namespace(priv->info) : "",
-                   priv->info ? g_base_info_get_name(priv->info) : g_type_name(priv->gtype),
-                   priv->gobj);
-        gjs_dumpstack();
+        gjs_log_stacktrace ("Object %s.%s (%p), has been already deallocated - "
+                            "impossible to access it. This might be caused by the "
+                            "object having been destroyed from C code using something "
+                            "such as destroy(), dispose(), or remove() vfuncs",
+                            priv->info ? g_base_info_get_namespace(priv->info) : "",
+                            priv->info ? g_base_info_get_name(priv->info) : g_type_name(priv->gtype),
+                            priv->gobj);
         return nullptr;
     }
 
     return priv->gobj;
 }
 
 bool
 gjs_typecheck_is_object(JSContext       *context,
                         JS::HandleObject object,
                         bool             throw_error)
 {
     return do_base_typecheck(context, object, throw_error);
 }
 
 bool
 gjs_typecheck_object(JSContext       *context,
                      JS::HandleObject object,
                      GType            expected_type,
                      bool             throw_error)
 {
     ObjectInstance *priv;
     bool result;
 
     if (!do_base_typecheck(context, object, throw_error))
         return false;
 
     priv = priv_from_js(context, object);
 
     if (priv == NULL) {
         if (throw_error) {
-- 
2.17.1