<p>Friendly Automation <strong>merged</strong> this change.</p><p><a href="https://gerrit.asterisk.org/10766">View Change</a></p><div style="white-space:pre-wrap">Approvals:
George Joseph: Looks good to me, approved
Friendly Automation: Approved for Submit
</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">stasis: Add statistics gathering in developer mode.<br><br>This change adds statistics gathering to Stasis topics,<br>subscriptions, and message types. These can be viewed using<br>CLI commands and provide insight into how Stasis is used<br>and how long certain operations take to execute.<br><br>These are only available when Asterisk is compiled in<br>developer mode and do not have any impact under normal<br>operation.<br><br>ASTERISK-28117<br><br>Change-Id: I94411b53767f89ee01714daaecf0c2f1666e863f<br>---<br>M include/asterisk/stasis.h<br>M include/asterisk/stasis_internal.h<br>M include/asterisk/stasis_message_router.h<br>M main/asterisk.c<br>M main/asterisk.exports.in<br>M main/stasis.c<br>M main/stasis_cache.c<br>M main/stasis_message_router.c<br>8 files changed, 848 insertions(+), 14 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/include/asterisk/stasis.h b/include/asterisk/stasis.h</span><br><span>index 6d423d9..2e274a6 100644</span><br><span>--- a/include/asterisk/stasis.h</span><br><span>+++ b/include/asterisk/stasis.h</span><br><span>@@ -604,8 +604,14 @@</span><br><span> * has been subscribed. This occurs immediately before accepted message</span><br><span> * types can be set and the callback must expect to receive it.</span><br><span> */</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+struct stasis_subscription *__stasis_subscribe(struct stasis_topic *topic,</span><br><span style="color: hsl(120, 100%, 40%);">+ stasis_subscription_cb callback, void *data, const char *file, int lineno, const char *func);</span><br><span style="color: hsl(120, 100%, 40%);">+#define stasis_subscribe(topic, callback, data) __stasis_subscribe(topic, callback, data, __FILE__, __LINE__, __PRETTY_FUNCTION__)</span><br><span style="color: hsl(120, 100%, 40%);">+#else</span><br><span> struct stasis_subscription *stasis_subscribe(struct stasis_topic *topic,</span><br><span> stasis_subscription_cb callback, void *data);</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span> </span><br><span> /*!</span><br><span> * \brief Create a subscription whose callbacks occur on a thread pool</span><br><span>@@ -633,8 +639,14 @@</span><br><span> * has been subscribed. This occurs immediately before accepted message</span><br><span> * types can be set and the callback must expect to receive it.</span><br><span> */</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+struct stasis_subscription *__stasis_subscribe_pool(struct stasis_topic *topic,</span><br><span style="color: hsl(120, 100%, 40%);">+ stasis_subscription_cb callback, void *data, const char *file, int lineno, const char *func);</span><br><span style="color: hsl(120, 100%, 40%);">+#define stasis_subscribe_pool(topic, callback, data) __stasis_subscribe_pool(topic, callback, data, __FILE__, __LINE__, __PRETTY_FUNCTION__)</span><br><span style="color: hsl(120, 100%, 40%);">+#else</span><br><span> struct stasis_subscription *stasis_subscribe_pool(struct stasis_topic *topic,</span><br><span> stasis_subscription_cb callback, void *data);</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span> </span><br><span> /*!</span><br><span> * \brief Indicate to a subscription that we are interested in a message type.</span><br><span>diff --git a/include/asterisk/stasis_internal.h b/include/asterisk/stasis_internal.h</span><br><span>index bc6122c..c9df032 100644</span><br><span>--- a/include/asterisk/stasis_internal.h</span><br><span>+++ b/include/asterisk/stasis_internal.h</span><br><span>@@ -60,11 +60,23 @@</span><br><span> * \return \c NULL on error.</span><br><span> * \since 12</span><br><span> */</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+struct stasis_subscription *internal_stasis_subscribe(</span><br><span style="color: hsl(120, 100%, 40%);">+ struct stasis_topic *topic,</span><br><span style="color: hsl(120, 100%, 40%);">+ stasis_subscription_cb callback,</span><br><span style="color: hsl(120, 100%, 40%);">+ void *data,</span><br><span style="color: hsl(120, 100%, 40%);">+ int needs_mailbox,</span><br><span style="color: hsl(120, 100%, 40%);">+ int use_thread_pool,</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *file,</span><br><span style="color: hsl(120, 100%, 40%);">+ int lineno,</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *func);</span><br><span style="color: hsl(120, 100%, 40%);">+#else</span><br><span> struct stasis_subscription *internal_stasis_subscribe(</span><br><span> struct stasis_topic *topic,</span><br><span> stasis_subscription_cb callback,</span><br><span> void *data,</span><br><span> int needs_mailbox,</span><br><span> int use_thread_pool);</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span> </span><br><span> #endif /* STASIS_INTERNAL_H_ */</span><br><span>diff --git a/include/asterisk/stasis_message_router.h b/include/asterisk/stasis_message_router.h</span><br><span>index 9897d62..93a2140 100644</span><br><span>--- a/include/asterisk/stasis_message_router.h</span><br><span>+++ b/include/asterisk/stasis_message_router.h</span><br><span>@@ -55,8 +55,14 @@</span><br><span> *</span><br><span> * \since 12</span><br><span> */</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+struct stasis_message_router *__stasis_message_router_create(</span><br><span style="color: hsl(120, 100%, 40%);">+ struct stasis_topic *topic, const char *file, int lineno, const char *func);</span><br><span style="color: hsl(120, 100%, 40%);">+#define stasis_message_router_create(topic) __stasis_message_router_create(topic, __FILE__, __LINE__, __PRETTY_FUNCTION__)</span><br><span style="color: hsl(120, 100%, 40%);">+#else</span><br><span> struct stasis_message_router *stasis_message_router_create(</span><br><span> struct stasis_topic *topic);</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span> </span><br><span> /*!</span><br><span> * \brief Create a new message router object.</span><br><span>@@ -71,8 +77,14 @@</span><br><span> *</span><br><span> * \since 12.8.0</span><br><span> */</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+struct stasis_message_router *__stasis_message_router_create_pool(</span><br><span style="color: hsl(120, 100%, 40%);">+ struct stasis_topic *topic, const char *file, int lineno, const char *func);</span><br><span style="color: hsl(120, 100%, 40%);">+#define stasis_message_router_create_pool(topic) __stasis_message_router_create_pool(topic, __FILE__, __LINE__, __PRETTY_FUNCTION__)</span><br><span style="color: hsl(120, 100%, 40%);">+#else</span><br><span> struct stasis_message_router *stasis_message_router_create_pool(</span><br><span> struct stasis_topic *topic);</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span> </span><br><span> /*!</span><br><span> * \brief Unsubscribe the router from the upstream topic.</span><br><span>diff --git a/main/asterisk.c b/main/asterisk.c</span><br><span>index 7eb49df..36e956f 100644</span><br><span>--- a/main/asterisk.c</span><br><span>+++ b/main/asterisk.c</span><br><span>@@ -4070,6 +4070,8 @@</span><br><span> check_init(ast_tps_init(), "Task Processor Core");</span><br><span> check_init(ast_fd_init(), "File Descriptor Debugging");</span><br><span> check_init(ast_pbx_init(), "ast_pbx_init");</span><br><span style="color: hsl(120, 100%, 40%);">+ check_init(aco_init(), "Configuration Option Framework");</span><br><span style="color: hsl(120, 100%, 40%);">+ check_init(stasis_init(), "Stasis");</span><br><span> #ifdef TEST_FRAMEWORK</span><br><span> check_init(ast_test_init(), "Test Framework");</span><br><span> #endif</span><br><span>@@ -4082,9 +4084,7 @@</span><br><span> check_init(ast_format_init(), "Formats");</span><br><span> check_init(ast_format_cache_init(), "Format Cache");</span><br><span> check_init(ast_codec_builtin_init(), "Built-in Codecs");</span><br><span style="color: hsl(0, 100%, 40%);">- check_init(aco_init(), "Configuration Option Framework");</span><br><span> check_init(ast_bucket_init(), "Bucket API");</span><br><span style="color: hsl(0, 100%, 40%);">- check_init(stasis_init(), "Stasis");</span><br><span> check_init(ast_stasis_system_init(), "Stasis system-level information");</span><br><span> check_init(ast_endpoint_stasis_init(), "Stasis Endpoint");</span><br><span> </span><br><span>diff --git a/main/asterisk.exports.in b/main/asterisk.exports.in</span><br><span>index f3549e6..0232855 100644</span><br><span>--- a/main/asterisk.exports.in</span><br><span>+++ b/main/asterisk.exports.in</span><br><span>@@ -27,6 +27,7 @@</span><br><span> LINKER_SYMBOL_PREFIXstrsep;</span><br><span> LINKER_SYMBOL_PREFIXsetenv;</span><br><span> LINKER_SYMBOL_PREFIXstasis_*;</span><br><span style="color: hsl(120, 100%, 40%);">+ LINKER_SYMBOL_PREFIX__stasis_*;</span><br><span> LINKER_SYMBOL_PREFIXunsetenv;</span><br><span> LINKER_SYMBOL_PREFIXstrcasestr;</span><br><span> LINKER_SYMBOL_PREFIXstrnlen;</span><br><span>diff --git a/main/stasis.c b/main/stasis.c</span><br><span>index 69ec1a5..3216e21 100644</span><br><span>--- a/main/stasis.c</span><br><span>+++ b/main/stasis.c</span><br><span>@@ -41,6 +41,9 @@</span><br><span> #include "asterisk/stasis_bridges.h"</span><br><span> #include "asterisk/stasis_endpoints.h"</span><br><span> #include "asterisk/config_options.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/cli.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span> </span><br><span> /*** DOCUMENTATION</span><br><span> <managerEvent language="en_US" name="UserEvent"></span><br><span>@@ -304,14 +307,67 @@</span><br><span> </span><br><span> STASIS_MESSAGE_TYPE_DEFN(stasis_subscription_change_type);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! The number of buckets to use for topic statistics */</span><br><span style="color: hsl(120, 100%, 40%);">+#define TOPIC_STATISTICS_BUCKETS 57</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! The number of buckets to use for subscription statistics */</span><br><span style="color: hsl(120, 100%, 40%);">+#define SUBSCRIPTION_STATISTICS_BUCKETS 57</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Container which stores statistics for topics */</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ao2_container *topic_statistics;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Container which stores statistics for subscriptions */</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ao2_container *subscription_statistics;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \internal */</span><br><span style="color: hsl(120, 100%, 40%);">+struct stasis_message_type_statistics {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! \brief The number of messages of this published */</span><br><span style="color: hsl(120, 100%, 40%);">+ int published;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! \brief The number of messages of this that did not reach a subscriber */</span><br><span style="color: hsl(120, 100%, 40%);">+ int unused;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! \brief The stasis message type */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct stasis_message_type *message_type;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Lock to protect the message types vector */</span><br><span style="color: hsl(120, 100%, 40%);">+AST_MUTEX_DEFINE_STATIC(message_type_statistics_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Vector containing message type information */</span><br><span style="color: hsl(120, 100%, 40%);">+static AST_VECTOR(, struct stasis_message_type_statistics) message_type_statistics;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \internal */</span><br><span style="color: hsl(120, 100%, 40%);">+struct stasis_topic_statistics {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! \brief The number of messages that were not dispatched to any subscriber */</span><br><span style="color: hsl(120, 100%, 40%);">+ int messages_not_dispatched;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! \brief The number of messages that were dispatched to at least 1 subscriber */</span><br><span style="color: hsl(120, 100%, 40%);">+ int messages_dispatched;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! \brief Highest time spent dispatching messages to subscribers */</span><br><span style="color: hsl(120, 100%, 40%);">+ int64_t highest_time_dispatched;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! \brief Lowest time spent dispatching messages to subscribers */</span><br><span style="color: hsl(120, 100%, 40%);">+ int64_t lowest_time_dispatched;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! \brief The number of subscribers to this topic */</span><br><span style="color: hsl(120, 100%, 40%);">+ int subscriber_count;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! \brief Name of the topic */</span><br><span style="color: hsl(120, 100%, 40%);">+ char name[0];</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! \internal */</span><br><span> struct stasis_topic {</span><br><span style="color: hsl(0, 100%, 40%);">- char *name;</span><br><span> /*! Variable length array of the subscribers */</span><br><span> AST_VECTOR(, struct stasis_subscription *) subscribers;</span><br><span> </span><br><span> /*! Topics forwarding into this topic */</span><br><span> AST_VECTOR(, struct stasis_topic *) upstream_topics;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+ struct stasis_topic_statistics *statistics;</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! Name of the topic */</span><br><span style="color: hsl(120, 100%, 40%);">+ char name[0];</span><br><span> };</span><br><span> </span><br><span> /* Forward declarations for the tightly-coupled subscription object */</span><br><span>@@ -337,28 +393,54 @@</span><br><span> * unsubscribed before we get here. */</span><br><span> ast_assert(AST_VECTOR_SIZE(&topic->subscribers) == 0);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- ast_free(topic->name);</span><br><span style="color: hsl(0, 100%, 40%);">- topic->name = NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> AST_VECTOR_FREE(&topic->subscribers);</span><br><span> AST_VECTOR_FREE(&topic->upstream_topics);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+ if (topic->statistics) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_unlink(topic_statistics, topic->statistics);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(topic->statistics, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+static struct stasis_topic_statistics *stasis_topic_statistics_create(const char *name)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct stasis_topic_statistics *statistics;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ statistics = ao2_alloc(sizeof(*statistics) + strlen(name) + 1, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!statistics) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ strcpy(statistics->name, name); /* SAFE */</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_link(topic_statistics, statistics);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return statistics;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> struct stasis_topic *stasis_topic_create(const char *name)</span><br><span> {</span><br><span> struct stasis_topic *topic;</span><br><span> int res = 0;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- topic = ao2_t_alloc(sizeof(*topic), topic_dtor, name);</span><br><span style="color: hsl(120, 100%, 40%);">+ topic = ao2_t_alloc(sizeof(*topic) + strlen(name) + 1, topic_dtor, name);</span><br><span> if (!topic) {</span><br><span> return NULL;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- topic->name = ast_strdup(name);</span><br><span style="color: hsl(120, 100%, 40%);">+ strcpy(topic->name, name); /* SAFE */</span><br><span> res |= AST_VECTOR_INIT(&topic->subscribers, INITIAL_SUBSCRIBERS_MAX);</span><br><span> res |= AST_VECTOR_INIT(&topic->upstream_topics, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+ topic->statistics = stasis_topic_statistics_create(name);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!topic->name || !topic->statistics || res) {</span><br><span style="color: hsl(120, 100%, 40%);">+#else</span><br><span> if (!topic->name || res) {</span><br><span style="color: hsl(0, 100%, 40%);">- ao2_cleanup(topic);</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(topic, -1);</span><br><span> return NULL;</span><br><span> }</span><br><span> </span><br><span>@@ -375,6 +457,35 @@</span><br><span> return AST_VECTOR_SIZE(&topic->subscribers);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+struct stasis_subscription_statistics {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! \brief The filename where the subscription originates */</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *file;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! \brief The line number where the subscription originates */</span><br><span style="color: hsl(120, 100%, 40%);">+ int lineno;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! \brief The function where the subscription originates */</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *func;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! \brief The number of messages that were filtered out */</span><br><span style="color: hsl(120, 100%, 40%);">+ int messages_dropped;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! \brief The number of messages that passed filtering */</span><br><span style="color: hsl(120, 100%, 40%);">+ int messages_passed;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! \brief Highest time spent invoking a message */</span><br><span style="color: hsl(120, 100%, 40%);">+ int64_t highest_time_invoked;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! \brief The message type that currently took the longest to process */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct stasis_message_type *highest_time_message_type;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! \brief Lowest time spent invoking a message */</span><br><span style="color: hsl(120, 100%, 40%);">+ int64_t lowest_time_invoked;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! \brief Using a mailbox to queue messages */</span><br><span style="color: hsl(120, 100%, 40%);">+ int uses_mailbox;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! \brief Using stasis threadpool for handling messages */</span><br><span style="color: hsl(120, 100%, 40%);">+ int uses_threadpool;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! \brief Name of the topic we subscribed to */</span><br><span style="color: hsl(120, 100%, 40%);">+ char *topic;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! \brief Unique ID of the subscription */</span><br><span style="color: hsl(120, 100%, 40%);">+ char uniqueid[0];</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! \internal */</span><br><span> struct stasis_subscription {</span><br><span> /*! Unique ID for this subscription */</span><br><span>@@ -403,6 +514,11 @@</span><br><span> enum stasis_subscription_message_formatters accepted_formatters;</span><br><span> /*! The message filter currently in use */</span><br><span> enum stasis_subscription_message_filter filter;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! Statistics information */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct stasis_subscription_statistics *statistics;</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span> };</span><br><span> </span><br><span> static void subscription_dtor(void *obj)</span><br><span>@@ -423,6 +539,13 @@</span><br><span> ast_cond_destroy(&sub->join_cond);</span><br><span> </span><br><span> AST_VECTOR_FREE(&sub->accepted_message_types);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+ if (sub->statistics) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_unlink(subscription_statistics, sub->statistics);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(sub->statistics, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span> }</span><br><span> </span><br><span> /*!</span><br><span>@@ -436,6 +559,12 @@</span><br><span> {</span><br><span> unsigned int final = stasis_subscription_final_message(sub, message);</span><br><span> int message_type_id = stasis_message_type_id(stasis_subscription_change_type());</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+ struct timeval start;</span><br><span style="color: hsl(120, 100%, 40%);">+ int elapsed;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ start = ast_tvnow();</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span> </span><br><span> /* Notify that the final message has been received */</span><br><span> if (final) {</span><br><span>@@ -462,6 +591,19 @@</span><br><span> ast_cond_signal(&sub->join_cond);</span><br><span> ao2_unlock(sub);</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+ elapsed = ast_tvdiff_ms(ast_tvnow(), start);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (elapsed > sub->statistics->highest_time_invoked) {</span><br><span style="color: hsl(120, 100%, 40%);">+ sub->statistics->highest_time_invoked = elapsed;</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_lock(sub->statistics);</span><br><span style="color: hsl(120, 100%, 40%);">+ sub->statistics->highest_time_message_type = stasis_message_type(message);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_unlock(sub->statistics);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (elapsed < sub->statistics->lowest_time_invoked) {</span><br><span style="color: hsl(120, 100%, 40%);">+ sub->statistics->lowest_time_invoked = elapsed;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span> }</span><br><span> </span><br><span> static void send_subscription_subscribe(struct stasis_topic *topic, struct stasis_subscription *sub);</span><br><span>@@ -471,12 +613,51 @@</span><br><span> {</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+static struct stasis_subscription_statistics *stasis_subscription_statistics_create(const char *uniqueid,</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *topic, int needs_mailbox, int use_thread_pool, const char *file, int lineno,</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *func)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct stasis_subscription_statistics *statistics;</span><br><span style="color: hsl(120, 100%, 40%);">+ size_t uniqueid_len = strlen(uniqueid) + 1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ statistics = ao2_alloc(sizeof(*statistics) + uniqueid_len + strlen(topic) + 1, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!statistics) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ statistics->file = file;</span><br><span style="color: hsl(120, 100%, 40%);">+ statistics->lineno = lineno;</span><br><span style="color: hsl(120, 100%, 40%);">+ statistics->func = func;</span><br><span style="color: hsl(120, 100%, 40%);">+ statistics->uses_mailbox = needs_mailbox;</span><br><span style="color: hsl(120, 100%, 40%);">+ statistics->uses_threadpool = use_thread_pool;</span><br><span style="color: hsl(120, 100%, 40%);">+ strcpy(statistics->uniqueid, uniqueid); /* SAFE */</span><br><span style="color: hsl(120, 100%, 40%);">+ statistics->topic = statistics->uniqueid + uniqueid_len;</span><br><span style="color: hsl(120, 100%, 40%);">+ strcpy(statistics->topic, topic); /* SAFE */</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_link(subscription_statistics, statistics);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return statistics;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+struct stasis_subscription *internal_stasis_subscribe(</span><br><span style="color: hsl(120, 100%, 40%);">+ struct stasis_topic *topic,</span><br><span style="color: hsl(120, 100%, 40%);">+ stasis_subscription_cb callback,</span><br><span style="color: hsl(120, 100%, 40%);">+ void *data,</span><br><span style="color: hsl(120, 100%, 40%);">+ int needs_mailbox,</span><br><span style="color: hsl(120, 100%, 40%);">+ int use_thread_pool,</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *file,</span><br><span style="color: hsl(120, 100%, 40%);">+ int lineno,</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *func)</span><br><span style="color: hsl(120, 100%, 40%);">+#else</span><br><span> struct stasis_subscription *internal_stasis_subscribe(</span><br><span> struct stasis_topic *topic,</span><br><span> stasis_subscription_cb callback,</span><br><span> void *data,</span><br><span> int needs_mailbox,</span><br><span> int use_thread_pool)</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span> {</span><br><span> struct stasis_subscription *sub;</span><br><span> </span><br><span>@@ -491,6 +672,15 @@</span><br><span> }</span><br><span> ast_uuid_generate_str(sub->uniqueid, sizeof(sub->uniqueid));</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+ sub->statistics = stasis_subscription_statistics_create(sub->uniqueid, topic->name, needs_mailbox,</span><br><span style="color: hsl(120, 100%, 40%);">+ use_thread_pool, file, lineno, func);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!sub->statistics) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(sub, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> if (needs_mailbox) {</span><br><span> char tps_name[AST_TASKPROCESSOR_MAX_NAME + 1];</span><br><span> </span><br><span>@@ -538,6 +728,18 @@</span><br><span> return sub;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+struct stasis_subscription *__stasis_subscribe(</span><br><span style="color: hsl(120, 100%, 40%);">+ struct stasis_topic *topic,</span><br><span style="color: hsl(120, 100%, 40%);">+ stasis_subscription_cb callback,</span><br><span style="color: hsl(120, 100%, 40%);">+ void *data,</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *file,</span><br><span style="color: hsl(120, 100%, 40%);">+ int lineno,</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *func)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return internal_stasis_subscribe(topic, callback, data, 1, 0, file, lineno, func);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+#else</span><br><span> struct stasis_subscription *stasis_subscribe(</span><br><span> struct stasis_topic *topic,</span><br><span> stasis_subscription_cb callback,</span><br><span>@@ -545,7 +747,20 @@</span><br><span> {</span><br><span> return internal_stasis_subscribe(topic, callback, data, 1, 0);</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+struct stasis_subscription *__stasis_subscribe_pool(</span><br><span style="color: hsl(120, 100%, 40%);">+ struct stasis_topic *topic,</span><br><span style="color: hsl(120, 100%, 40%);">+ stasis_subscription_cb callback,</span><br><span style="color: hsl(120, 100%, 40%);">+ void *data,</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *file,</span><br><span style="color: hsl(120, 100%, 40%);">+ int lineno,</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *func)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return internal_stasis_subscribe(topic, callback, data, 1, 1, file, lineno, func);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+#else</span><br><span> struct stasis_subscription *stasis_subscribe_pool(</span><br><span> struct stasis_topic *topic,</span><br><span> stasis_subscription_cb callback,</span><br><span>@@ -553,6 +768,7 @@</span><br><span> {</span><br><span> return internal_stasis_subscribe(topic, callback, data, 1, 1);</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span> </span><br><span> static int sub_cleanup(void *data)</span><br><span> {</span><br><span>@@ -808,6 +1024,11 @@</span><br><span> topic_add_subscription(</span><br><span> AST_VECTOR_GET(&topic->upstream_topics, idx), sub);</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+ topic->statistics->subscriber_count += 1;</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> ao2_unlock(topic);</span><br><span> </span><br><span> return 0;</span><br><span>@@ -825,6 +1046,13 @@</span><br><span> }</span><br><span> res = AST_VECTOR_REMOVE_ELEM_UNORDERED(&topic->subscribers, sub,</span><br><span> AST_VECTOR_ELEM_CLEANUP_NOOP);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!res) {</span><br><span style="color: hsl(120, 100%, 40%);">+ topic->statistics->subscriber_count -= 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> ao2_unlock(topic);</span><br><span> </span><br><span> return res;</span><br><span>@@ -885,8 +1113,10 @@</span><br><span> * \param message The message to send</span><br><span> * \param synchronous If non-zero, synchronize on the subscriber receiving</span><br><span> * the message</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 if message was not dispatched</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 1 if message was dispatched</span><br><span> */</span><br><span style="color: hsl(0, 100%, 40%);">-static void dispatch_message(struct stasis_subscription *sub,</span><br><span style="color: hsl(120, 100%, 40%);">+static unsigned int dispatch_message(struct stasis_subscription *sub,</span><br><span> struct stasis_message *message,</span><br><span> int synchronous)</span><br><span> {</span><br><span>@@ -938,14 +1168,22 @@</span><br><span> break;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- return;</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_atomic_fetchadd_int(&sub->statistics->messages_dropped, +1);</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span> </span><br><span> } while (0);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_atomic_fetchadd_int(&sub->statistics->messages_passed, +1);</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> if (!sub->mailbox) {</span><br><span> /* Dispatch directly */</span><br><span> subscription_invoke(sub, message);</span><br><span style="color: hsl(0, 100%, 40%);">- return;</span><br><span style="color: hsl(120, 100%, 40%);">+ return 1;</span><br><span> }</span><br><span> </span><br><span> /* Bump the message for the taskprocessor push. This will get de-ref'd</span><br><span>@@ -957,6 +1195,7 @@</span><br><span> /* Push failed; ugh. */</span><br><span> ast_log(LOG_ERROR, "Dropping async dispatch\n");</span><br><span> ao2_cleanup(message);</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span> }</span><br><span> } else {</span><br><span> struct sync_task_data std;</span><br><span>@@ -972,7 +1211,7 @@</span><br><span> ao2_cleanup(message);</span><br><span> ast_mutex_destroy(&std.lock);</span><br><span> ast_cond_destroy(&std.cond);</span><br><span style="color: hsl(0, 100%, 40%);">- return;</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span> }</span><br><span> </span><br><span> ast_mutex_lock(&std.lock);</span><br><span>@@ -984,6 +1223,8 @@</span><br><span> ast_mutex_destroy(&std.lock);</span><br><span> ast_cond_destroy(&std.cond);</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 1;</span><br><span> }</span><br><span> </span><br><span> /*!</span><br><span>@@ -997,12 +1238,41 @@</span><br><span> struct stasis_message *message, struct stasis_subscription *sync_sub)</span><br><span> {</span><br><span> size_t i;</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned int dispatched = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+ int message_type_id = stasis_message_type_id(stasis_message_type(message));</span><br><span style="color: hsl(120, 100%, 40%);">+ struct stasis_message_type_statistics *statistics;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct timeval start;</span><br><span style="color: hsl(120, 100%, 40%);">+ int elapsed;</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span> </span><br><span> ast_assert(topic != NULL);</span><br><span> ast_assert(message != NULL);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_mutex_lock(&message_type_statistics_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (message_type_id >= AST_VECTOR_SIZE(&message_type_statistics)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct stasis_message_type_statistics new_statistics = {</span><br><span style="color: hsl(120, 100%, 40%);">+ .published = 0,</span><br><span style="color: hsl(120, 100%, 40%);">+ };</span><br><span style="color: hsl(120, 100%, 40%);">+ if (AST_VECTOR_REPLACE(&message_type_statistics, message_type_id, new_statistics)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_mutex_unlock(&message_type_statistics_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+ return;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ statistics = AST_VECTOR_GET_ADDR(&message_type_statistics, message_type_id);</span><br><span style="color: hsl(120, 100%, 40%);">+ statistics->message_type = stasis_message_type(message);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_mutex_unlock(&message_type_statistics_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_atomic_fetchadd_int(&statistics->published, +1);</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* If there are no subscribers don't bother */</span><br><span> if (!stasis_topic_subscribers(topic)) {</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_atomic_fetchadd_int(&statistics->unused, +1);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_atomic_fetchadd_int(&topic->statistics->messages_not_dispatched, +1);</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span> return;</span><br><span> }</span><br><span> </span><br><span>@@ -1011,15 +1281,35 @@</span><br><span> * Make sure we hold onto a reference while dispatching.</span><br><span> */</span><br><span> ao2_ref(topic, +1);</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+ start = ast_tvnow();</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span> ao2_lock(topic);</span><br><span> for (i = 0; i < AST_VECTOR_SIZE(&topic->subscribers); ++i) {</span><br><span> struct stasis_subscription *sub = AST_VECTOR_GET(&topic->subscribers, i);</span><br><span> </span><br><span> ast_assert(sub != NULL);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- dispatch_message(sub, message, (sub == sync_sub));</span><br><span style="color: hsl(120, 100%, 40%);">+ dispatched += dispatch_message(sub, message, (sub == sync_sub));</span><br><span> }</span><br><span> ao2_unlock(topic);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+ elapsed = ast_tvdiff_ms(ast_tvnow(), start);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (elapsed > topic->statistics->highest_time_dispatched) {</span><br><span style="color: hsl(120, 100%, 40%);">+ topic->statistics->highest_time_dispatched = elapsed;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (elapsed < topic->statistics->lowest_time_dispatched) {</span><br><span style="color: hsl(120, 100%, 40%);">+ topic->statistics->lowest_time_dispatched = elapsed;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (dispatched) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_atomic_fetchadd_int(&topic->statistics->messages_dispatched, +1);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_atomic_fetchadd_int(&statistics->unused, +1);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_atomic_fetchadd_int(&topic->statistics->messages_not_dispatched, +1);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> ao2_ref(topic, -1);</span><br><span> }</span><br><span> </span><br><span>@@ -1805,9 +2095,458 @@</span><br><span> </span><br><span> /*! @} */</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief CLI command implementation for 'stasis statistics show subscriptions'</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static char *statistics_show_subscriptions(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ao2_iterator iter;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct stasis_subscription_statistics *statistics;</span><br><span style="color: hsl(120, 100%, 40%);">+ int count = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ int dropped = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ int passed = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+#define FMT_HEADERS "%-64s %10s %10s %16s %16s\n"</span><br><span style="color: hsl(120, 100%, 40%);">+#define FMT_FIELDS "%-64s %10d %10d %16ld %16ld\n"</span><br><span style="color: hsl(120, 100%, 40%);">+#define FMT_FIELDS2 "%-64s %10d %10d\n"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case CLI_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+ e->command = "stasis statistics show subscriptions";</span><br><span style="color: hsl(120, 100%, 40%);">+ e->usage =</span><br><span style="color: hsl(120, 100%, 40%);">+ "Usage: stasis statistics show subscriptions\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ " Shows a list of subscriptions and their general statistics\n";</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ case CLI_GENERATE:</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (a->argc != e->args) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return CLI_SHOWUSAGE;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "\n" FMT_HEADERS, "Subscription", "Dropped", "Passed", "Lowest Invoke", "Highest Invoke");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ iter = ao2_iterator_init(subscription_statistics, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ while ((statistics = ao2_iterator_next(&iter))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, FMT_FIELDS, statistics->uniqueid, statistics->messages_dropped, statistics->messages_passed,</span><br><span style="color: hsl(120, 100%, 40%);">+ statistics->lowest_time_invoked, statistics->highest_time_invoked);</span><br><span style="color: hsl(120, 100%, 40%);">+ dropped += statistics->messages_dropped;</span><br><span style="color: hsl(120, 100%, 40%);">+ passed += statistics->messages_passed;</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(statistics, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ ++count;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_iterator_destroy(&iter);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, FMT_FIELDS2, "Total", dropped, passed);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "\n%d subscriptions\n\n", count);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#undef FMT_HEADERS</span><br><span style="color: hsl(120, 100%, 40%);">+#undef FMT_FIELDS</span><br><span style="color: hsl(120, 100%, 40%);">+#undef FMT_FIELDS2</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return CLI_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief CLI tab completion for subscription statistics names</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static char *subscription_statistics_complete_name(const char *word, int state)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct stasis_subscription_statistics *statistics;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ao2_iterator it_statistics;</span><br><span style="color: hsl(120, 100%, 40%);">+ int wordlen = strlen(word);</span><br><span style="color: hsl(120, 100%, 40%);">+ int which = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *result = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ it_statistics = ao2_iterator_init(subscription_statistics, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ while ((statistics = ao2_iterator_next(&it_statistics))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!strncasecmp(word, statistics->uniqueid, wordlen)</span><br><span style="color: hsl(120, 100%, 40%);">+ && ++which > state) {</span><br><span style="color: hsl(120, 100%, 40%);">+ result = ast_strdup(statistics->uniqueid);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(statistics, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (result) {</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_iterator_destroy(&it_statistics);</span><br><span style="color: hsl(120, 100%, 40%);">+ return result;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief CLI command implementation for 'stasis statistics show subscription'</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static char *statistics_show_subscription(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct stasis_subscription_statistics *statistics;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case CLI_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+ e->command = "stasis statistics show subscription";</span><br><span style="color: hsl(120, 100%, 40%);">+ e->usage =</span><br><span style="color: hsl(120, 100%, 40%);">+ "Usage: stasis statistics show subscription <uniqueid>\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ " Show stasis subscription statistics.\n";</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ case CLI_GENERATE:</span><br><span style="color: hsl(120, 100%, 40%);">+ if (a->pos == 4) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return subscription_statistics_complete_name(a->word, a->n);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (a->argc != 5) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return CLI_SHOWUSAGE;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ statistics = ao2_find(subscription_statistics, a->argv[4], OBJ_SEARCH_KEY);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!statistics) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "Specified subscription '%s' does not exist\n", a->argv[4]);</span><br><span style="color: hsl(120, 100%, 40%);">+ return CLI_FAILURE;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "Subscription: %s\n", statistics->uniqueid);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "Topic: %s\n", statistics->topic);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "Source filename: %s\n", S_OR(statistics->file, "<unavailable>"));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "Source line number: %d\n", statistics->lineno);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "Source function: %s\n", S_OR(statistics->func, "<unavailable>"));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "Number of messages dropped due to filtering: %d\n", statistics->messages_dropped);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "Number of messages passed to subscriber callback: %d\n", statistics->messages_passed);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "Using mailbox to queue messages: %s\n", statistics->uses_mailbox ? "Yes" : "No");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "Using stasis threadpool for handling messages: %s\n", statistics->uses_threadpool ? "Yes" : "No");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "Lowest amount of time (in milliseconds) spent invoking message: %ld\n", statistics->lowest_time_invoked);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "Highest amount of time (in milliseconds) spent invoking message: %ld\n", statistics->highest_time_invoked);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_lock(statistics);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (statistics->highest_time_message_type) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "Offender message type for highest invoking time: %s\n", stasis_message_type_name(statistics->highest_time_message_type));</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_unlock(statistics);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(statistics, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return CLI_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief CLI command implementation for 'stasis statistics show topics'</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static char *statistics_show_topics(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ao2_iterator iter;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct stasis_topic_statistics *statistics;</span><br><span style="color: hsl(120, 100%, 40%);">+ int count = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ int not_dispatched = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ int dispatched = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+#define FMT_HEADERS "%-64s %10s %10s %16s %16s\n"</span><br><span style="color: hsl(120, 100%, 40%);">+#define FMT_FIELDS "%-64s %10d %10d %16ld %16ld\n"</span><br><span style="color: hsl(120, 100%, 40%);">+#define FMT_FIELDS2 "%-64s %10d %10d\n"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case CLI_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+ e->command = "stasis statistics show topics";</span><br><span style="color: hsl(120, 100%, 40%);">+ e->usage =</span><br><span style="color: hsl(120, 100%, 40%);">+ "Usage: stasis statistics show topics\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ " Shows a list of topics and their general statistics\n";</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ case CLI_GENERATE:</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (a->argc != e->args) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return CLI_SHOWUSAGE;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "\n" FMT_HEADERS, "Topic", "Dropped", "Dispatched", "Lowest Dispatch", "Highest Dispatch");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ iter = ao2_iterator_init(topic_statistics, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ while ((statistics = ao2_iterator_next(&iter))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, FMT_FIELDS, statistics->name, statistics->messages_not_dispatched, statistics->messages_dispatched,</span><br><span style="color: hsl(120, 100%, 40%);">+ statistics->lowest_time_dispatched, statistics->highest_time_dispatched);</span><br><span style="color: hsl(120, 100%, 40%);">+ not_dispatched += statistics->messages_not_dispatched;</span><br><span style="color: hsl(120, 100%, 40%);">+ dispatched += statistics->messages_dispatched;</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(statistics, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ ++count;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_iterator_destroy(&iter);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, FMT_FIELDS2, "Total", not_dispatched, dispatched);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "\n%d topics\n\n", count);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#undef FMT_HEADERS</span><br><span style="color: hsl(120, 100%, 40%);">+#undef FMT_FIELDS</span><br><span style="color: hsl(120, 100%, 40%);">+#undef FMT_FIELDS2</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return CLI_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief CLI tab completion for topic statistics names</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static char *topic_statistics_complete_name(const char *word, int state)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct stasis_topic_statistics *statistics;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ao2_iterator it_statistics;</span><br><span style="color: hsl(120, 100%, 40%);">+ int wordlen = strlen(word);</span><br><span style="color: hsl(120, 100%, 40%);">+ int which = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *result = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ it_statistics = ao2_iterator_init(topic_statistics, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ while ((statistics = ao2_iterator_next(&it_statistics))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!strncasecmp(word, statistics->name, wordlen)</span><br><span style="color: hsl(120, 100%, 40%);">+ && ++which > state) {</span><br><span style="color: hsl(120, 100%, 40%);">+ result = ast_strdup(statistics->name);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(statistics, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (result) {</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_iterator_destroy(&it_statistics);</span><br><span style="color: hsl(120, 100%, 40%);">+ return result;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief CLI command implementation for 'stasis statistics show topic'</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static char *statistics_show_topic(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct stasis_topic_statistics *statistics;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case CLI_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+ e->command = "stasis statistics show topic";</span><br><span style="color: hsl(120, 100%, 40%);">+ e->usage =</span><br><span style="color: hsl(120, 100%, 40%);">+ "Usage: stasis statistics show topic <name>\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ " Show stasis topic statistics.\n";</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ case CLI_GENERATE:</span><br><span style="color: hsl(120, 100%, 40%);">+ if (a->pos == 4) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return topic_statistics_complete_name(a->word, a->n);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (a->argc != 5) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return CLI_SHOWUSAGE;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ statistics = ao2_find(topic_statistics, a->argv[4], OBJ_SEARCH_KEY);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!statistics) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "Specified topic '%s' does not exist\n", a->argv[4]);</span><br><span style="color: hsl(120, 100%, 40%);">+ return CLI_FAILURE;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "Topic: %s\n", statistics->name);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "Number of messages published that went to no subscriber: %d\n", statistics->messages_not_dispatched);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "Number of messages that went to at least one subscriber: %d\n", statistics->messages_dispatched);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "Lowest amount of time (in milliseconds) spent dispatching message: %ld\n", statistics->lowest_time_dispatched);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "Highest amount of time (in milliseconds) spent dispatching messages: %ld\n", statistics->highest_time_dispatched);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "Number of subscribers: %d\n", statistics->subscriber_count);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(statistics, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return CLI_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief CLI command implementation for 'stasis statistics show messages'</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static char *statistics_show_messages(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int i;</span><br><span style="color: hsl(120, 100%, 40%);">+ int count = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ int published = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ int unused = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+#define FMT_HEADERS "%-64s %10s %10s\n"</span><br><span style="color: hsl(120, 100%, 40%);">+#define FMT_FIELDS "%-64s %10d %10d\n"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case CLI_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+ e->command = "stasis statistics show messages";</span><br><span style="color: hsl(120, 100%, 40%);">+ e->usage =</span><br><span style="color: hsl(120, 100%, 40%);">+ "Usage: stasis statistics show messages\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ " Shows a list of message types and their general statistics\n";</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ case CLI_GENERATE:</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (a->argc != e->args) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return CLI_SHOWUSAGE;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "\n" FMT_HEADERS, "Message Type", "Published", "Unused");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_mutex_lock(&message_type_statistics_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < AST_VECTOR_SIZE(&message_type_statistics); ++i) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct stasis_message_type_statistics *statistics = AST_VECTOR_GET_ADDR(&message_type_statistics, i);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!statistics->message_type) {</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, FMT_FIELDS, stasis_message_type_name(statistics->message_type), statistics->published,</span><br><span style="color: hsl(120, 100%, 40%);">+ statistics->unused);</span><br><span style="color: hsl(120, 100%, 40%);">+ published += statistics->published;</span><br><span style="color: hsl(120, 100%, 40%);">+ unused += statistics->unused;</span><br><span style="color: hsl(120, 100%, 40%);">+ ++count;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_mutex_unlock(&message_type_statistics_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, FMT_FIELDS, "Total", published, unused);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "\n%d seen message types\n\n", count);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#undef FMT_HEADERS</span><br><span style="color: hsl(120, 100%, 40%);">+#undef FMT_FIELDS</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return CLI_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_cli_entry cli_stasis_statistics[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_CLI_DEFINE(statistics_show_subscriptions, "Show subscriptions with general statistics"),</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_CLI_DEFINE(statistics_show_subscription, "Show subscription statistics"),</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_CLI_DEFINE(statistics_show_topics, "Show topics with general statistics"),</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_CLI_DEFINE(statistics_show_topic, "Show topic statistics"),</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_CLI_DEFINE(statistics_show_messages, "Show message types with general statistics"),</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int subscription_statistics_hash(const void *obj, const int flags)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct stasis_subscription_statistics *object;</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *key;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (flags & OBJ_SEARCH_MASK) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case OBJ_SEARCH_KEY:</span><br><span style="color: hsl(120, 100%, 40%);">+ key = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ case OBJ_SEARCH_OBJECT:</span><br><span style="color: hsl(120, 100%, 40%);">+ object = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+ key = object->uniqueid;</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ default:</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Hash can only work on something with a full key. */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_assert(0);</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ return ast_str_case_hash(key);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int subscription_statistics_cmp(void *obj, void *arg, int flags)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct stasis_subscription_statistics *object_left = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct stasis_subscription_statistics *object_right = arg;</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *right_key = arg;</span><br><span style="color: hsl(120, 100%, 40%);">+ int cmp;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (flags & OBJ_SEARCH_MASK) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case OBJ_SEARCH_OBJECT:</span><br><span style="color: hsl(120, 100%, 40%);">+ right_key = object_right->uniqueid;</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Fall through */</span><br><span style="color: hsl(120, 100%, 40%);">+ case OBJ_SEARCH_KEY:</span><br><span style="color: hsl(120, 100%, 40%);">+ cmp = strcasecmp(object_left->uniqueid, right_key);</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ case OBJ_SEARCH_PARTIAL_KEY:</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Not supported by container */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_assert(0);</span><br><span style="color: hsl(120, 100%, 40%);">+ cmp = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ default:</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * What arg points to is specific to this traversal callback</span><br><span style="color: hsl(120, 100%, 40%);">+ * and has no special meaning to astobj2.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ cmp = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (cmp) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * At this point the traversal callback is identical to a sorted</span><br><span style="color: hsl(120, 100%, 40%);">+ * container.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ return CMP_MATCH;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int topic_statistics_hash(const void *obj, const int flags)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct stasis_topic_statistics *object;</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *key;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (flags & OBJ_SEARCH_MASK) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case OBJ_SEARCH_KEY:</span><br><span style="color: hsl(120, 100%, 40%);">+ key = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ case OBJ_SEARCH_OBJECT:</span><br><span style="color: hsl(120, 100%, 40%);">+ object = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+ key = object->name;</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ default:</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Hash can only work on something with a full key. */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_assert(0);</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ return ast_str_case_hash(key);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int topic_statistics_cmp(void *obj, void *arg, int flags)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct stasis_topic_statistics *object_left = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct stasis_topic_statistics *object_right = arg;</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *right_key = arg;</span><br><span style="color: hsl(120, 100%, 40%);">+ int cmp;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (flags & OBJ_SEARCH_MASK) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case OBJ_SEARCH_OBJECT:</span><br><span style="color: hsl(120, 100%, 40%);">+ right_key = object_right->name;</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Fall through */</span><br><span style="color: hsl(120, 100%, 40%);">+ case OBJ_SEARCH_KEY:</span><br><span style="color: hsl(120, 100%, 40%);">+ cmp = strcasecmp(object_left->name, right_key);</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ case OBJ_SEARCH_PARTIAL_KEY:</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Not supported by container */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_assert(0);</span><br><span style="color: hsl(120, 100%, 40%);">+ cmp = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ default:</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * What arg points to is specific to this traversal callback</span><br><span style="color: hsl(120, 100%, 40%);">+ * and has no special meaning to astobj2.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ cmp = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (cmp) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * At this point the traversal callback is identical to a sorted</span><br><span style="color: hsl(120, 100%, 40%);">+ * container.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ return CMP_MATCH;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! \brief Cleanup function for graceful shutdowns */</span><br><span> static void stasis_cleanup(void)</span><br><span> {</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli_unregister_multiple(cli_stasis_statistics, ARRAY_LEN(cli_stasis_statistics));</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_FREE(&message_type_statistics);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(subscription_statistics);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(topic_statistics);</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span> ast_threadpool_shutdown(pool);</span><br><span> pool = NULL;</span><br><span> STASIS_MESSAGE_TYPE_CLEANUP(stasis_subscription_change_type);</span><br><span>@@ -1902,5 +2641,28 @@</span><br><span> return -1;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Statistics information is stored separately so that we don't alter or interrupt the lifetime of the underlying</span><br><span style="color: hsl(120, 100%, 40%);">+ * topic or subscripton.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ subscription_statistics = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, SUBSCRIPTION_STATISTICS_BUCKETS,</span><br><span style="color: hsl(120, 100%, 40%);">+ subscription_statistics_hash, 0, subscription_statistics_cmp);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!subscription_statistics) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ topic_statistics = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, TOPIC_STATISTICS_BUCKETS,</span><br><span style="color: hsl(120, 100%, 40%);">+ topic_statistics_hash, 0, topic_statistics_cmp);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!topic_statistics) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_INIT(&message_type_statistics, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_cli_register_multiple(cli_stasis_statistics, ARRAY_LEN(cli_stasis_statistics))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> return 0;</span><br><span> }</span><br><span>diff --git a/main/stasis_cache.c b/main/stasis_cache.c</span><br><span>index bc975fd..5aa04fb 100644</span><br><span>--- a/main/stasis_cache.c</span><br><span>+++ b/main/stasis_cache.c</span><br><span>@@ -971,7 +971,11 @@</span><br><span> }</span><br><span> ast_free(new_name);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+ caching_topic->sub = internal_stasis_subscribe(original_topic, caching_topic_exec, caching_topic, 0, 0, __FILE__, __LINE__, __PRETTY_FUNCTION__);</span><br><span style="color: hsl(120, 100%, 40%);">+#else</span><br><span> caching_topic->sub = internal_stasis_subscribe(original_topic, caching_topic_exec, caching_topic, 0, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span> if (caching_topic->sub == NULL) {</span><br><span> ao2_ref(caching_topic, -1);</span><br><span> </span><br><span>diff --git a/main/stasis_message_router.c b/main/stasis_message_router.c</span><br><span>index 197f7f9..9a390ef 100644</span><br><span>--- a/main/stasis_message_router.c</span><br><span>+++ b/main/stasis_message_router.c</span><br><span>@@ -204,8 +204,14 @@</span><br><span> }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+static struct stasis_message_router *stasis_message_router_create_internal(</span><br><span style="color: hsl(120, 100%, 40%);">+ struct stasis_topic *topic, int use_thread_pool, const char *file, int lineno,</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *func)</span><br><span style="color: hsl(120, 100%, 40%);">+#else</span><br><span> static struct stasis_message_router *stasis_message_router_create_internal(</span><br><span> struct stasis_topic *topic, int use_thread_pool)</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span> {</span><br><span> int res;</span><br><span> struct stasis_message_router *router;</span><br><span>@@ -224,11 +230,20 @@</span><br><span> return NULL;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+ if (use_thread_pool) {</span><br><span style="color: hsl(120, 100%, 40%);">+ router->subscription = __stasis_subscribe_pool(topic, router_dispatch, router, file, lineno, func);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ router->subscription = __stasis_subscribe(topic, router_dispatch, router, file, lineno, func);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+#else</span><br><span> if (use_thread_pool) {</span><br><span> router->subscription = stasis_subscribe_pool(topic, router_dispatch, router);</span><br><span> } else {</span><br><span> router->subscription = stasis_subscribe(topic, router_dispatch, router);</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> if (!router->subscription) {</span><br><span> ao2_ref(router, -1);</span><br><span> </span><br><span>@@ -241,17 +256,33 @@</span><br><span> return router;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+struct stasis_message_router *__stasis_message_router_create(</span><br><span style="color: hsl(120, 100%, 40%);">+ struct stasis_topic *topic, const char *file, int lineno, const char *func)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return stasis_message_router_create_internal(topic, 0, file, lineno, func);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+#else</span><br><span> struct stasis_message_router *stasis_message_router_create(</span><br><span> struct stasis_topic *topic)</span><br><span> {</span><br><span> return stasis_message_router_create_internal(topic, 0);</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef AST_DEVMODE</span><br><span style="color: hsl(120, 100%, 40%);">+struct stasis_message_router *__stasis_message_router_create_pool(</span><br><span style="color: hsl(120, 100%, 40%);">+ struct stasis_topic *topic, const char *file, int lineno, const char *func)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return stasis_message_router_create_internal(topic, 1, file, lineno, func);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+#else</span><br><span> struct stasis_message_router *stasis_message_router_create_pool(</span><br><span> struct stasis_topic *topic)</span><br><span> {</span><br><span> return stasis_message_router_create_internal(topic, 1);</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span> </span><br><span> void stasis_message_router_unsubscribe(struct stasis_message_router *router)</span><br><span> {</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/10766">change 10766</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/10766"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-MessageType: merged </div>
<div style="display:none"> Gerrit-Change-Id: I94411b53767f89ee01714daaecf0c2f1666e863f </div>
<div style="display:none"> Gerrit-Change-Number: 10766 </div>
<div style="display:none"> Gerrit-PatchSet: 3 </div>
<div style="display:none"> Gerrit-Owner: Joshua C. Colp <jcolp@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Benjamin Keith Ford <bford@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Friendly Automation (1000185) </div>
<div style="display:none"> Gerrit-Reviewer: George Joseph <gjoseph@digium.com> </div>