diff options
author | tilghman <tilghman@f38db490-d61c-443f-a65b-d21fe96a405b> | 2008-02-18 04:43:33 +0000 |
---|---|---|
committer | tilghman <tilghman@f38db490-d61c-443f-a65b-d21fe96a405b> | 2008-02-18 04:43:33 +0000 |
commit | a5efdcb36154ed5c5d6325ccc1b4dd99490b48e9 (patch) | |
tree | 1f70ab1c1a8d010ade37a053edebff107ad9fcc9 | |
parent | fc61184a360cc7cbbf3c4678368cfa045ff33d31 (diff) |
Context tracing for channels
(closes issue #11268)
Reported by: moy
Patches:
chantrace-datastored-encapsulated-rev94934.patch uploaded by moy (license 222)
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@103754 f38db490-d61c-443f-a65b-d21fe96a405b
-rw-r--r-- | CHANGES | 3 | ||||
-rw-r--r-- | build_tools/cflags.xml | 2 | ||||
-rw-r--r-- | funcs/func_channel.c | 24 | ||||
-rw-r--r-- | include/asterisk/channel.h | 28 | ||||
-rw-r--r-- | main/channel.c | 143 | ||||
-rw-r--r-- | main/cli.c | 10 | ||||
-rw-r--r-- | main/pbx.c | 3 |
7 files changed, 212 insertions, 1 deletions
@@ -513,3 +513,6 @@ Miscellaneous do not come from the remote party. * Added the 'n' option to the SpeechBackground application to tell it to not answer the channel if it has not already been answered. + * Added a compiler flag, CHANNEL_TRACE, which permits channel tracing to be + turned on, via the CHANNEL(trace) dialplan function. Could be useful for + dialplan debugging. diff --git a/build_tools/cflags.xml b/build_tools/cflags.xml index fa69c1263..b49974797 100644 --- a/build_tools/cflags.xml +++ b/build_tools/cflags.xml @@ -59,4 +59,6 @@ <member name="BUSYDETECT_DEBUG" displayname="Enable additional busy detection debugging"> <defaultenabled>no</defaultenabled> </member> + <member name="CHANNEL_TRACE" displayname="Enable CHANNEL(trace) function"> + </member> </category> diff --git a/funcs/func_channel.c b/funcs/func_channel.c index d920ac21f..42b3c68c9 100644 --- a/funcs/func_channel.c +++ b/funcs/func_channel.c @@ -71,6 +71,13 @@ static int func_channel_read(struct ast_channel *chan, const char *function, ast_copy_string(buf, ast_getformatname(chan->readformat), len); else if (!strcasecmp(data, "audiowriteformat")) ast_copy_string(buf, ast_getformatname(chan->writeformat), len); +#ifdef CHANNEL_TRACE + else if (!strcasecmp(data, "trace")) { + ast_channel_lock(chan); + ast_copy_string(buf, ast_channel_trace_is_enabled(chan) ? "1" : "0", len); + ast_channel_unlock(chan); + } +#endif else if (!strcasecmp(data, "tonezone") && chan->zone) locked_copy_string(chan, buf, chan->zone->country, len); else if (!strcasecmp(data, "language")) @@ -105,6 +112,20 @@ static int func_channel_write(struct ast_channel *chan, const char *function, locked_string_field_set(chan, language, value); else if (!strcasecmp(data, "musicclass")) locked_string_field_set(chan, musicclass, value); +#ifdef CHANNEL_TRACE + else if (!strcasecmp(data, "trace")) { + ast_channel_lock(chan); + if (ast_true(value)) + ret = ast_channel_trace_enable(chan); + else if (ast_false(value)) + ret = ast_channel_trace_disable(chan); + else { + ret = -1; + ast_log(LOG_WARNING, "Invalid value for CHANNEL(trace)."); + } + ast_channel_unlock(chan); + } +#endif else if (!strcasecmp(data, "tonezone")) { struct ind_tone_zone *new_zone; if (!(new_zone = ast_get_indication_zone(value))) { @@ -156,6 +177,9 @@ static struct ast_custom_function channel_function = { "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" +#ifdef CHANNEL_TRACE + "R/W trace whether or not context tracing is enabled\n" +#endif "\n" "chan_sip provides the following additional options:\n" "R/O rtpqos Get QOS information about the RTP stream\n" diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index da48c51de..0faddb870 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -826,6 +826,34 @@ void ast_channel_unregister(const struct ast_channel_tech *tech); */ const struct ast_channel_tech *ast_get_channel_tech(const char *name); +#ifdef CHANNEL_TRACE +/*! \brief Update the context backtrace if tracing is enabled + * \return Returns 0 on success, -1 on failure + */ +int ast_channel_trace_update(struct ast_channel *chan); + +/*! \brief Enable context tracing in the channel + * \return Returns 0 on success, -1 on failure + */ +int ast_channel_trace_enable(struct ast_channel *chan); + +/*! \brief Disable context tracing in the channel. + * \note Does not remove current trace entries + * \return Returns 0 on success, -1 on failure + */ +int ast_channel_trace_disable(struct ast_channel *chan); + +/*! \brief Whether or not context tracing is enabled + * \return Returns -1 when the trace is enabled. 0 if not. + */ +int ast_channel_trace_is_enabled(struct ast_channel *chan); + +/*! \brief Put the channel backtrace in a string + * \return Returns the amount of lines in the backtrace. -1 on error. + */ +int ast_channel_trace_serialize(struct ast_channel *chan, struct ast_str **out); +#endif + /*! \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. diff --git a/main/channel.c b/main/channel.c index 05f54ffdf..f8f1fe637 100644 --- a/main/channel.c +++ b/main/channel.c @@ -104,6 +104,22 @@ struct chanlist { AST_LIST_ENTRY(chanlist) list; }; +#ifdef CHANNEL_TRACE +/*! \brief Structure to hold channel context backtrace 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; +}; +#endif + /*! \brief the list of registered channel types */ static AST_LIST_HEAD_NOLOCK_STATIC(backends, chanlist); @@ -314,6 +330,133 @@ static struct ast_cli_entry cli_channel[] = { AST_CLI_DEFINE(handle_cli_core_show_channeltype, "Give more details on that channel type") }; +#ifdef CHANNEL_TRACE +/*! \brief Destructor for the channel trace datastore */ +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); +} + +/*! \brief Datastore to put the linked list of ast_chan_trace and trace status */ +const struct ast_datastore_info ast_chan_trace_datastore_info = { + .type = "ChanTrace", + .destroy = ast_chan_trace_destroy_cb +}; + +/*! \brief Put the channel backtrace in a string */ +int ast_channel_trace_serialize(struct ast_channel *chan, struct ast_str **buf) +{ + int total = 0; + struct ast_chan_trace *trace; + struct ast_chan_trace_data *traced; + struct ast_datastore *store; + + ast_channel_lock(chan); + store = ast_channel_datastore_find(chan, &ast_chan_trace_datastore_info, NULL); + if (!store) { + 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"); + total = -1; + break; + } + total++; + } + ast_channel_unlock(chan); + return total; +} + +/* !\brief Whether or not context tracing is enabled */ +int ast_channel_trace_is_enabled(struct ast_channel *chan) +{ + struct ast_datastore *store = ast_channel_datastore_find(chan, &ast_chan_trace_datastore_info, NULL); + if (!store) + return 0; + return ((struct ast_chan_trace_data *)store->data)->enabled; +} + +/*! \brief Update the context backtrace data if tracing is enabled */ +static int ast_channel_trace_data_update(struct ast_channel *chan, struct ast_chan_trace_data *traced) +{ + struct ast_chan_trace *trace; + if (!traced->enabled) + return 0; + /* If the last saved context does not match the current one + OR we have not saved any context 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 Update the context backtrace if tracing is enabled */ +int ast_channel_trace_update(struct ast_channel *chan) +{ + struct ast_datastore *store = ast_channel_datastore_find(chan, &ast_chan_trace_datastore_info, NULL); + if (!store) + return 0; + return ast_channel_trace_data_update(chan, store->data); +} + +/*! \brief Enable context tracing in the channel */ +int ast_channel_trace_enable(struct ast_channel *chan) +{ + struct ast_datastore *store = ast_channel_datastore_find(chan, &ast_chan_trace_datastore_info, NULL); + struct ast_chan_trace_data *traced; + if (!store) { + store = ast_channel_datastore_alloc(&ast_chan_trace_datastore_info, "ChanTrace"); + if (!store) + return -1; + traced = ast_calloc(1, sizeof(*traced)); + if (!traced) { + ast_channel_datastore_free(store); + return -1; + } + store->data = traced; + AST_LIST_HEAD_INIT_NOLOCK(&traced->trace); + ast_channel_datastore_add(chan, store); + } + ((struct ast_chan_trace_data *)store->data)->enabled = 1; + ast_channel_trace_data_update(chan, store->data); + return 0; +} + +/*! \brief Disable context tracing in the channel */ +int ast_channel_trace_disable(struct ast_channel *chan) +{ + struct ast_datastore *store = ast_channel_datastore_find(chan, &ast_chan_trace_datastore_info, NULL); + if (!store) + return 0; + ((struct ast_chan_trace_data *)store->data)->enabled = 0; + return 0; +} +#endif /* CHANNEL_TRACE */ + /*! \brief Checks to see if a channel is needing hang up */ int ast_check_hangup(struct ast_channel *chan) { diff --git a/main/cli.c b/main/cli.c index 8813c6a3f..806542102 100644 --- a/main/cli.c +++ b/main/cli.c @@ -987,6 +987,9 @@ static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_ar char nf[256], wf[256], rf[256]; long elapsed_seconds=0; int hour=0, min=0, sec=0; +#ifdef CHANNEL_TRACE + int trace_enabled; +#endif switch (cmd) { case CLI_INIT: @@ -1071,7 +1074,12 @@ static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_ar 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); - +#ifdef CHANNEL_TRACE + trace_enabled = ast_channel_trace_is_enabled(c); + ast_cli(a->fd, " Context Trace: %s\n", trace_enabled ? "Enabled" : "Disabled"); + if (trace_enabled && ast_channel_trace_serialize(c, &out)) + ast_cli(a->fd, " Trace:\n%s\n", out->str); +#endif ast_channel_unlock(c); return CLI_SUCCESS; } diff --git a/main/pbx.c b/main/pbx.c index 2df20327e..e584ea07d 100644 --- a/main/pbx.c +++ b/main/pbx.c @@ -2673,6 +2673,9 @@ static int pbx_extension_helper(struct ast_channel *c, struct ast_context *con, ast_copy_string(c->exten, exten, sizeof(c->exten)); c->priority = priority; pbx_substitute_variables(passdata, sizeof(passdata), c, e); +#ifdef CHANNEL_TRACE + ast_channel_trace_update(c); +#endif ast_debug(1, "Launching '%s'\n", app->name); if (VERBOSITY_ATLEAST(3)) { char tmp[80], tmp2[80], tmp3[EXT_DATA_SIZE]; |