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
This commit is contained in:
parent
fc61184a36
commit
a5efdcb361
3
CHANGES
3
CHANGES
|
@ -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.
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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.
|
||||
|
|
143
main/channel.c
143
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)
|
||||
{
|
||||
|
|
10
main/cli.c
10
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;
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
|
|
Reference in New Issue