#include "eina_types.h"
#include "eina_value.h"
+#include "eina_inlist.h"
#include <stdarg.h>
/**
/**
* @brief Increases the refcount of @a model.
+ * @param model The model to increase reference.
+ * @return The @a model with reference increased.
*
* @see eina_model_new()
* @see eina_model_unref()
* @since 1.2
*/
EAPI Eina_Model *eina_model_ref(Eina_Model *model) EINA_ARG_NONNULL(1);
+
+/**
+ * @brief Increases the refcount of @a model, informs reference identifier.
+ * @param model The model to increase reference.
+ * @param id An identifier to mark this reference.
+ * @param label An optional label to help debug, may be @c NULL.
+ * @return The @a model with reference increased.
+ *
+ * This extended version of reference explicitly marks the origin of
+ * the reference and eina_model_xunref() should be used to check and
+ * remove it.
+ *
+ * Usually the @a id is another object, like a parent object, or some
+ * class/structure/file/function that is holding the reference for
+ * some reason.
+ *
+ * Its purpose is to help debuging if Eina was compiled with model
+ * usage debug enabled and environment variable @c EINA_MODEL_DEBUG=1
+ * is set.
+ *
+ * It is recommended to use eina_model_xref() and eina_model_xunref()
+ * pair whenever you want to be sure you released your
+ * references. Both at your own type, or using applications. As an
+ * example #EINA_MODEL_INTERFACE_CHILDREN_INARRAY will use this to
+ * make sure it deleted every managed children.
+ *
+ * In order to debug leaks, consider using eina_model_xrefs_get() or
+ * eina_models_usage_dump() for a global picture. However, some
+ * references are not tracked, namely:
+ *
+ * @li eina_model_new()
+ * @li eina_model_child_get()
+ * @li eina_model_child_iterator_get()
+ * @li eina_model_child_reversed_iterator_get()
+ * @li eina_model_child_sorted_iterator_get()
+ * @li eina_model_child_filtered_iterator_get()
+ * @li eina_model_child_slice_iterator_get()
+ * @li eina_model_child_slice_reversed_iterator_get()
+ * @li eina_model_child_slice_sorted_iterator_get()
+ * @li eina_model_child_slice_filtered_iterator_get()
+ *
+ * @note this function is slower than eina_model_ref() if
+ * @c EINA_MODEL_DEBUG is set to "1" or "backtrace". Otherwise it
+ * should have the same performance cost.
+ *
+ * @see eina_model_ref()
+ * @see eina_model_xunref()
+ * @since 1.2
+ */
+EAPI Eina_Model *eina_model_xref(Eina_Model *model,
+ const void *id,
+ const char *label) EINA_ARG_NONNULL(1, 2);
+
/**
* @brief Decreases the refcount of @a model.
+ * @param model The model to decrease reference.
+ *
+ * After this function returns, consider @a model pointer invalid.
*
* @see eina_model_ref()
* @see eina_model_del()
* @since 1.2
*/
EAPI void eina_model_unref(Eina_Model *model) EINA_ARG_NONNULL(1);
+
+/**
+ * @brief Decreases the refcount of @a model, informs reference identifier.
+ * @param model The model to decrease reference.
+ * @param id An identifier to mark this reference.
+ *
+ * This function will match eina_model_xref() and the @a id must match
+ * a previously call, otherwise it will produce an error if @c
+ * EINA_MODEL_DEBUG is set to "1" or "backtrace", and the reference is
+ * not decreased!
+ *
+ * After this function returns, consider @a model pointer invalid.
+ *
+ * @note this function is slower than eina_model_unref() if
+ * @c EINA_MODEL_DEBUG is set to "1" or "backtrace". Otherwise it
+ * should have the same performance cost.
+ *
+ * @see eina_model_xref()
+ * @since 1.2
+ */
+EAPI void eina_model_xunref(Eina_Model *model,
+ const void *id) EINA_ARG_NONNULL(1, 2);
+
/**
* @brief Returns the number of references to @a model.
+ * @param model The model to query number of references.
+ * @return number of references to model
*
* @see eina_model_ref()
* @see eina_model_unref()
+ * @see eina_model_xref()
+ * @see eina_model_xunref()
+ * @see eina_model_xrefs_get()
* @since 1.2
*/
EAPI int eina_model_refcount(const Eina_Model *model) EINA_ARG_NONNULL(1);
+typedef struct _Eina_Model_XRef Eina_Model_XRef;
+struct _Eina_Model_XRef
+{
+ EINA_INLIST;
+ const void *id; /**< as given to eina_model_xref() */
+ struct {
+ const void * const *symbols; /**< only if @c EINA_MODEL_DEBUG=backtrace is set, otherwise is @c NULL */
+ unsigned int count; /**< only if @c EINA_MODEL_DEBUG=backtrace is set, otherwise is 0 */
+ } backtrace;
+ char label[];
+};
+
+/**
+ * @brief Returns the current references of this model.
+ * @param model The model to query references.
+ * @return List of reference holders as Eina_Model_XRef. This is the internal
+ * list for speed purposes, do not modify or free it in anyway!
+ *
+ * @note This list only exist if environment variable
+ * @c EINA_MODEL_DEBUG is set to "1" or "backtrace".
+ *
+ * @note The backtrace information is only available if environment
+ * variable @c EINA_MODEL_DEBUG=backtrace is set.
+ * @since 1.2
+ */
+EAPI const Eina_Inlist *eina_model_xrefs_get(const Eina_Model *model) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT EINA_MALLOC;
+
/**
* @brief Add a callback to be called when @a event_name is emited.
*
EAPI extern const Eina_Model_Interface *EINA_MODEL_INTERFACE_CHILDREN_INARRAY;
/**
+ * @brief Dump usage of all existing modules.
+ * @since 1.2
+ */
+EAPI void eina_models_usage_dump(void);
+
+/**
+ * @brief Return a list of all live models.
+ * @return a newly allocated list of Eina_Model. Free using
+ * eina_models_list_free()
+ *
+ * @note this is meant to debug purposes, do not modify the models in
+ * any way!
+ *
+ * @note due performance reasons, this is only @b enabled when
+ * @c EINA_MODEL_DEBUG is set to "1" or "backtrace".
+ *
+ * @since 1.2
+ */
+EAPI Eina_List *eina_models_list_get(void);
+
+/**
+ * @brief Release list returned by eina_models_list_get()
+ * @param list the list to release.
+ */
+EAPI void eina_models_list_free(Eina_List *list);
+
+/**
* @}
*/
void *alloca (size_t);
#endif
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
+#endif
+
#include "eina_config.h"
#include "eina_private.h"
#include "eina_error.h"
static Eina_Hash *_eina_model_descriptions = NULL;
static Eina_Lock _eina_model_descriptions_lock;
static int _eina_model_log_dom = -1;
+static enum {
+ EINA_MODEL_DEBUG_NONE = 0,
+ EINA_MODEL_DEBUG_CHECK = 1,
+ EINA_MODEL_DEBUG_BACKTRACE = 2,
+} _eina_model_debug = EINA_MODEL_DEBUG_NONE;
+static Eina_Lock _eina_model_debug_list_lock;
+static Eina_List *_eina_model_debug_list = NULL;
static const char _eina_model_str_deleted[] = "deleted";
static const char _eina_model_str_freed[] = "freed";
int walking; /**< increased while walking entries lists */
} listeners;
void **privates; /**< private data per type and interface, each level gets its own stuff */
+ Eina_Inlist *xrefs; /**< if EINA_MODEL_DEBUG and eina_model_xref() is used */
int refcount; /**< number of users of this model instance */
Eina_Bool deleted:1; /**< if deleted but still have references */
EINA_MAGIC
{
Eina_Iterator_Model_Base *it;
it = (Eina_Iterator_Model_Base *)base;
- _eina_model_unref(it->model);
+ eina_model_xunref(it->model, it);
free(it);
}
it->base.get_container = _eina_model_type_base_child_iterator_get_container;
it->base.free = _eina_model_type_base_child_iterator_free;
- it->model = eina_model_ref(model);
+ it->model = eina_model_xref(model, it, "eina_model_child_slice_iterator_get");
it->current = start;
it->end = start + count;
{
Eina_Iterator_Model_Base_Reversed *it;
it = (Eina_Iterator_Model_Base_Reversed *)base;
- _eina_model_unref(it->model);
+ eina_model_xunref(it->model, it);
free(it);
}
it->base.get_container = _eina_model_type_base_child_reversed_iterator_get_container;
it->base.free = _eina_model_type_base_child_reversed_iterator_free;
- it->model = eina_model_ref(model);
+ it->model = eina_model_xref(model, it, "eina_model_child_slice_reversed_iterator_get");
it->current = start + count;
it->end = start;
Eina_Iterator_Model_Base_Sorted *it;
unsigned int i;
it = (Eina_Iterator_Model_Base_Sorted *)base;
- _eina_model_unref(it->model);
+ eina_model_xunref(it->model, it);
for (i = 0; i < it->count; i++)
_eina_model_unref(it->elements[i]);
it->base.get_container = _eina_model_type_base_child_sorted_iterator_get_container;
it->base.free = _eina_model_type_base_child_sorted_iterator_free;
- it->model = eina_model_ref(model);
+ it->model = eina_model_xref(model, it, "eina_model_child_slice_sorted_iterator_get");
it->current = 0;
it->count = count;
{
Eina_Iterator_Model_Base_Filtered *it;
it = (Eina_Iterator_Model_Base_Filtered *)base;
- _eina_model_unref(it->model);
+ eina_model_xunref(it->model, it);
free(it);
}
it->base.get_container = _eina_model_type_base_child_filtered_iterator_get_container;
it->base.free = _eina_model_type_base_child_filtered_iterator_free;
- it->model = eina_model_ref(model);
+ it->model = eina_model_xref(model, it, "eina_model_child_slice_filtered_iterator_get");
it->match = match;
it->data = data;
it->current = start;
itr = priv->members;
itr_end = itr + count;
for (; itr < itr_end; itr++)
- _eina_model_unref(*itr);
+ eina_model_xunref(*itr, EINA_MODEL_INTERFACE_CHILDREN_INARRAY);
eina_inarray_flush(priv);
return EINA_TRUE;
if (!eina_inarray_replace_at(priv, position, &child))
return EINA_FALSE;
- eina_model_ref(child);
- _eina_model_unref(old);
+ eina_model_xref(child, EINA_MODEL_INTERFACE_CHILDREN_INARRAY,
+ "eina_model_child_set");
+ eina_model_xunref(old, EINA_MODEL_INTERFACE_CHILDREN_INARRAY);
return EINA_TRUE;
}
if (!eina_inarray_remove_at(priv, position))
return EINA_FALSE;
- _eina_model_unref(old);
+ eina_model_xunref(old, EINA_MODEL_INTERFACE_CHILDREN_INARRAY);
return EINA_TRUE;
}
if (!eina_inarray_insert_at(priv, position, &child))
return EINA_FALSE;
- eina_model_ref(child);
+ eina_model_xref(child, EINA_MODEL_INTERFACE_CHILDREN_INARRAY,
+ "eina_model_child_insert_at");
return EINA_TRUE;
}
return EINA_FALSE;
}
+ choice = getenv("EINA_MODEL_DEBUG");
+ if (choice)
+ {
+ if (strcmp(choice, "1") == 0)
+ _eina_model_debug = EINA_MODEL_DEBUG_CHECK;
+ else if (strcmp(choice, "backtrace") == 0)
+ _eina_model_debug = EINA_MODEL_DEBUG_BACKTRACE;
+ }
+
#ifdef EINA_DEFAULT_MEMPOOL
choice = "pass_through";
#else
goto on_init_fail_hash_desc;
}
+ if (!eina_lock_new(&_eina_model_debug_list_lock))
+ {
+ ERR("Cannot create model debug list lock in model init.");
+ goto on_init_fail_lock_debug;
+ }
+
EINA_ERROR_MODEL_FAILED = eina_error_msg_static_register(
EINA_ERROR_MODEL_FAILED_STR);
EINA_ERROR_MODEL_METHOD_MISSING = eina_error_msg_static_register(
return EINA_TRUE;
+ on_init_fail_lock_debug:
+ eina_hash_free(_eina_model_descriptions);
on_init_fail_hash_desc:
eina_lock_free(&_eina_model_descriptions_lock);
on_init_fail_lock_desc:
Eina_Bool
eina_model_shutdown(void)
{
+ eina_lock_take(&_eina_model_debug_list_lock);
+ if (eina_list_count(_eina_model_debug_list) > 0)
+ ERR("%d models are still alive!", eina_list_count(_eina_model_debug_list));
+ eina_lock_release(&_eina_model_debug_list_lock);
+ eina_lock_free(&_eina_model_debug_list_lock);
+
eina_lock_take(&_eina_model_inner_mps_lock);
if (eina_hash_population(_eina_model_inner_mps) != 0)
ERR("Cannot free eina_model internal memory pools -- still in use!");
}
model->refcount = 1;
+ model->xrefs = NULL;
model->deleted = EINA_FALSE;
EINA_MAGIC_SET(model, EINA_MAGIC_MODEL);
goto failed_constructor;
}
+ if (EINA_UNLIKELY(_eina_model_debug))
+ {
+ eina_lock_take(&_eina_model_debug_list_lock);
+ _eina_model_debug_list = eina_list_append
+ (_eina_model_debug_list, model);
+ eina_lock_release(&_eina_model_debug_list_lock);
+ }
+
return model;
failed_constructor:
model, model->desc->cache.types[0]->name,
model->refcount, model->deleted);
+ if (EINA_UNLIKELY(_eina_model_debug))
+ {
+ if (model->xrefs)
+ {
+ ERR("Model %p (%s) released with references pending:",
+ model, model->desc->cache.types[0]->name);
+ while (model->xrefs)
+ {
+ Eina_Model_XRef *ref = (Eina_Model_XRef *)model->xrefs;
+ model->xrefs = eina_inlist_remove(model->xrefs, model->xrefs);
+
+ ERR("xref: %p '%s'", ref->id, ref->label);
+ free(ref);
+ }
+ }
+
+ eina_lock_take(&_eina_model_debug_list_lock);
+ _eina_model_debug_list = eina_list_remove
+ (_eina_model_debug_list, model);
+ eina_lock_release(&_eina_model_debug_list_lock);
+ }
+
/* flush every interface, natural order */
for (i = 0; i < desc->total.ifaces; i++)
if (desc->cache.ifaces[i]->flush)
return model;
}
+static Eina_Model *
+_eina_model_xref_add(Eina_Model *model, const void *id, const char *label)
+{
+ Eina_Model_XRef *ref;
+ void *bt[256];
+ int btlen, labellen;
+
+ labellen = label ? strlen(label): 0;
+ btlen = 0;
+
+#ifdef HAVE_BACKTRACE
+ if (_eina_model_debug == EINA_MODEL_DEBUG_BACKTRACE)
+ btlen = backtrace(bt, EINA_C_ARRAY_LENGTH(bt));
+#endif
+
+ ref = calloc(1, sizeof(*ref) + (btlen * sizeof(void *)) + (labellen + 1));
+ EINA_SAFETY_ON_NULL_RETURN_VAL(ref, NULL);
+
+ ref->id = id;
+ memcpy(ref->label, label, labellen);
+ ref->label[labellen] = '\0';
+ ref->backtrace.count = btlen;
+ if (btlen == 0) ref->backtrace.symbols = NULL;
+ else
+ {
+ void *ptr = (unsigned char *)ref + sizeof(*ref) + (labellen + 1);
+ ref->backtrace.symbols = ptr;
+ memcpy(ptr, bt, btlen * sizeof(void *));
+ }
+
+ model->xrefs = eina_inlist_append(model->xrefs, EINA_INLIST_GET(ref));
+ return model;
+}
+
+EAPI Eina_Model *
+eina_model_xref(Eina_Model *model, const void *id, const char *label)
+{
+ EINA_MODEL_INSTANCE_CHECK_VAL(model, NULL);
+ DBG("model %p (%s) refcount=%d deleted=" FMT_UCHAR" id=%p label=%s",
+ model, model->desc->cache.types[0]->name,
+ model->refcount, model->deleted, id, label ? label : "");
+
+ model->refcount++;
+
+ if (EINA_LIKELY(!_eina_model_debug))
+ return model;
+
+ return _eina_model_xref_add(model, id, label);
+}
+
EAPI void
eina_model_unref(Eina_Model *model)
{
_eina_model_unref(model);
}
+EAPI void
+eina_model_xunref(Eina_Model *model, const void *id)
+{
+ Eina_Model_XRef *ref;
+ EINA_MODEL_INSTANCE_CHECK(model);
+
+ if (EINA_LIKELY(!_eina_model_debug))
+ {
+ _eina_model_unref(model);
+ return;
+ }
+
+ EINA_INLIST_FOREACH(model->xrefs, ref)
+ {
+ if (ref->id != id) continue;
+
+ model->xrefs = eina_inlist_remove(model->xrefs, EINA_INLIST_GET(ref));
+ free(ref);
+ _eina_model_unref(model);
+ return;
+ }
+
+ ERR("Could not find existing reference %p to model %p", id, model);
+}
+
EAPI int
eina_model_refcount(const Eina_Model *model)
{
return model->refcount;
}
+EAPI const Eina_Inlist *
+eina_model_xrefs_get(const Eina_Model *model)
+{
+ EINA_MODEL_INSTANCE_CHECK_VAL(model, NULL);
+ return model->xrefs;
+}
+
EAPI Eina_Bool
eina_model_event_callback_add(Eina_Model *model, const char *event_name, Eina_Model_Event_Cb cb, const void *data)
{
if (p_memory) *p_memory = st.memory;
return EINA_FALSE;
}
+
+EAPI void
+eina_models_usage_dump(void)
+{
+ const Eina_List *l;
+ const Eina_Model *m;
+
+ eina_lock_take(&_eina_model_debug_list_lock);
+
+ puts("DDD: model refs info (type, holders, backtrace)");
+ puts("DDD: -------------- -------------- ---------------------------------");
+
+ EINA_LIST_FOREACH(_eina_model_debug_list, l, m)
+ {
+ Eina_Model_XRef *ref;
+
+ printf("DDD: %14p %14d %s\n",
+ m, m->refcount, m->desc->cache.types[0]->name);
+
+ EINA_INLIST_FOREACH(m->xrefs, ref)
+ {
+ printf("DDD: id: %p '%s'\n",
+ ref->id, ref->label);
+ if (ref->backtrace.count)
+ {
+ char **symbols;
+ unsigned int i;
+
+#ifdef HAVE_BACKTRACE_SYMBOLS
+ symbols = backtrace_symbols((void * const *)ref->backtrace.symbols,
+ ref->backtrace.count);
+#else
+ symbols = NULL;
+#endif
+
+ printf("DDD: Backtrace: Address Symbol\n");
+ for (i = 0; i < ref->backtrace.count; i++)
+ printf("DDD: %14p %s\n",
+ ref->backtrace.symbols[i],
+ symbols ? symbols[i] : "???");
+
+ free(symbols);
+ puts("DDD:");
+ }
+ }
+ }
+
+ eina_lock_release(&_eina_model_debug_list_lock);
+}
+
+EAPI Eina_List *
+eina_models_list_get(void)
+{
+ const Eina_List *l;
+ Eina_Model *m;
+ Eina_List *ret = NULL;
+
+ eina_lock_take(&_eina_model_debug_list_lock);
+
+ EINA_LIST_FOREACH(_eina_model_debug_list, l, m)
+ {
+ ret = eina_list_append
+ (ret, eina_model_xref
+ (m, eina_models_list_get, "eina_models_list_get"));
+ }
+
+ eina_lock_release(&_eina_model_debug_list_lock);
+
+ return ret;
+}
+
+EAPI void
+eina_models_list_free(Eina_List *list)
+{
+ Eina_Model *m;
+
+ EINA_LIST_FREE(list, m)
+ eina_model_xunref(m, eina_models_list_get);
+}