| From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
| From: Lloyd Pique <lpique@google.com> |
| Date: Fri, 29 Jan 2021 14:26:03 -0800 |
| Subject: [PATCH 1/3] client: Support client protocol loggers |
| |
| This is very much based on commit 450f06e2 which added server protocol |
| loggers. |
| |
| Adds a new pair of public API functions: |
| |
| * wl_display_add_protocol_logger_client allows the client to register a |
| function to be called to log each message that is sent or received. |
| |
| * wl_protocol_logger_client_destroy allows the client to unregister the |
| function. |
| |
| As with the server protocol loggers, this is akin to setting |
| WAYLAND_DEBUG=1, but allows the client code to choose how the messages |
| are logged, and it can also enable and disable logging at run time. |
| |
| The logging logic for the client was also changed to log all events, not |
| just the ones that have listeners or dispatchers. |
| |
| Signed-off-by: Lloyd Pique <lpique@google.com> |
| |
| diff --git a/src/wayland-client-core.h b/src/wayland-client-core.h |
| index 0cd96e0..547ae04 100644 |
| --- a/src/wayland-client-core.h |
| +++ b/src/wayland-client-core.h |
| @@ -267,6 +267,32 @@ wl_display_read_events(struct wl_display *display); |
| void |
| wl_log_set_handler_client(wl_log_func_t handler); |
| |
| +enum wl_protocol_logger_client_type { |
| + WL_PROTOCOL_LOGGER_CLIENT_REQUEST, |
| + WL_PROTOCOL_LOGGER_CLIENT_EVENT, |
| +}; |
| + |
| +struct wl_protocol_logger_client_message { |
| + struct wl_proxy *proxy; |
| + int message_opcode; |
| + const struct wl_message *message; |
| + int arguments_count; |
| + const union wl_argument *arguments; |
| +}; |
| + |
| +typedef void (*wl_protocol_logger_client_func_t)( |
| + void *user_data, |
| + enum wl_protocol_logger_client_type direction, |
| + const struct wl_protocol_logger_client_message *message); |
| + |
| +struct wl_protocol_logger_client * |
| +wl_display_add_protocol_logger_client(struct wl_display *display, |
| + wl_protocol_logger_client_func_t, |
| + void *user_data); |
| + |
| +void |
| +wl_protocol_logger_client_destroy(struct wl_protocol_logger_client *logger); |
| + |
| #ifdef __cplusplus |
| } |
| #endif |
| diff --git a/src/wayland-client.c b/src/wayland-client.c |
| index 21d4606..7f5a651 100644 |
| --- a/src/wayland-client.c |
| +++ b/src/wayland-client.c |
| @@ -107,12 +107,47 @@ struct wl_display { |
| int reader_count; |
| uint32_t read_serial; |
| pthread_cond_t reader_cond; |
| + |
| + struct wl_list protocol_loggers; |
| }; |
| |
| /** \endcond */ |
| |
| +struct wl_protocol_logger_client { |
| + struct wl_list link; |
| + wl_protocol_logger_client_func_t func; |
| + void *user_data; |
| +}; |
| + |
| static int debug_client = 0; |
| |
| +static void |
| +log_closure(struct wl_closure *closure, struct wl_proxy* proxy, int send) |
| +{ |
| + struct wl_display *display = proxy->display; |
| + struct wl_protocol_logger_client *protocol_logger; |
| + struct wl_protocol_logger_client_message message; |
| + |
| + if (debug_client) |
| + wl_closure_print(closure, &proxy->object, send); |
| + |
| + if (!wl_list_empty(&display->protocol_loggers)) { |
| + message.proxy = proxy; |
| + message.message_opcode = closure->opcode; |
| + message.message = closure->message; |
| + message.arguments_count = closure->count; |
| + message.arguments = closure->args; |
| + wl_list_for_each(protocol_logger, &display->protocol_loggers, |
| + link) { |
| + protocol_logger->func( |
| + protocol_logger->user_data, |
| + send ? WL_PROTOCOL_LOGGER_CLIENT_REQUEST : |
| + WL_PROTOCOL_LOGGER_CLIENT_EVENT, |
| + &message); |
| + } |
| + } |
| +} |
| + |
| /** |
| * This helper function wakes up all threads that are |
| * waiting for display->reader_cond (i. e. when reading is done, |
| @@ -751,8 +786,7 @@ wl_proxy_marshal_array_constructor_versioned(struct wl_proxy *proxy, |
| goto err_unlock; |
| } |
| |
| - if (debug_client) |
| - wl_closure_print(closure, &proxy->object, true); |
| + log_closure(closure, proxy, true); |
| |
| if (wl_closure_send(closure, proxy->display->connection)) { |
| wl_log("Error sending request: %s\n", strerror(errno)); |
| @@ -1056,6 +1090,7 @@ wl_display_connect_to_fd(int fd) |
| pthread_mutex_init(&display->mutex, NULL); |
| pthread_cond_init(&display->reader_cond, NULL); |
| display->reader_count = 0; |
| + wl_list_init(&display->protocol_loggers); |
| |
| wl_map_insert_new(&display->objects, 0, NULL); |
| |
| @@ -1177,6 +1212,7 @@ wl_display_disconnect(struct wl_display *display) |
| wl_map_release(&display->objects); |
| wl_event_queue_release(&display->default_queue); |
| wl_event_queue_release(&display->display_queue); |
| + wl_list_remove(&display->protocol_loggers); |
| pthread_mutex_destroy(&display->mutex); |
| pthread_cond_destroy(&display->reader_cond); |
| close(display->fd); |
| @@ -1439,16 +1475,12 @@ dispatch_event(struct wl_display *display, struct wl_event_queue *queue) |
| |
| pthread_mutex_unlock(&display->mutex); |
| |
| - if (proxy->dispatcher) { |
| - if (debug_client) |
| - wl_closure_print(closure, &proxy->object, false); |
| + log_closure(closure, proxy, false); |
| |
| + if (proxy->dispatcher) { |
| wl_closure_dispatch(closure, proxy->dispatcher, |
| &proxy->object, opcode); |
| } else if (proxy->object.implementation) { |
| - if (debug_client) |
| - wl_closure_print(closure, &proxy->object, false); |
| - |
| wl_closure_invoke(closure, WL_CLOSURE_INVOKE_CLIENT, |
| &proxy->object, opcode, proxy->user_data); |
| } |
| @@ -2280,3 +2312,60 @@ wl_log_set_handler_client(wl_log_func_t handler) |
| { |
| wl_log_handler = handler; |
| } |
| + |
| +/** Adds a new protocol client logger. |
| + * |
| + * When a new protocol message arrives or is sent from the client |
| + * all the protocol logger functions will be called, carrying the |
| + * \a user_data pointer, the type of the message (request or |
| + * event) and the actual message. |
| + * The lifetime of the messages passed to the logger function ends |
| + * when they return so the messages cannot be stored and accessed |
| + * later. |
| + * |
| + * \a errno is set on error. |
| + * |
| + * \param display The display object |
| + * \param func The function to call to log a new protocol message |
| + * \param user_data The user data pointer to pass to \a func |
| + * |
| + * \return The protocol logger object on success, NULL on failure. |
| + * |
| + * \sa wl_protocol_logger_client_destroy |
| + * |
| + * \memberof wl_display |
| + */ |
| +WL_EXPORT struct wl_protocol_logger_client * |
| +wl_display_add_protocol_logger_client(struct wl_display *display, |
| + wl_protocol_logger_client_func_t func, |
| + void *user_data) |
| +{ |
| + struct wl_protocol_logger_client *logger; |
| + |
| + logger = malloc(sizeof *logger); |
| + if (!logger) |
| + return NULL; |
| + |
| + logger->func = func; |
| + logger->user_data = user_data; |
| + wl_list_insert(&display->protocol_loggers, &logger->link); |
| + |
| + return logger; |
| +} |
| + |
| +/** Destroys a protocol client logger. |
| + * |
| + * This function destroys a protocol client logger and removes it from the |
| + * display it was added to with \a wl_display_add_protocol_logger_client. |
| + * The \a logger object becomes invalid after calling this function. |
| + * |
| + * \sa wl_display_add_protocol_logger_client |
| + * |
| + * \memberof wl_protocol_logger_client |
| + */ |
| +WL_EXPORT void |
| +wl_protocol_logger_client_destroy(struct wl_protocol_logger_client *logger) |
| +{ |
| + wl_list_remove(&logger->link); |
| + free(logger); |
| +} |
| diff --git a/tests/protocol-logger-test.c b/tests/protocol-logger-test.c |
| index 80c74aa..e409368 100644 |
| --- a/tests/protocol-logger-test.c |
| +++ b/tests/protocol-logger-test.c |
| @@ -52,6 +52,12 @@ struct compositor { |
| struct wl_client *client; |
| }; |
| |
| +struct client { |
| + struct wl_display *display; |
| + struct wl_callback *cb; |
| + int message; |
| +}; |
| + |
| struct message { |
| enum wl_protocol_logger_type type; |
| const char *class; |
| @@ -82,6 +88,36 @@ struct message { |
| }, |
| }; |
| |
| +struct client_message { |
| + enum wl_protocol_logger_client_type type; |
| + const char *class; |
| + int opcode; |
| + const char *message_name; |
| + int args_count; |
| +} client_messages[] = { |
| + { |
| + .type = WL_PROTOCOL_LOGGER_CLIENT_REQUEST, |
| + .class = "wl_display", |
| + .opcode = 0, |
| + .message_name = "sync", |
| + .args_count = 1, |
| + }, |
| + { |
| + .type = WL_PROTOCOL_LOGGER_CLIENT_EVENT, |
| + .class = "wl_display", |
| + .opcode = 1, |
| + .message_name = "delete_id", |
| + .args_count = 1, |
| + }, |
| + { |
| + .type = WL_PROTOCOL_LOGGER_CLIENT_EVENT, |
| + .class = "wl_callback", |
| + .opcode = 0, |
| + .message_name = "done", |
| + .args_count = 1, |
| + }, |
| +}; |
| + |
| static void |
| logger_func(void *user_data, enum wl_protocol_logger_type type, |
| const struct wl_protocol_logger_message *message) |
| @@ -98,6 +134,20 @@ logger_func(void *user_data, enum wl_protocol_logger_type type, |
| c->client = wl_resource_get_client(message->resource); |
| } |
| |
| +static void |
| +client_logger_func(void *user_data, enum wl_protocol_logger_client_type type, |
| + const struct wl_protocol_logger_client_message *message) |
| +{ |
| + struct client *c = user_data; |
| + struct client_message *msg = &client_messages[c->message++]; |
| + |
| + assert(msg->type == type); |
| + assert(strcmp(msg->class, wl_proxy_get_class(message->proxy)) == 0); |
| + assert(msg->opcode == message->message_opcode); |
| + assert(strcmp(msg->message_name, message->message->name) == 0); |
| + assert(msg->args_count == message->arguments_count); |
| +} |
| + |
| static void |
| callback_done(void *data, struct wl_callback *cb, uint32_t time) |
| { |
| @@ -114,11 +164,9 @@ TEST(logger) |
| |
| const char *socket; |
| struct compositor compositor = { 0 }; |
| - struct { |
| - struct wl_display *display; |
| - struct wl_callback *cb; |
| - } client; |
| + struct client client = { 0 }; |
| struct wl_protocol_logger *logger; |
| + struct wl_protocol_logger_client *logger_client; |
| |
| require_xdg_runtime_dir(); |
| |
| @@ -130,6 +178,8 @@ TEST(logger) |
| logger_func, &compositor); |
| |
| client.display = wl_display_connect(socket); |
| + logger_client = wl_display_add_protocol_logger_client( |
| + client.display, client_logger_func, &client); |
| client.cb = wl_display_sync(client.display); |
| wl_callback_add_listener(client.cb, &callback_listener, NULL); |
| wl_display_flush(client.display); |
| @@ -142,6 +192,7 @@ TEST(logger) |
| wl_display_dispatch(client.display); |
| wl_display_disconnect(client.display); |
| |
| + wl_protocol_logger_client_destroy(logger_client); |
| wl_client_destroy(compositor.client); |
| wl_protocol_logger_destroy(logger); |
| wl_display_destroy(compositor.display); |