Index: build_tools/cflags.xml
===================================================================
--- build_tools/cflags.xml (revision 94974)
+++ build_tools/cflags.xml (working copy)
@@ -48,4 +48,6 @@
+
+
Index: funcs/func_channel.c
===================================================================
--- funcs/func_channel.c (revision 94974)
+++ funcs/func_channel.c (working copy)
@@ -71,6 +71,13 @@
ast_copy_string(buf, ast_getformatname(chan->readformat), len);
else if (!strcasecmp(data, "audiowriteformat"))
ast_copy_string(buf, ast_getformatname(chan->writeformat), len);
+#ifdef CHAN_TRACE_FUNC
+ 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 @@
locked_string_field_set(chan, language, value);
else if (!strcasecmp(data, "musicclass"))
locked_string_field_set(chan, musicclass, value);
+#ifdef CHAN_TRACE_FUNC
+ 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 @@
"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 CHAN_TRACE_FUNC
+ "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"
Index: include/asterisk/channel.h
===================================================================
--- include/asterisk/channel.h (revision 94974)
+++ include/asterisk/channel.h (working copy)
@@ -820,6 +820,34 @@
*/
const struct ast_channel_tech *ast_get_channel_tech(const char *name);
+#ifdef CHAN_TRACE_FUNC
+/*! \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.
Index: main/channel.c
===================================================================
--- main/channel.c (revision 94974)
+++ main/channel.c (working copy)
@@ -104,6 +104,22 @@
AST_LIST_ENTRY(chanlist) list;
};
+#ifdef CHAN_TRACE_FUNC
+/*! \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);
@@ -315,6 +331,133 @@
AST_CLI_DEFINE(handle_cli_core_show_channeltype, "Give more details on that channel type")
};
+#ifdef CHAN_TRACE_FUNC
+/*! \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 /* CHAN_TRACE_FUNC */
+
/*! \brief Checks to see if a channel is needing hang up */
int ast_check_hangup(struct ast_channel *chan)
{
Index: main/cli.c
===================================================================
--- main/cli.c (revision 94974)
+++ main/cli.c (working copy)
@@ -984,6 +984,9 @@
char nf[256], wf[256], rf[256];
long elapsed_seconds=0;
int hour=0, min=0, sec=0;
+#ifdef CHAN_TRACE_FUNC
+ int trace_enabled;
+#endif
switch (cmd) {
case CLI_INIT:
@@ -1068,7 +1071,12 @@
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 CHAN_TRACE_FUNC
+ 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;
}
Index: main/pbx.c
===================================================================
--- main/pbx.c (revision 94974)
+++ main/pbx.c (working copy)
@@ -2648,6 +2648,9 @@
ast_copy_string(c->exten, exten, sizeof(c->exten));
c->priority = priority;
pbx_substitute_variables(passdata, sizeof(passdata), c, e);
+#ifdef CHAN_TRACE_FUNC
+ 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];