<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>