diff --git a/include/asterisk/backtrace.h b/include/asterisk/backtrace.h new file mode 100644 index 0000000000000000000000000000000000000000..fef1d86faa47c60f2e00487603c79fae416ffbea --- /dev/null +++ b/include/asterisk/backtrace.h @@ -0,0 +1,97 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2013, Digium, Inc. + * + * Matt Jordan <mjordan@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * \brief Asterisk backtrace generation + * + * This file provides backtrace generation utilities + */ + + +#ifndef BACKTRACE_H_ +#define BACKTRACE_H_ + +#define AST_MAX_BT_FRAMES 32 + +#ifdef HAVE_BKTR +#define ast_bt_get_addresses(bt) __ast_bt_get_addresses((bt)) +#define ast_bt_create() __ast_bt_create() +#define ast_bt_destroy(bt) __ast_bt_destroy((bt)) +#define ast_bt_get_symbols(addresses, num_frames) __ast_bt_get_symbols((addresses), (num_frames)) +#else +#define ast_bt_get_addresses(bt) 0 +#define ast_bt_create() NULL +#define ast_bt_destroy(bt) NULL +#define ast_bt_get_symbols(addresses, num_frames) NULL +#endif + +/* \brief + * + * A structure to hold backtrace information. This structure provides an easy means to + * store backtrace information or pass backtraces to other functions. + */ +struct ast_bt { + /*! The addresses of the stack frames. This is filled in by calling the glibc backtrace() function */ + void *addresses[AST_MAX_BT_FRAMES]; + /*! The number of stack frames in the backtrace */ + int num_frames; + /*! Tells if the ast_bt structure was dynamically allocated */ + unsigned int alloced:1; +}; + +#ifdef HAVE_BKTR + +/* \brief + * Allocates memory for an ast_bt and stores addresses and symbols. + * + * \return Returns NULL on failure, or the allocated ast_bt on success + * \since 1.6.1 + */ +struct ast_bt *__ast_bt_create(void); + +/* \brief + * Fill an allocated ast_bt with addresses + * + * \retval 0 Success + * \retval -1 Failure + * \since 1.6.1 + */ +int __ast_bt_get_addresses(struct ast_bt *bt); + +/* \brief + * + * Free dynamically allocated portions of an ast_bt + * + * \retval NULL. + * \since 1.6.1 + */ +void *__ast_bt_destroy(struct ast_bt *bt); + +/* \brief Retrieve symbols for a set of backtrace addresses + * + * \param addresses A list of addresses, such as the ->addresses structure element of struct ast_bt. + * \param num_frames Number of addresses in the addresses list + * \retval NULL Unable to allocate memory + * \return List of strings. This should be freed with a single call to free. + * \since 1.6.2.16 + */ +char **__ast_bt_get_symbols(void **addresses, size_t num_frames); + +#endif /* HAVE_BKTR */ + +#endif /* BACKTRACE_H_ */ diff --git a/include/asterisk/lock.h b/include/asterisk/lock.h index d7e895c77e29831dd54b6122411f005be2235b44..4b1752cc42fb305200e8071e53c3c4f30c078165 100644 --- a/include/asterisk/lock.h +++ b/include/asterisk/lock.h @@ -59,6 +59,7 @@ #include "asterisk/time.h" #endif +#include "asterisk/backtrace.h" #include "asterisk/logger.h" #include "asterisk/compiler.h" diff --git a/include/asterisk/logger.h b/include/asterisk/logger.h index 1632291df48dce1c27aeaad62e1048ab69d24942..09d2b4c7a6f0ec5b950fe073ae7b2e2263ad35ae 100644 --- a/include/asterisk/logger.h +++ b/include/asterisk/logger.h @@ -80,7 +80,10 @@ struct ast_callid; void ast_log_callid(int level, const char *file, int line, const char *function, struct ast_callid *callid, const char *fmt, ...) __attribute__((format(printf, 6, 7))); -void ast_backtrace(void); +/*! + * \brief Log a backtrace of the current thread's execution stack to the Asterisk log + */ +void ast_log_backtrace(void); /*! \brief Reload logger without rotating log files */ int logger_reload(void); @@ -362,63 +365,6 @@ void ast_callid_strnprint(char *buffer, size_t buffer_size, struct ast_callid *c #define ast_verb(level, ...) __ast_verbose(__FILE__, __LINE__, __PRETTY_FUNCTION__, level, __VA_ARGS__) #define ast_verb_callid(level, callid, ...) __ast_verbose_callid(__FILE__, __LINE__, __PRETTY_FUNCTION__, level, callid, __VA_ARGS__) -#ifndef _LOGGER_BACKTRACE_H -#define _LOGGER_BACKTRACE_H -#ifdef HAVE_BKTR -#define AST_MAX_BT_FRAMES 32 -/* \brief - * - * A structure to hold backtrace information. This structure provides an easy means to - * store backtrace information or pass backtraces to other functions. - */ -struct ast_bt { - /*! The addresses of the stack frames. This is filled in by calling the glibc backtrace() function */ - void *addresses[AST_MAX_BT_FRAMES]; - /*! The number of stack frames in the backtrace */ - int num_frames; - /*! Tells if the ast_bt structure was dynamically allocated */ - unsigned int alloced:1; -}; - -/* \brief - * Allocates memory for an ast_bt and stores addresses and symbols. - * - * \return Returns NULL on failure, or the allocated ast_bt on success - * \since 1.6.1 - */ -struct ast_bt *ast_bt_create(void); - -/* \brief - * Fill an allocated ast_bt with addresses - * - * \retval 0 Success - * \retval -1 Failure - * \since 1.6.1 - */ -int ast_bt_get_addresses(struct ast_bt *bt); - -/* \brief - * - * Free dynamically allocated portions of an ast_bt - * - * \retval NULL. - * \since 1.6.1 - */ -void *ast_bt_destroy(struct ast_bt *bt); - -/* \brief Retrieve symbols for a set of backtrace addresses - * - * \param addresses A list of addresses, such as the ->addresses structure element of struct ast_bt. - * \param num_frames Number of addresses in the addresses list - * \retval NULL Unable to allocate memory - * \return List of strings - * \since 1.6.2.16 - */ -char **ast_bt_get_symbols(void **addresses, size_t num_frames); - -#endif /* HAVE_BKTR */ -#endif /* _LOGGER_BACKTRACE_H */ - #if defined(__cplusplus) || defined(c_plusplus) } #endif diff --git a/main/astmm.c b/main/astmm.c index 9396d09873e8a06b4f2f8d85d41365d74c44af72..d01b745b0569b6ce0040beb0a5a66c8ae1be7d08 100644 --- a/main/astmm.c +++ b/main/astmm.c @@ -42,6 +42,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/lock.h" #include "asterisk/strings.h" #include "asterisk/unaligned.h" +#include "asterisk/backtrace.h" /*! * The larger the number the faster memory can be freed. @@ -78,13 +79,13 @@ static FILE *mmlog; struct ast_region { AST_LIST_ENTRY(ast_region) node; + struct ast_bt *bt; size_t len; unsigned int cache; /* region was allocated as part of a cache pool */ unsigned int lineno; enum func_type which; char file[64]; char func[40]; - /*! * \brief Lower guard fence. * @@ -157,6 +158,25 @@ AST_MUTEX_DEFINE_STATIC_NOTRACKING(reglock); } \ } while (0) +static void print_backtrace(struct ast_bt *bt) +{ + int i = 0; + char **strings; + + if (!bt) { + return; + } + + if ((strings = ast_bt_get_symbols(bt->addresses, bt->num_frames))) { + astmm_log("Memory allocation backtrace:\n"); + for (i = 3; i < bt->num_frames - 2; i++) { + astmm_log("#%d: [%p] %s\n", i - 3, bt->addresses[i], strings[i]); + } + free(strings); + } +} + + /*! * \internal * @@ -191,6 +211,7 @@ static void *__ast_alloc_region(size_t size, const enum func_type which, const c reg->cache = cache; reg->lineno = lineno; reg->which = which; + reg->bt = ast_bt_create(); ast_copy_string(reg->file, file, sizeof(reg->file)); ast_copy_string(reg->func, func, sizeof(reg->func)); @@ -262,6 +283,7 @@ static void region_data_check(struct ast_region *reg) if (*pos != FREED_MAGIC) { astmm_log("WARNING: Memory corrupted after free of %p allocated at %s %s() line %d\n", reg->data, reg->file, reg->func, reg->lineno); + print_backtrace(reg->bt); my_do_crash(); break; } @@ -321,6 +343,9 @@ static void region_free(struct ast_freed_regions *freed, struct ast_region *reg) if (old) { region_data_check(old); + if (old->bt) { + old->bt = ast_bt_destroy(old->bt); + } free(old); } } @@ -380,12 +405,14 @@ static void region_check_fences(struct ast_region *reg) if (*fence != FENCE_MAGIC) { astmm_log("WARNING: Low fence violation of %p allocated at %s %s() line %d\n", reg->data, reg->file, reg->func, reg->lineno); + print_backtrace(reg->bt); my_do_crash(); } fence = (unsigned int *) (reg->data + reg->len); if (get_unaligned_uint32(fence) != FENCE_MAGIC) { astmm_log("WARNING: High fence violation of %p allocated at %s %s() line %d\n", reg->data, reg->file, reg->func, reg->lineno); + print_backtrace(reg->bt); my_do_crash(); } } diff --git a/main/astobj2.c b/main/astobj2.c index 9f5e05d4312013e424d751acd190842c5c5e8ce4..5bfba4e68ad51eb6d840cca80cac6a18a393384b 100644 --- a/main/astobj2.c +++ b/main/astobj2.c @@ -525,7 +525,7 @@ int __ao2_ref_debug(void *user_data, int delta, const char *tag, const char *fil struct astobj2 *obj = INTERNAL_OBJ(user_data); if (obj == NULL) { - ast_backtrace(); + ast_log_backtrace(); ast_assert(0); return -1; } diff --git a/main/backtrace.c b/main/backtrace.c new file mode 100644 index 0000000000000000000000000000000000000000..001699d0016bae9f8a10d7eedb0557c9b333a2c6 --- /dev/null +++ b/main/backtrace.c @@ -0,0 +1,222 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2013, Digium, Inc. + * + * Matt Jordan <mjordan@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * \brief Asterisk backtrace generation + * + * This file provides backtrace generation utilities + */ + +/*** MODULEINFO + <support_level>core</support_level> + ***/ + +#include "asterisk.h" +ASTERISK_FILE_VERSION(__FILE__, "$Revision$"); + +#include "asterisk/backtrace.h" +#include "asterisk/utils.h" +#include "asterisk/strings.h" + +#ifdef HAVE_BKTR +#include <execinfo.h> +#if defined(HAVE_DLADDR) && defined(HAVE_BFD) && defined(BETTER_BACKTRACES) +#include <dlfcn.h> +#include <bfd.h> +#endif + +/* Undefine the overrides for memory allocation. astmm.c uses these functions + * as well. + */ +#undef calloc +#undef malloc +#undef free +#undef realloc + +struct ast_bt *__ast_bt_create(void) +{ + struct ast_bt *bt = calloc(1, sizeof(*bt)); + if (!bt) { + return NULL; + } + bt->alloced = 1; + + ast_bt_get_addresses(bt); + + return bt; +} + +int __ast_bt_get_addresses(struct ast_bt *bt) +{ + bt->num_frames = backtrace(bt->addresses, AST_MAX_BT_FRAMES); + return 0; +} + +void *__ast_bt_destroy(struct ast_bt *bt) +{ + if (bt->alloced) { + free(bt); + } + return NULL; +} + +char **__ast_bt_get_symbols(void **addresses, size_t num_frames) +{ + char **strings = NULL; +#if defined(BETTER_BACKTRACES) + int stackfr; + bfd *bfdobj; /* bfd.h */ + Dl_info dli; /* dlfcn.h */ + long allocsize; + asymbol **syms = NULL; /* bfd.h */ + bfd_vma offset; /* bfd.h */ + const char *lastslash; + asection *section; + const char *file, *func; + unsigned int line; + char address_str[128]; + char msg[1024]; + size_t strings_size; + size_t *eachlen; +#endif + +#if defined(BETTER_BACKTRACES) + strings_size = num_frames * sizeof(*strings); + eachlen = calloc(num_frames, sizeof(*eachlen)); + + if (!(strings = calloc(num_frames, sizeof(*strings)))) { + return NULL; + } + + for (stackfr = 0; stackfr < num_frames; stackfr++) { + int found = 0, symbolcount; + + msg[0] = '\0'; + + if (!dladdr(addresses[stackfr], &dli)) { + continue; + } + + if (strcmp(dli.dli_fname, "asterisk") == 0) { + char asteriskpath[256]; + if (!(dli.dli_fname = ast_utils_which("asterisk", asteriskpath, sizeof(asteriskpath)))) { + /* This will fail to find symbols */ + dli.dli_fname = "asterisk"; + } + } + + lastslash = strrchr(dli.dli_fname, '/'); + if ( (bfdobj = bfd_openr(dli.dli_fname, NULL)) && + bfd_check_format(bfdobj, bfd_object) && + (allocsize = bfd_get_symtab_upper_bound(bfdobj)) > 0 && + (syms = malloc(allocsize)) && + (symbolcount = bfd_canonicalize_symtab(bfdobj, syms))) { + + if (bfdobj->flags & DYNAMIC) { + offset = addresses[stackfr] - dli.dli_fbase; + } else { + offset = addresses[stackfr] - (void *)0; + } + + for (section = bfdobj->sections; section; section = section->next) { + if ( !bfd_get_section_flags(bfdobj, section) & SEC_ALLOC || + section->vma > offset || + section->size + section->vma < offset) { + continue; + } + + if (!bfd_find_nearest_line(bfdobj, section, syms, offset - section->vma, &file, &func, &line)) { + continue; + } + + /* file can possibly be null even with a success result from bfd_find_nearest_line */ + file = file ? file : ""; + + /* Stack trace output */ + found++; + if ((lastslash = strrchr(file, '/'))) { + const char *prevslash; + for (prevslash = lastslash - 1; *prevslash != '/' && prevslash >= file; prevslash--); + if (prevslash >= file) { + lastslash = prevslash; + } + } + if (dli.dli_saddr == NULL) { + address_str[0] = '\0'; + } else { + snprintf(address_str, sizeof(address_str), " (%p+%lX)", + dli.dli_saddr, + (unsigned long) (addresses[stackfr] - dli.dli_saddr)); + } + snprintf(msg, sizeof(msg), "%s:%u %s()%s", + lastslash ? lastslash + 1 : file, line, + S_OR(func, "???"), + address_str); + + break; /* out of section iteration */ + } + } + if (bfdobj) { + bfd_close(bfdobj); + free(syms); + } + + /* Default output, if we cannot find the information within BFD */ + if (!found) { + if (dli.dli_saddr == NULL) { + address_str[0] = '\0'; + } else { + snprintf(address_str, sizeof(address_str), " (%p+%lX)", + dli.dli_saddr, + (unsigned long) (addresses[stackfr] - dli.dli_saddr)); + } + snprintf(msg, sizeof(msg), "%s %s()%s", + lastslash ? lastslash + 1 : dli.dli_fname, + S_OR(dli.dli_sname, "<unknown>"), + address_str); + } + + if (!ast_strlen_zero(msg)) { + char **tmp; + eachlen[stackfr] = strlen(msg); + if (!(tmp = realloc(strings, strings_size + eachlen[stackfr] + 1))) { + free(strings); + strings = NULL; + break; /* out of stack frame iteration */ + } + strings = tmp; + strings[stackfr] = (char *) strings + strings_size; + ast_copy_string(strings[stackfr], msg, eachlen[stackfr] + 1); + strings_size += eachlen[stackfr] + 1; + } + } + + if (strings) { + /* Recalculate the offset pointers */ + strings[0] = (char *) strings + num_frames * sizeof(*strings); + for (stackfr = 1; stackfr < num_frames; stackfr++) { + strings[stackfr] = strings[stackfr - 1] + eachlen[stackfr - 1] + 1; + } + } +#else /* !defined(BETTER_BACKTRACES) */ + strings = backtrace_symbols(addresses, num_frames); +#endif /* defined(BETTER_BACKTRACES) */ + return strings; +} + +#endif /* HAVE_BKTR */ diff --git a/main/logger.c b/main/logger.c index fd0310a504574759ee7034943144ee7d5aa5adf8..ae1537acdc04df765026fbc3c47bb1adef5da591 100644 --- a/main/logger.c +++ b/main/logger.c @@ -45,6 +45,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") /* When we include logger.h again it will trample on some stuff in syslog.h, but * nothing we care about in here. */ #include <syslog.h> +#include <signal.h> +#include <time.h> +#include <sys/stat.h> +#include <fcntl.h> #include "asterisk/_private.h" #include "asterisk/paths.h" /* use ast_config_AST_LOG_DIR */ @@ -64,19 +68,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/syslog.h" #include "asterisk/buildinfo.h" #include "asterisk/ast_version.h" - -#include <signal.h> -#include <time.h> -#include <sys/stat.h> -#include <fcntl.h> -#ifdef HAVE_BKTR -#include <execinfo.h> -#define MAX_BACKTRACE_FRAMES 20 -# if defined(HAVE_DLADDR) && defined(HAVE_BFD) && defined(BETTER_BACKTRACES) -# include <dlfcn.h> -# include <bfd.h> -# endif -#endif +#include "asterisk/backtrace.h" /*** DOCUMENTATION ***/ @@ -1576,189 +1568,8 @@ void ast_log_callid(int level, const char *file, int line, const char *function, va_end(ap); } -#ifdef HAVE_BKTR - -struct ast_bt *ast_bt_create(void) -{ - struct ast_bt *bt = ast_calloc(1, sizeof(*bt)); - if (!bt) { - ast_log(LOG_ERROR, "Unable to allocate memory for backtrace structure!\n"); - return NULL; - } - - bt->alloced = 1; - - ast_bt_get_addresses(bt); - - return bt; -} - -int ast_bt_get_addresses(struct ast_bt *bt) -{ - bt->num_frames = backtrace(bt->addresses, AST_MAX_BT_FRAMES); - - return 0; -} - -void *ast_bt_destroy(struct ast_bt *bt) -{ - if (bt->alloced) { - ast_free(bt); - } - - return NULL; -} - -char **ast_bt_get_symbols(void **addresses, size_t num_frames) -{ - char **strings = NULL; -#if defined(BETTER_BACKTRACES) - int stackfr; - bfd *bfdobj; /* bfd.h */ - Dl_info dli; /* dlfcn.h */ - long allocsize; - asymbol **syms = NULL; /* bfd.h */ - bfd_vma offset; /* bfd.h */ - const char *lastslash; - asection *section; - const char *file, *func; - unsigned int line; - char address_str[128]; - char msg[1024]; - size_t strings_size; - size_t *eachlen; -#endif - -#if defined(BETTER_BACKTRACES) - strings_size = num_frames * sizeof(*strings); - eachlen = ast_calloc(num_frames, sizeof(*eachlen)); - - if (!(strings = ast_calloc(num_frames, sizeof(*strings)))) { - return NULL; - } - - for (stackfr = 0; stackfr < num_frames; stackfr++) { - int found = 0, symbolcount; - - msg[0] = '\0'; - - if (!dladdr(addresses[stackfr], &dli)) { - continue; - } - - if (strcmp(dli.dli_fname, "asterisk") == 0) { - char asteriskpath[256]; - if (!(dli.dli_fname = ast_utils_which("asterisk", asteriskpath, sizeof(asteriskpath)))) { - /* This will fail to find symbols */ - ast_debug(1, "Failed to find asterisk binary for debug symbols.\n"); - dli.dli_fname = "asterisk"; - } - } - - lastslash = strrchr(dli.dli_fname, '/'); - if ( (bfdobj = bfd_openr(dli.dli_fname, NULL)) && - bfd_check_format(bfdobj, bfd_object) && - (allocsize = bfd_get_symtab_upper_bound(bfdobj)) > 0 && - (syms = ast_malloc(allocsize)) && - (symbolcount = bfd_canonicalize_symtab(bfdobj, syms))) { - - if (bfdobj->flags & DYNAMIC) { - offset = addresses[stackfr] - dli.dli_fbase; - } else { - offset = addresses[stackfr] - (void *) 0; - } - - for (section = bfdobj->sections; section; section = section->next) { - if ( !bfd_get_section_flags(bfdobj, section) & SEC_ALLOC || - section->vma > offset || - section->size + section->vma < offset) { - continue; - } - - if (!bfd_find_nearest_line(bfdobj, section, syms, offset - section->vma, &file, &func, &line)) { - continue; - } - - /* file can possibly be null even with a success result from bfd_find_nearest_line */ - file = file ? file : ""; - - /* Stack trace output */ - found++; - if ((lastslash = strrchr(file, '/'))) { - const char *prevslash; - for (prevslash = lastslash - 1; *prevslash != '/' && prevslash >= file; prevslash--); - if (prevslash >= file) { - lastslash = prevslash; - } - } - if (dli.dli_saddr == NULL) { - address_str[0] = '\0'; - } else { - snprintf(address_str, sizeof(address_str), " (%p+%lX)", - dli.dli_saddr, - (unsigned long) (addresses[stackfr] - dli.dli_saddr)); - } - snprintf(msg, sizeof(msg), "%s:%u %s()%s", - lastslash ? lastslash + 1 : file, line, - S_OR(func, "???"), - address_str); - - break; /* out of section iteration */ - } - } - if (bfdobj) { - bfd_close(bfdobj); - if (syms) { - ast_free(syms); - } - } - - /* Default output, if we cannot find the information within BFD */ - if (!found) { - if (dli.dli_saddr == NULL) { - address_str[0] = '\0'; - } else { - snprintf(address_str, sizeof(address_str), " (%p+%lX)", - dli.dli_saddr, - (unsigned long) (addresses[stackfr] - dli.dli_saddr)); - } - snprintf(msg, sizeof(msg), "%s %s()%s", - lastslash ? lastslash + 1 : dli.dli_fname, - S_OR(dli.dli_sname, "<unknown>"), - address_str); - } - - if (!ast_strlen_zero(msg)) { - char **tmp; - eachlen[stackfr] = strlen(msg); - if (!(tmp = ast_realloc(strings, strings_size + eachlen[stackfr] + 1))) { - ast_free(strings); - strings = NULL; - break; /* out of stack frame iteration */ - } - strings = tmp; - strings[stackfr] = (char *) strings + strings_size; - ast_copy_string(strings[stackfr], msg, eachlen[stackfr] + 1); - strings_size += eachlen[stackfr] + 1; - } - } - - if (strings) { - /* Recalculate the offset pointers */ - strings[0] = (char *) strings + num_frames * sizeof(*strings); - for (stackfr = 1; stackfr < num_frames; stackfr++) { - strings[stackfr] = strings[stackfr - 1] + eachlen[stackfr - 1] + 1; - } - } -#else /* !defined(BETTER_BACKTRACES) */ - strings = backtrace_symbols(addresses, num_frames); -#endif /* defined(BETTER_BACKTRACES) */ - return strings; -} - -#endif /* HAVE_BKTR */ -void ast_backtrace(void) +void ast_log_backtrace(void) { #ifdef HAVE_BKTR struct ast_bt *bt; diff --git a/utils/extconf.c b/utils/extconf.c index c62001a7768445cc945896f5f57fc1013058f3c1..c679c4c3e31c7475f0813ca6b5e01d1c49ff2ef6 100644 --- a/utils/extconf.c +++ b/utils/extconf.c @@ -110,7 +110,7 @@ struct ast_channel #define VERBOSE_PREFIX_3 " -- " #define VERBOSE_PREFIX_4 " > " -void ast_backtrace(void); +void ast_log_backtrace(void); void ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt, ...) __attribute__((format(printf, 5, 6)));