[PATCH weston v2 1/8] libweston: add weston_debug API and implementation
Maniraj Devadoss
external.mdevadoss at de.adit-jv.com
Mon Sep 11 15:35:52 UTC 2017
From: Pekka Paalanen <pq at iki.fi>
weston_debug is both a libweston API for relaying debugging messages,
and the compositor-debug wayland protocol implementation for accessing those
debug messages from a Wayland client.
weston_debug_compositor_{create,destroy}() are private API, hence not
exported.
Signed-off-by: Pekka Paalanen <pq at iki.fi>
append the debug scope name along with the timestamp in
weston_debug_scope_timestamp API
modified to use the compositor-debug protocol from wayland-protocols
note:This patch depends on an unreleased version of wayland-protocols.
so a wayland-protocols release (with the compositor-debug protocol
addition) is needed before merging this patch upstream.
Signed-off-by: Maniraj Devadoss <external.mdevadoss at de.adit-jv.com>
---
Makefile.am | 6 +
libweston/compositor.c | 5 +
libweston/compositor.h | 8 +
libweston/weston-debug.c | 755 +++++++++++++++++++++++++++++++++++++++++++++++
libweston/weston-debug.h | 107 +++++++
5 files changed, 881 insertions(+)
create mode 100644 libweston/weston-debug.c
create mode 100644 libweston/weston-debug.h
diff --git a/Makefile.am b/Makefile.am
index 58a5c59..61fe9e8 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -108,6 +108,8 @@ libweston_ at LIBWESTON_MAJOR@_la_SOURCES = \
libweston/linux-dmabuf.h \
libweston/pixel-formats.c \
libweston/pixel-formats.h \
+ libweston/weston-debug.c \
+ libweston/weston-debug.h \
shared/helpers.h \
shared/matrix.c \
shared/matrix.h \
@@ -166,6 +168,8 @@ endif
nodist_libweston_ at LIBWESTON_MAJOR@_la_SOURCES = \
protocol/weston-screenshooter-protocol.c \
protocol/weston-screenshooter-server-protocol.h \
+ protocol/compositor-debug-unstable-v1-protocol.c \
+ protocol/compositor-debug-unstable-v1-server-protocol.h \
protocol/text-cursor-position-protocol.c \
protocol/text-cursor-position-server-protocol.h \
protocol/text-input-unstable-v1-protocol.c \
@@ -872,6 +876,8 @@ endif
BUILT_SOURCES += \
protocol/weston-screenshooter-protocol.c \
protocol/weston-screenshooter-client-protocol.h \
+ protocol/compositor-debug-unstable-v1-protocol.c \
+ protocol/compositor-debug-unstable-v1-client-protocol.h \
protocol/text-cursor-position-client-protocol.h \
protocol/text-cursor-position-protocol.c \
protocol/text-input-unstable-v1-protocol.c \
diff --git a/libweston/compositor.c b/libweston/compositor.c
index 813b663..c4d27b4 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -5281,6 +5281,9 @@ weston_compositor_create(struct wl_display *display, void *user_data)
ec, bind_presentation))
goto fail;
+ if (weston_debug_compositor_create(ec) < 0)
+ goto fail;
+
if (weston_input_init(ec) != 0)
goto fail;
@@ -5567,6 +5570,8 @@ weston_compositor_destroy(struct weston_compositor *compositor)
weston_plugin_api_destroy_list(compositor);
+ weston_debug_compositor_destroy(compositor);
+
free(compositor);
}
diff --git a/libweston/compositor.h b/libweston/compositor.h
index 769203a..d239174 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -849,6 +849,7 @@ struct weston_backend {
struct weston_desktop_xwayland;
struct weston_desktop_xwayland_interface;
+struct weston_debug_compositor;
struct weston_compositor {
struct wl_signal destroy_signal;
@@ -944,6 +945,7 @@ struct weston_compositor {
/* Whether to let the compositor run without any input device. */
bool require_input;
+ struct weston_debug_compositor *weston_debug;
};
struct weston_buffer {
@@ -1944,6 +1946,12 @@ weston_pending_output_coldplug(struct weston_compositor *compositor);
struct weston_output *
weston_output_from_resource(struct wl_resource *resource);
+int
+weston_debug_compositor_create(struct weston_compositor *compositor);
+
+void
+weston_debug_compositor_destroy(struct weston_compositor *compositor);
+
#ifdef __cplusplus
}
#endif
diff --git a/libweston/weston-debug.c b/libweston/weston-debug.c
new file mode 100644
index 0000000..2c32c47
--- /dev/null
+++ b/libweston/weston-debug.c
@@ -0,0 +1,755 @@
+/*
+ * Copyright © 2017 Pekka Paalanen <pq at iki.fi>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "weston-debug.h"
+#include "helpers.h"
+#include "compositor.h"
+
+#include "compositor-debug-unstable-v1-server-protocol.h"
+
+#include <unistd.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/time.h>
+
+/** Main weston-debug context
+ *
+ * One per weston_compositor.
+ *
+ * \internal
+ */
+struct weston_debug_compositor {
+ struct weston_compositor *compositor;
+ struct wl_listener compositor_destroy_listener;
+ struct wl_global *global;
+ struct wl_list scope_list; /**< weston_debug_scope::compositor_link */
+
+ struct weston_debug_scope *list;
+};
+
+/** weston-debug message scope
+ *
+ * This is used for scoping debugging messages. Clients can subscribe to
+ * only the scopes they are interested in. A scope is identified by its name
+ * (also referred to as debug stream name).
+ */
+struct weston_debug_scope {
+ char *name;
+ char *desc;
+ weston_debug_scope_cb begin_cb;
+ void *user_data;
+ struct wl_list stream_list; /**< weston_debug_stream::scope_link */
+ struct wl_list compositor_link;
+};
+
+/** A debug stream created by a client
+ *
+ * A client provides a file descriptor for the server to write debug
+ * messages into. A weston_debug_stream is associated to one
+ * weston_debug_scope via the scope name, and the scope provides the messages.
+ * There can be several streams for the same scope, all streams getting the
+ * same messages.
+ */
+struct weston_debug_stream {
+ int fd; /**< client provided fd */
+ struct wl_resource *resource; /**< weston_debug_stream_v1 object */
+ struct wl_list scope_link;
+};
+
+static void
+list_scope_begin(struct weston_debug_stream *stream, void *user_data)
+{
+ struct weston_debug_compositor *wdc = user_data;
+ struct weston_debug_scope *scope;
+
+ weston_debug_stream_printf(stream, "Supported debug streams:\n");
+
+ wl_list_for_each(scope, &wdc->scope_list, compositor_link) {
+ weston_debug_stream_printf(stream, "\n * Name: %s\n", scope->name);
+ weston_debug_stream_write(stream,
+ scope->desc, strlen(scope->desc));
+ }
+
+ weston_debug_stream_complete(stream);
+}
+
+static struct weston_debug_scope *
+list_scope_init(struct weston_debug_compositor *wdc)
+{
+ struct weston_debug_scope *scope;
+
+ static const char desc[] =
+ "Prints the list of available debug stream names.\n";
+
+ scope = weston_compositor_add_debug_scope(wdc->compositor, "list",
+ desc, list_scope_begin,
+ wdc);
+
+ return scope;
+}
+
+static struct weston_debug_scope *
+get_scope(struct weston_debug_compositor *wdc, const char *name)
+{
+ struct weston_debug_scope *scope;
+
+ wl_list_for_each(scope, &wdc->scope_list, compositor_link)
+ if (strcmp(name, scope->name) == 0)
+ return scope;
+
+ return NULL;
+}
+
+static void
+stream_close_unlink(struct weston_debug_stream *stream)
+{
+ if (stream->fd != -1)
+ close(stream->fd);
+ stream->fd = -1;
+
+ wl_list_remove(&stream->scope_link);
+ wl_list_init(&stream->scope_link);
+}
+
+static void WL_PRINTF(2, 3)
+stream_close_on_failure(struct weston_debug_stream *stream,
+ const char *fmt, ...)
+{
+ char *msg;
+ va_list ap;
+ int ret;
+
+ stream_close_unlink(stream);
+
+ va_start(ap, fmt);
+ ret = vasprintf(&msg, fmt, ap);
+ va_end(ap);
+
+ if (ret > 0) {
+ zcompositor_debug_stream_v1_send_failure(stream->resource, msg);
+ free(msg);
+ } else {
+ zcompositor_debug_stream_v1_send_failure(stream->resource,
+ "MEMFAIL");
+ }
+}
+
+static struct weston_debug_stream *
+stream_create(struct weston_debug_compositor *wdc, const char *name,
+ int32_t streamfd, struct wl_resource *stream_resource)
+{
+ struct weston_debug_stream *stream;
+ struct weston_debug_scope *scope;
+
+ stream = zalloc(sizeof *stream);
+ if (!stream)
+ return NULL;
+
+ stream->fd = streamfd;
+ stream->resource = stream_resource;
+
+ scope = get_scope(wdc, name);
+ if (scope) {
+ wl_list_insert(&scope->stream_list, &stream->scope_link);
+
+ if (scope->begin_cb)
+ scope->begin_cb(stream, scope->user_data);
+ } else {
+ wl_list_init(&stream->scope_link);
+ stream_close_on_failure(stream,
+ "Debug stream name '%s' is unknown.",
+ name);
+ }
+
+ return stream;
+}
+
+static void
+stream_destroy(struct wl_resource *stream_resource)
+{
+ struct weston_debug_stream *stream;
+
+ stream = wl_resource_get_user_data(stream_resource);
+
+ if (stream->fd != -1)
+ close(stream->fd);
+ wl_list_remove(&stream->scope_link);
+ free(stream);
+}
+
+static void
+weston_debug_stream_destroy(struct wl_client *client,
+ struct wl_resource *stream_resource)
+{
+ wl_resource_destroy(stream_resource);
+}
+
+static const struct zcompositor_debug_stream_v1_interface
+ weston_debug_stream_impl = {
+ weston_debug_stream_destroy
+};
+
+static void
+weston_debug_destroy(struct wl_client *client,
+ struct wl_resource *global_resource)
+{
+ wl_resource_destroy(global_resource);
+}
+
+static void
+weston_debug_subscribe(struct wl_client *client,
+ struct wl_resource *global_resource,
+ const char *name,
+ int32_t streamfd,
+ uint32_t new_stream_id)
+{
+ struct weston_debug_compositor *wdc;
+ struct wl_resource *stream_resource;
+ uint32_t version;
+ struct weston_debug_stream *stream;
+
+ wdc = wl_resource_get_user_data(global_resource);
+ version = wl_resource_get_version(global_resource);
+
+ stream_resource = wl_resource_create(client,
+ &zcompositor_debug_stream_v1_interface,
+ version, new_stream_id);
+ if (!stream_resource)
+ goto fail;
+
+ stream = stream_create(wdc, name, streamfd, stream_resource);
+ if (!stream)
+ goto fail;
+
+ wl_resource_set_implementation(stream_resource,
+ &weston_debug_stream_impl,
+ stream, stream_destroy);
+ return;
+
+fail:
+ close(streamfd);
+ wl_client_post_no_memory(client);
+}
+
+static const struct zcompositor_debug_v1_interface weston_debug_impl = {
+ weston_debug_destroy,
+ weston_debug_subscribe
+};
+
+static void
+bind_weston_debug(struct wl_client *client,
+ void *data, uint32_t version, uint32_t id)
+{
+ struct weston_debug_compositor *wdc = data;
+ struct wl_resource *resource;
+
+ resource = wl_resource_create(client,
+ &zcompositor_debug_v1_interface,
+ version, id);
+ if (!resource) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+ wl_resource_set_implementation(resource, &weston_debug_impl,
+ wdc, NULL);
+}
+
+/** Initialize weston-debug structure
+ *
+ * \param compositor The libweston compositor.
+ * \return 0 on success, -1 on failure.
+ *
+ * weston_debug_compositor is a singleton for each weston_compositor.
+ *
+ * Sets weston_compositor::weston_debug.
+ *
+ * \internal
+ */
+int
+weston_debug_compositor_create(struct weston_compositor *compositor)
+{
+ struct weston_debug_compositor *wdc;
+
+ if (compositor->weston_debug)
+ return -1;
+
+ wdc = zalloc(sizeof *wdc);
+ if (!wdc)
+ return -1;
+
+ wdc->compositor = compositor;
+ wl_list_init(&wdc->scope_list);
+
+ compositor->weston_debug = wdc;
+
+ wdc->list = list_scope_init(wdc);
+
+ return 0;
+}
+
+/** Destroy weston_debug_compositor structure
+ *
+ * \param compositor The libweston compositor whose weston-debug to tear down.
+ *
+ * Clears weston_compositor::weston_debug.
+ *
+ * \internal
+ */
+void
+weston_debug_compositor_destroy(struct weston_compositor *compositor)
+{
+ struct weston_debug_compositor *wdc = compositor->weston_debug;
+ struct weston_debug_scope *scope;
+
+ weston_debug_scope_destroy(wdc->list);
+
+ if (wdc->global)
+ wl_global_destroy(wdc->global);
+
+ wl_list_for_each(scope, &wdc->scope_list, compositor_link)
+ weston_log("Internal warning: debug scope '%s' has not been destroyed.\n",
+ scope->name);
+
+ /* Remove head to not crash if scope removed later. */
+ wl_list_remove(&wdc->scope_list);
+
+ free(wdc);
+
+ compositor->weston_debug = NULL;
+}
+
+/** Enable weston-debug protocol extension
+ *
+ * \param compositor The libweston compositor where to enable.
+ *
+ * This enables the weston_debug_v1 Wayland protocol extension which any
+ * client can use to get debug messsages from the compositor.
+ *
+ * WARNING: This feature should not be used in production. If a client
+ * provides a file descriptor that blocks writes, it will block the whole
+ * compositor indefinitely.
+ *
+ * There is no control on which client is allowed to subscribe to debug
+ * messages. Any and all clients are allowed.
+ *
+ * The debug extension is disabled by default, and once enabled, cannot be
+ * disabled again.
+ */
+WL_EXPORT void
+weston_compositor_enable_debug_protocol(struct weston_compositor *compositor)
+{
+ struct weston_debug_compositor *wdc;
+
+ wdc = compositor->weston_debug;
+ if (!wdc)
+ return;
+
+ if (wdc->global)
+ return;
+
+ wdc->global = wl_global_create(compositor->wl_display,
+ &zcompositor_debug_v1_interface, 1,
+ wdc, bind_weston_debug);
+ if (!wdc->global)
+ return;
+
+ weston_log("WARNING: debug protocol has been enabled. "
+ "This is a potential denial-of-service attack vector and "
+ "information leak.\n");
+}
+
+/** Register a new debug stream name, creating a debug scope
+ *
+ * \param compositor The libweston compositor where to add.
+ * \param name The debug stream/scope name.
+ * \param desc The debug scope description for humans.
+ * \param begin_cb Optional callback when a client subscribes to this scope.
+ * \param user_data Optional user data pointer for the callback.
+ * \return A valid pointer on success, NULL on failure.
+ *
+ * This function is used to create a debug scope. All debug message printing
+ * happens for a scope, which allows clients to subscribe to the kind of
+ * debug messages they want by \c name.
+ *
+ * \c name must be unique in the \c weston_compositor instance. \c name and
+ * \c description must both be provided. The description is printed when a
+ * client asks for a list of supported debug scopes.
+ *
+ * \c begin_cb, if not NULL, is called when a client subscribes to the
+ * debug scope creating a debug stream. This is for debug scopes that need
+ * to print messages as a response to a client appearing, e.g. printing a
+ * list of windows on demand or a static preamble. The argument \c user_data
+ * is passed in to the callback and is otherwise unused.
+ *
+ * For one-shot debug streams, \c begin_cb should finally call
+ * weston_debug_stream_complete() to close the stream and tell the client
+ * the printing is complete. Otherwise the client expects more to be written
+ * to its file descriptor.
+ *
+ * The debug scope must be destroyed before destroying the
+ * \c weston_compositor.
+ *
+ * \memberof weston_debug_scope
+ * \sa weston_debug_stream, weston_debug_scope_cb
+ */
+WL_EXPORT struct weston_debug_scope *
+weston_compositor_add_debug_scope(struct weston_compositor *compositor,
+ const char *name,
+ const char *description,
+ weston_debug_scope_cb begin_cb,
+ void *user_data)
+{
+ struct weston_debug_compositor *wdc;
+ struct weston_debug_scope *scope;
+
+ if (!compositor || !name || !description) {
+ weston_log("Error: cannot add a debug scope without name or description.\n");
+ return NULL;
+ }
+
+ wdc = compositor->weston_debug;
+ if (!wdc) {
+ weston_log("Error: cannot add debug scope '%s', infra not initialized.\n",
+ name);
+ return NULL;
+ }
+
+ if (get_scope(wdc, name)){
+ weston_log("Error: debug scope named '%s' is already registered.\n",
+ name);
+ return NULL;
+ }
+
+ scope = zalloc(sizeof *scope);
+ if (!scope) {
+ weston_log("Error adding debug scope '%s': out of memory.\n",
+ name);
+ return NULL;
+ }
+
+ scope->name = strdup(name);
+ scope->desc = strdup(description);
+ scope->begin_cb = begin_cb;
+ scope->user_data = user_data;
+ wl_list_init(&scope->stream_list);
+
+ if (!scope->name || !scope->desc) {
+ weston_log("Error adding debug scope '%s': out of memory.\n",
+ name);
+ free(scope->name);
+ free(scope->desc);
+ free(scope);
+ return NULL;
+ }
+
+ wl_list_insert(wdc->scope_list.prev, &scope->compositor_link);
+
+ return scope;
+}
+
+/** Destroy a debug scope
+ *
+ * \param scope The debug scope to destroy.
+ *
+ * Destroys the debug scope, closing all open streams subscribed to it and
+ * sending them each a \c weston_debug_stream_v1.failure event.
+ *
+ * \memberof weston_debug_scope
+ */
+WL_EXPORT void
+weston_debug_scope_destroy(struct weston_debug_scope *scope)
+{
+ struct weston_debug_stream *stream;
+
+ if (!scope)
+ return;
+
+ while (!wl_list_empty(&scope->stream_list)) {
+ stream = wl_container_of(scope->stream_list.prev,
+ stream, scope_link);
+
+ stream_close_on_failure(stream, "debug name removed");
+ }
+
+ wl_list_remove(&scope->compositor_link);
+ free(scope->name);
+ free(scope->desc);
+ free(scope);
+}
+
+/** Are there any active subscriptions to the scope?
+ *
+ * \param scope The debug scope to check.
+ * \return True if any streams are open for this scope, false otherwise.
+ *
+ * As printing some debugging messages may be relatively expensive, one
+ * can use this function to determine if there is a need to gather the
+ * debugging information at all. If this function returns false, all
+ * printing for this scope is dropped, so gathering the information is
+ * pointless.
+ *
+ * The return value of this function should not be stored, as new clients
+ * may subscribe to the debug scope later.
+ *
+ * \memberof weston_debug_scope
+ */
+WL_EXPORT bool
+weston_debug_scope_is_enabled(struct weston_debug_scope *scope)
+{
+ if (!scope)
+ return false;
+
+ return !wl_list_empty(&scope->stream_list);
+}
+
+/** Write data into a specific debug stream
+ *
+ * \param stream The debug stream to write into.
+ * \param data[in] Pointer to the data to write.
+ * \param len Number of bytes to write.
+ *
+ * Writes the given data (binary verbatim) into the debug stream.
+ * If \c len is zero or negative, the write is silently dropped.
+ *
+ * Writing is continued until all data has been written or
+ * a write fails. If the write fails due to a signal, it is re-tried.
+ * Otherwise on failure, the stream is closed and
+ * \c weston_debug_stream_v1.failure event is sent to the client.
+ *
+ * \memberof weston_debug_stream
+ */
+WL_EXPORT void
+weston_debug_stream_write(struct weston_debug_stream *stream,
+ const char *data, size_t len)
+{
+ ssize_t len_ = len;
+ ssize_t ret;
+ int e;
+
+ if (stream->fd == -1)
+ return;
+
+ while (len_ > 0) {
+ ret = write(stream->fd, data, len_);
+ e = errno;
+ if (ret < 0) {
+ if (e == EINTR)
+ continue;
+
+ stream_close_on_failure(stream,
+ "Error writing %zd bytes: %s (%d)",
+ len_, strerror(e), e);
+ break;
+ }
+
+ len_ -= ret;
+ data += ret;
+ }
+}
+
+/** Write a formatted string into a specific debug stream (varargs)
+ *
+ * \param stream The debug stream to write into.
+ * \param fmt Printf-style format string.
+ * \param ap Formatting arguments.
+ *
+ * The behavioral details are the same as for weston_debug_stream_write().
+ *
+ * \memberof weston_debug_stream
+ */
+WL_EXPORT void
+weston_debug_stream_vprintf(struct weston_debug_stream *stream,
+ const char *fmt, va_list ap)
+{
+ char *str;
+ int len;
+
+ len = vasprintf(&str, fmt, ap);
+ if (len >= 0) {
+ weston_debug_stream_write(stream, str, len);
+ free(str);
+ } else {
+ stream_close_on_failure(stream, "Out of memory");
+ }
+}
+
+/** Write a formatted string into a specific debug stream
+ *
+ * \param stream The debug stream to write into.
+ * \param fmt Printf-style format string and arguments.
+ *
+ * The behavioral details are the same as for weston_debug_stream_write().
+ *
+ * \memberof weston_debug_stream
+ */
+WL_EXPORT void
+weston_debug_stream_printf(struct weston_debug_stream *stream,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ weston_debug_stream_vprintf(stream, fmt, ap);
+ va_end(ap);
+}
+
+/** Close the debug stream and send success event
+ *
+ * \param stream The debug stream to close.
+ *
+ * Closes the debug stream and sends \c weston_debug_stream_v1.complete
+ * event to the client. This tells the client the debug information dump
+ * is complete.
+ *
+ * \memberof weston_debug_stream
+ */
+WL_EXPORT void
+weston_debug_stream_complete(struct weston_debug_stream *stream)
+{
+ stream_close_unlink(stream);
+ zcompositor_debug_stream_v1_send_complete(stream->resource);
+}
+
+/** Write debug data for a scope
+ *
+ * \param scope The debug scope to write for.
+ * \param data[in] Pointer to the data to write.
+ * \param len Number of bytes to write.
+ *
+ * Writes the given data to all subscribed clients' streams.
+ *
+ * The behavioral details for each stream are the same as for
+ * weston_debug_stream_write().
+ *
+ * \memberof weston_debug_scope
+ */
+WL_EXPORT void
+weston_debug_scope_write(struct weston_debug_scope *scope,
+ const char *data, size_t len)
+{
+ struct weston_debug_stream *stream;
+
+ if (!scope)
+ return;
+
+ wl_list_for_each(stream, &scope->stream_list, scope_link)
+ weston_debug_stream_write(stream, data, len);
+}
+
+/** Write a formatted string for a scope (varargs)
+ *
+ * \param scope The debug scope to write for.
+ * \param fmt Printf-style format string.
+ * \param ap Formatting arguments.
+ *
+ * Writes to formatted string to all subscribed clients' streams.
+ *
+ * The behavioral details for each stream are the same as for
+ * weston_debug_stream_write().
+ *
+ * \memberof weston_debug_scope
+ */
+WL_EXPORT void
+weston_debug_scope_vprintf(struct weston_debug_scope *scope,
+ const char *fmt, va_list ap)
+{
+ static const char oom[] = "Out of memory";
+ char *str;
+ int len;
+
+ if (!weston_debug_scope_is_enabled(scope))
+ return;
+
+ len = vasprintf(&str, fmt, ap);
+ if (len >= 0) {
+ weston_debug_scope_write(scope, str, len);
+ free(str);
+ } else {
+ weston_debug_scope_write(scope, oom, sizeof oom - 1);
+ }
+}
+
+/** Write a formatted string for a scope
+ *
+ * \param scope The debug scope to write for.
+ * \param fmt Printf-style format string and arguments.
+ *
+ * Writes to formatted string to all subscribed clients' streams.
+ *
+ * The behavioral details for each stream are the same as for
+ * weston_debug_stream_write().
+ *
+ * \memberof weston_debug_scope
+ */
+WL_EXPORT void
+weston_debug_scope_printf(struct weston_debug_scope *scope,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ weston_debug_scope_vprintf(scope, fmt, ap);
+ va_end(ap);
+}
+
+/** Format current time as a string
+ * and append the debug scope name to it
+ *
+ * \param scope[in] debug scope.
+ * \param buf[out] Buffer to store the string.
+ * \param len Available size in the buffer in bytes.
+ * \return \c buf
+ *
+ * Reads the current local wall-clock time and formats it into a string.
+ * and append the debug scope name to it.
+ * The string is nul-terminated, even if truncated.
+ */
+WL_EXPORT char *
+weston_debug_scope_timestamp(struct weston_debug_scope *scope,
+ char *buf, size_t len)
+{
+ struct timeval tv;
+ struct tm *bdt;
+ char string[128];
+ size_t ret = 0;
+
+ gettimeofday(&tv, NULL);
+
+ bdt = localtime(&tv.tv_sec);
+ if (bdt)
+ ret = strftime(string, sizeof string,
+ "%Y-%m-%d %H:%M:%S", bdt);
+
+ if (ret > 0)
+ snprintf(buf, len, "[%s.%03ld][%s]", string,
+ tv.tv_usec / 1000, scope->name);
+ else
+ snprintf(buf, len, "[?][%s]", scope->name);
+
+ return buf;
+}
diff --git a/libweston/weston-debug.h b/libweston/weston-debug.h
new file mode 100644
index 0000000..c76cec8
--- /dev/null
+++ b/libweston/weston-debug.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright © 2017 Pekka Paalanen <pq at iki.fi>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef WESTON_DEBUG_H
+#define WESTON_DEBUG_H
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct weston_compositor;
+
+void
+weston_compositor_enable_debug_protocol(struct weston_compositor *);
+
+struct weston_debug_scope;
+struct weston_debug_stream;
+
+/** weston_debug_scope callback
+ *
+ * \param stream The debug stream.
+ * \param user_data The \c user_data argument given to
+ * weston_compositor_add_debug_scope()
+ *
+ * \memberof weston_debug_scope
+ * \sa weston_debug_stream
+ */
+typedef void (*weston_debug_scope_cb)(struct weston_debug_stream *stream,
+ void *user_data);
+
+struct weston_debug_scope *
+weston_compositor_add_debug_scope(struct weston_compositor *compositor,
+ const char *name,
+ const char *description,
+ weston_debug_scope_cb begin_cb,
+ void *user_data);
+
+void
+weston_debug_scope_destroy(struct weston_debug_scope *scope);
+
+bool
+weston_debug_scope_is_enabled(struct weston_debug_scope *scope);
+
+void
+weston_debug_scope_write(struct weston_debug_scope *scope,
+ const char *data, size_t len);
+
+void
+weston_debug_scope_vprintf(struct weston_debug_scope *scope,
+ const char *fmt, va_list ap);
+
+void
+weston_debug_scope_printf(struct weston_debug_scope *scope,
+ const char *fmt, ...)
+ __attribute__ ((format (printf, 2, 3)));
+
+void
+weston_debug_stream_write(struct weston_debug_stream *stream,
+ const char *data, size_t len);
+
+void
+weston_debug_stream_vprintf(struct weston_debug_stream *stream,
+ const char *fmt, va_list ap);
+
+void
+weston_debug_stream_printf(struct weston_debug_stream *stream,
+ const char *fmt, ...)
+ __attribute__ ((format (printf, 2, 3)));
+
+void
+weston_debug_stream_complete(struct weston_debug_stream *stream);
+
+char *
+weston_debug_scope_timestamp(struct weston_debug_scope *scope,
+ char *buf, size_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* WESTON_DEBUG_H */
--
2.7.4
More information about the wayland-devel
mailing list