mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2026-05-29 21:37:54 -04:00
pulse-server: add facilities to dump internal state
Add a `dump_state()` function which can be used to dump many parts of the internal state of the server into a `FILE` in JSON format.
This commit is contained in:
parent
bd87902da6
commit
88b3d9525e
3 changed files with 674 additions and 0 deletions
|
|
@ -341,6 +341,7 @@ pipewire_module_protocol_pulse_sources = [
|
||||||
'module-protocol-pulse/client.c',
|
'module-protocol-pulse/client.c',
|
||||||
'module-protocol-pulse/collect.c',
|
'module-protocol-pulse/collect.c',
|
||||||
'module-protocol-pulse/cmd.c',
|
'module-protocol-pulse/cmd.c',
|
||||||
|
'module-protocol-pulse/debug.c',
|
||||||
'module-protocol-pulse/extension.c',
|
'module-protocol-pulse/extension.c',
|
||||||
'module-protocol-pulse/extensions/ext-device-manager.c',
|
'module-protocol-pulse/extensions/ext-device-manager.c',
|
||||||
'module-protocol-pulse/extensions/ext-device-restore.c',
|
'module-protocol-pulse/extensions/ext-device-restore.c',
|
||||||
|
|
|
||||||
663
src/modules/module-protocol-pulse/debug.c
Normal file
663
src/modules/module-protocol-pulse/debug.c
Normal file
|
|
@ -0,0 +1,663 @@
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <spa/utils/dict.h>
|
||||||
|
#include <spa/utils/list.h>
|
||||||
|
#include <spa/support/loop.h>
|
||||||
|
|
||||||
|
#include <pipewire/map.h>
|
||||||
|
#include <pipewire/properties.h>
|
||||||
|
|
||||||
|
#include "client.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "defs.h"
|
||||||
|
#include "format.h"
|
||||||
|
#include "internal.h"
|
||||||
|
#include "message.h"
|
||||||
|
#include "module.h"
|
||||||
|
#include "operation.h"
|
||||||
|
#include "pending-sample.h"
|
||||||
|
#include "quirks.h"
|
||||||
|
#include "sample.h"
|
||||||
|
#include "sample-play.h"
|
||||||
|
#include "server.h"
|
||||||
|
#include "stream.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "volume.h"
|
||||||
|
|
||||||
|
enum s_type {
|
||||||
|
t_unknown,
|
||||||
|
t_key,
|
||||||
|
t_value,
|
||||||
|
t_obj_begin,
|
||||||
|
t_array_begin,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct s_scope {
|
||||||
|
struct s_scope *prev;
|
||||||
|
enum s_type type;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct serializer {
|
||||||
|
FILE *target;
|
||||||
|
|
||||||
|
struct s_scope first;
|
||||||
|
struct s_scope *current;
|
||||||
|
|
||||||
|
enum s_type last;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ========================================================================== */
|
||||||
|
|
||||||
|
static void s_enter_container(struct serializer *s, struct s_scope *scope, const char c, enum s_type type)
|
||||||
|
{
|
||||||
|
if (s->last == t_value)
|
||||||
|
fputc(',', s->target);
|
||||||
|
|
||||||
|
scope->prev = s->current;
|
||||||
|
scope->type = s->last = type;
|
||||||
|
s->current = scope;
|
||||||
|
|
||||||
|
fputc(c, s->target);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void s_leave_container(struct serializer *s, struct s_scope *scope, const char c, enum s_type type)
|
||||||
|
{
|
||||||
|
spa_assert(s->current == scope);
|
||||||
|
spa_assert(s->last == t_value || s->last == type);
|
||||||
|
spa_assert(s->current->type == type);
|
||||||
|
|
||||||
|
s->current = scope->prev;
|
||||||
|
s->last = t_value;
|
||||||
|
|
||||||
|
fputc(c, s->target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================================================================== */
|
||||||
|
|
||||||
|
#define s_object(s, ...) \
|
||||||
|
do { \
|
||||||
|
struct s_scope __scope; \
|
||||||
|
s_enter_container((s), &__scope, '{', t_obj_begin); \
|
||||||
|
do { __VA_ARGS__ } while (false); \
|
||||||
|
s_leave_container((s), &__scope, '}', t_obj_begin); \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
#define s_k_object(s, key, ...) \
|
||||||
|
do { \
|
||||||
|
s_key((s), (key)); \
|
||||||
|
s_object((s), __VA_ARGS__); \
|
||||||
|
} while (false);
|
||||||
|
|
||||||
|
#define s_array(s, ...) \
|
||||||
|
do { \
|
||||||
|
struct s_scope __scope; \
|
||||||
|
s_enter_container((s), &__scope, '[', t_array_begin); \
|
||||||
|
do { __VA_ARGS__ } while (false); \
|
||||||
|
s_leave_container((s), &__scope, ']', t_array_begin); \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
#define s_k_array(s, key, ...) \
|
||||||
|
do { \
|
||||||
|
s_key((s), (key)); \
|
||||||
|
s_array((s), __VA_ARGS__); \
|
||||||
|
} while (false);
|
||||||
|
|
||||||
|
/* ========================================================================== */
|
||||||
|
|
||||||
|
static void s_emit_string(FILE *file, const char *str)
|
||||||
|
{
|
||||||
|
for (; *str; str++) {
|
||||||
|
const char ch = *str;
|
||||||
|
|
||||||
|
if (ch == '\\')
|
||||||
|
fputs("\\\\", file);
|
||||||
|
else if (ch == '"')
|
||||||
|
fputs("\\\"", file);
|
||||||
|
else if (ch == '\n')
|
||||||
|
fputs("\\n", file);
|
||||||
|
else if (ch == '\t')
|
||||||
|
fputs("\\t", file);
|
||||||
|
else if (iscntrl(ch))
|
||||||
|
fprintf(file, "\\u%04X", (unsigned int) ch);
|
||||||
|
else
|
||||||
|
fputc(ch, file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void s_key(struct serializer *s, const char *key)
|
||||||
|
{
|
||||||
|
spa_assert(s->last == t_obj_begin || s->last == t_value);
|
||||||
|
spa_assert(s->current->type == t_obj_begin);
|
||||||
|
|
||||||
|
if (s->last == t_value)
|
||||||
|
fputc(',', s->target);
|
||||||
|
|
||||||
|
s->last = t_key;
|
||||||
|
|
||||||
|
fputc('"', s->target);
|
||||||
|
s_emit_string(s->target, key);
|
||||||
|
fputs("\":", s->target);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool can_emit_value(const struct serializer *s)
|
||||||
|
{
|
||||||
|
switch (s->current->type) {
|
||||||
|
case t_obj_begin:
|
||||||
|
return s->last == t_key;
|
||||||
|
case t_array_begin:
|
||||||
|
return s->last == t_value || s->last == t_array_begin;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define s_emit_value(s, ...) \
|
||||||
|
do { \
|
||||||
|
spa_assert(can_emit_value(s)); \
|
||||||
|
if ((s)->last == t_value) \
|
||||||
|
fputc(',', (s)->target); \
|
||||||
|
do { __VA_ARGS__ } while (false); \
|
||||||
|
(s)->last = t_value; \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
static void s_str(struct serializer *s, const char *str)
|
||||||
|
{
|
||||||
|
s_emit_value(s, {
|
||||||
|
if (str) {
|
||||||
|
fputc('"', s->target);
|
||||||
|
s_emit_string(s->target, str);
|
||||||
|
fputc('"', s->target);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fputs("null", s->target);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void s_bool(struct serializer *s, bool x)
|
||||||
|
{
|
||||||
|
s_emit_value(s, {
|
||||||
|
fputs(x ? "true" : "false", s->target);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void s_int(struct serializer *s, int64_t x)
|
||||||
|
{
|
||||||
|
s_emit_value(s, {
|
||||||
|
fprintf(s->target, "%" PRId64, x);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void s_float(struct serializer *s, double x)
|
||||||
|
{
|
||||||
|
s_emit_value(s, {
|
||||||
|
fprintf(s->target, "%f", x);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void s_ptr(struct serializer *s, const void *ptr)
|
||||||
|
{
|
||||||
|
s_emit_value(s, {
|
||||||
|
if (ptr)
|
||||||
|
fprintf(s->target, "\"%p\"", ptr);
|
||||||
|
else
|
||||||
|
fputs("null", s->target);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================================================================== */
|
||||||
|
|
||||||
|
static void s_k_bool(struct serializer *s, const char *key, bool x)
|
||||||
|
{
|
||||||
|
s_key(s, key);
|
||||||
|
s_bool(s, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void s_k_int(struct serializer *s, const char *key, int64_t x)
|
||||||
|
{
|
||||||
|
s_key(s, key);
|
||||||
|
s_int(s, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void s_k_str(struct serializer *s, const char *key, const char *str)
|
||||||
|
{
|
||||||
|
s_key(s, key);
|
||||||
|
s_str(s, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================================================================== */
|
||||||
|
|
||||||
|
#define CHECK_NULL(ptr) \
|
||||||
|
do { \
|
||||||
|
if (!(ptr)) { \
|
||||||
|
s_ptr(s, NULL); \
|
||||||
|
return; \
|
||||||
|
} \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
static void dump_props(struct serializer *s, const struct pw_properties *props)
|
||||||
|
{
|
||||||
|
CHECK_NULL(props);
|
||||||
|
|
||||||
|
s_object(s, {
|
||||||
|
const struct spa_dict_item *item;
|
||||||
|
spa_dict_for_each (item, &props->dict)
|
||||||
|
s_k_str(s, item->key, item->value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_source(struct serializer *s, const struct spa_source *source)
|
||||||
|
{
|
||||||
|
CHECK_NULL(source);
|
||||||
|
|
||||||
|
s_object(s, {
|
||||||
|
s_k_int(s, "fd", source->fd);
|
||||||
|
s_k_int(s, "mask", source->mask);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_message(struct serializer *s, const struct message *m)
|
||||||
|
{
|
||||||
|
CHECK_NULL(m);
|
||||||
|
|
||||||
|
s_object(s, {
|
||||||
|
s_k_int(s, "channel", m->channel);
|
||||||
|
s_k_int(s, "length", m->length);
|
||||||
|
s_k_int(s, "allocated", m->allocated);
|
||||||
|
s_k_array(s, "extra", {
|
||||||
|
s_int(s, m->extra[0]);
|
||||||
|
s_int(s, m->extra[1]);
|
||||||
|
s_int(s, m->extra[2]);
|
||||||
|
s_int(s, m->extra[3]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_channel_map(struct serializer *s, const struct channel_map *cm)
|
||||||
|
{
|
||||||
|
CHECK_NULL(cm);
|
||||||
|
|
||||||
|
s_object(s, {
|
||||||
|
s_k_int(s, "channels", cm->channels);
|
||||||
|
s_k_array(s, "map", {
|
||||||
|
for (size_t i = 0; i < cm->channels; i++)
|
||||||
|
s_int(s, cm->map[i]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_sample_spec(struct serializer *s, const struct sample_spec *ss)
|
||||||
|
{
|
||||||
|
CHECK_NULL(ss);
|
||||||
|
|
||||||
|
s_object(s, {
|
||||||
|
s_k_int(s, "channels", ss->channels);
|
||||||
|
s_k_int(s, "format", ss->format);
|
||||||
|
s_k_int(s, "rate", ss->rate);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_volume(struct serializer *s, const struct volume *volume)
|
||||||
|
{
|
||||||
|
CHECK_NULL(volume);
|
||||||
|
|
||||||
|
s_object(s, {
|
||||||
|
s_k_int(s, "channels", volume->channels);
|
||||||
|
s_k_array(s, "values", {
|
||||||
|
for (size_t i = 0; i < volume->channels; i++)
|
||||||
|
s_float(s, volume->values[i]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_sample(struct serializer *s, const struct sample *sample)
|
||||||
|
{
|
||||||
|
CHECK_NULL(sample);
|
||||||
|
|
||||||
|
s_object(s, {
|
||||||
|
s_k_int(s, "ref", sample->ref);
|
||||||
|
s_k_int(s, "index", sample->index);
|
||||||
|
s_k_str(s, "name", sample->name);
|
||||||
|
s_k_int(s, "length", sample->length);
|
||||||
|
s_key(s, "map"); dump_channel_map(s, &sample->map);
|
||||||
|
s_key(s, "ss"); dump_sample_spec(s, &sample->ss);
|
||||||
|
s_key(s, "props"); dump_props(s, sample->props);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_pw_stream(struct serializer *s, struct pw_stream *stream)
|
||||||
|
{
|
||||||
|
CHECK_NULL(stream);
|
||||||
|
|
||||||
|
s_object(s, {
|
||||||
|
const char *error;
|
||||||
|
enum pw_stream_state state = pw_stream_get_state(stream, &error);
|
||||||
|
|
||||||
|
s_k_array(s, "state", {
|
||||||
|
s_int(s, state);
|
||||||
|
s_str(s, pw_stream_state_as_string(state));
|
||||||
|
s_str(s, error);
|
||||||
|
});
|
||||||
|
|
||||||
|
s_k_str(s, "name", pw_stream_get_name(stream));
|
||||||
|
s_k_int(s, "node-id", pw_stream_get_node_id(stream));
|
||||||
|
|
||||||
|
s_key(s, "props"); dump_props(s, pw_stream_get_properties(stream));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_stream(struct serializer *s, const struct stream *stream)
|
||||||
|
{
|
||||||
|
CHECK_NULL(stream);
|
||||||
|
|
||||||
|
s_object(s, {
|
||||||
|
s_k_int(s, "id", stream->id);
|
||||||
|
s_k_int(s, "channel", stream->channel);
|
||||||
|
s_k_int(s, "create_tag", stream->create_tag);
|
||||||
|
|
||||||
|
s_k_int(s, "type", stream->type);
|
||||||
|
s_k_int(s, "direction", stream->direction);
|
||||||
|
|
||||||
|
s_k_object(s, "attr", {
|
||||||
|
s_k_int(s, "maxlength", stream->attr.maxlength);
|
||||||
|
s_k_int(s, "tlength", stream->attr.tlength);
|
||||||
|
s_k_int(s, "prebuf", stream->attr.prebuf);
|
||||||
|
s_k_int(s, "minreq", stream->attr.minreq);
|
||||||
|
s_k_int(s, "fragsize", stream->attr.fragsize);
|
||||||
|
});
|
||||||
|
|
||||||
|
s_k_int(s, "read_index", stream->read_index);
|
||||||
|
s_k_int(s, "write_index", stream->write_index);
|
||||||
|
|
||||||
|
s_k_int(s, "underrun_for", stream->underrun_for);
|
||||||
|
s_k_bool(s, "is_underrun", stream->is_underrun);
|
||||||
|
s_k_int(s, "playing_for", stream->playing_for);
|
||||||
|
|
||||||
|
s_k_int(s, "requested", stream->requested);
|
||||||
|
s_k_int(s, "last_quantum", stream->last_quantum);
|
||||||
|
s_k_int(s, "delay", stream->delay);
|
||||||
|
s_k_int(s, "timestamp", stream->timestamp);
|
||||||
|
|
||||||
|
s_k_int(s, "frame_size", stream->frame_size);
|
||||||
|
s_k_int(s, "rate", stream->rate);
|
||||||
|
|
||||||
|
s_key(s, "map"); dump_channel_map(s, &stream->map);
|
||||||
|
s_key(s, "ss"); dump_sample_spec(s, &stream->ss);
|
||||||
|
s_key(s, "volume"); dump_volume(s, &stream->volume);
|
||||||
|
|
||||||
|
s_k_bool(s, "muted", stream->muted);
|
||||||
|
s_k_bool(s, "volume_set", stream->volume_set);
|
||||||
|
s_k_bool(s, "muted_set", stream->muted_set);
|
||||||
|
|
||||||
|
s_k_bool(s, "corked", stream->corked);
|
||||||
|
|
||||||
|
s_k_int(s, "drain_tag", stream->drain_tag);
|
||||||
|
s_k_bool(s, "draining", stream->draining);
|
||||||
|
|
||||||
|
s_k_bool(s, "early_requests", stream->early_requests);
|
||||||
|
s_k_bool(s, "adjust_latency", stream->adjust_latency);
|
||||||
|
|
||||||
|
s_k_bool(s, "in_prebuf", stream->in_prebuf);
|
||||||
|
s_k_bool(s, "killed", stream->killed);
|
||||||
|
s_k_bool(s, "pending", stream->pending);
|
||||||
|
|
||||||
|
s_key(s, "stream"); dump_pw_stream(s, stream->stream);
|
||||||
|
|
||||||
|
s_key(s, "props"); dump_props(s, stream->props);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_client(struct serializer *s, const struct client *client)
|
||||||
|
{
|
||||||
|
CHECK_NULL(client);
|
||||||
|
|
||||||
|
s_object(s, {
|
||||||
|
s_k_int(s, "ref", client->ref);
|
||||||
|
s_k_str(s, "name", client->name);
|
||||||
|
|
||||||
|
s_key(s, "source"); dump_source(s, client->source);
|
||||||
|
|
||||||
|
s_k_int(s, "version", client->version);
|
||||||
|
s_k_int(s, "quirks", client->quirks);
|
||||||
|
|
||||||
|
s_k_array(s, "quirks", {
|
||||||
|
s_int(s, client->quirks);
|
||||||
|
s_array(s, {
|
||||||
|
if (client->quirks & QUIRK_FORCE_S16_FORMAT)
|
||||||
|
s_str(s, "force-s16-info");
|
||||||
|
if (client->quirks & QUIRK_REMOVE_CAPTURE_DONT_MOVE)
|
||||||
|
s_str(s, "remove-capture-dont-move");
|
||||||
|
if (client->quirks & QUIRK_BLOCK_SOURCE_VOLUME)
|
||||||
|
s_str(s, "block-source-volume");
|
||||||
|
if (client->quirks & QUIRK_BLOCK_SINK_VOLUME)
|
||||||
|
s_str(s, "block-sink-volume");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
s_k_array(s, "subscribed", {
|
||||||
|
s_int(s, client->subscribed);
|
||||||
|
s_array(s, {
|
||||||
|
if (client->subscribed & SUBSCRIPTION_MASK_SINK)
|
||||||
|
s_str(s, "sink");
|
||||||
|
if (client->subscribed & SUBSCRIPTION_MASK_SOURCE)
|
||||||
|
s_str(s, "source");
|
||||||
|
if (client->subscribed & SUBSCRIPTION_MASK_SINK_INPUT)
|
||||||
|
s_str(s, "sink-input");
|
||||||
|
if (client->subscribed & SUBSCRIPTION_MASK_SOURCE_OUTPUT)
|
||||||
|
s_str(s, "source-output");
|
||||||
|
if (client->subscribed & SUBSCRIPTION_MASK_MODULE)
|
||||||
|
s_str(s, "module");
|
||||||
|
if (client->subscribed & SUBSCRIPTION_MASK_CLIENT)
|
||||||
|
s_str(s, "client");
|
||||||
|
if (client->subscribed & SUBSCRIPTION_MASK_SAMPLE_CACHE)
|
||||||
|
s_str(s, "sample-cache");
|
||||||
|
if (client->subscribed & SUBSCRIPTION_MASK_SERVER)
|
||||||
|
s_str(s, "server");
|
||||||
|
if (client->subscribed & SUBSCRIPTION_MASK_AUTOLOAD)
|
||||||
|
s_str(s, "autoload");
|
||||||
|
if (client->subscribed & SUBSCRIPTION_MASK_CARD)
|
||||||
|
s_str(s, "card");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
s_k_int(s, "in_index", client->in_index);
|
||||||
|
s_k_int(s, "out_index", client->out_index);
|
||||||
|
|
||||||
|
s_k_bool(s, "authenticated", client->authenticated);
|
||||||
|
s_k_bool(s, "disconnected", client->disconnect);
|
||||||
|
|
||||||
|
s_k_int(s, "connect_tag", client->connect_tag);
|
||||||
|
|
||||||
|
s_k_str(s, "default_sink", client->default_sink);
|
||||||
|
s_k_str(s, "default_source", client->default_source);
|
||||||
|
s_key(s, "routes"); dump_props(s, client->routes);
|
||||||
|
|
||||||
|
s_key(s, "message"); dump_message(s, client->message);
|
||||||
|
|
||||||
|
s_k_array(s, "out_messages", {
|
||||||
|
const struct message *m;
|
||||||
|
spa_list_for_each(m, &client->out_messages, link)
|
||||||
|
dump_message(s, m);
|
||||||
|
});
|
||||||
|
|
||||||
|
s_k_array(s, "streams", {
|
||||||
|
size_t size = pw_map_get_size(&client->streams);
|
||||||
|
for (size_t i = 0; i < size; i++) {
|
||||||
|
const struct stream *stream = pw_map_lookup(&client->streams, i);
|
||||||
|
if (stream)
|
||||||
|
dump_stream(s, stream);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
s_k_array(s, "operations", {
|
||||||
|
const struct operation *o;
|
||||||
|
spa_list_for_each(o, &client->operations, link) {
|
||||||
|
s_object(s, {
|
||||||
|
s_k_int(s, "tag", o->tag);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
s_k_array(s, "pending_samples", {
|
||||||
|
const struct pending_sample *ps;
|
||||||
|
spa_list_for_each(ps, &client->pending_samples, link) {
|
||||||
|
s_object(s, {
|
||||||
|
s_k_int(s, "tag", ps->tag);
|
||||||
|
s_k_object(s, "play", {
|
||||||
|
s_k_int(s, "id", ps->play->id);
|
||||||
|
s_k_int(s, "offset", ps->play->offset);
|
||||||
|
s_k_int(s, "stride", ps->play->stride);
|
||||||
|
s_k_object(s, "sample", {
|
||||||
|
s_k_int(s, "ref", ps->play->sample->ref);
|
||||||
|
s_k_int(s, "index", ps->play->sample->index);
|
||||||
|
s_k_str(s, "name", ps->play->sample->name);
|
||||||
|
s_k_int(s, "length", ps->play->sample->length);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
s_key(s, "props"); dump_props(s, client->props);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_server(struct serializer *s, const struct server *server)
|
||||||
|
{
|
||||||
|
CHECK_NULL(server);
|
||||||
|
|
||||||
|
s_object(s, {
|
||||||
|
s_k_int(s, "af", server->addr.ss_family);
|
||||||
|
|
||||||
|
s_key(s, "source"); dump_source(s, server->source);
|
||||||
|
|
||||||
|
s_k_bool(s, "activated", server->activated);
|
||||||
|
|
||||||
|
s_k_object(s, "client_stats", {
|
||||||
|
s_k_int(s, "current", server->n_clients);
|
||||||
|
s_k_int(s, "max", server->max_clients);
|
||||||
|
s_k_int(s, "waiting", server->wait_clients);
|
||||||
|
});
|
||||||
|
|
||||||
|
s_k_int(s, "listen_backlog", server->listen_backlog);
|
||||||
|
s_k_str(s, "client_access", server->client_access);
|
||||||
|
|
||||||
|
s_k_array(s, "clients", {
|
||||||
|
const struct client *client;
|
||||||
|
spa_list_for_each(client, &server->clients, link)
|
||||||
|
dump_client(s, client);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_module(struct serializer *s, const struct module *module)
|
||||||
|
{
|
||||||
|
CHECK_NULL(module);
|
||||||
|
|
||||||
|
s_object(s, {
|
||||||
|
s_k_int(s, "index", module->index);
|
||||||
|
|
||||||
|
s_k_object(s, "info", {
|
||||||
|
s_k_str(s, "name", module->info->name);
|
||||||
|
s_k_bool(s, "load_once", module->info->load_once);
|
||||||
|
});
|
||||||
|
|
||||||
|
s_k_bool(s, "loaded", module->loaded);
|
||||||
|
s_k_bool(s, "unloading", module->unloading);
|
||||||
|
s_k_str(s, "args", module->args);
|
||||||
|
s_key(s, "props"); dump_props(s, module->props);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_frac(struct serializer *s, const char *key, const struct spa_fraction *frac)
|
||||||
|
{
|
||||||
|
CHECK_NULL(frac);
|
||||||
|
|
||||||
|
s_k_array(s, key, {
|
||||||
|
s_int(s, frac->num);
|
||||||
|
s_int(s, frac->denom);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_impl(struct serializer *s, const struct impl *impl)
|
||||||
|
{
|
||||||
|
CHECK_NULL(impl);
|
||||||
|
|
||||||
|
s_object(s, {
|
||||||
|
s_k_object(s, "stats", {
|
||||||
|
s_k_int(s, "allocated", impl->stat.allocated);
|
||||||
|
s_k_int(s, "n_allocated", impl->stat.n_allocated);
|
||||||
|
s_k_int(s, "accumulated", impl->stat.accumulated);
|
||||||
|
s_k_int(s, "n_accumulated", impl->stat.n_accumulated);
|
||||||
|
s_k_int(s, "sample_cache", impl->stat.sample_cache);
|
||||||
|
});
|
||||||
|
|
||||||
|
s_k_object(s, "defs", {
|
||||||
|
dump_frac(s, "min_req", &impl->defs.min_req);
|
||||||
|
dump_frac(s, "default_req", &impl->defs.default_req);
|
||||||
|
|
||||||
|
dump_frac(s, "min_frag", &impl->defs.min_frag);
|
||||||
|
dump_frac(s, "default_frag", &impl->defs.default_frag);
|
||||||
|
|
||||||
|
dump_frac(s, "default_tlength", &impl->defs.default_tlength);
|
||||||
|
dump_frac(s, "min_quantum", &impl->defs.min_quantum);
|
||||||
|
|
||||||
|
s_key(s, "sample_spec"); dump_sample_spec(s, &impl->defs.sample_spec);
|
||||||
|
s_key(s, "channel_map"); dump_channel_map(s, &impl->defs.channel_map);
|
||||||
|
|
||||||
|
s_k_int(s, "quantum_limit", impl->defs.quantum_limit);
|
||||||
|
});
|
||||||
|
|
||||||
|
s_k_array(s, "servers", {
|
||||||
|
const struct server *server;
|
||||||
|
spa_list_for_each(server, &impl->servers, link)
|
||||||
|
dump_server(s, server);
|
||||||
|
});
|
||||||
|
|
||||||
|
s_k_array(s, "modules", {
|
||||||
|
size_t size = pw_map_get_size(&impl->modules);
|
||||||
|
for (size_t i = 0; i < size; i++) {
|
||||||
|
const struct module *module = pw_map_lookup(&impl->modules, i);
|
||||||
|
if (module)
|
||||||
|
dump_module(s, module);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
s_k_array(s, "samples", {
|
||||||
|
size_t size = pw_map_get_size(&impl->samples);
|
||||||
|
for (size_t i = 0; i < size; i++) {
|
||||||
|
const struct sample *sample = pw_map_lookup(&impl->samples, i);
|
||||||
|
if (sample)
|
||||||
|
dump_sample(s, sample);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
s_k_array(s, "cleanup_clients", {
|
||||||
|
const struct client *client;
|
||||||
|
spa_list_for_each(client, &impl->cleanup_clients, link)
|
||||||
|
dump_client(s, client);
|
||||||
|
});
|
||||||
|
|
||||||
|
s_k_array(s, "free_messages", {
|
||||||
|
const struct message *m;
|
||||||
|
spa_list_for_each(m, &impl->free_messages, link)
|
||||||
|
dump_message(s, m);
|
||||||
|
});
|
||||||
|
|
||||||
|
s_key(s, "props"); dump_props(s, impl->props);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump_state(const struct impl *impl, FILE *target)
|
||||||
|
{
|
||||||
|
struct serializer s = {
|
||||||
|
.target = target,
|
||||||
|
.current = &s.first,
|
||||||
|
};
|
||||||
|
|
||||||
|
dump_impl(&s, impl);
|
||||||
|
}
|
||||||
10
src/modules/module-protocol-pulse/debug.h
Normal file
10
src/modules/module-protocol-pulse/debug.h
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
#ifndef PULSE_SERVER_DEBUG_H
|
||||||
|
#define PULSE_SERVER_DEBUG_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
struct impl;
|
||||||
|
|
||||||
|
void dump_state(const struct impl *impl, FILE *target);
|
||||||
|
|
||||||
|
#endif /* PULSE_SERVER_DEBUG_H */
|
||||||
Loading…
Add table
Add a link
Reference in a new issue