
Rather than using 'begin_cb' rename it to a more suitable name. Further more instead of using the scope use the subscription to pass as an argument. The source scope is attached to the subscription when creating it so we can access it that way. This also adds a _complete and a _printf method for the subscription such that the callbacks can use to write data to only _that_ subscription and to close/complete it, otherwise writing to a scope results in writing to all subscriptions for that scope which is not correct. In the same time, the scope counter-parts of _write and _complete will now use the subscription function as well. Signed-off-by: Marius Vlad <marius.vlad@collabora.com> Suggested-by: Daniel Stone <daniels@collabora.com>
870 lines
25 KiB
C
870 lines
25 KiB
C
/*
|
|
* Copyright © 2017 Pekka Paalanen <pq@iki.fi>
|
|
* Copyright © 2018 Zodiac Inflight Innovations
|
|
*
|
|
* 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-log.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>
|
|
|
|
/** Main weston-log context
|
|
*
|
|
* One per weston_compositor. Stores list of scopes created and a list pending
|
|
* subscriptions.
|
|
*
|
|
* A pending subscription is a subscription to a scope which hasn't been
|
|
* created. When the scope is finally created the pending subscription will be
|
|
* removed from the pending subscription list, but not before was added in the
|
|
* scope's subscription list and that of the subscriber list.
|
|
*
|
|
* Pending subscriptions only make sense for other types of streams, other than
|
|
* those created by weston-debug protocol. In the case of the weston-debug
|
|
* protocol, the subscription processes is done automatically whenever a client
|
|
* connects and subscribes to a scope which was previously advertised by the
|
|
* compositor.
|
|
*
|
|
* @internal
|
|
*/
|
|
struct weston_log_context {
|
|
struct wl_listener compositor_destroy_listener;
|
|
struct wl_global *global;
|
|
struct wl_list scope_list; /**< weston_log_scope::compositor_link */
|
|
struct wl_list pending_subscription_list; /**< weston_log_subscription::source_link */
|
|
};
|
|
|
|
/** weston-log message scope
|
|
*
|
|
* This is used for scoping logging/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_log_scope {
|
|
char *name;
|
|
char *desc;
|
|
weston_log_scope_cb new_subscriber;
|
|
void *user_data;
|
|
struct wl_list compositor_link;
|
|
struct wl_list subscription_list; /**< weston_log_subscription::source_link */
|
|
};
|
|
|
|
/** Ties a subscriber to a scope
|
|
*
|
|
* A subscription is created each time we'd want to subscribe to a scope. From
|
|
* the stream type we can retrieve the subscriber and from the subscriber we
|
|
* reach each of the streams callbacks. See also weston_log_subscriber object.
|
|
*
|
|
* When a subscription has been created we store it in the scope's subscription
|
|
* list and in the subscriber's subscription list. The subscription might be a
|
|
* pending subscription until the scope for which there's was a subscribe has
|
|
* been created. The scope creation will take of looking through the pending
|
|
* subscription list.
|
|
*
|
|
* A subscription can reached from a subscriber subscription list by using the
|
|
* streams base class.
|
|
*
|
|
* @internal
|
|
*/
|
|
struct weston_log_subscription {
|
|
struct weston_log_subscriber *owner;
|
|
struct wl_list owner_link; /**< weston_log_subscriber::subscription_list */
|
|
|
|
char *scope_name;
|
|
struct weston_log_scope *source;
|
|
struct wl_list source_link; /**< weston_log_scope::subscription_list or
|
|
weston_log_context::pending_subscription_list */
|
|
};
|
|
|
|
static struct weston_log_subscription *
|
|
find_pending_subscription(struct weston_log_context *log_ctx,
|
|
const char *scope_name)
|
|
{
|
|
struct weston_log_subscription *sub;
|
|
|
|
wl_list_for_each(sub, &log_ctx->pending_subscription_list, source_link)
|
|
if (!strncmp(sub->scope_name, scope_name, strlen(scope_name)))
|
|
return sub;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/** Create a pending subscription and add it the list of pending subscriptions
|
|
*
|
|
* @param owner a subscriber represented by weston_log_subscriber object
|
|
* @param scope_name the name of the scope (which we don't have in the list of scopes)
|
|
* @param log_ctx the logging context used to add the pending subscription
|
|
*
|
|
* @memberof weston_log_subscription
|
|
*/
|
|
static void
|
|
weston_log_subscription_create_pending(struct weston_log_subscriber *owner,
|
|
const char *scope_name,
|
|
struct weston_log_context *log_ctx)
|
|
{
|
|
assert(owner);
|
|
assert(scope_name);
|
|
struct weston_log_subscription *sub = zalloc(sizeof(*sub));
|
|
|
|
if (!sub)
|
|
return;
|
|
|
|
sub->scope_name = strdup(scope_name);
|
|
sub->owner = owner;
|
|
|
|
wl_list_insert(&log_ctx->pending_subscription_list, &sub->source_link);
|
|
}
|
|
|
|
/** Destroys the pending subscription created previously with
|
|
* weston_log_subscription_create_pending()
|
|
*
|
|
* @param sub the weston_log_subscription object to remove from the list
|
|
* of subscriptions and to destroy the subscription
|
|
*
|
|
* @memberof weston_log_subscription
|
|
*/
|
|
static void
|
|
weston_log_subscription_destroy_pending(struct weston_log_subscription *sub)
|
|
{
|
|
assert(sub);
|
|
/* pending subsriptions do not have a source */
|
|
wl_list_remove(&sub->source_link);
|
|
free(sub->scope_name);
|
|
free(sub);
|
|
}
|
|
|
|
static void
|
|
weston_log_subscription_write(struct weston_log_subscription *sub,
|
|
const char *data, size_t len)
|
|
{
|
|
if (sub->owner && sub->owner->write)
|
|
sub->owner->write(sub->owner, data, len);
|
|
}
|
|
|
|
static void
|
|
weston_log_subscription_vprintf(struct weston_log_subscription *sub,
|
|
const char *fmt, va_list ap)
|
|
{
|
|
static const char oom[] = "Out of memory";
|
|
char *str;
|
|
int len;
|
|
|
|
if (!weston_log_scope_is_enabled(sub->source))
|
|
return;
|
|
|
|
len = vasprintf(&str, fmt, ap);
|
|
if (len >= 0) {
|
|
weston_log_subscription_write(sub, str, len);
|
|
free(str);
|
|
} else {
|
|
weston_log_subscription_write(sub, oom, sizeof oom - 1);
|
|
}
|
|
}
|
|
|
|
/** Creates a new subscription using the subscriber by \c owner.
|
|
*
|
|
* The subscription created is added to the \c owner subscription list.
|
|
* Destroying the subscription using weston_log_subscription_destroy() will
|
|
* remove the link from the subscription list and free storage alloc'ed.
|
|
*
|
|
* Note that this adds the subscription to the scope's subscription list
|
|
* hence the \c scope required argument.
|
|
*
|
|
* @param owner the subscriber owner, must be created before creating a
|
|
* subscription
|
|
* @param scope the scope in order to add the subscription to the scope's
|
|
* subscription list
|
|
* @returns a weston_log_subscription object in case of success, or NULL
|
|
* otherwise
|
|
*
|
|
* @internal
|
|
* @sa weston_log_subscription_destroy, weston_log_subscription_remove,
|
|
* weston_log_subscription_add
|
|
*/
|
|
void
|
|
weston_log_subscription_create(struct weston_log_subscriber *owner,
|
|
struct weston_log_scope *scope)
|
|
{
|
|
struct weston_log_subscription *sub;
|
|
assert(owner);
|
|
assert(scope);
|
|
assert(scope->name);
|
|
|
|
sub = zalloc(sizeof(*sub));
|
|
if (!sub)
|
|
return;
|
|
|
|
sub->owner = owner;
|
|
sub->scope_name = strdup(scope->name);
|
|
|
|
wl_list_insert(&sub->owner->subscription_list, &sub->owner_link);
|
|
|
|
weston_log_subscription_add(scope, sub);
|
|
weston_log_run_cb_new_subscriber(sub);
|
|
}
|
|
|
|
/** Destroys the subscription
|
|
*
|
|
* Removes the subscription from the scopes subscription list and from
|
|
* subscriber's subscription list. It destroys the subscription afterwads.
|
|
*
|
|
* @memberof weston_log_subscription
|
|
*/
|
|
void
|
|
weston_log_subscription_destroy(struct weston_log_subscription *sub)
|
|
{
|
|
assert(sub);
|
|
if (sub->owner)
|
|
wl_list_remove(&sub->owner_link);
|
|
|
|
weston_log_subscription_remove(sub);
|
|
free(sub->scope_name);
|
|
free(sub);
|
|
}
|
|
|
|
/** Retrieve a subscription by using the subscriber
|
|
*
|
|
* This is useful when trying to find a subscription from the subscriber by
|
|
* having only access to the stream.
|
|
*
|
|
* @param subscriber the subscriber in question
|
|
* @returns a weston_log_subscription object
|
|
*
|
|
* @internal
|
|
*/
|
|
struct weston_log_subscription *
|
|
weston_log_subscriber_get_only_subscription(struct weston_log_subscriber *subscriber)
|
|
{
|
|
struct weston_log_subscription *sub;
|
|
/* unlikely, but can happen */
|
|
if (wl_list_length(&subscriber->subscription_list) == 0)
|
|
return NULL;
|
|
|
|
assert(wl_list_length(&subscriber->subscription_list) == 1);
|
|
|
|
return wl_container_of(subscriber->subscription_list.prev,
|
|
sub, owner_link);
|
|
}
|
|
|
|
/** Adds the subscription \c sub to the subscription list of the
|
|
* scope.
|
|
*
|
|
* This should used when the scope has been created, and the subscription \c
|
|
* sub has be created before calling this function.
|
|
*
|
|
* @param scope the scope
|
|
* @param sub the subscription, it must be created before, see
|
|
* weston_log_subscription_create()
|
|
*
|
|
* @internal
|
|
*/
|
|
void
|
|
weston_log_subscription_add(struct weston_log_scope *scope,
|
|
struct weston_log_subscription *sub)
|
|
{
|
|
assert(scope);
|
|
assert(sub);
|
|
/* don't allow subscriptions to have a source already! */
|
|
assert(!sub->source);
|
|
|
|
sub->source = scope;
|
|
wl_list_insert(&scope->subscription_list, &sub->source_link);
|
|
}
|
|
|
|
/** Removes the subscription from the scope's subscription list
|
|
*
|
|
* @internal
|
|
*/
|
|
void
|
|
weston_log_subscription_remove(struct weston_log_subscription *sub)
|
|
{
|
|
assert(sub);
|
|
if (sub->source)
|
|
wl_list_remove(&sub->source_link);
|
|
sub->source = NULL;
|
|
}
|
|
|
|
/** Look-up the scope from the scope list stored in the log context, by
|
|
* matching against the \c name.
|
|
*
|
|
* @param log_ctx
|
|
* @param name the scope name, see weston_compositor_add_log_scope()
|
|
* @returns NULL if none found, or a pointer to a weston_log_scope
|
|
*
|
|
* @internal
|
|
*/
|
|
struct weston_log_scope *
|
|
weston_log_get_scope(struct weston_log_context *log_ctx, const char *name)
|
|
{
|
|
struct weston_log_scope *scope;
|
|
wl_list_for_each(scope, &log_ctx->scope_list, compositor_link)
|
|
if (strcmp(name, scope->name) == 0)
|
|
return scope;
|
|
return NULL;
|
|
}
|
|
|
|
/** Wrapper to invoke the weston_log_scope_cb. Allows to call the cb new_subscriber of
|
|
* a log scope.
|
|
*
|
|
* @internal
|
|
*/
|
|
void
|
|
weston_log_run_cb_new_subscriber(struct weston_log_subscription *sub)
|
|
{
|
|
if (sub->source->new_subscriber)
|
|
sub->source->new_subscriber(sub, sub->source->user_data);
|
|
}
|
|
|
|
/** Advertise the log scope name and the log scope description
|
|
*
|
|
* This is only used by the weston-debug protocol!
|
|
*
|
|
* @internal
|
|
*/
|
|
void
|
|
weston_debug_protocol_advertise_scopes(struct weston_log_context *log_ctx,
|
|
struct wl_resource *res)
|
|
{
|
|
struct weston_log_scope *scope;
|
|
wl_list_for_each(scope, &log_ctx->scope_list, compositor_link)
|
|
weston_debug_v1_send_available(res, scope->name, scope->desc);
|
|
}
|
|
|
|
/**
|
|
* Connect weston_compositor structure to weston_log_context structure.
|
|
*
|
|
* \param compositor
|
|
* \param log_ctx
|
|
* \return 0 on success, -1 on failure
|
|
*
|
|
* Sets weston_compositor::weston_log_ctx.
|
|
*/
|
|
int
|
|
weston_log_ctx_compositor_setup(struct weston_compositor *compositor,
|
|
struct weston_log_context *log_ctx)
|
|
{
|
|
assert(!compositor->weston_log_ctx);
|
|
assert(log_ctx);
|
|
|
|
compositor->weston_log_ctx = log_ctx;
|
|
return 0;
|
|
}
|
|
|
|
/** Creates weston_log_context structure
|
|
*
|
|
* \return NULL in case of failure, or a weston_log_context object in case of
|
|
* success
|
|
*
|
|
* weston_log_context is a singleton for each weston_compositor.
|
|
*
|
|
*/
|
|
WL_EXPORT struct weston_log_context *
|
|
weston_log_ctx_compositor_create(void)
|
|
{
|
|
struct weston_log_context *log_ctx;
|
|
|
|
log_ctx = zalloc(sizeof *log_ctx);
|
|
if (!log_ctx)
|
|
return NULL;
|
|
|
|
wl_list_init(&log_ctx->scope_list);
|
|
wl_list_init(&log_ctx->pending_subscription_list);
|
|
|
|
return log_ctx;
|
|
}
|
|
|
|
/** Destroy weston_log_context structure
|
|
*
|
|
* \param compositor The libweston compositor whose weston-debug to tear down.
|
|
*
|
|
* Clears weston_compositor::weston_log_ctx.
|
|
*
|
|
*/
|
|
WL_EXPORT void
|
|
weston_log_ctx_compositor_destroy(struct weston_compositor *compositor)
|
|
{
|
|
struct weston_log_context *log_ctx = compositor->weston_log_ctx;
|
|
struct weston_log_scope *scope;
|
|
struct weston_log_subscription *pending_sub, *pending_sub_tmp;
|
|
|
|
if (log_ctx->global)
|
|
wl_global_destroy(log_ctx->global);
|
|
|
|
wl_list_for_each(scope, &log_ctx->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(&log_ctx->scope_list);
|
|
|
|
/* Remove any pending subscription(s) which nobody subscribed to */
|
|
wl_list_for_each_safe(pending_sub, pending_sub_tmp,
|
|
&log_ctx->pending_subscription_list, source_link) {
|
|
weston_log_subscription_destroy_pending(pending_sub);
|
|
}
|
|
|
|
/* pending_subscription_list should be empty at this point */
|
|
|
|
free(log_ctx);
|
|
|
|
compositor->weston_log_ctx = 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 messages 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_log_context *log_ctx = compositor->weston_log_ctx;
|
|
assert(log_ctx);
|
|
if (log_ctx->global)
|
|
return;
|
|
|
|
log_ctx->global = wl_global_create(compositor->wl_display,
|
|
&weston_debug_v1_interface, 1,
|
|
log_ctx, weston_log_bind_weston_debug);
|
|
if (!log_ctx->global)
|
|
return;
|
|
|
|
weston_log("WARNING: debug protocol has been enabled. "
|
|
"This is a potential denial-of-service attack vector and "
|
|
"information leak.\n");
|
|
}
|
|
|
|
/** Determine if the debug protocol has been enabled
|
|
*
|
|
* \param wc The libweston compositor to verify if debug protocol has been enabled
|
|
*/
|
|
WL_EXPORT bool
|
|
weston_compositor_is_debug_protocol_enabled(struct weston_compositor *wc)
|
|
{
|
|
return wc->weston_log_ctx->global != NULL;
|
|
}
|
|
|
|
/** Register a new stream name, creating a log scope.
|
|
*
|
|
* @param log_ctx The weston_log_context where to add.
|
|
* @param name The debug stream/scope name; must not be NULL.
|
|
* @param description The log scope description for humans; must not be NULL.
|
|
* @param new_subscriber Optional callback when a client subscribes to this
|
|
* scope.
|
|
* @param user_data Optional user data pointer for the callback.
|
|
* @returns A valid pointer on success, NULL on failure.
|
|
*
|
|
* This function is used to create a log 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. For the weston-debug protocol,
|
|
* subscription for the scope will happen automatically but for other types of
|
|
* streams, weston_log_subscribe() should be called as to create a subscription
|
|
* and tie it to the scope created by this function.
|
|
*
|
|
* \p name must be unique in the weston_compositor instance. \p name
|
|
* and \p description must both be provided. In case of the weston-debug
|
|
* protocol, the description is printed when a client asks for a list of
|
|
* supported log scopes.
|
|
*
|
|
* \p new_subscriber, if not NULL, is called when a client subscribes to the log
|
|
* scope creating a debug stream. This is for log 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 \p user_data is
|
|
* passed in to the callback and is otherwise unused.
|
|
*
|
|
* For one-shot debug streams, \c new_subscriber should finally call
|
|
* weston_log_subscription_complete() to close the stream and tell the client
|
|
* the printing is complete. Otherwise the client expects more data to be
|
|
* written. The complete callback in weston_log_subscriber should be installed
|
|
* to trigger it and it is set-up automatically for the weston-debug protocol.
|
|
*
|
|
* As subscription can take place before creating the scope, any pending
|
|
* subscriptions to scope added by weston_log_subscribe(), will be checked
|
|
* against the scope being created and if found will be added to the scope's
|
|
* subscription list.
|
|
*
|
|
* The log scope must be destroyed using weston_compositor_log_scope_destroy()
|
|
* before destroying the weston_compositor.
|
|
*
|
|
* @memberof weston_log_scope
|
|
* @sa weston_log_scope_cb, weston_log_subscribe
|
|
*/
|
|
WL_EXPORT struct weston_log_scope *
|
|
weston_compositor_add_log_scope(struct weston_log_context *log_ctx,
|
|
const char *name,
|
|
const char *description,
|
|
weston_log_scope_cb new_subscriber,
|
|
void *user_data)
|
|
{
|
|
struct weston_log_scope *scope;
|
|
struct weston_log_subscription *pending_sub = NULL;
|
|
|
|
if (!name || !description) {
|
|
weston_log("Error: cannot add a debug scope without name or description.\n");
|
|
return NULL;
|
|
}
|
|
|
|
if (!log_ctx) {
|
|
weston_log("Error: cannot add debug scope '%s', infra not initialized.\n",
|
|
name);
|
|
return NULL;
|
|
}
|
|
|
|
if (weston_log_get_scope(log_ctx, 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->new_subscriber = new_subscriber;
|
|
scope->user_data = user_data;
|
|
wl_list_init(&scope->subscription_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(log_ctx->scope_list.prev, &scope->compositor_link);
|
|
|
|
/* check if there are any pending subscriptions to this scope */
|
|
while ((pending_sub = find_pending_subscription(log_ctx, scope->name)) != NULL) {
|
|
weston_log_subscription_create(pending_sub->owner, scope);
|
|
|
|
/* remove it from pending */
|
|
weston_log_subscription_destroy_pending(pending_sub);
|
|
}
|
|
|
|
|
|
return scope;
|
|
}
|
|
|
|
/** Destroy a log scope
|
|
*
|
|
* @param scope The log scope to destroy; may be NULL.
|
|
*
|
|
* Destroys the log scope, calling each stream's destroy callback if one was
|
|
* installed/created.
|
|
*
|
|
* @memberof weston_log_scope
|
|
*/
|
|
WL_EXPORT void
|
|
weston_compositor_log_scope_destroy(struct weston_log_scope *scope)
|
|
{
|
|
struct weston_log_subscription *sub, *sub_tmp;
|
|
|
|
if (!scope)
|
|
return;
|
|
|
|
wl_list_for_each_safe(sub, sub_tmp, &scope->subscription_list, source_link) {
|
|
/* destroy each subscription */
|
|
if (sub->owner->destroy)
|
|
sub->owner->destroy(sub->owner);
|
|
|
|
weston_log_subscription_destroy(sub);
|
|
}
|
|
|
|
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 log scope to check; may be NULL.
|
|
* \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.
|
|
*
|
|
* If the given scope is NULL, this function will always return false,
|
|
* making it safe to use in teardown or destroy code, provided the
|
|
* scope is initialized to NULL before creation and set to NULL after
|
|
* destruction.
|
|
*
|
|
* \memberof weston_log_scope
|
|
*/
|
|
WL_EXPORT bool
|
|
weston_log_scope_is_enabled(struct weston_log_scope *scope)
|
|
{
|
|
if (!scope)
|
|
return false;
|
|
|
|
return !wl_list_empty(&scope->subscription_list);
|
|
}
|
|
|
|
/** Close the stream's complete callback if one was installed/created.
|
|
*
|
|
*/
|
|
WL_EXPORT void
|
|
weston_log_subscription_complete(struct weston_log_subscription *sub)
|
|
{
|
|
if (sub->owner && sub->owner->complete)
|
|
sub->owner->complete(sub->owner);
|
|
}
|
|
|
|
/** Close the log scope.
|
|
*
|
|
* @param scope The log scope to complete; may be NULL.
|
|
*
|
|
* Complete the log scope, calling each stream's complete callback if one was
|
|
* installed/created. This can be useful to signal the reading end that the
|
|
* data has been transmited and should no longer expect that written over the
|
|
* stream. Particularly useful for the weston-debug protocol.
|
|
*
|
|
* @memberof weston_log_scope
|
|
* @sa weston_compositor_add_log_scope, weston_compositor_log_scope_destroy
|
|
*/
|
|
WL_EXPORT void
|
|
weston_log_scope_complete(struct weston_log_scope *scope)
|
|
{
|
|
struct weston_log_subscription *sub;
|
|
|
|
if (!scope)
|
|
return;
|
|
|
|
wl_list_for_each(sub, &scope->subscription_list, source_link)
|
|
weston_log_subscription_complete(sub);
|
|
}
|
|
|
|
/** Write log data for a scope
|
|
*
|
|
* \param scope The debug scope to write for; may be NULL, in which case
|
|
* nothing will be written.
|
|
* \param[in] data Pointer to the data to write.
|
|
* \param len Number of bytes to write.
|
|
*
|
|
* Writes the given data to all subscribed clients' streams.
|
|
*
|
|
* \memberof weston_log_scope
|
|
*/
|
|
WL_EXPORT void
|
|
weston_log_scope_write(struct weston_log_scope *scope,
|
|
const char *data, size_t len)
|
|
{
|
|
struct weston_log_subscription *sub;
|
|
|
|
if (!scope)
|
|
return;
|
|
|
|
wl_list_for_each(sub, &scope->subscription_list, source_link)
|
|
weston_log_subscription_write(sub, data, len);
|
|
}
|
|
|
|
/** Write a formatted string for a scope (varargs)
|
|
*
|
|
* \param scope The log scope to write for; may be NULL, in which case
|
|
* nothing will be written.
|
|
* \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_log_scope
|
|
*/
|
|
WL_EXPORT void
|
|
weston_log_scope_vprintf(struct weston_log_scope *scope,
|
|
const char *fmt, va_list ap)
|
|
{
|
|
static const char oom[] = "Out of memory";
|
|
char *str;
|
|
int len;
|
|
|
|
if (!weston_log_scope_is_enabled(scope))
|
|
return;
|
|
|
|
len = vasprintf(&str, fmt, ap);
|
|
if (len >= 0) {
|
|
weston_log_scope_write(scope, str, len);
|
|
free(str);
|
|
} else {
|
|
weston_log_scope_write(scope, oom, sizeof oom - 1);
|
|
}
|
|
}
|
|
|
|
/** Write a formatted string for a scope
|
|
*
|
|
* \param scope The log scope to write for; may be NULL, in which case
|
|
* nothing will be written.
|
|
* \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_log_scope
|
|
*/
|
|
WL_EXPORT void
|
|
weston_log_scope_printf(struct weston_log_scope *scope,
|
|
const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
weston_log_scope_vprintf(scope, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
/** Write a formatted string for a subscription
|
|
*
|
|
* \param sub The subscription to write for; may be NULL, in which case
|
|
* nothing will be written.
|
|
* \param fmt Printf-style format string and arguments.
|
|
*
|
|
* Writes to formatted string to the stream that created the subscription.
|
|
*
|
|
*/
|
|
WL_EXPORT void
|
|
weston_log_subscription_printf(struct weston_log_subscription *sub,
|
|
const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
weston_log_subscription_vprintf(sub, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
/** Write debug scope name and current time into string
|
|
*
|
|
* \param[in] scope debug scope; may be NULL
|
|
* \param[out] buf 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, if a scope is available.
|
|
* The string is NUL-terminated, even if truncated.
|
|
*/
|
|
WL_EXPORT char *
|
|
weston_log_scope_timestamp(struct weston_log_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) ? scope->name : "no scope");
|
|
} else {
|
|
snprintf(buf, len, "[?][%s]",
|
|
(scope) ? scope->name : "no scope");
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
/** Subscribe to a scope
|
|
*
|
|
* Creates a subscription which is used to subscribe the \p subscriber
|
|
* to the scope \c scope_name.
|
|
*
|
|
* If \c scope_name has already been created (using
|
|
* weston_compositor_add_log_scope) the subscription will take place
|
|
* immediately, otherwise we store the subscription into a pending list. See
|
|
* also weston_compositor_add_log_scope().
|
|
*
|
|
* @param log_ctx the log context, used for accessing pending list
|
|
* @param subscriber the subscriber, which has to be created before
|
|
* @param scope_name the scope name. In case the scope is not created
|
|
* we temporarily store the subscription in the pending list.
|
|
*/
|
|
WL_EXPORT void
|
|
weston_log_subscribe(struct weston_log_context *log_ctx,
|
|
struct weston_log_subscriber *subscriber,
|
|
const char *scope_name)
|
|
{
|
|
assert(log_ctx);
|
|
assert(subscriber);
|
|
assert(scope_name);
|
|
|
|
struct weston_log_scope *scope;
|
|
|
|
scope = weston_log_get_scope(log_ctx, scope_name);
|
|
if (scope)
|
|
weston_log_subscription_create(subscriber, scope);
|
|
else
|
|
/*
|
|
* if we don't have already as scope for it, add it to pending
|
|
* subscription list
|
|
*/
|
|
weston_log_subscription_create_pending(subscriber, scope_name, log_ctx);
|
|
}
|