mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2026-05-29 21:37:54 -04:00
Merge branch 'pw_pulse_debug' into 'master'
Draft: pulse-server: add facilities to dump internal state See merge request pipewire/pipewire!1117
This commit is contained in:
commit
7c12e821b2
4 changed files with 677 additions and 0 deletions
|
|
@ -341,6 +341,7 @@ pipewire_module_protocol_pulse_sources = [
|
|||
'module-protocol-pulse/client.c',
|
||||
'module-protocol-pulse/collect.c',
|
||||
'module-protocol-pulse/cmd.c',
|
||||
'module-protocol-pulse/debug.c',
|
||||
'module-protocol-pulse/extension.c',
|
||||
'module-protocol-pulse/extensions/ext-device-manager.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 */
|
||||
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "client.h"
|
||||
#include "collect.h"
|
||||
#include "debug.h"
|
||||
#include "log.h"
|
||||
#include "manager.h"
|
||||
#include "message-handler.h"
|
||||
|
|
@ -114,6 +115,8 @@ static int core_object_message_handler(struct client *client, struct pw_manager_
|
|||
int res = malloc_trim(0);
|
||||
fprintf(response, "%d", res);
|
||||
#endif
|
||||
} else if (spa_streq(message, "pipewire-pulse:dump-state")) {
|
||||
dump_state(client->impl, response);
|
||||
} else {
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue