[pulseaudio-discuss] [PATCH] core: add ring buffer for log

Deng Zhengrong dzrongg at gmail.com
Mon Jul 9 16:22:45 PDT 2012


The usage model for this ring buffer is that we can peek into what's going on
under the hood without restarting pulseaudio daemon, we enable this via logging
all level's messages.
---
 src/map-file                    |    1 +
 src/pulse/introspect.c          |   34 ++++++
 src/pulse/introspect.h          |    3 +
 src/pulsecore/llist.h           |    3 +
 src/pulsecore/log.c             |  245 +++++++++++++++++++++++++++++---------
 src/pulsecore/log.h             |    3 +
 src/pulsecore/native-common.h   |    1 +
 src/pulsecore/pdispatch.c       |    1 +
 src/pulsecore/protocol-native.c |   27 +++++
 src/pulsecore/strlist.c         |    1 -
 src/pulsecore/tagstruct.c       |    6 +
 src/pulsecore/tagstruct.h       |    2 +
 src/pulsecore/thread-posix.c    |    4 +
 src/pulsecore/thread-win32.c    |    4 +
 src/pulsecore/thread.h          |    2 +
 src/utils/pactl.c               |   15 +++
 16 files changed, 293 insertions(+), 59 deletions(-)

diff --git a/src/map-file b/src/map-file
index a20314c..2801c57 100644
--- a/src/map-file
+++ b/src/map-file
@@ -46,6 +46,7 @@ pa_context_get_protocol_version;
 pa_context_get_sample_info_by_index;
 pa_context_get_sample_info_by_name;
 pa_context_get_sample_info_list;
+pa_context_get_log;
 pa_context_get_server;
 pa_context_get_server_info;
 pa_context_get_server_protocol_version;
diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
index 9ca3fd3..686b01f 100644
--- a/src/pulse/introspect.c
+++ b/src/pulse/introspect.c
@@ -79,6 +79,40 @@ pa_operation* pa_context_stat(pa_context *c, pa_stat_info_cb_t cb, void *userdat
     return pa_context_send_simple_command(c, PA_COMMAND_STAT, context_stat_callback, (pa_operation_cb_t) cb, userdata);
 }
 
+/*** Logs ***/
+static void context_get_log_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    const char *p = NULL;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+            goto finish;
+    } else if (pa_tagstruct_gets(t, &p) < 0) {
+        pa_context_fail(o->context, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (o->callback) {
+        pa_log_info_cb_t cb = (pa_log_info_cb_t) o->callback;
+        cb(o->context, p, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_log(pa_context *c, pa_log_info_cb_t cb, void *userdata) {
+    return pa_context_send_simple_command(c, PA_COMMAND_GET_LOG, context_get_log_callback, (pa_operation_cb_t) cb, userdata);
+}
+
 /*** Server Info ***/
 
 static void context_get_server_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
index 6ea4536..ea86fcd 100644
--- a/src/pulse/introspect.h
+++ b/src/pulse/introspect.h
@@ -633,6 +633,9 @@ pa_operation* pa_context_stat(pa_context *c, pa_stat_info_cb_t cb, void *userdat
 
 /** @} */
 
+typedef void (*pa_log_info_cb_t) (pa_context *c, const char *buffer, void *userdata);
+pa_operation* pa_context_get_log(pa_context *c, pa_log_info_cb_t cb, void *userdata);
+
 /** @{ \name Cached Samples */
 
 /** Stores information about sample cache entries. Please note that this structure
diff --git a/src/pulsecore/llist.h b/src/pulsecore/llist.h
index 27f174a..aadd40e 100644
--- a/src/pulsecore/llist.h
+++ b/src/pulsecore/llist.h
@@ -31,6 +31,9 @@
 #define PA_LLIST_HEAD(t,name)                                           \
     t *name
 
+#define PA_STATIC_LLIST_HEAD(t,name)                                    \
+    static t *name = (t*) NULL;
+
 /* The pointers in the linked list's items. Use this in the item structure */
 #define PA_LLIST_FIELDS(t)                                              \
     t *next, *prev
diff --git a/src/pulsecore/log.c b/src/pulsecore/log.c
index 8eaef54..e0e545c 100644
--- a/src/pulsecore/log.c
+++ b/src/pulsecore/log.c
@@ -50,6 +50,7 @@
 #include <pulsecore/once.h>
 #include <pulsecore/ratelimit.h>
 #include <pulsecore/thread.h>
+#include <pulsecore/llist.h>
 
 #include "log.h"
 
@@ -265,6 +266,117 @@ static void init_defaults(void) {
     } PA_ONCE_END;
 }
 
+#define PA_LOG_SLOTS 200
+#define PA_LOG_SLOT_LENGTH 512
+
+static inline int next_slot(int nr) {
+    nr++;
+    if (nr >= PA_LOG_SLOTS)
+        nr = 0;
+    return nr;
+}
+
+static inline int prev_slot(int nr) {
+    nr--;
+    if (nr < 0)
+        nr = PA_LOG_SLOTS - 1;
+    return nr;
+}
+
+struct log_slot {
+    PA_LLIST_FIELDS(struct log_slot);
+
+    pthread_t tid;
+
+    int last_slot;
+
+    char slots[PA_LOG_SLOTS][PA_LOG_SLOT_LENGTH];
+    pa_usec_t timestamps[PA_LOG_SLOTS];
+
+    int loop_iter;      /* this field is used for log reading only */
+};
+
+PA_STATIC_LLIST_HEAD(struct log_slot, log_slots);
+static pa_static_mutex log_slots_mutex = PA_STATIC_MUTEX_INIT;
+
+static struct log_slot *get_current_thread_log_slots(void) {
+    pa_mutex *mutex;
+    pthread_t tid;
+    struct log_slot *slot, *new;
+
+    tid = pa_thread_get_tid(pa_thread_self());
+
+    mutex = pa_static_mutex_get(&log_slots_mutex, TRUE, TRUE);
+    pa_mutex_lock(mutex);
+    if (!log_slots) {
+        log_slots = pa_xnew0(struct log_slot, 1);
+        log_slots->tid = tid;
+        log_slots->last_slot = 0;
+        PA_LLIST_INIT(struct log_slot, log_slots);
+
+        pa_mutex_unlock(mutex);
+
+        return log_slots;
+    }
+
+    /* search for matching tid */
+    PA_LLIST_FOREACH(slot, log_slots) {
+        if (slot->tid == tid) {
+            pa_mutex_unlock(mutex);
+            return slot;
+        }
+    }
+
+    /* if not found, create new item */
+    new = pa_xnew0(struct log_slot, 1);
+    new->tid = tid;
+    new->last_slot = 0;
+
+    PA_LLIST_PREPEND(struct log_slot, log_slots, new);
+
+    pa_mutex_unlock(mutex);
+
+    return new;
+}
+
+void pa_log_get_strbuf(pa_strbuf *buf) {
+    pa_mutex *mutex;
+    struct log_slot *slot;
+    int i = 0;
+
+    mutex = pa_static_mutex_get(&log_slots_mutex, TRUE, TRUE);
+    pa_mutex_lock(mutex);
+
+    /* setup iterators */
+    PA_LLIST_FOREACH(slot, log_slots) {
+        slot->loop_iter = prev_slot(slot->last_slot);
+    }
+
+    /* extract at most PA_LOG_SLOTS logs */
+    while (i < PA_LOG_SLOTS) {
+        struct log_slot *max_slot = NULL;
+        pa_usec_t max_ts = 0;
+
+        PA_LLIST_FOREACH(slot, log_slots) {
+            pa_usec_t ts = slot->timestamps[slot->loop_iter];
+            if (ts > max_ts) {
+                max_ts = ts;
+                max_slot = slot;
+            }
+        }
+
+        if (!max_slot)
+            break;
+
+        pa_strbuf_puts(buf, max_slot->slots[max_slot->loop_iter]);
+        max_slot->loop_iter = prev_slot(max_slot->loop_iter);
+
+        i++;
+    }
+
+    pa_mutex_unlock(mutex);
+}
+
 void pa_log_levelv_meta(
         pa_log_level_t level,
         const char*file,
@@ -280,6 +392,8 @@ void pa_log_levelv_meta(
     pa_log_level_t _maximum_level;
     unsigned _show_backtrace;
     pa_log_flags_t _flags;
+    pa_usec_t ts = 0;
+    struct log_slot *slot = NULL;
 
     /* We don't use dynamic memory allocation here to minimize the hit
      * in RT threads */
@@ -295,10 +409,8 @@ void pa_log_levelv_meta(
     _show_backtrace = PA_MAX(show_backtrace, show_backtrace_override);
     _flags = flags | flags_override;
 
-    if (PA_LIKELY(level > _maximum_level)) {
-        errno = saved_errno;
-        return;
-    }
+    ts = pa_rtclock_now();
+    slot = get_current_thread_log_slots();
 
     pa_vsnprintf(text, sizeof(text), format, ap);
 
@@ -354,82 +466,99 @@ void pa_log_levelv_meta(
         if (t[strspn(t, "\t ")] == 0)
             continue;
 
-        switch (_target) {
-
-            case PA_LOG_STDERR: {
-                const char *prefix = "", *suffix = "", *grey = "";
-                char *local_t;
+        if (level <= _maximum_level) {
+            switch (_target) {
+                case PA_LOG_STDERR: {
+                    const char *prefix = "", *suffix = "", *grey = "";
+                    char *local_t;
 
 #ifndef OS_IS_WIN32
-                /* Yes indeed. Useless, but fun! */
-                if ((_flags & PA_LOG_COLORS) && isatty(STDERR_FILENO)) {
-                    if (level <= PA_LOG_ERROR)
-                        prefix = "\x1B[1;31m";
-                    else if (level <= PA_LOG_WARN)
-                        prefix = "\x1B[1m";
-
-                    if (bt)
-                        grey = "\x1B[2m";
-
-                    if (grey[0] || prefix[0])
-                        suffix = "\x1B[0m";
-                }
+                    /* Yes indeed. Useless, but fun! */
+                    if ((_flags & PA_LOG_COLORS) && isatty(STDERR_FILENO)) {
+                        if (level <= PA_LOG_ERROR)
+                            prefix = "\x1B[1;31m";
+                        else if (level <= PA_LOG_WARN)
+                            prefix = "\x1B[1m";
+
+                        if (bt)
+                            grey = "\x1B[2m";
+
+                        if (grey[0] || prefix[0])
+                            suffix = "\x1B[0m";
+                    }
 #endif
 
-                /* We shouldn't be using dynamic allocation here to
-                 * minimize the hit in RT threads */
-                if ((local_t = pa_utf8_to_locale(t)))
-                    t = local_t;
+                    /* We shouldn't be using dynamic allocation here to
+                     * minimize the hit in RT threads */
+                    if ((local_t = pa_utf8_to_locale(t)))
+                        t = local_t;
 
-                if (_flags & PA_LOG_PRINT_LEVEL)
-                    fprintf(stderr, "%s%c: %s%s%s%s%s%s\n", timestamp, level_to_char[level], location, prefix, t, grey, pa_strempty(bt), suffix);
-                else
-                    fprintf(stderr, "%s%s%s%s%s%s%s\n", timestamp, location, prefix, t, grey, pa_strempty(bt), suffix);
+                    if (_flags & PA_LOG_PRINT_LEVEL)
+                        fprintf(stderr, "%s%c: %s%s%s%s%s%s\n", timestamp, level_to_char[level], location, prefix, t, grey, pa_strempty(bt), suffix);
+                    else
+                        fprintf(stderr, "%s%s%s%s%s%s%s\n", timestamp, location, prefix, t, grey, pa_strempty(bt), suffix);
 #ifdef OS_IS_WIN32
-                fflush(stderr);
+                    fflush(stderr);
 #endif
 
-                pa_xfree(local_t);
+                    pa_xfree(local_t);
 
-                break;
-            }
+                    break;
+                }
 
 #ifdef HAVE_SYSLOG_H
-            case PA_LOG_SYSLOG: {
-                char *local_t;
+                case PA_LOG_SYSLOG: {
+                    char *local_t;
 
-                openlog(ident, LOG_PID, LOG_USER);
+                    openlog(ident, LOG_PID, LOG_USER);
 
-                if ((local_t = pa_utf8_to_locale(t)))
-                    t = local_t;
+                    if ((local_t = pa_utf8_to_locale(t)))
+                        t = local_t;
 
-                syslog(level_to_syslog[level], "%s%s%s%s", timestamp, location, t, pa_strempty(bt));
-                pa_xfree(local_t);
+                    syslog(level_to_syslog[level], "%s%s%s%s", timestamp, location, t, pa_strempty(bt));
+                    pa_xfree(local_t);
 
-                break;
-            }
+                    break;
+                }
 #endif
 
-            case PA_LOG_FD: {
-                if (log_fd >= 0) {
-                    char metadata[256];
+                case PA_LOG_FD: {
+                    if (log_fd >= 0) {
+                        char metadata[256];
 
-                    pa_snprintf(metadata, sizeof(metadata), "\n%c %s %s", level_to_char[level], timestamp, location);
+                        pa_snprintf(metadata, sizeof(metadata), "\n%c %s %s", level_to_char[level], timestamp, location);
 
-                    if ((write(log_fd, metadata, strlen(metadata)) < 0) || (write(log_fd, t, strlen(t)) < 0)) {
-                        saved_errno = errno;
-                        pa_log_set_fd(-1);
-                        fprintf(stderr, "%s\n", "Error writing logs to a file descriptor. Redirect log messages to console.");
-                        fprintf(stderr, "%s %s\n", metadata, t);
-                        pa_log_set_target(PA_LOG_STDERR);
+                        if ((write(log_fd, metadata, strlen(metadata)) < 0) || (write(log_fd, t, strlen(t)) < 0)) {
+                            saved_errno = errno;
+                            pa_log_set_fd(-1);
+                            fprintf(stderr, "%s\n", "Error writing logs to a file descriptor. Redirect log messages to console.");
+                            fprintf(stderr, "%s %s\n", metadata, t);
+                            pa_log_set_target(PA_LOG_STDERR);
+                        }
                     }
-                }
 
-                break;
+                    break;
+                }
+                case PA_LOG_NULL:
+                default:
+                    break;
             }
-            case PA_LOG_NULL:
-            default:
-                break;
+        }
+
+        /* log all data to our ring buffer log */
+        {
+            char *buffer;
+
+            slot->last_slot = next_slot(slot->last_slot);
+
+            slot->timestamps[slot->last_slot] = ts;
+
+            buffer = slot->slots[slot->last_slot];
+
+            if (_flags & PA_LOG_PRINT_LEVEL)
+                pa_snprintf(buffer, PA_LOG_SLOT_LENGTH, "%s%c: %s%s%s\n", timestamp, level_to_char[level], location, t, pa_strempty(bt));
+            else
+                pa_snprintf(buffer, PA_LOG_SLOT_LENGTH, "%s%s%s%s\n", timestamp, location, t, pa_strempty(bt));
         }
     }
 
diff --git a/src/pulsecore/log.h b/src/pulsecore/log.h
index 8dd056b..c346688 100644
--- a/src/pulsecore/log.h
+++ b/src/pulsecore/log.h
@@ -26,6 +26,7 @@
 #include <stdarg.h>
 #include <stdlib.h>
 
+#include <pulsecore/strbuf.h>
 #include <pulsecore/macro.h>
 #include <pulse/gccmacro.h>
 
@@ -142,4 +143,6 @@ LOG_FUNC(error, PA_LOG_ERROR)
 
 pa_bool_t pa_log_ratelimit(pa_log_level_t level);
 
+void pa_log_get_strbuf(pa_strbuf *buf);
+
 #endif
diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h
index dad82e0..e92dbc3 100644
--- a/src/pulsecore/native-common.h
+++ b/src/pulsecore/native-common.h
@@ -46,6 +46,7 @@ enum {
     PA_COMMAND_LOOKUP_SOURCE,
     PA_COMMAND_DRAIN_PLAYBACK_STREAM,
     PA_COMMAND_STAT,
+    PA_COMMAND_GET_LOG,
     PA_COMMAND_GET_PLAYBACK_LATENCY,
     PA_COMMAND_CREATE_UPLOAD_STREAM,
     PA_COMMAND_DELETE_UPLOAD_STREAM,
diff --git a/src/pulsecore/pdispatch.c b/src/pulsecore/pdispatch.c
index 9a9ef4e..ed88f73 100644
--- a/src/pulsecore/pdispatch.c
+++ b/src/pulsecore/pdispatch.c
@@ -64,6 +64,7 @@ static const char *command_names[PA_COMMAND_MAX] = {
     [PA_COMMAND_LOOKUP_SOURCE] = "LOOKUP_SOURCE",
     [PA_COMMAND_DRAIN_PLAYBACK_STREAM] = "DRAIN_PLAYBACK_STREAM",
     [PA_COMMAND_STAT] = "STAT",
+    [PA_COMMAND_GET_LOG] = "GET_LOG",
     [PA_COMMAND_GET_PLAYBACK_LATENCY] = "GET_PLAYBACK_LATENCY",
     [PA_COMMAND_CREATE_UPLOAD_STREAM] = "CREATE_UPLOAD_STREAM",
     [PA_COMMAND_DELETE_UPLOAD_STREAM] = "DELETE_UPLOAD_STREAM",
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index c39efc6..9b51345 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -262,6 +262,7 @@ static void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta
 static void command_set_client_name(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 static void command_lookup(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 static void command_stat(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_get_log(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 static void command_get_playback_latency(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 static void command_get_record_latency(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 static void command_create_upload_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
@@ -310,6 +311,7 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
     [PA_COMMAND_LOOKUP_SINK] = command_lookup,
     [PA_COMMAND_LOOKUP_SOURCE] = command_lookup,
     [PA_COMMAND_STAT] = command_stat,
+    [PA_COMMAND_GET_LOG] = command_get_log,
     [PA_COMMAND_GET_PLAYBACK_LATENCY] = command_get_playback_latency,
     [PA_COMMAND_GET_RECORD_LATENCY] = command_get_record_latency,
     [PA_COMMAND_CREATE_UPLOAD_STREAM] = command_create_upload_stream,
@@ -2789,6 +2791,31 @@ static void command_stat(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta
     pa_pstream_send_tagstruct(c->pstream, reply);
 }
 
+static void command_get_log(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    pa_tagstruct *reply;
+    pa_strbuf *strbuf;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (!pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+
+    reply = reply_new(tag);
+
+    strbuf = pa_strbuf_new();
+    pa_log_get_strbuf(strbuf);
+    pa_tagstruct_put_strbuf(reply, strbuf);
+    pa_strbuf_free(strbuf);
+
+    pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
 static void command_get_playback_latency(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
     pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     pa_tagstruct *reply;
diff --git a/src/pulsecore/strlist.c b/src/pulsecore/strlist.c
index 4c06fee..8a48e8c 100644
--- a/src/pulsecore/strlist.c
+++ b/src/pulsecore/strlist.c
@@ -27,7 +27,6 @@
 
 #include <pulse/xmalloc.h>
 
-#include <pulsecore/strbuf.h>
 #include <pulsecore/macro.h>
 #include <pulsecore/core-util.h>
 
diff --git a/src/pulsecore/tagstruct.c b/src/pulsecore/tagstruct.c
index 762947a..9606c75 100644
--- a/src/pulsecore/tagstruct.c
+++ b/src/pulsecore/tagstruct.c
@@ -95,6 +95,12 @@ static void extend(pa_tagstruct*t, size_t l) {
     t->data = pa_xrealloc(t->data, t->allocated = t->length+l+100);
 }
 
+void pa_tagstruct_put_strbuf(pa_tagstruct*t, pa_strbuf *s) {
+    char *buf = pa_strbuf_tostring(s);
+    pa_tagstruct_puts(t, buf);
+    pa_xfree(buf);
+}
+
 void pa_tagstruct_puts(pa_tagstruct*t, const char *s) {
     size_t l;
     pa_assert(t);
diff --git a/src/pulsecore/tagstruct.h b/src/pulsecore/tagstruct.h
index 5f729bc..4d77cd1 100644
--- a/src/pulsecore/tagstruct.h
+++ b/src/pulsecore/tagstruct.h
@@ -33,6 +33,7 @@
 #include <pulse/proplist.h>
 
 #include <pulsecore/macro.h>
+#include <pulsecore/strbuf.h>
 
 typedef struct pa_tagstruct pa_tagstruct;
 
@@ -71,6 +72,7 @@ const uint8_t* pa_tagstruct_data(pa_tagstruct*t, size_t *l);
 
 void pa_tagstruct_put(pa_tagstruct *t, ...);
 
+void pa_tagstruct_put_strbuf(pa_tagstruct*t, pa_strbuf *s);
 void pa_tagstruct_puts(pa_tagstruct*t, const char *s);
 void pa_tagstruct_putu8(pa_tagstruct*t, uint8_t c);
 void pa_tagstruct_putu32(pa_tagstruct*t, uint32_t i);
diff --git a/src/pulsecore/thread-posix.c b/src/pulsecore/thread-posix.c
index 3f4ae5c..c5395fe 100644
--- a/src/pulsecore/thread-posix.c
+++ b/src/pulsecore/thread-posix.c
@@ -206,6 +206,10 @@ const char *pa_thread_get_name(pa_thread *t) {
     return t->name;
 }
 
+int pa_thread_get_tid(pa_thread *t) {
+    return (int)t->id;
+}
+
 void pa_thread_yield(void) {
 #ifdef HAVE_PTHREAD_YIELD
     pthread_yield();
diff --git a/src/pulsecore/thread-win32.c b/src/pulsecore/thread-win32.c
index 89c8c46..fcbf43b 100644
--- a/src/pulsecore/thread-win32.c
+++ b/src/pulsecore/thread-win32.c
@@ -144,6 +144,10 @@ const char *pa_thread_get_name(pa_thread *t) {
     return NULL;
 }
 
+int pa_thread_get_tid(pa_thread *t) {
+    return (int)t->thread;
+}
+
 void pa_thread_yield(void) {
     Sleep(0);
 }
diff --git a/src/pulsecore/thread.h b/src/pulsecore/thread.h
index 9cabb89..53bf6d6 100644
--- a/src/pulsecore/thread.h
+++ b/src/pulsecore/thread.h
@@ -50,6 +50,8 @@ void pa_thread_set_data(pa_thread *t, void *userdata);
 const char *pa_thread_get_name(pa_thread *t);
 void pa_thread_set_name(pa_thread *t, const char *name);
 
+int pa_thread_get_tid(pa_thread *t);
+
 typedef struct pa_tls pa_tls;
 
 pa_tls* pa_tls_new(pa_free_cb_t free_cb);
diff --git a/src/utils/pactl.c b/src/utils/pactl.c
index e15520c..610b229 100644
--- a/src/utils/pactl.c
+++ b/src/utils/pactl.c
@@ -95,6 +95,7 @@ static enum {
     NONE,
     EXIT,
     STAT,
+    LOG,
     INFO,
     UPLOAD_SAMPLE,
     PLAY_SAMPLE,
@@ -167,6 +168,12 @@ static void stat_callback(pa_context *c, const pa_stat_info *i, void *userdata)
     complete_action();
 }
 
+static void get_log_callback(pa_context *c, const char *buf, void *userdata) {
+    if (buf != NULL)
+        printf("%s\n", buf);
+    complete_action();
+}
+
 static void get_server_info_callback(pa_context *c, const pa_server_info *i, void *useerdata) {
     char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
 
@@ -1119,6 +1126,10 @@ static void context_state_callback(pa_context *c, void *userdata) {
                         break;
                     actions++;
 
+                case LOG:
+                    pa_operation_unref(pa_context_get_log(c, get_log_callback, NULL));
+                    break;
+
                 case INFO:
                     pa_operation_unref(pa_context_get_server_info(c, get_server_info_callback, NULL));
                     break;
@@ -1383,6 +1394,7 @@ static int parse_volume(const char *vol_spec, pa_volume_t *vol, enum volume_flag
 static void help(const char *argv0) {
 
     printf("%s %s %s\n",    argv0, _("[options]"), "stat [short]");
+    printf("%s %s %s\n",    argv0, _("[options]"), "log");
     printf("%s %s %s\n",    argv0, _("[options]"), "info");
     printf("%s %s %s %s\n", argv0, _("[options]"), "list [short]", _("[TYPE]"));
     printf("%s %s %s\n",    argv0, _("[options]"), "exit");
@@ -1485,6 +1497,9 @@ int main(int argc, char *argv[]) {
             if (optind+1 < argc && pa_streq(argv[optind+1], "short"))
                 short_list_format = TRUE;
 
+        } else if (pa_streq(argv[optind], "log")) {
+            action = LOG;
+
         } else if (pa_streq(argv[optind], "info"))
             action = INFO;
 
-- 
1.7.7.6



More information about the pulseaudio-discuss mailing list