eina_model: add xref/xunref, xrefs_get and models_usage_dump!

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 :-)




SVN revision: 67821
This commit is contained in:
Gustavo Sverzut Barbieri 2012-02-10 10:48:39 +00:00
parent 5c8833cbbe
commit 687e59a102
3 changed files with 386 additions and 14 deletions

View 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(

View 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.
*
@ -1418,6 +1532,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);
/**
* @}
*/

View 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);
}