From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Lloyd Pique <lpique@google.com>
Date: Thu, 10 Mar 2022 14:56:02 -0800
Subject: [PATCH 1/6] connection: Simplify wl_closure_print

Client message observers 1/6

Cleans up wl_closure_print(), and adds a client-private closure_log()
intermediate function.

This patch simplifies wl_closure_print() slightly by moving some client-only
details to a new closure_log() intermediate function. This new function will
also handle delivering messages to the new listener callback in a subsequent
patch.

closure_log() internally handles the check for logging being enabled,
simplifying its callers, and returns early if logging is not enabled. This check
becomes a bit more complex when there can be listeners.

closure_log() also handles the work same transformation performed by
id_from_object(), by making a copy of the args, and applying the transform to
the copy before passing the arguments to wl_closure_print(). Doing it this way
means the same arguments can also be passed to the eventual listeners.

The boolean "discarded" argument for wl_closure_print() has been replaced by a
"discarded_reason" string argument, allowing an arbitrary reason string to be
passed in. For now only "discarded[]" is printed as an empty reason string is
passed if the message was discarded, but that will also change.

Signed-off-by: Lloyd Pique <lpique@google.com>

diff --git a/COPYING b/COPYING
index eb25a4e..843b844 100644
--- a/COPYING
+++ b/COPYING
@@ -2,6 +2,7 @@ Copyright © 2008-2012 Kristian Høgsberg
 Copyright © 2010-2012 Intel Corporation
 Copyright © 2011 Benjamin Franzke
 Copyright © 2012 Collabora, Ltd.
+Copyright 2022 Google LLC
 
 Permission is hereby granted, free of charge, to any person obtaining a
 copy of this software and associated documentation files (the "Software"),
diff --git a/src/connection.c b/src/connection.c
index ceaeac1..110b614 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -1264,7 +1264,7 @@ wl_closure_queue(struct wl_closure *closure, struct wl_connection *connection)
 
 void
 wl_closure_print(struct wl_closure *closure, struct wl_object *target,
-		 int send, int discarded, uint32_t (*n_parse)(union wl_argument *arg))
+		 bool send, const char *discarded_reason)
 {
 	int i;
 	struct argument_details arg;
@@ -1283,9 +1283,11 @@ wl_closure_print(struct wl_closure *closure, struct wl_object *target,
 	clock_gettime(CLOCK_REALTIME, &tp);
 	time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000);
 
-	fprintf(f, "[%7u.%03u] %s%s%s@%u.%s(",
+	fprintf(f, "[%7u.%03u] %s%s%s%s%s@%u.%s(",
 		time / 1000, time % 1000,
-		discarded ? "discarded " : "",
+		(discarded_reason != NULL) ? "discarded[" : "",
+		(discarded_reason != NULL) ? discarded_reason : "",
+		(discarded_reason != NULL) ? "] " : "",
 		send ? " -> " : "",
 		target->interface->name, target->id,
 		closure->message->name);
@@ -1330,10 +1332,7 @@ wl_closure_print(struct wl_closure *closure, struct wl_object *target,
 				fprintf(f, "nil");
 			break;
 		case 'n':
-			if (n_parse)
-				nval = n_parse(&closure->args[i]);
-			else
-				nval = closure->args[i].n;
+			nval = closure->args[i].n;
 
 			fprintf(f, "new id %s@",
 				(closure->message->types[i]) ?
diff --git a/src/wayland-client.c b/src/wayland-client.c
index 105f9be..ae47307 100644
--- a/src/wayland-client.c
+++ b/src/wayland-client.c
@@ -115,6 +115,73 @@ struct wl_display {
 
 static int debug_client = 0;
 
+/**
+ * This helper function adjusts the closure arguments before they are logged.
+ * On the client, after the call to create_proxies(), NEW_ID arguments will
+ * point to a wl_proxy accessible via arg.o instead of being an int32
+ * accessible by arg.n, which is what wl_closure_print() attempts to print.
+ * This helper transforms the argument back into an id, so wl_closure_print()
+ * doesn't need to handle that as a special case.
+ *
+ * \param closure  closure to adjust
+ * \param send     if this is closure is for a request
+ *
+ */
+static void
+adjust_closure_args_for_logging(struct wl_closure *closure, bool send)
+{
+	int i;
+	struct argument_details arg;
+	const struct wl_proxy *proxy;
+	const char *signature = closure->message->signature;
+
+	// No adjustment needed for a send.
+	if (send)
+		return;
+
+	for (i = 0; i < closure->count; i++) {
+		signature = get_next_argument(signature, &arg);
+
+		switch (arg.type) {
+		case 'n':
+			proxy = (struct wl_proxy *)closure->args[i].o;
+			closure->args[i].n = proxy ? proxy->object.id : 0;
+			break;
+		}
+	}
+}
+
+/**
+ * This function helps log closures from the client, assuming logging is
+ * enabled.
+ *
+ * \param closure    closure for the message
+ * \param proxy      proxy for the message
+ * \param send       true if this is closure is for a request
+ * \param discarded  true if this is message is being discarded
+ *
+ */
+static void
+closure_log(struct wl_closure *closure, struct wl_proxy *proxy, bool send,
+	    bool discarded)
+{
+	struct wl_closure adjusted_closure = { 0 };
+
+	if (!debug_client)
+		return;
+
+	// Note: The real closure has extra data (referenced by its args
+	// immediately following the structure in memory, but we don't
+	// need to duplicate that.
+	memcpy(&adjusted_closure, closure, sizeof(struct wl_closure));
+
+	// Adjust the closure arguments.
+	adjust_closure_args_for_logging(&adjusted_closure, send);
+
+	wl_closure_print(&adjusted_closure, &proxy->object, send,
+			 discarded ? "" : NULL);
+}
+
 /**
  * This helper function wakes up all threads that are
  * waiting for display->reader_cond (i. e. when reading is done,
@@ -885,8 +952,7 @@ wl_proxy_marshal_array_flags(struct wl_proxy *proxy, uint32_t opcode,
 		goto err_unlock;
 	}
 
-	if (debug_client)
-		wl_closure_print(closure, &proxy->object, true, false, NULL);
+	closure_log(closure, proxy, true, false);
 
 	if (wl_closure_send(closure, proxy->display->connection)) {
 		wl_log("Error sending request: %s\n", strerror(errno));
@@ -1579,19 +1645,6 @@ queue_event(struct wl_display *display, int len)
 	return size;
 }
 
-static uint32_t
-id_from_object(union wl_argument *arg)
-{
-	struct wl_proxy *proxy;
-
-	if (arg->o) {
-		proxy = (struct wl_proxy *)arg->o;
-		return proxy->object.id;
-	}
-
-	return 0;
-}
-
 static void
 dispatch_event(struct wl_display *display, struct wl_event_queue *queue)
 {
@@ -1610,8 +1663,7 @@ dispatch_event(struct wl_display *display, struct wl_event_queue *queue)
 	proxy = closure->proxy;
 	proxy_destroyed = !!(proxy->flags & WL_PROXY_FLAG_DESTROYED);
 	if (proxy_destroyed) {
-		if (debug_client)
-			wl_closure_print(closure, &proxy->object, false, true, id_from_object);
+		closure_log(closure, proxy, false, true);
 		destroy_queued_closure(closure);
 		return;
 	}
@@ -1619,15 +1671,11 @@ 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, false, id_from_object);
-
+		closure_log(closure, proxy, false, false);
 		wl_closure_dispatch(closure, proxy->dispatcher,
 				    &proxy->object, opcode);
 	} else if (proxy->object.implementation) {
-		if (debug_client)
-			wl_closure_print(closure, &proxy->object, false, false, id_from_object);
-
+		closure_log(closure, proxy, false, false);
 		wl_closure_invoke(closure, WL_CLOSURE_INVOKE_CLIENT,
 				  &proxy->object, opcode, proxy->user_data);
 	}
diff --git a/src/wayland-private.h b/src/wayland-private.h
index 9274f1b..66fc78f 100644
--- a/src/wayland-private.h
+++ b/src/wayland-private.h
@@ -211,9 +211,8 @@ int
 wl_closure_queue(struct wl_closure *closure, struct wl_connection *connection);
 
 void
-wl_closure_print(struct wl_closure *closure,
-		 struct wl_object *target, int send, int discarded,
-		 uint32_t (*n_parse)(union wl_argument *arg));
+wl_closure_print(struct wl_closure *closure, struct wl_object *target,
+		 bool send, const char *discarded_reason);
 
 void
 wl_closure_destroy(struct wl_closure *closure);
diff --git a/src/wayland-server.c b/src/wayland-server.c
index d51acc6..be98f7d 100644
--- a/src/wayland-server.c
+++ b/src/wayland-server.c
@@ -157,7 +157,7 @@ log_closure(struct wl_resource *resource,
 	struct wl_protocol_logger_message message;
 
 	if (debug_server)
-		wl_closure_print(closure, object, send, false, NULL);
+		wl_closure_print(closure, object, send, NULL);
 
 	if (!wl_list_empty(&display->protocol_loggers)) {
 		message.resource = resource;
