From a44c356190f44f1532208f1a0612a878bbafce27 Mon Sep 17 00:00:00 2001 From: Ray Strode 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 object_init_list; using ParamRef = std::unique_ptr; using ParamRefArray = std::vector; static std::unordered_map 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), ¶m, 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