From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Lloyd Pique Date: Fri, 11 Mar 2022 17:57:37 -0800 Subject: [PATCH 3/6] protocol-logger-test: Demonstrate logging Client message observers 3/6 Adds code demonstrating how to replicate the output produced by the internal wl_closure_print() using the client message observer interface. If you run protocol-logger-test with "WAYLAND_DEBUG=client", you can see the client messages logged to stderr twice, with the same strings. Signed-off-by: Lloyd Pique diff --git a/tests/protocol-logger-test.c b/tests/protocol-logger-test.c index 3b9dc3e..082f055 100644 --- a/tests/protocol-logger-test.c +++ b/tests/protocol-logger-test.c @@ -80,6 +80,7 @@ struct client { struct wl_display *display; struct wl_callback *cb; struct wl_client_observer *sequence_observer; + struct wl_client_observer *stderr_logger; struct expected_client_message *expected_msg; int expected_msg_count; @@ -190,6 +191,130 @@ client_sequence_observer_func( "arg count mismatch: %s", details_msg); } +// A slightly simplified version of get_next_argument() from src/connection.c +static const char * +get_next_argument_type(const char *signature, char *type) +{ + for (; *signature; ++signature) { + assert(strchr("iufsonah?", *signature) != NULL); + switch (*signature) { + case 'i': + case 'u': + case 'f': + case 's': + case 'o': + case 'n': + case 'a': + case 'h': + *type = *signature; + return signature + 1; + case '?': + break; + } + } + *type = 0; + return signature; +} + +// This duplicates what the internal wl_closure_print function does, and can be +// used as a starting point for a client or server that wants to log messages. +static void +client_log_to_stderr_demo(void *user_data, enum wl_client_message_type type, + const struct wl_client_observed_message *message) +{ + int i; + char arg_type; + const char *signature = message->message->signature; + const union wl_argument *args = message->arguments; + struct wl_proxy *arg_proxy; + const char *arg_class; + struct timespec tp; + unsigned long long time; + FILE *f; + char *buffer; + size_t buffer_length; + + f = open_memstream(&buffer, &buffer_length); + if (f == NULL) + return; + + clock_gettime(CLOCK_REALTIME, &tp); + time = (tp.tv_sec * 1000000LL) + (tp.tv_nsec / 1000); + + // Note: server logger will be given message->resource, and should + // use wl_resource_get_class and wl_resolurce_get_id. + fprintf(f, "[%7llu.%03llu] %s%s%s%s%s@%u.%s(", time / 1000, time % 1000, + (message->discarded_reason_str ? "discarded[" : ""), + (message->discarded_reason_str ? message->discarded_reason_str + : ""), + (message->discarded_reason_str ? "] " : ""), + (type == WL_CLIENT_MESSAGE_REQUEST) ? " -> " : "", + wl_proxy_get_class(message->proxy), + wl_proxy_get_id(message->proxy), message->message->name); + + for (i = 0; i < message->arguments_count; i++) { + signature = get_next_argument_type(signature, &arg_type); + if (i > 0) + fprintf(f, ", "); + + switch (arg_type) { + case 'u': + fprintf(f, "%u", args[i].u); + break; + case 'i': + fprintf(f, "%d", args[i].i); + break; + case 'f': + fprintf(f, "%f", wl_fixed_to_double(args[i].f)); + break; + case 's': + if (args[i].s) + fprintf(f, "\"%s\"", args[i].s); + else + fprintf(f, "nil"); + break; + case 'o': + if (args[i].o) { + // Note: server logger should instead cast to + // wl_resource, and use wl_resource_get_class + // and wl_resource_get_id. + arg_proxy = (struct wl_proxy *)(args[i].o); + arg_class = wl_proxy_get_class(arg_proxy); + + fprintf(f, "%s@%u", + arg_class ? arg_class : "[unknown]", + wl_proxy_get_id(arg_proxy)); + } else { + fprintf(f, "nil"); + } + break; + case 'n': + fprintf(f, "new id %s@", + (message->message->types[i]) + ? message->message->types[i]->name + : "[unknown]"); + if (args[i].n != 0) + fprintf(f, "%u", args[i].n); + else + fprintf(f, "nil"); + break; + case 'a': + fprintf(f, "array"); + break; + case 'h': + fprintf(f, "fd %d", args[i].h); + break; + } + } + + fprintf(f, ")\n"); + + if (fclose(f) == 0) { + fprintf(stderr, "%s", buffer); + free(buffer); + } +} + static void callback_done(void *data, struct wl_callback *cb, uint32_t time) { @@ -218,12 +343,15 @@ logger_setup(struct compositor *compositor, struct client *client) client->display = wl_display_connect(socket); client->sequence_observer = wl_display_create_client_observer( client->display, client_sequence_observer_func, client); + client->stderr_logger = wl_display_create_client_observer( + client->display, client_log_to_stderr_demo, client); } static void logger_teardown(struct compositor *compositor, struct client *client) { wl_client_observer_destroy(client->sequence_observer); + wl_client_observer_destroy(client->stderr_logger); wl_display_disconnect(client->display); wl_client_destroy(compositor->client);