Signed-off-by: Marius Vlad <marius.vlad@collabora.com>dev
parent
8f329e25f7
commit
69e7571e63
@ -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 <libweston/weston-debug.h> |
||||
#include "helpers.h" |
||||
#include <libweston/libweston.h> |
||||
|
||||
#include "weston-log-internal.h" |
||||
#include "weston-debug-server-protocol.h" |
||||
|
||||
#include <assert.h> |
||||
#include <unistd.h> |
||||
#include <stdarg.h> |
||||
#include <string.h> |
||||
#include <errno.h> |
||||
#include <sys/time.h> |
||||
|
||||
/** 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); |
||||
} |
Loading…
Reference in new issue