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