Index: build_tools/cflags.xml
===================================================================
--- build_tools/cflags.xml (revision 134479)
+++ build_tools/cflags.xml (working copy)
@@ -18,4 +18,6 @@
yes
+
+
Index: channels/iax2-parser.h
===================================================================
--- channels/iax2-parser.h (revision 134479)
+++ channels/iax2-parser.h (working copy)
@@ -157,4 +157,8 @@
void iax_frame_wrap(struct iax_frame *fr, struct ast_frame *f);
struct iax_frame *iax_frame_new(int direction, int datalen, unsigned int cacheable);
void iax_frame_free(struct iax_frame *fr);
+
+#ifdef DEBUG_IAX_CACHE
+extern struct ast_cli_entry cli_iax2_show_memory;
#endif
+#endif
Index: channels/chan_iax2.c
===================================================================
--- channels/chan_iax2.c (revision 134479)
+++ channels/chan_iax2.c (working copy)
@@ -11077,6 +11077,7 @@
ast_manager_unregister( "IAXnetstats" );
ast_unregister_application(papp);
ast_cli_unregister_multiple(cli_iax2, sizeof(cli_iax2) / sizeof(struct ast_cli_entry));
+ ast_cli_unregister(&cli_iax2_show_memory);
ast_unregister_switch(&iax2_switch);
ast_channel_unregister(&iax2_tech);
delete_users();
@@ -11213,6 +11214,7 @@
AST_LIST_HEAD_INIT(&iaxq.queue);
ast_cli_register_multiple(cli_iax2, sizeof(cli_iax2) / sizeof(struct ast_cli_entry));
+ ast_cli_register(&cli_iax2_show_memory);
ast_register_application(papp, iax2_prov_app, psyn, pdescrip);
Index: channels/iax2-parser.c
===================================================================
--- channels/iax2-parser.c (revision 134479)
+++ channels/iax2-parser.c (working copy)
@@ -41,6 +41,8 @@
#include "asterisk/unaligned.h"
#include "asterisk/lock.h"
#include "asterisk/threadstorage.h"
+#include "asterisk/options.h"
+#include "asterisk/cli.h"
#include "iax2.h"
#include "iax2-parser.h"
@@ -59,7 +61,19 @@
/*! \brief This is just so iax_frames, a list head struct for holding a list of
* iax_frame structures, is defined. */
AST_LIST_HEAD_NOLOCK(iax_frames, iax_frame);
+
+#ifdef DEBUG_IAX_CACHE
+struct debug_cache {
+ struct iax_frame *fr;
+ pthread_t owner;
+ size_t size;
+ char inuse;
+ AST_LIST_ENTRY(debug_cache) list;
+};
+
+AST_LIST_HEAD(debug_cache_head, debug_cache) debug_cache_head;
#endif
+#endif
static void internaloutput(const char *str)
{
@@ -954,6 +968,9 @@
#if !defined(LOW_MEMORY)
struct iax_frames *iax_frames;
+#ifdef DEBUG_IAX_CACHE
+ struct debug_cache *citem;
+#endif
/* Attempt to get a frame from this thread's cache */
if ((iax_frames = ast_threadstorage_get(&frame_cache, sizeof(*iax_frames)))) {
@@ -972,6 +989,28 @@
if (!(fr = ast_calloc_cache(1, sizeof(*fr) + datalen)))
return NULL;
fr->afdatalen = datalen;
+#ifdef DEBUG_IAX_CACHE
+ if ((citem = ast_calloc(1, sizeof(*citem)))) {
+ citem->fr = fr;
+ citem->owner = pthread_self();
+ citem->size = datalen;
+ AST_LIST_LOCK(&debug_cache_head);
+ AST_LIST_INSERT_TAIL(&debug_cache_head, citem, list);
+ AST_LIST_UNLOCK(&debug_cache_head);
+ }
+ } else {
+ AST_LIST_LOCK(&debug_cache_head);
+ AST_LIST_TRAVERSE(&debug_cache_head, citem, list) {
+ if (citem->fr == fr) {
+ if (option_debug) {
+ ast_log(LOG_DEBUG, "[%ld] Reusing cache item %p with size %ld for a frame of size %ld\n", pthread_self(), citem, (long)citem->size, (long)datalen);
+ }
+ citem->inuse = 1;
+ break;
+ }
+ }
+ AST_LIST_UNLOCK(&debug_cache_head);
+#endif
}
#else
if (!(fr = ast_calloc(1, sizeof(*fr) + datalen)))
@@ -998,7 +1037,10 @@
{
#if !defined(LOW_MEMORY)
struct iax_frames *iax_frames;
+#ifdef DEBUG_IAX_CACHE
+ struct debug_cache *citem;
#endif
+#endif
/* Note: does not remove from scheduler! */
if (fr->direction == DIRECTION_INGRESS)
@@ -1013,12 +1055,40 @@
#if !defined(LOW_MEMORY)
if (!fr->cacheable || !(iax_frames = ast_threadstorage_get(&frame_cache, sizeof(*iax_frames)))) {
+#ifdef DEBUG_IAX_CACHE
+ struct debug_cache *citem;
+ AST_LIST_LOCK(&debug_cache_head);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&debug_cache_head, citem, list) {
+ if (citem->fr == fr) {
+ AST_LIST_REMOVE_CURRENT(&debug_cache_head, list);
+ if (option_debug) {
+ ast_log(LOG_DEBUG, "[%ld] Freeing frame %p of size %d, because threadstorage failed\n", pthread_self(), fr, citem->size);
+ }
+ ast_free(citem);
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+ AST_LIST_UNLOCK(&debug_cache_head);
+#endif
free(fr);
return;
}
fr->direction = 0;
AST_LIST_INSERT_HEAD(iax_frames, fr, list);
+#ifdef DEBUG_IAX_CACHE
+ AST_LIST_LOCK(&debug_cache_head);
+ AST_LIST_TRAVERSE(&debug_cache_head, citem, list) {
+ if (citem->fr == fr) {
+ if (option_debug) {
+ ast_log(LOG_DEBUG, "[%ld] Freeing IAX frame of size %ld\n", pthread_self(), (long)citem->size);
+ }
+ citem->inuse = 0;
+ break;
+ }
+ }
+ AST_LIST_UNLOCK(&debug_cache_head);
+#endif
#else
free(fr);
#endif
@@ -1035,7 +1105,34 @@
free(frames);
}
+
+#ifdef DEBUG_IAX_CACHE
+static int iax2_show_memory(int fd, int argc, char *argv[])
+{
+ struct debug_cache *citem;
+ int header = 0;
+ AST_LIST_LOCK(&debug_cache_head);
+ AST_LIST_TRAVERSE(&debug_cache_head, citem, list) {
+ if (!header) {
+ header++;
+ ast_cli(fd, " %11s %11s %5s %5s\n", "Thread ID", "Pointer", "Size", "InUse");
+ }
+ ast_cli(fd, "[%11.11ld] %11.11ld %5.5d %1s\n", (long)citem->owner, (long)citem->fr, (int)citem->size, citem->inuse ? "X" : "");
+ }
+ AST_LIST_UNLOCK(&debug_cache_head);
+ return 0;
+}
+
+static char iax2_show_memory_usage[] =
+"Usage: iax2 show memory\n"
+" Display currently cached IAX frames.\n";
+
+struct ast_cli_entry cli_iax2_show_memory =
+ { { "iax2", "show", "memory", NULL },
+ iax2_show_memory, "Show IAX2 frame memory cache allocations",
+ iax2_show_memory_usage };
#endif
+#endif
int iax_get_frames(void) { return frames; }
int iax_get_iframes(void) { return iframes; }