[PATCH wayland] Add API to install protocol loggers on the server wl_display

Giulio Camuffo giuliocamuffo at gmail.com
Wed Mar 23 14:31:16 UTC 2016


The new wl_display_add_protocol_logger allows to set a function as
a logger, which will get called when a new request is received or an
event is sent.
This is akin to setting WAYLAND_DEBUG=1, but more powerful because it
can be enabled at run time and allows to show the log e.g. in a UI view.

Signed-off-by: Giulio Camuffo <giulio.camuffo at kdab.com>
---
 src/connection.c          | 117 ++++++++++++++++++++++++++++++++++------------
 src/wayland-private.h     |   4 ++
 src/wayland-server-core.h |  13 ++++++
 src/wayland-server.c      |  94 ++++++++++++++++++++++++++++++++++---
 4 files changed, 192 insertions(+), 36 deletions(-)

diff --git a/src/connection.c b/src/connection.c
index c0e322f..f6447c0 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -1181,71 +1181,128 @@ wl_closure_queue(struct wl_closure *closure, struct wl_connection *connection)
 	return result;
 }
 
-void
-wl_closure_print(struct wl_closure *closure, struct wl_object *target, int send)
+static inline int
+min(int a, int b)
+{
+	return a < b ? a : b;
+}
+
+/** Formats the closure and returns a static const char * with the value,
+ *  in the 'interface at id.message_name(args)' format.
+ *  DO NOT free or mess with the returned pointer.
+ */
+const char *
+wl_closure_format(struct wl_closure *closure, struct wl_object *target)
 {
+	static char *buffer = NULL;
+	static size_t buf_size;
 	int i;
 	struct argument_details arg;
 	const char *signature = closure->message->signature;
-	struct timespec tp;
-	unsigned int time;
+	size_t size = 0;
 
-	clock_gettime(CLOCK_REALTIME, &tp);
-	time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000);
+	if (!buffer) {
+		buf_size = 128;
+		buffer = malloc(buf_size);
+	}
 
-	fprintf(stderr, "[%10.3f] %s%s@%u.%s(",
-		time / 1000.0,
-		send ? " -> " : "",
-		target->interface->name, target->id,
-		closure->message->name);
+	size = snprintf(buffer, buf_size, "%s@%u.%s(",
+			target->interface->name, target->id,
+			closure->message->name);
 
 	for (i = 0; i < closure->count; i++) {
 		signature = get_next_argument(signature, &arg);
 		if (i > 0)
-			fprintf(stderr, ", ");
+			size += snprintf(buffer + size,
+					 buf_size - min(size, buf_size),
+					 ", ");
 
 		switch (arg.type) {
 		case 'u':
-			fprintf(stderr, "%u", closure->args[i].u);
+			size += snprintf(buffer + size,
+					 buf_size - min(size, buf_size),
+					 "%u", closure->args[i].u);
 			break;
 		case 'i':
-			fprintf(stderr, "%d", closure->args[i].i);
+			size += snprintf(buffer + size,
+					 buf_size - min(size, buf_size),
+					 "%d", closure->args[i].i);
 			break;
 		case 'f':
-			fprintf(stderr, "%f",
-				wl_fixed_to_double(closure->args[i].f));
+			size += snprintf(buffer + size,
+					 buf_size - min(size, buf_size), "%f",
+					 wl_fixed_to_double(closure->args[i].f));
 			break;
 		case 's':
-			fprintf(stderr, "\"%s\"", closure->args[i].s);
+			size += snprintf(buffer + size,
+					 buf_size - min(size, buf_size),
+					 "\"%s\"", closure->args[i].s);
 			break;
 		case 'o':
 			if (closure->args[i].o)
-				fprintf(stderr, "%s@%u",
-					closure->args[i].o->interface->name,
-					closure->args[i].o->id);
+				size += snprintf(buffer + size,
+						 buf_size - min(size, buf_size), "%s@%u",
+						 closure->args[i].o->interface->name,
+						 closure->args[i].o->id);
 			else
-				fprintf(stderr, "nil");
+				size += snprintf(buffer + size,
+						 buf_size - min(size, buf_size),
+						 "nil");
 			break;
 		case 'n':
-			fprintf(stderr, "new id %s@",
-				(closure->message->types[i]) ?
-				 closure->message->types[i]->name :
-				  "[unknown]");
+			size += snprintf(buffer + size,
+					 buf_size - min(size, buf_size),
+					 "new id %s@",
+					 (closure->message->types[i]) ?
+					 closure->message->types[i]->name :
+					 "[unknown]");
 			if (closure->args[i].n != 0)
-				fprintf(stderr, "%u", closure->args[i].n);
+				size += snprintf(buffer + size,
+						 buf_size - min(size, buf_size),
+						 "%u", closure->args[i].n);
 			else
-				fprintf(stderr, "nil");
+				size += snprintf(buffer + size,
+						 buf_size - min(size, buf_size),
+						 "nil");
 			break;
 		case 'a':
-			fprintf(stderr, "array");
+			size += snprintf(buffer + size,
+					 buf_size - min(size, buf_size),
+					 "array");
 			break;
 		case 'h':
-			fprintf(stderr, "fd %d", closure->args[i].h);
+			size += snprintf(buffer + size,
+					 buf_size - min(size, buf_size),
+					 "fd %d", closure->args[i].h);
 			break;
 		}
 	}
 
-	fprintf(stderr, ")\n");
+	size += snprintf(buffer + size,
+			 buf_size - min(size, buf_size), ")");
+
+	if (size >= buf_size) {
+		buf_size = size + 1;
+		buffer = realloc(buffer, buf_size);
+		return wl_closure_format(closure, target);
+	}
+
+	return buffer;
+}
+
+void
+wl_closure_print(struct wl_closure *closure, struct wl_object *target, int send)
+{
+	struct timespec tp;
+	unsigned int time;
+
+	clock_gettime(CLOCK_REALTIME, &tp);
+	time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000);
+
+	fprintf(stderr, "[%10.3f] %s%s\n",
+		time / 1000.0,
+		send ? "-> " : "",
+		wl_closure_format(closure, target));
 }
 
 void
diff --git a/src/wayland-private.h b/src/wayland-private.h
index 994bc45..6689195 100644
--- a/src/wayland-private.h
+++ b/src/wayland-private.h
@@ -210,6 +210,10 @@ void
 wl_closure_print(struct wl_closure *closure,
 		 struct wl_object *target, int send);
 
+const char *
+wl_closure_format(struct wl_closure *closure,
+		  struct wl_object *target);
+
 void
 wl_closure_destroy(struct wl_closure *closure);
 
diff --git a/src/wayland-server-core.h b/src/wayland-server-core.h
index 9980c29..ea9201d 100644
--- a/src/wayland-server-core.h
+++ b/src/wayland-server-core.h
@@ -514,6 +514,19 @@ wl_shm_buffer_create(struct wl_client *client,
 
 void wl_log_set_handler_server(wl_log_func_t handler);
 
+enum wl_protocol_logger_direction {
+    WL_PROTOCOL_LOGGER_INCOMING,
+    WL_PROTOCOL_LOGGER_OUTGOING,
+};
+
+typedef void (*wl_protocol_logger_func_t)(void *, struct wl_resource *,
+                                          enum wl_protocol_logger_direction, const char *);
+void wl_add_protocol_logger(struct wl_display *display,
+                            wl_protocol_logger_func_t, void *user_data);
+
+void wl_remove_protocol_logger(struct wl_display *display,
+                            wl_protocol_logger_func_t, void *user_data);
+
 #ifdef  __cplusplus
 }
 #endif
diff --git a/src/wayland-server.c b/src/wayland-server.c
index e47ccec..6118f60 100644
--- a/src/wayland-server.c
+++ b/src/wayland-server.c
@@ -95,6 +95,7 @@ struct wl_display {
 	struct wl_list global_list;
 	struct wl_list socket_list;
 	struct wl_list client_list;
+	struct wl_list protocol_loggers;
 
 	struct wl_signal destroy_signal;
 	struct wl_signal create_client_signal;
@@ -121,10 +122,39 @@ struct wl_resource {
 	void *data;
 	int version;
 	wl_dispatcher_func_t dispatcher;
+	struct wl_resource *parent;
+};
+
+struct wl_protocol_logger {
+	struct wl_list link;
+	wl_protocol_logger_func_t func;
+	void *user_data;
 };
 
 static int debug_server = 0;
 
+static void
+closure_print(struct wl_resource *resource, struct wl_closure *closure, int send)
+{
+	struct wl_object *object = &resource->object;
+	struct wl_display *display = resource->client->display;
+	struct wl_protocol_logger *protocol_logger;
+	const char *message;
+
+	if (debug_server)
+		wl_closure_print(closure, object, send);
+
+	if (!wl_list_empty(&display->protocol_loggers)) {
+		message = wl_closure_format(closure, object);
+		wl_list_for_each(protocol_logger, &display->protocol_loggers, link) {
+			protocol_logger->func(protocol_logger->user_data,
+					      resource,
+					      send ? WL_PROTOCOL_LOGGER_OUTGOING :
+						     WL_PROTOCOL_LOGGER_INCOMING, message);
+		}
+	}
+}
+
 WL_EXPORT void
 wl_resource_post_event_array(struct wl_resource *resource, uint32_t opcode,
 			     union wl_argument *args)
@@ -143,8 +173,7 @@ wl_resource_post_event_array(struct wl_resource *resource, uint32_t opcode,
 	if (wl_closure_send(closure, resource->client->connection))
 		resource->client->error = 1;
 
-	if (debug_server)
-		wl_closure_print(closure, object, true);
+	closure_print(resource, closure, true);
 
 	wl_closure_destroy(closure);
 }
@@ -183,8 +212,7 @@ wl_resource_queue_event_array(struct wl_resource *resource, uint32_t opcode,
 	if (wl_closure_queue(closure, resource->client->connection))
 		resource->client->error = 1;
 
-	if (debug_server)
-		wl_closure_print(closure, object, true);
+	closure_print(resource, closure, true);
 
 	wl_closure_destroy(closure);
 }
@@ -331,8 +359,7 @@ wl_client_connection_data(int fd, uint32_t mask, void *data)
 			break;
 		}
 
-		if (debug_server)
-			wl_closure_print(closure, object, false);
+		closure_print(resource, closure, false);
 
 		if ((resource_flags & WL_MAP_ENTRY_LEGACY) ||
 		    resource->dispatcher == NULL) {
@@ -879,6 +906,7 @@ wl_display_create(void)
 	wl_list_init(&display->socket_list);
 	wl_list_init(&display->client_list);
 	wl_list_init(&display->registry_resource_list);
+	wl_list_init(&display->protocol_loggers);
 
 	wl_signal_init(&display->destroy_signal);
 	wl_signal_init(&display->create_client_signal);
@@ -1463,6 +1491,60 @@ wl_log_set_handler_server(wl_log_func_t handler)
 	wl_log_handler = handler;
 }
 
+/** Adds a new protocol logger.
+ *
+ * When a new protocol message arrives or is sent from the server
+ * all the protocol logger functions will be called, carrying the
+ * \a user_data pointer, the relevant \a wl_resource, the direction
+ * of the message (incoming or outgoing) and the actual message.
+ *
+ * \param func The function to call to log a new protocol message
+ * \param user_data The user data pointer to pass to \a func
+ *
+ * \sa wl_remove_protocol_logger
+ *
+ * \memberof wl_display
+ */
+WL_EXPORT void
+wl_add_protocol_logger(struct wl_display *display,
+		       wl_protocol_logger_func_t func, void *user_data)
+{
+	struct wl_protocol_logger *logger;
+
+	logger = malloc(sizeof *logger);
+	if (!logger)
+		return;
+
+	logger->func = func;
+	logger->user_data = user_data;
+	wl_list_init(&logger->link);
+	wl_list_insert(&display->protocol_loggers, &logger->link);
+}
+
+/** Removes a protocol logger.
+ *
+ * If a protocol logger was previously added with the same \a func and
+ * \a user_data, it will be removed from the display's list so that it
+ * will not be invoked anymore.
+ *
+ * \sa wl_add_protocol_logger
+ *
+ * \memberof wl_display
+ */
+WL_EXPORT void
+wl_remove_protocol_logger(struct wl_display *display,
+		          wl_protocol_logger_func_t func, void *user_data)
+{
+	struct wl_protocol_logger *logger;
+	wl_list_for_each(logger, &display->protocol_loggers, link) {
+		if (logger->func == func && logger->user_data == user_data) {
+			wl_list_remove(&logger->link);
+			free(logger);
+			return;
+		}
+	}
+}
+
 /** Add support for a wl_shm pixel format
  *
  * \param display The display object
-- 
2.7.4



More information about the wayland-devel mailing list