diff --git a/libweston/meson.build b/libweston/meson.build index f5b67fe5..941eed9d 100644 --- a/libweston/meson.build +++ b/libweston/meson.build @@ -26,6 +26,7 @@ srcs_libweston = [ 'screenshooter.c', 'timeline.c', 'touch-calibration.c', + 'weston-log-wayland.c', 'weston-debug.c', 'zoom.c', '../shared/matrix.c', diff --git a/libweston/weston-debug.c b/libweston/weston-debug.c index ed41850e..e1e6cefd 100644 --- a/libweston/weston-debug.c +++ b/libweston/weston-debug.c @@ -31,7 +31,6 @@ #include #include "weston-log-internal.h" - #include "weston-debug-server-protocol.h" #include @@ -108,27 +107,6 @@ struct weston_log_subscription { weston_log_context::pending_subscription_list */ }; - -/** 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_log_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 { - struct weston_log_subscriber base; - int fd; /**< client provided fd */ - struct wl_resource *resource; /**< weston_debug_stream_v1 object */ -}; - -static struct weston_debug_stream * -to_weston_debug_stream(struct weston_log_subscriber *sub) -{ - return container_of(sub, struct weston_debug_stream, base); -} - /** Creates a new subscription using the subscriber by \c owner. * * The subscription created is added to the \c owner subscription list. @@ -286,243 +264,6 @@ weston_debug_protocol_advertise_scopes(struct weston_log_context *log_ctx, weston_debug_v1_send_available(res, scope->name, scope->desc); } -static void -stream_close_unlink(struct weston_debug_stream *stream) -{ - if (stream->fd != -1) - close(stream->fd); - stream->fd = -1; -} - -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) { - weston_debug_stream_v1_send_failure(stream->resource, msg); - free(msg); - } else { - weston_debug_stream_v1_send_failure(stream->resource, - "MEMFAIL"); - } -} - -/** Write data into a specific debug stream - * - * \param sub The subscriber's stream to write into; must not be NULL. - * \param[in] data 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 - */ -static void -weston_debug_stream_write(struct weston_log_subscriber *sub, - const char *data, size_t len) -{ - ssize_t len_ = len; - ssize_t ret; - int e; - struct weston_debug_stream *stream = to_weston_debug_stream(sub); - - 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; - } -} - -/** Close the debug stream and send success event - * - * \param sub Subscriber's 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 - */ -static void -weston_debug_stream_complete(struct weston_log_subscriber *sub) -{ - struct weston_debug_stream *stream = to_weston_debug_stream(sub); - - stream_close_unlink(stream); - weston_debug_stream_v1_send_complete(stream->resource); -} - -static void -weston_debug_stream_to_destroy(struct weston_log_subscriber *sub) -{ - struct weston_debug_stream *stream = to_weston_debug_stream(sub); - stream_close_on_failure(stream, "debug name removed"); -} - -static struct weston_debug_stream * -stream_create(struct weston_log_context *log_ctx, const char *name, - int32_t streamfd, struct wl_resource *stream_resource) -{ - struct weston_debug_stream *stream; - struct weston_log_scope *scope; - struct weston_log_subscription *sub; - - stream = zalloc(sizeof *stream); - if (!stream) - return NULL; - - - stream->fd = streamfd; - stream->resource = stream_resource; - - stream->base.write = weston_debug_stream_write; - stream->base.destroy = weston_debug_stream_to_destroy; - stream->base.complete = weston_debug_stream_complete; - wl_list_init(&stream->base.subscription_list); - - sub = weston_log_subscription_create(&stream->base, name); - - scope = weston_log_get_scope(log_ctx, name); - if (scope) { - weston_log_subscription_add(scope, sub); - if (scope->begin_cb) - scope->begin_cb(scope, scope->user_data); - } else { - 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; - struct weston_log_subscription *sub = NULL; - - stream = wl_resource_get_user_data(stream_resource); - - if (stream->fd != -1) - close(stream->fd); - - sub = weston_log_subscriber_get_only_subscription(&stream->base); - weston_log_subscription_remove(sub); - weston_log_subscription_destroy(sub); - - 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 weston_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_log_context *log_ctx; - struct wl_resource *stream_resource; - uint32_t version; - struct weston_debug_stream *stream; - - log_ctx = wl_resource_get_user_data(global_resource); - version = wl_resource_get_version(global_resource); - - stream_resource = wl_resource_create(client, - &weston_debug_stream_v1_interface, - version, new_stream_id); - if (!stream_resource) - goto fail; - - stream = stream_create(log_ctx, 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 weston_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_log_context *log_ctx = data; - struct wl_resource *resource; - - resource = wl_resource_create(client, - &weston_debug_v1_interface, - version, id); - if (!resource) { - wl_client_post_no_memory(client); - return; - } - wl_resource_set_implementation(resource, &weston_debug_impl, - log_ctx, NULL); - - weston_debug_protocol_advertise_scopes(log_ctx, resource); -} - /** * Connect weston_compositor structure to weston_log_context structure. * @@ -620,7 +361,7 @@ weston_compositor_enable_debug_protocol(struct weston_compositor *compositor) log_ctx->global = wl_global_create(compositor->wl_display, &weston_debug_v1_interface, 1, - log_ctx, bind_weston_debug); + log_ctx, weston_log_bind_weston_debug); if (!log_ctx->global) return; diff --git a/libweston/weston-log-internal.h b/libweston/weston-log-internal.h index 84f1f10a..d93195c6 100644 --- a/libweston/weston-log-internal.h +++ b/libweston/weston-log-internal.h @@ -77,6 +77,11 @@ weston_log_subscription_add(struct weston_log_scope *scope, void weston_log_subscription_remove(struct weston_log_subscription *sub); + +void +weston_log_bind_weston_debug(struct wl_client *client, + void *data, uint32_t version, uint32_t id); + struct weston_log_scope * weston_log_get_scope(struct weston_log_context *log_ctx, const char *name); diff --git a/libweston/weston-log-wayland.c b/libweston/weston-log-wayland.c new file mode 100644 index 00000000..d1fcbf53 --- /dev/null +++ b/libweston/weston-log-wayland.c @@ -0,0 +1,295 @@ +/* + * Copyright © 2019 Collabora Ltd + * + * 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 +#include "helpers.h" +#include + +#include "weston-log-internal.h" +#include "weston-debug-server-protocol.h" + +#include +#include +#include +#include +#include +#include + +/** 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_log_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 { + struct weston_log_subscriber base; + int fd; /**< client provided fd */ + struct wl_resource *resource; /**< weston_debug_stream_v1 object */ +}; + +static struct weston_debug_stream * +to_weston_debug_stream(struct weston_log_subscriber *sub) +{ + return container_of(sub, struct weston_debug_stream, base); +} + +static void +stream_close_unlink(struct weston_debug_stream *stream) +{ + if (stream->fd != -1) + close(stream->fd); + stream->fd = -1; +} + +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) { + weston_debug_stream_v1_send_failure(stream->resource, msg); + free(msg); + } else { + weston_debug_stream_v1_send_failure(stream->resource, + "MEMFAIL"); + } +} + +/** Write data into a specific debug stream + * + * \param sub The subscriber's stream to write into; must not be NULL. + * \param[in] data 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 + */ +static void +weston_debug_stream_write(struct weston_log_subscriber *sub, + const char *data, size_t len) +{ + ssize_t len_ = len; + ssize_t ret; + int e; + struct weston_debug_stream *stream = to_weston_debug_stream(sub); + + 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; + } +} + +/** Close the debug stream and send success event + * + * \param sub Subscriber's 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 + */ +static void +weston_debug_stream_complete(struct weston_log_subscriber *sub) +{ + struct weston_debug_stream *stream = to_weston_debug_stream(sub); + + stream_close_unlink(stream); + weston_debug_stream_v1_send_complete(stream->resource); +} + +static void +weston_debug_stream_to_destroy(struct weston_log_subscriber *sub) +{ + struct weston_debug_stream *stream = to_weston_debug_stream(sub); + stream_close_on_failure(stream, "debug name removed"); +} + +static struct weston_debug_stream * +stream_create(struct weston_log_context *log_ctx, const char *name, + int32_t streamfd, struct wl_resource *stream_resource) +{ + struct weston_debug_stream *stream; + struct weston_log_scope *scope; + struct weston_log_subscription *sub; + + stream = zalloc(sizeof *stream); + if (!stream) + return NULL; + + stream->fd = streamfd; + stream->resource = stream_resource; + + stream->base.write = weston_debug_stream_write; + stream->base.destroy = weston_debug_stream_to_destroy; + stream->base.complete = weston_debug_stream_complete; + wl_list_init(&stream->base.subscription_list); + + + scope = weston_log_get_scope(log_ctx, name); + if (scope) { + sub = weston_log_subscription_create(&stream->base, name); + weston_log_subscription_add(scope, sub); + weston_log_run_begin_cb(scope); + } else { + 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; + struct weston_log_subscription *sub = NULL; + + stream = wl_resource_get_user_data(stream_resource); + + if (stream->fd != -1) + close(stream->fd); + + sub = weston_log_subscriber_get_only_subscription(&stream->base); + weston_log_subscription_remove(sub); + weston_log_subscription_destroy(sub); + + 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 weston_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_log_context *log_ctx; + struct wl_resource *stream_resource; + uint32_t version; + struct weston_debug_stream *stream; + + log_ctx = wl_resource_get_user_data(global_resource); + version = wl_resource_get_version(global_resource); + + stream_resource = wl_resource_create(client, + &weston_debug_stream_v1_interface, + version, new_stream_id); + if (!stream_resource) + goto fail; + + stream = stream_create(log_ctx, 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 weston_debug_v1_interface weston_debug_impl = { + weston_debug_destroy, + weston_debug_subscribe +}; + +void +weston_log_bind_weston_debug(struct wl_client *client, + void *data, uint32_t version, uint32_t id) +{ + struct weston_log_context *log_ctx = data; + struct wl_resource *resource; + + resource = wl_resource_create(client, + &weston_debug_v1_interface, + version, id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &weston_debug_impl, + log_ctx, NULL); + + weston_debug_protocol_advertise_scopes(log_ctx, resource); +}