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