aboutsummaryrefslogtreecommitdiffstats
path: root/main/astobj2.c
diff options
context:
space:
mode:
authormurf <murf@f38db490-d61c-443f-a65b-d21fe96a405b>2008-04-16 17:45:28 +0000
committermurf <murf@f38db490-d61c-443f-a65b-d21fe96a405b>2008-04-16 17:45:28 +0000
commitd3a9bac0e7363798636b92ab10230700181d5711 (patch)
tree9a25de121f977a660ebbaf4f5e24807d9338180f /main/astobj2.c
parent8dd150d41ca06d45bcdf60dd82a556e8b7cc4f36 (diff)
Introducing various astobj2 enhancements, chief being a refcount tracing feature, and various documentation updates in astobj2.h, and the addition of standalone utility, refcounter, that will filter the trace output for unbalanced, unfreed objects. This comes from the team/murf/bug11210 branch.
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@114175 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'main/astobj2.c')
-rw-r--r--main/astobj2.c308
1 files changed, 269 insertions, 39 deletions
diff --git a/main/astobj2.c b/main/astobj2.c
index 8c783955b..007f12545 100644
--- a/main/astobj2.c
+++ b/main/astobj2.c
@@ -25,6 +25,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/astobj2.h"
#include "asterisk/utils.h"
#include "asterisk/cli.h"
+#define REF_FILE "/tmp/refs"
/*!
* astobj2 objects are always preceded by this data structure,
@@ -126,6 +127,18 @@ static inline struct astobj2 *INTERNAL_OBJ(void *user_data)
*/
#define EXTERNAL_OBJ(_p) ((_p) == NULL ? NULL : (_p)->user_data)
+/* the underlying functions common to debug and non-debug versions */
+
+static int __ao2_ref(void *user_data, const int delta);
+static void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn);
+static struct ao2_container *__ao2_container_alloc(struct ao2_container *c, const uint n_buckets, ao2_hash_fn *hash_fn,
+ ao2_callback_fn *cmp_fn);
+static struct bucket_list *__ao2_link(struct ao2_container *c, void *user_data);
+static void *__ao2_callback(struct ao2_container *c,
+ const enum search_flags flags, ao2_callback_fn *cb_fn, void *arg,
+ char *tag, char *file, int line, const char *funcname);
+static void * __ao2_iterator_next(struct ao2_iterator *a, struct bucket_list **q);
+
int ao2_lock(void *user_data)
{
struct astobj2 *p = INTERNAL_OBJ(user_data);
@@ -154,18 +167,72 @@ int ao2_unlock(void *user_data)
return ast_mutex_unlock(&p->priv_data.lock);
}
+int ao2_trylock(void *user_data)
+{
+ struct astobj2 *p = INTERNAL_OBJ(user_data);
+ int ret;
+
+ if (p == NULL)
+ return -1;
+ ret = ast_mutex_trylock(&p->priv_data.lock);
+#ifdef AO2_DEBUG
+ if (!ret)
+ ast_atomic_fetchadd_int(&ao2.total_locked, 1);
+#endif
+ return ret;
+}
+
+void *ao2_object_get_lockaddr(void *obj)
+{
+ struct astobj2 *p = INTERNAL_OBJ(obj);
+
+ if (p == NULL)
+ return NULL;
+
+ return &p->priv_data.lock;
+}
+
/*
* The argument is a pointer to the user portion.
*/
-int ao2_ref(void *user_data, const int delta)
+
+
+int _ao2_ref_debug(void *user_data, const int delta, char *tag, char *file, int line, const char *funcname)
+{
+ struct astobj2 *obj = INTERNAL_OBJ(user_data);
+
+ if (obj == NULL)
+ return -1;
+
+ if (delta != 0) {
+ FILE *refo = fopen(REF_FILE,"a");
+ fprintf(refo, "%p %s%d %s:%d:%s (%s) [@%d]\n", user_data, (delta<0? "":"+"), delta, file, line, funcname, tag, obj->priv_data.ref_counter);
+ fclose(refo);
+ }
+ if (obj->priv_data.ref_counter + delta == 0 && obj->priv_data.destructor_fn != NULL) { /* this isn't protected with lock; just for o/p */
+ FILE *refo = fopen(REF_FILE,"a");
+ fprintf(refo, "%p **call destructor** %s:%d:%s (%s)\n", user_data, file, line, funcname, tag);
+ fclose(refo);
+ }
+ return __ao2_ref(user_data, delta);
+}
+
+int _ao2_ref(void *user_data, const int delta)
{
- int current_value;
- int ret;
struct astobj2 *obj = INTERNAL_OBJ(user_data);
if (obj == NULL)
return -1;
+ return __ao2_ref(user_data, delta);
+}
+
+static int __ao2_ref(void *user_data, const int delta)
+{
+ struct astobj2 *obj = INTERNAL_OBJ(user_data);
+ int current_value;
+ int ret;
+
/* if delta is 0, just return the refcount */
if (delta == 0)
return (obj->priv_data.ref_counter);
@@ -183,8 +250,9 @@ int ao2_ref(void *user_data, const int delta)
ast_log(LOG_ERROR, "refcount %d on object %p\n", current_value, user_data);
if (current_value <= 0) { /* last reference, destroy the object */
- if (obj->priv_data.destructor_fn != NULL)
+ if (obj->priv_data.destructor_fn != NULL) {
obj->priv_data.destructor_fn(user_data);
+ }
ast_mutex_destroy(&obj->priv_data.lock);
#ifdef AO2_DEBUG
@@ -205,7 +273,7 @@ int ao2_ref(void *user_data, const int delta)
* We always alloc at least the size of a void *,
* for debugging purposes.
*/
-void *ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn)
+static void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn)
{
/* allocation */
struct astobj2 *obj;
@@ -234,9 +302,38 @@ void *ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn)
return EXTERNAL_OBJ(obj);
}
+void *_ao2_alloc_debug(size_t data_size, ao2_destructor_fn destructor_fn, char *tag, char *file, int line, const char *funcname)
+{
+ /* allocation */
+ void *obj;
+ FILE *refo = fopen(REF_FILE,"a");
+
+ obj = __ao2_alloc(data_size, destructor_fn);
+
+ if (obj == NULL)
+ return NULL;
+
+ if (refo) {
+ fprintf(refo, "%p =1 %s:%d:%s (%s)\n", obj, file, line, funcname, tag);
+ fclose(refo);
+ }
+
+ /* return a pointer to the user data */
+ return obj;
+}
+
+void *_ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn)
+{
+ return __ao2_alloc(data_size, destructor_fn);
+}
+
+
/* internal callback to destroy a container. */
static void container_destruct(void *c);
+/* internal callback to destroy a container. */
+static void container_destruct_debug(void *c);
+
/* each bucket in the container is a tailq. */
AST_LIST_HEAD_NOLOCK(bucket, bucket_list);
@@ -291,15 +388,11 @@ static int hash_zero(const void *user_obj, const int flags)
/*
* A container is just an object, after all!
*/
-struct ao2_container *
-ao2_container_alloc(const uint n_buckets, ao2_hash_fn *hash_fn,
- ao2_callback_fn *cmp_fn)
+static struct ao2_container *__ao2_container_alloc(struct ao2_container *c, const uint n_buckets, ao2_hash_fn *hash_fn,
+ ao2_callback_fn *cmp_fn)
{
/* XXX maybe consistency check on arguments ? */
/* compute the container size */
- size_t container_size = sizeof(struct ao2_container) + n_buckets * sizeof(struct bucket);
-
- struct ao2_container *c = ao2_alloc(container_size, container_destruct);
if (!c)
return NULL;
@@ -316,6 +409,30 @@ ao2_container_alloc(const uint n_buckets, ao2_hash_fn *hash_fn,
return c;
}
+struct ao2_container *_ao2_container_alloc_debug(const uint n_buckets, ao2_hash_fn *hash_fn,
+ ao2_callback_fn *cmp_fn, char *tag, char *file, int line, const char *funcname)
+{
+ /* XXX maybe consistency check on arguments ? */
+ /* compute the container size */
+ size_t container_size = sizeof(struct ao2_container) + n_buckets * sizeof(struct bucket);
+ struct ao2_container *c = _ao2_alloc_debug(container_size, container_destruct_debug, tag, file, line, funcname);
+
+ return __ao2_container_alloc(c, n_buckets, hash_fn, cmp_fn);
+}
+
+struct ao2_container *
+_ao2_container_alloc(const uint n_buckets, ao2_hash_fn *hash_fn,
+ ao2_callback_fn *cmp_fn)
+{
+ /* XXX maybe consistency check on arguments ? */
+ /* compute the container size */
+
+ size_t container_size = sizeof(struct ao2_container) + n_buckets * sizeof(struct bucket);
+ struct ao2_container *c = _ao2_alloc(container_size, container_destruct);
+
+ return __ao2_container_alloc(c, n_buckets, hash_fn, cmp_fn);
+}
+
/*!
* return the number of elements in the container
*/
@@ -338,7 +455,8 @@ struct bucket_list {
/*
* link an object to a container
*/
-void *ao2_link(struct ao2_container *c, void *user_data)
+
+static struct bucket_list *__ao2_link(struct ao2_container *c, void *user_data)
{
int i;
/* create a new list entry */
@@ -363,9 +481,30 @@ void *ao2_link(struct ao2_container *c, void *user_data)
p->version = ast_atomic_fetchadd_int(&c->version, 1);
AST_LIST_INSERT_TAIL(&c->buckets[i], p, entry);
ast_atomic_fetchadd_int(&c->elements, 1);
- ao2_ref(user_data, +1);
- ao2_unlock(c);
+
+ /* the last two operations (ao2_ref, ao2_unlock) must be done by the calling func */
+ return p;
+}
+
+void *_ao2_link_debug(struct ao2_container *c, void *user_data, char *tag, char *file, int line, const char *funcname)
+{
+ struct bucket_list *p = __ao2_link(c, user_data);
+ if (p) {
+ _ao2_ref_debug(user_data, +1, tag, file, line, funcname);
+ ao2_unlock(c);
+ }
+ return p;
+}
+
+void *_ao2_link(struct ao2_container *c, void *user_data)
+{
+ struct bucket_list *p = __ao2_link(c, user_data);
+
+ if (p) {
+ _ao2_ref(user_data, +1);
+ ao2_unlock(c);
+ }
return p;
}
@@ -381,12 +520,23 @@ int ao2_match_by_addr(void *user_data, void *arg, int flags)
* Unlink an object from the container
* and destroy the associated * ao2_bucket_list structure.
*/
-void *ao2_unlink(struct ao2_container *c, void *user_data)
+void *_ao2_unlink_debug(struct ao2_container *c, void *user_data, char *tag,
+ char *file, int line, const char *funcname)
{
if (INTERNAL_OBJ(user_data) == NULL) /* safety check on the argument */
return NULL;
- ao2_callback(c, OBJ_UNLINK | OBJ_POINTER | OBJ_NODATA, ao2_match_by_addr, user_data);
+ _ao2_callback_debug(c, OBJ_UNLINK | OBJ_POINTER | OBJ_NODATA, ao2_match_by_addr, user_data, tag, file, line, funcname);
+
+ return NULL;
+}
+
+void *_ao2_unlink(struct ao2_container *c, void *user_data)
+{
+ if (INTERNAL_OBJ(user_data) == NULL) /* safety check on the argument */
+ return NULL;
+
+ _ao2_callback(c, OBJ_UNLINK | OBJ_POINTER | OBJ_NODATA, ao2_match_by_addr, user_data);
return NULL;
}
@@ -396,6 +546,7 @@ void *ao2_unlink(struct ao2_container *c, void *user_data)
*/
static int cb_true(void *user_data, void *arg, int flags)
{
+ ast_log(LOG_ERROR,"If you see this, something is strange!\n");
return CMP_MATCH;
}
@@ -403,10 +554,13 @@ static int cb_true(void *user_data, void *arg, int flags)
* Browse the container using different stategies accoding the flags.
* \return Is a pointer to an object or to a list of object if OBJ_MULTIPLE is
* specified.
+ * Luckily, for debug purposes, the added args (tag, file, line, funcname)
+ * aren't an excessive load to the system, as the callback should not be
+ * called as often as, say, the ao2_ref func is called.
*/
-void *ao2_callback(struct ao2_container *c,
- const enum search_flags flags,
- ao2_callback_fn *cb_fn, void *arg)
+static void *__ao2_callback(struct ao2_container *c,
+ const enum search_flags flags, ao2_callback_fn *cb_fn, void *arg,
+ char *tag, char *file, int line, const char *funcname)
{
int i, last; /* search boundaries */
void *ret = NULL;
@@ -461,7 +615,10 @@ void *ao2_callback(struct ao2_container *c,
if (!(flags & OBJ_NODATA)) { /* if must return the object, record the value */
/* it is important to handle this case before the unlink */
ret = EXTERNAL_OBJ(cur->astobj);
- ao2_ref(ret, 1);
+ if (tag)
+ _ao2_ref_debug(ret, 1, tag, file, line, funcname);
+ else
+ _ao2_ref(ret, 1);
}
if (flags & OBJ_UNLINK) { /* must unlink */
@@ -472,7 +629,10 @@ void *ao2_callback(struct ao2_container *c,
AST_LIST_REMOVE_CURRENT(entry);
/* update number of elements and version */
ast_atomic_fetchadd_int(&c->elements, -1);
- ao2_ref(EXTERNAL_OBJ(x->astobj), -1);
+ if (tag)
+ _ao2_ref_debug(EXTERNAL_OBJ(x->astobj), -1, tag, file, line, funcname);
+ else
+ _ao2_ref(EXTERNAL_OBJ(x->astobj), -1);
free(x); /* free the link record */
}
@@ -496,12 +656,31 @@ void *ao2_callback(struct ao2_container *c,
return ret;
}
+void *_ao2_callback_debug(struct ao2_container *c,
+ const enum search_flags flags,
+ ao2_callback_fn *cb_fn, void *arg,
+ char *tag, char *file, int line, const char *funcname)
+{
+ return __ao2_callback(c,flags, cb_fn, arg, tag, file, line, funcname);
+}
+
+void *_ao2_callback(struct ao2_container *c,const enum search_flags flags,
+ ao2_callback_fn *cb_fn, void *arg)
+{
+ return __ao2_callback(c,flags, cb_fn, arg, NULL, NULL, 0, NULL);
+}
+
/*!
* the find function just invokes the default callback with some reasonable flags.
*/
-void *ao2_find(struct ao2_container *c, void *arg, enum search_flags flags)
+void *_ao2_find_debug(struct ao2_container *c, void *arg, enum search_flags flags, char *tag, char *file, int line, const char *funcname)
{
- return ao2_callback(c, flags, c->cmp_fn, arg);
+ return _ao2_callback_debug(c, flags, c->cmp_fn, arg, tag, file, line, funcname);
+}
+
+void *_ao2_find(struct ao2_container *c, void *arg, enum search_flags flags)
+{
+ return _ao2_callback(c, flags, c->cmp_fn, arg);
}
/*!
@@ -520,12 +699,14 @@ struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags)
/*
* move to the next element in the container.
*/
-void * ao2_iterator_next(struct ao2_iterator *a)
+static void * __ao2_iterator_next(struct ao2_iterator *a, struct bucket_list **q)
{
int lim;
struct bucket_list *p = NULL;
void *ret = NULL;
+ *q = NULL;
+
if (INTERNAL_OBJ(a->c) == NULL)
return NULL;
@@ -567,7 +748,40 @@ found:
a->c_version = a->c->version;
ret = EXTERNAL_OBJ(p->astobj);
/* inc refcount of returned object */
- ao2_ref(ret, 1);
+ *q = p;
+ }
+
+ return ret;
+}
+
+void * _ao2_iterator_next_debug(struct ao2_iterator *a, char *tag, char *file, int line, const char *funcname)
+{
+ struct bucket_list *p;
+ void *ret = NULL;
+
+ ret = __ao2_iterator_next(a, &p);
+
+ if (p) {
+ /* inc refcount of returned object */
+ _ao2_ref_debug(ret, 1, tag, file, line, funcname);
+ }
+
+ if (!(a->flags & F_AO2I_DONTLOCK))
+ ao2_unlock(a->c);
+
+ return ret;
+}
+
+void * _ao2_iterator_next(struct ao2_iterator *a)
+{
+ struct bucket_list *p = NULL;
+ void *ret = NULL;
+
+ ret = __ao2_iterator_next(a, &p);
+
+ if (p) {
+ /* inc refcount of returned object */
+ _ao2_ref(ret, 1);
}
if (!(a->flags & F_AO2I_DONTLOCK))
@@ -581,7 +795,13 @@ found:
*/
static int cd_cb(void *obj, void *arg, int flag)
{
- ao2_ref(obj, -1);
+ _ao2_ref(obj, -1);
+ return 0;
+}
+
+static int cd_cb_debug(void *obj, void *arg, int flag)
+{
+ _ao2_ref_debug(obj, -1, "deref object via container destroy", __FILE__, __LINE__, __PRETTY_FUNCTION__);
return 0;
}
@@ -589,7 +809,18 @@ static void container_destruct(void *_c)
{
struct ao2_container *c = _c;
- ao2_callback(c, OBJ_UNLINK, cd_cb, NULL);
+ _ao2_callback(c, OBJ_UNLINK, cd_cb, NULL);
+
+#ifdef AO2_DEBUG
+ ast_atomic_fetchadd_int(&ao2.total_containers, -1);
+#endif
+}
+
+static void container_destruct_debug(void *_c)
+{
+ struct ao2_container *c = _c;
+
+ _ao2_callback_debug(c, OBJ_UNLINK, cd_cb_debug, NULL, "container_destruct_debug called", __FILE__, __LINE__, __PRETTY_FUNCTION__);
#ifdef AO2_DEBUG
ast_atomic_fetchadd_int(&ao2.total_containers, -1);
@@ -666,7 +897,7 @@ static char *handle_astobj2_test(struct ast_cli_entry *e, int cmd, struct ast_cl
* allocate a container with no default callback, and no hash function.
* No hash means everything goes in the same bucket.
*/
- c1 = ao2_container_alloc(100, NULL /* no callback */, NULL /* no hash */);
+ c1 = ao2_t_container_alloc(100, NULL /* no callback */, NULL /* no hash */,"test");
ast_cli(a->fd, "container allocated as %p\n", c1);
/*
@@ -676,42 +907,41 @@ static char *handle_astobj2_test(struct ast_cli_entry *e, int cmd, struct ast_cl
*/
for (i = 0; i < lim; i++) {
ast_mark(prof_id, 1 /* start */);
- obj = ao2_alloc(80, NULL);
+ obj = ao2_t_alloc(80, NULL,"test");
ast_mark(prof_id, 0 /* stop */);
ast_cli(a->fd, "object %d allocated as %p\n", i, obj);
sprintf(obj, "-- this is obj %d --", i);
ao2_link(c1, obj);
}
ast_cli(a->fd, "testing callbacks\n");
- ao2_callback(c1, 0, print_cb, &a->fd);
-
+ ao2_t_callback(c1, 0, print_cb, &a->fd,"test callback");
ast_cli(a->fd, "testing iterators, remove every second object\n");
{
struct ao2_iterator ai;
int x = 0;
ai = ao2_iterator_init(c1, 0);
- while ( (obj = ao2_iterator_next(&ai)) ) {
+ while ( (obj = ao2_t_iterator_next(&ai,"test")) ) {
ast_cli(a->fd, "iterator on <%s>\n", obj);
if (x++ & 1)
- ao2_unlink(c1, obj);
- ao2_ref(obj, -1);
+ ao2_t_unlink(c1, obj,"test");
+ ao2_t_ref(obj, -1,"test");
}
ast_cli(a->fd, "testing iterators again\n");
ai = ao2_iterator_init(c1, 0);
- while ( (obj = ao2_iterator_next(&ai)) ) {
+ while ( (obj = ao2_t_iterator_next(&ai,"test")) ) {
ast_cli(a->fd, "iterator on <%s>\n", obj);
- ao2_ref(obj, -1);
+ ao2_t_ref(obj, -1,"test");
}
}
ast_cli(a->fd, "testing callbacks again\n");
- ao2_callback(c1, 0, print_cb, &a->fd);
+ ao2_t_callback(c1, 0, print_cb, &a->fd,"test callback");
ast_verbose("now you should see an error message:\n");
- ao2_ref(&i, -1); /* i is not a valid object so we print an error here */
+ ao2_t_ref(&i, -1, ""); /* i is not a valid object so we print an error here */
ast_cli(a->fd, "destroy container\n");
- ao2_ref(c1, -1); /* destroy container */
+ ao2_t_ref(c1, -1, ""); /* destroy container */
handle_astobj2_stats(e, CLI_HANDLER, &fake_args);
return CLI_SUCCESS;
}