eina_model: add xref/xunref, xrefs_get and models_usage_dump!
authorbarbieri <barbieri@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Fri, 10 Feb 2012 10:48:39 +0000 (10:48 +0000)
committerbarbieri <barbieri@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Fri, 10 Feb 2012 10:48:39 +0000 (10:48 +0000)
Let's try to help debug by allowing extended reference management that
takes in account an identifier. This identifier is accounted on xref
and xunref and must match.

xrefs_get will return the list of such references, for debugging purposes.

eina_models_list_get() was added to return all live models, just
tracked when EINA_MODEL_DEBUG is enabled.

eina_models_usage_dump() was added and use the same infrastructure as
eina_models_list_get() and eina_model_xrefs_get() to aid debugging :-)

git-svn-id: svn+ssh://svn.enlightenment.org/var/svn/e/trunk/eina@67821 7cbeb6ba-43b4-40fd-8cce-4c39aea84d33

configure.ac
src/include/eina_model.h
src/lib/eina_model.c

index 01bcca2..06f1136 100644 (file)
@@ -480,7 +480,9 @@ AC_SUBST([EINA_CONFIGURE_HAVE_DIRENT_H])
 ### Checks for library functions
 AC_ISC_POSIX
 AC_FUNC_ALLOCA
-AC_CHECK_FUNCS([strlcpy openat fstatat fpathconf execvp])
+AC_CHECK_FUNCS([strlcpy openat fstatat fpathconf execvp backtrace backtrace_symbols])
+
+AC_CHECK_HEADERS([execinfo.h], [AC_DEFINE([HAVE_EXECINFO_H], [1], [Have execinfo.h])])
 
 AC_MSG_CHECKING([for dirfd])
 AC_LINK_IFELSE(
index 996a135..d91f477 100644 (file)
@@ -21,6 +21,7 @@
 
 #include "eina_types.h"
 #include "eina_value.h"
+#include "eina_inlist.h"
 #include <stdarg.h>
 
 /**
@@ -262,29 +263,142 @@ EAPI Eina_Bool eina_model_interface_implemented(const Eina_Model *model, const E
 
 /**
  * @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.
  *
@@ -1419,6 +1533,33 @@ EAPI void eina_model_interface_children_sort(const Eina_Model_Interface *iface,
 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);
+
+/**
  * @}
  */
 
index 7b85eec..2e2afd1 100644 (file)
@@ -37,6 +37,10 @@ extern "C"
 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"
@@ -66,6 +70,13 @@ static char *_eina_model_mp_choice = NULL;
 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";
@@ -1003,6 +1014,7 @@ struct _Eina_Model
       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
@@ -1616,7 +1628,7 @@ _eina_model_type_base_child_iterator_free(Eina_Iterator *base)
 {
    Eina_Iterator_Model_Base *it;
    it = (Eina_Iterator_Model_Base *)base;
-   _eina_model_unref(it->model);
+   eina_model_xunref(it->model, it);
    free(it);
 }
 
@@ -1632,7 +1644,7 @@ _eina_model_type_base_child_iterator_get(Eina_Model *model, unsigned int start,
    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;
 
@@ -1678,7 +1690,7 @@ _eina_model_type_base_child_reversed_iterator_free(Eina_Iterator *base)
 {
    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);
 }
 
@@ -1708,7 +1720,7 @@ _eina_model_type_base_child_reversed_iterator_get(Eina_Model *model, unsigned in
    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;
 
@@ -1753,7 +1765,7 @@ _eina_model_type_base_child_sorted_iterator_free(Eina_Iterator *base)
    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]);
@@ -1788,7 +1800,7 @@ _eina_model_type_base_child_sorted_iterator_get(Eina_Model *model, unsigned int
    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;
 
@@ -1854,7 +1866,7 @@ _eina_model_type_base_child_filtered_iterator_free(Eina_Iterator *base)
 {
    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);
 }
 
@@ -1870,7 +1882,7 @@ _eina_model_type_base_child_filtered_iterator_get(Eina_Model *model, unsigned in
    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;
@@ -2750,7 +2762,7 @@ _eina_model_interface_children_inarray_destructor(Eina_Model *model)
    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;
@@ -2787,8 +2799,9 @@ _eina_model_interface_children_inarray_set(Eina_Model *model, unsigned int posit
    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;
 }
 
@@ -2806,7 +2819,7 @@ _eina_model_interface_children_inarray_del(Eina_Model *model, unsigned int posit
    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;
 }
 
@@ -2818,7 +2831,8 @@ _eina_model_interface_children_inarray_insert_at(Eina_Model *model, unsigned int
    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;
 }
 
@@ -2922,6 +2936,15 @@ eina_model_init(void)
         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
@@ -2966,6 +2989,12 @@ eina_model_init(void)
         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(
@@ -2988,6 +3017,8 @@ eina_model_init(void)
 
    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:
@@ -3019,6 +3050,12 @@ eina_model_init(void)
 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!");
@@ -3125,6 +3162,7 @@ eina_model_new(const Eina_Model_Type *type)
      }
 
    model->refcount = 1;
+   model->xrefs = NULL;
    model->deleted = EINA_FALSE;
    EINA_MAGIC_SET(model, EINA_MAGIC_MODEL);
 
@@ -3169,6 +3207,14 @@ eina_model_new(const Eina_Model_Type *type)
         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:
@@ -3206,6 +3252,28 @@ _eina_model_free(Eina_Model *model)
        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)
@@ -3477,6 +3545,56 @@ eina_model_ref(Eina_Model *model)
    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)
 {
@@ -3484,6 +3602,31 @@ 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)
 {
@@ -3491,6 +3634,13 @@ 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)
 {
@@ -5187,3 +5337,82 @@ eina_model_struct_get(const Eina_Model *model, const Eina_Value_Struct_Desc **p_
    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);
+}