[pulseaudio-discuss] [PATCH] core: add ring buffer for log
Deng Zhengrong
dzrongg at gmail.com
Sat Jun 23 04:17:49 PDT 2012
---
src/daemon/daemon-conf.c | 5 +-
src/map-file | 1 +
src/pulse/introspect.c | 34 ++++++++++
src/pulse/introspect.h | 3 +
src/pulsecore/cli-command.c | 4 +-
src/pulsecore/llist.h | 3 +
src/pulsecore/log.c | 134 +++++++++++++++++++++++++++++++++++++++
src/pulsecore/log.h | 4 +
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 +++++
18 files changed, 248 insertions(+), 3 deletions(-)
diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c
index dd2e7b6..80d7467 100644
--- a/src/daemon/daemon-conf.c
+++ b/src/daemon/daemon-conf.c
@@ -182,7 +182,10 @@ int pa_daemon_conf_set_log_target(pa_daemon_conf *c, const char *string) {
if (pa_streq(string, "auto"))
c->auto_log_target = 1;
- else if (pa_streq(string, "syslog")) {
+ else if (pa_streq(string, "ring")) {
+ c->auto_log_target = 0;
+ c->log_target = PA_LOG_RING;
+ } else if (pa_streq(string, "syslog")) {
c->auto_log_target = 0;
c->log_target = PA_LOG_SYSLOG;
} else if (pa_streq(string, "stderr")) {
diff --git a/src/map-file b/src/map-file
index 69cf25b..812875a 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 38a9d1c..2f2ef98 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 0072f5d..62db314 100644
--- a/src/pulse/introspect.h
+++ b/src/pulse/introspect.h
@@ -629,6 +629,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/cli-command.c b/src/pulsecore/cli-command.c
index fc9465b..6d1d717 100644
--- a/src/pulsecore/cli-command.c
+++ b/src/pulsecore/cli-command.c
@@ -186,7 +186,7 @@ static const struct command commands[] = {
{ "kill-client", pa_cli_command_kill_client, "Kill a client (args: index)", 2},
{ "kill-sink-input", pa_cli_command_kill_sink_input, "Kill a sink input (args: index)", 2},
{ "kill-source-output", pa_cli_command_kill_source_output, "Kill a source output (args: index)", 2},
- { "set-log-target", pa_cli_command_log_target, "Change the log target (args: null,auto,syslog,stderr,file:PATH)", 2},
+ { "set-log-target", pa_cli_command_log_target, "Change the log target (args: null,ring,auto,syslog,stderr,file:PATH)", 2},
{ "set-log-level", pa_cli_command_log_level, "Change the log level (args: numeric level)", 2},
{ "set-log-meta", pa_cli_command_log_meta, "Show source code location in log messages (args: bool)", 2},
{ "set-log-time", pa_cli_command_log_time, "Show timestamps in log messages (args: bool)", 2},
@@ -1506,6 +1506,8 @@ static int pa_cli_command_log_target(pa_core *c, pa_tokenizer *t, pa_strbuf *buf
if (pa_streq(m, "null"))
pa_log_set_target(PA_LOG_NULL);
+ else if (pa_streq(m, "ring"))
+ pa_log_set_target(PA_LOG_RING);
else if (pa_streq(m, "syslog"))
pa_log_set_target(PA_LOG_SYSLOG);
else if (pa_streq(m, "stderr") || pa_streq(m, "auto")) {
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..4338729 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 */
@@ -300,6 +414,11 @@ void pa_log_levelv_meta(
return;
}
+ if (_target == PA_LOG_RING) {
+ ts = pa_rtclock_now();
+ slot = get_current_thread_log_slots();
+ }
+
pa_vsnprintf(text, sizeof(text), format, ap);
if ((_flags & PA_LOG_PRINT_META) && file && line > 0 && func)
@@ -355,6 +474,21 @@ void pa_log_levelv_meta(
continue;
switch (_target) {
+ case PA_LOG_RING: {
+ 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));
+ break;
+ }
case PA_LOG_STDERR: {
const char *prefix = "", *suffix = "", *grey = "";
diff --git a/src/pulsecore/log.h b/src/pulsecore/log.h
index 8dd056b..d6560ff 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>
@@ -37,6 +38,7 @@ typedef enum pa_log_target {
PA_LOG_SYSLOG,
PA_LOG_NULL, /* to /dev/null */
PA_LOG_FD, /* to a file descriptor, e.g. a char device */
+ PA_LOG_RING, /* to a ring buffer */
PA_LOG_TARGET_MAX
} pa_log_target_t;
@@ -142,4 +144,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 8fde023..1927de2 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 c24254a..f526b2d 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);
@@ -309,6 +310,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,
@@ -2786,6 +2788,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 a0f1f10..7b01fce 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 5346b94..d598db1 100644
--- a/src/utils/pactl.c
+++ b/src/utils/pactl.c
@@ -94,6 +94,7 @@ static enum {
NONE,
EXIT,
STAT,
+ LOG,
INFO,
UPLOAD_SAMPLE,
PLAY_SAMPLE,
@@ -165,6 +166,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];
@@ -1107,6 +1114,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;
@@ -1367,6 +1378,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");
@@ -1468,6 +1480,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