Index: funcs/func_channel.c =================================================================== --- funcs/func_channel.c (revision 94934) +++ funcs/func_channel.c (working copy) @@ -71,6 +71,15 @@ ast_copy_string(buf, ast_getformatname(chan->readformat), len); else if (!strcasecmp(data, "audiowriteformat")) ast_copy_string(buf, ast_getformatname(chan->writeformat), len); + else if (!strcasecmp(data, "trace")) { + struct ast_datastore *datastore; + struct ast_chan_trace_data *traced; + ast_channel_lock(chan); + datastore = ast_channel_datastore_find(chan, &ast_chan_trace_datastore_info, NULL); + traced = datastore ? datastore->data : NULL; + ast_copy_string(buf, traced && traced->enabled ? "1" : "0", len); + ast_channel_unlock(chan); + } else if (!strcasecmp(data, "tonezone") && chan->zone) locked_copy_string(chan, buf, chan->zone->country, len); else if (!strcasecmp(data, "language")) @@ -105,6 +114,23 @@ locked_string_field_set(chan, language, value); else if (!strcasecmp(data, "musicclass")) locked_string_field_set(chan, musicclass, value); + else if (!strcasecmp(data, "trace")) { + struct ast_datastore *datastore; + struct ast_chan_trace_data *traced; + ast_channel_lock(chan); + datastore = ast_channel_datastore_find(chan, &ast_chan_trace_datastore_info, NULL); + traced = datastore ? datastore->data : NULL; + if (ast_true(value)) + ret = ast_channel_enable_context_trace(chan, traced); + else if (ast_false(value)) { + if (traced) + traced->enabled = 0; + } else { + ret = -1; + ast_log(LOG_WARNING, "Invalid value for CHANNEL(trace)."); + } + ast_channel_unlock(chan); + } else if (!strcasecmp(data, "tonezone")) { struct ind_tone_zone *new_zone; if (!(new_zone = ast_get_indication_zone(value))) { @@ -156,6 +182,7 @@ "R/W tonezone zone for indications played\n" "R/W txgain set txgain level on channel drivers that support it\n" "R/O videonativeformat format used natively for video\n" + "R/W trace whether or not context tracing is enabled\n" "\n" "chan_sip provides the following additional options:\n" "R/O rtpqos Get QOS information about the RTP stream\n" Index: include/asterisk/channel.h =================================================================== --- include/asterisk/channel.h (revision 94934) +++ include/asterisk/channel.h (working copy) @@ -226,6 +226,20 @@ int cid_tns; /*!< Callerid Transit Network Select */ }; +/*! \brief Structure to hold channel context trace data */ +struct ast_chan_trace_data { + int enabled; + AST_LIST_HEAD_NOLOCK(, ast_chan_trace) trace; +}; + +/*! \brief Structure to save contexts where an ast_chan has been into */ +struct ast_chan_trace { + char context[AST_MAX_CONTEXT]; + char exten[AST_MAX_EXTENSION]; + int priority; + AST_LIST_ENTRY(ast_chan_trace) entry; +}; + /*! \brief Structure to describe a channel "technology", ie a channel driver See for examples: @@ -355,6 +369,11 @@ */ extern unsigned long global_fin, global_fout; +/*! + * Datastore info for channel context traces + */ +extern const struct ast_datastore_info ast_chan_trace_datastore_info; + enum ast_channel_adsicpe { AST_ADSI_UNKNOWN, AST_ADSI_AVAILABLE, @@ -820,6 +839,12 @@ */ const struct ast_channel_tech *ast_get_channel_tech(const char *name); +/*! \brief Update context trace on channel. It MUST be called only if the channel trace is enabled */ +int ast_channel_update_trace(struct ast_channel *chan, struct ast_chan_trace_data *traced); + +/*! \brief Enable tracing in the channel. Must be called with the channel locked */ +int ast_channel_enable_context_trace(struct ast_channel *chan, struct ast_chan_trace_data *traced); + /*! \brief Hang up a channel * \note This function performs a hard hangup on a channel. Unlike the soft-hangup, this function * performs all stream stopping, etc, on the channel that needs to end. Index: include/asterisk/pbx.h =================================================================== --- include/asterisk/pbx.h (revision 94934) +++ include/asterisk/pbx.h (working copy) @@ -25,6 +25,7 @@ #include "asterisk/sched.h" #include "asterisk/chanvars.h" +#include "asterisk/channel.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { @@ -807,6 +808,11 @@ /*! * \note Will lock the channel. */ +int pbx_builtin_serialize_trace(struct ast_channel *chan, struct ast_str **buf, struct ast_chan_trace_data *traced); + +/*! + * \note Will lock the channel. + */ const char *pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name); /*! Index: main/channel.c =================================================================== --- main/channel.c (revision 94934) +++ main/channel.c (working copy) @@ -315,6 +315,21 @@ AST_CLI_DEFINE(handle_cli_core_show_channeltype, "Give more details on that channel type") }; +static void ast_chan_trace_destroy_cb(void *data) +{ + struct ast_chan_trace *trace; + struct ast_chan_trace_data *traced = data; + while ((trace = AST_LIST_REMOVE_HEAD(&traced->trace, entry))) { + ast_free(trace); + } + ast_free(traced); +} + +const struct ast_datastore_info ast_chan_trace_datastore_info = { + .type = "ChanTrace", + .destroy = ast_chan_trace_destroy_cb +}; + /*! \brief Checks to see if a channel is needing hang up */ int ast_check_hangup(struct ast_channel *chan) { @@ -1398,6 +1413,64 @@ clone->rawreadformat = clone->nativeformats; } +/*! \brief Update context trace on channel. It MUST be called only if the channel trace is enabled */ +int ast_channel_update_trace(struct ast_channel *chan, struct ast_chan_trace_data *traced) +{ + struct ast_datastore *datastore; + struct ast_chan_trace *trace; + /* if the trace data structure was not specified, we search it */ + if (!traced) { + datastore = ast_channel_datastore_find(chan, &ast_chan_trace_datastore_info, NULL); + if (!datastore) { + ast_log(LOG_ERROR, "No datastore in channel for context traces.\n"); + return -1; + } else + traced = datastore->data; + } + /* If we already started the trace and the last saved context does not match the current one + OR we have trace enabled and no context saved so far, + then save the current context */ + if ((!AST_LIST_EMPTY(&traced->trace) && strcasecmp(AST_LIST_FIRST(&traced->trace)->context, chan->context)) || + (AST_LIST_EMPTY(&traced->trace))) { + /* Just do some debug logging */ + if (AST_LIST_EMPTY(&traced->trace)) + ast_log(LOG_DEBUG, "Setting initial trace context to %s\n", chan->context); + else + ast_log(LOG_DEBUG, "Changing trace context from %s to %s\n", AST_LIST_FIRST(&traced->trace)->context, chan->context); + /* alloc or bail out */ + trace = ast_malloc(sizeof(*trace)); + if (!trace) + return -1; + /* save the current location and store it in the trace list */ + ast_copy_string(trace->context, chan->context, sizeof(trace->context)); + ast_copy_string(trace->exten, chan->exten, sizeof(trace->exten)); + trace->priority = chan->priority; + AST_LIST_INSERT_HEAD(&traced->trace, trace, entry); + } + return 0; +} + +/*! \brief Enable tracing in the channel. Must be called with the channel locked */ +int ast_channel_enable_context_trace(struct ast_channel *chan, struct ast_chan_trace_data *traced) +{ + struct ast_datastore *datastore; + /* if there is no trace data structure, we should allocate one to enable tracing */ + if (!traced) { + datastore = ast_channel_datastore_alloc(&ast_chan_trace_datastore_info, "ChanTrace"); + if (!datastore) + return -1; + traced = ast_calloc(1, sizeof(*traced)); + if (!traced) + return -1; + datastore->data = traced; + AST_LIST_HEAD_INIT_NOLOCK(&traced->trace); + ast_channel_datastore_add(chan, datastore); + } + traced->enabled = 1; + ast_channel_update_trace(chan, traced); + return 0; +} + /*! \brief Hangup a channel */ int ast_hangup(struct ast_channel *chan) { Index: main/cli.c =================================================================== --- main/cli.c (revision 94934) +++ main/cli.c (working copy) @@ -979,6 +979,8 @@ { struct ast_channel *c=NULL; struct timeval now; + struct ast_datastore *store; + struct ast_chan_trace_data *traced; struct ast_str *out = ast_str_alloca(2048); char cdrtime[256]; char nf[256], wf[256], rf[256]; @@ -1068,7 +1070,11 @@ ast_cli(a->fd," Variables:\n%s\n", out->str); if (c->cdr && ast_cdr_serialize_variables(c->cdr, &out, '=', '\n', 1)) ast_cli(a->fd," CDR Variables:\n%s\n", out->str); - + store = ast_channel_datastore_find(c, &ast_chan_trace_datastore_info, NULL); + traced = store ? store->data : NULL; + ast_cli(a->fd, " Context Trace: %s\n", traced && traced->enabled ? "Enabled" : "Disabled"); + if (traced && traced->enabled && pbx_builtin_serialize_trace(c, &out, traced)) + ast_cli(a->fd, " Trace:\n%s\n", out->str); ast_channel_unlock(c); return CLI_SUCCESS; } Index: main/pbx.c =================================================================== --- main/pbx.c (revision 94934) +++ main/pbx.c (working copy) @@ -2612,6 +2612,8 @@ { struct ast_exten *e; struct ast_app *app; + struct ast_datastore *datastore; + struct ast_chan_trace_data *traced; int res; struct pbx_find_info q = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */ char passdata[EXT_DATA_SIZE]; @@ -2648,6 +2650,10 @@ ast_copy_string(c->exten, exten, sizeof(c->exten)); c->priority = priority; pbx_substitute_variables(passdata, sizeof(passdata), c, e); + datastore = ast_channel_datastore_find(c, &ast_chan_trace_datastore_info, NULL); + traced = datastore ? datastore->data : NULL; + if (traced && traced->enabled) + ast_channel_update_trace(c, traced); ast_debug(1, "Launching '%s'\n", app->name); if (VERBOSITY_ATLEAST(3)) { char tmp[80], tmp2[80], tmp3[EXT_DATA_SIZE]; @@ -7116,6 +7122,37 @@ return total; } +int pbx_builtin_serialize_trace(struct ast_channel *chan, struct ast_str **buf, struct ast_chan_trace_data *traced) +{ + int total = 0; + struct ast_chan_trace *trace; + struct ast_datastore *store; + + if (!chan) + return 0; + ast_channel_lock(chan); + if (!traced) { + store = ast_channel_datastore_find(chan, &ast_chan_trace_datastore_info, NULL); + if (!store) { + ast_log(LOG_WARNING, "No trace to serialize!\n"); + ast_channel_unlock(chan); + return total; + } + traced = store->data; + } + (*buf)->used = 0; + (*buf)->str[0] = '\0'; + AST_LIST_TRAVERSE(&traced->trace, trace, entry) { + if (ast_str_append(buf, 0, "[%d] => %s, %s, %d\n", total, trace->context, trace->exten, trace->priority) < 0) { + ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n"); + break; + } + total++; + } + ast_channel_unlock(chan); + return total; +} + const char *pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name) { struct ast_var_t *variables;