• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
2From: Lloyd Pique <lpique@google.com>
3Date: Fri, 29 Jan 2021 14:26:03 -0800
4Subject: [PATCH 1/3] client: Support client protocol loggers
5
6This is very much based on commit 450f06e2 which added server protocol
7loggers.
8
9Adds a new pair of public API functions:
10
11* wl_display_add_protocol_logger_client allows the client to register a
12  function to be called to log each message that is sent or received.
13
14* wl_protocol_logger_client_destroy allows the client to unregister the
15  function.
16
17As with the server protocol loggers, this is akin to setting
18WAYLAND_DEBUG=1, but allows the client code to choose how the messages
19are logged, and it can also enable and disable logging at run time.
20
21The logging logic for the client was also changed to log all events, not
22just the ones that have listeners or dispatchers.
23
24Signed-off-by: Lloyd Pique <lpique@google.com>
25
26diff --git a/src/wayland-client-core.h b/src/wayland-client-core.h
27index 0cd96e0..547ae04 100644
28--- a/src/wayland-client-core.h
29+++ b/src/wayland-client-core.h
30@@ -267,6 +267,32 @@ wl_display_read_events(struct wl_display *display);
31 void
32 wl_log_set_handler_client(wl_log_func_t handler);
33
34+enum wl_protocol_logger_client_type {
35+	WL_PROTOCOL_LOGGER_CLIENT_REQUEST,
36+	WL_PROTOCOL_LOGGER_CLIENT_EVENT,
37+};
38+
39+struct wl_protocol_logger_client_message {
40+	struct wl_proxy *proxy;
41+	int message_opcode;
42+	const struct wl_message *message;
43+	int arguments_count;
44+	const union wl_argument *arguments;
45+};
46+
47+typedef void (*wl_protocol_logger_client_func_t)(
48+		void *user_data,
49+		enum wl_protocol_logger_client_type direction,
50+		const struct wl_protocol_logger_client_message *message);
51+
52+struct wl_protocol_logger_client *
53+wl_display_add_protocol_logger_client(struct wl_display *display,
54+				      wl_protocol_logger_client_func_t,
55+				      void *user_data);
56+
57+void
58+wl_protocol_logger_client_destroy(struct wl_protocol_logger_client *logger);
59+
60 #ifdef  __cplusplus
61 }
62 #endif
63diff --git a/src/wayland-client.c b/src/wayland-client.c
64index 21d4606..7f5a651 100644
65--- a/src/wayland-client.c
66+++ b/src/wayland-client.c
67@@ -107,12 +107,47 @@ struct wl_display {
68 	int reader_count;
69 	uint32_t read_serial;
70 	pthread_cond_t reader_cond;
71+
72+	struct wl_list protocol_loggers;
73 };
74
75 /** \endcond */
76
77+struct wl_protocol_logger_client {
78+	struct wl_list link;
79+	wl_protocol_logger_client_func_t func;
80+	void *user_data;
81+};
82+
83 static int debug_client = 0;
84
85+static void
86+log_closure(struct wl_closure *closure, struct wl_proxy* proxy, int send)
87+{
88+	struct wl_display *display = proxy->display;
89+	struct wl_protocol_logger_client *protocol_logger;
90+	struct wl_protocol_logger_client_message message;
91+
92+	if (debug_client)
93+		wl_closure_print(closure, &proxy->object, send);
94+
95+	if (!wl_list_empty(&display->protocol_loggers)) {
96+		message.proxy = proxy;
97+		message.message_opcode = closure->opcode;
98+		message.message = closure->message;
99+		message.arguments_count = closure->count;
100+		message.arguments = closure->args;
101+		wl_list_for_each(protocol_logger, &display->protocol_loggers,
102+				 link) {
103+			protocol_logger->func(
104+				protocol_logger->user_data,
105+				send ? WL_PROTOCOL_LOGGER_CLIENT_REQUEST :
106+				       WL_PROTOCOL_LOGGER_CLIENT_EVENT,
107+				&message);
108+		}
109+	}
110+}
111+
112 /**
113  * This helper function wakes up all threads that are
114  * waiting for display->reader_cond (i. e. when reading is done,
115@@ -751,8 +786,7 @@ wl_proxy_marshal_array_constructor_versioned(struct wl_proxy *proxy,
116 		goto err_unlock;
117 	}
118
119-	if (debug_client)
120-		wl_closure_print(closure, &proxy->object, true);
121+	log_closure(closure, proxy, true);
122
123 	if (wl_closure_send(closure, proxy->display->connection)) {
124 		wl_log("Error sending request: %s\n", strerror(errno));
125@@ -1056,6 +1090,7 @@ wl_display_connect_to_fd(int fd)
126 	pthread_mutex_init(&display->mutex, NULL);
127 	pthread_cond_init(&display->reader_cond, NULL);
128 	display->reader_count = 0;
129+	wl_list_init(&display->protocol_loggers);
130
131 	wl_map_insert_new(&display->objects, 0, NULL);
132
133@@ -1177,6 +1212,7 @@ wl_display_disconnect(struct wl_display *display)
134 	wl_map_release(&display->objects);
135 	wl_event_queue_release(&display->default_queue);
136 	wl_event_queue_release(&display->display_queue);
137+	wl_list_remove(&display->protocol_loggers);
138 	pthread_mutex_destroy(&display->mutex);
139 	pthread_cond_destroy(&display->reader_cond);
140 	close(display->fd);
141@@ -1439,16 +1475,12 @@ dispatch_event(struct wl_display *display, struct wl_event_queue *queue)
142
143 	pthread_mutex_unlock(&display->mutex);
144
145-	if (proxy->dispatcher) {
146-		if (debug_client)
147-			wl_closure_print(closure, &proxy->object, false);
148+	log_closure(closure, proxy, false);
149
150+	if (proxy->dispatcher) {
151 		wl_closure_dispatch(closure, proxy->dispatcher,
152 				    &proxy->object, opcode);
153 	} else if (proxy->object.implementation) {
154-		if (debug_client)
155-			wl_closure_print(closure, &proxy->object, false);
156-
157 		wl_closure_invoke(closure, WL_CLOSURE_INVOKE_CLIENT,
158 				  &proxy->object, opcode, proxy->user_data);
159 	}
160@@ -2280,3 +2312,60 @@ wl_log_set_handler_client(wl_log_func_t handler)
161 {
162 	wl_log_handler = handler;
163 }
164+
165+/** Adds a new protocol client logger.
166+ *
167+ * When a new protocol message arrives or is sent from the client
168+ * all the protocol logger functions will be called, carrying the
169+ * \a user_data pointer, the type of the message (request or
170+ * event) and the actual message.
171+ * The lifetime of the messages passed to the logger function ends
172+ * when they return so the messages cannot be stored and accessed
173+ * later.
174+ *
175+ * \a errno is set on error.
176+ *
177+ * \param display The display object
178+ * \param func The function to call to log a new protocol message
179+ * \param user_data The user data pointer to pass to \a func
180+ *
181+ * \return The protocol logger object on success, NULL on failure.
182+ *
183+ * \sa wl_protocol_logger_client_destroy
184+ *
185+ * \memberof wl_display
186+ */
187+WL_EXPORT struct wl_protocol_logger_client *
188+wl_display_add_protocol_logger_client(struct wl_display *display,
189+				      wl_protocol_logger_client_func_t func,
190+				      void *user_data)
191+{
192+	struct wl_protocol_logger_client *logger;
193+
194+	logger = malloc(sizeof *logger);
195+	if (!logger)
196+		return NULL;
197+
198+	logger->func = func;
199+	logger->user_data = user_data;
200+	wl_list_insert(&display->protocol_loggers, &logger->link);
201+
202+	return logger;
203+}
204+
205+/** Destroys a protocol client logger.
206+ *
207+ * This function destroys a protocol client logger and removes it from the
208+ * display it was added to with \a wl_display_add_protocol_logger_client.
209+ * The \a logger object becomes invalid after calling this function.
210+ *
211+ * \sa wl_display_add_protocol_logger_client
212+ *
213+ * \memberof wl_protocol_logger_client
214+ */
215+WL_EXPORT void
216+wl_protocol_logger_client_destroy(struct wl_protocol_logger_client *logger)
217+{
218+	wl_list_remove(&logger->link);
219+	free(logger);
220+}
221diff --git a/tests/protocol-logger-test.c b/tests/protocol-logger-test.c
222index 80c74aa..e409368 100644
223--- a/tests/protocol-logger-test.c
224+++ b/tests/protocol-logger-test.c
225@@ -52,6 +52,12 @@ struct compositor {
226 	struct wl_client *client;
227 };
228
229+struct client {
230+	struct wl_display *display;
231+	struct wl_callback *cb;
232+        int message;
233+};
234+
235 struct message {
236 	enum wl_protocol_logger_type type;
237 	const char *class;
238@@ -82,6 +88,36 @@ struct message {
239 	},
240 };
241
242+struct client_message {
243+	enum wl_protocol_logger_client_type type;
244+	const char *class;
245+	int opcode;
246+	const char *message_name;
247+	int args_count;
248+} client_messages[] = {
249+	{
250+		.type = WL_PROTOCOL_LOGGER_CLIENT_REQUEST,
251+		.class = "wl_display",
252+		.opcode = 0,
253+		.message_name = "sync",
254+		.args_count = 1,
255+	},
256+	{
257+		.type = WL_PROTOCOL_LOGGER_CLIENT_EVENT,
258+		.class = "wl_display",
259+		.opcode = 1,
260+		.message_name = "delete_id",
261+		.args_count = 1,
262+	},
263+	{
264+		.type = WL_PROTOCOL_LOGGER_CLIENT_EVENT,
265+		.class = "wl_callback",
266+		.opcode = 0,
267+		.message_name = "done",
268+		.args_count = 1,
269+	},
270+};
271+
272 static void
273 logger_func(void *user_data, enum wl_protocol_logger_type type,
274 	    const struct wl_protocol_logger_message *message)
275@@ -98,6 +134,20 @@ logger_func(void *user_data, enum wl_protocol_logger_type type,
276 	c->client = wl_resource_get_client(message->resource);
277 }
278
279+static void
280+client_logger_func(void *user_data, enum wl_protocol_logger_client_type type,
281+		const struct wl_protocol_logger_client_message *message)
282+{
283+	struct client *c = user_data;
284+	struct client_message *msg = &client_messages[c->message++];
285+
286+	assert(msg->type == type);
287+	assert(strcmp(msg->class, wl_proxy_get_class(message->proxy)) == 0);
288+	assert(msg->opcode == message->message_opcode);
289+	assert(strcmp(msg->message_name, message->message->name) == 0);
290+	assert(msg->args_count == message->arguments_count);
291+}
292+
293 static void
294 callback_done(void *data, struct wl_callback *cb, uint32_t time)
295 {
296@@ -114,11 +164,9 @@ TEST(logger)
297
298 	const char *socket;
299 	struct compositor compositor = { 0 };
300-	struct {
301-		struct wl_display *display;
302-		struct wl_callback *cb;
303-	} client;
304+	struct client client = { 0 };
305 	struct wl_protocol_logger *logger;
306+	struct wl_protocol_logger_client *logger_client;
307
308 	require_xdg_runtime_dir();
309
310@@ -130,6 +178,8 @@ TEST(logger)
311 						logger_func, &compositor);
312
313 	client.display = wl_display_connect(socket);
314+	logger_client = wl_display_add_protocol_logger_client(
315+		client.display, client_logger_func, &client);
316 	client.cb = wl_display_sync(client.display);
317 	wl_callback_add_listener(client.cb, &callback_listener, NULL);
318 	wl_display_flush(client.display);
319@@ -142,6 +192,7 @@ TEST(logger)
320 	wl_display_dispatch(client.display);
321 	wl_display_disconnect(client.display);
322
323+	wl_protocol_logger_client_destroy(logger_client);
324 	wl_client_destroy(compositor.client);
325 	wl_protocol_logger_destroy(logger);
326 	wl_display_destroy(compositor.display);
327