[RFC wayland 2/2] Add a wayland protocol dumper wayland-tracer

Boyan Ding stu_dby at 126.com
Sat Jul 19 19:18:03 PDT 2014


Signed-off-by: Boyan Ding <stu_dby at 126.com>
---
 .gitignore   |   1 +
 Makefile.am  |  10 ++
 configure.ac |   7 ++
 src/tracer.c | 351 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 369 insertions(+)
 create mode 100644 src/tracer.c

diff --git a/.gitignore b/.gitignore
index c146bac..510b7ae 100644
--- a/.gitignore
+++ b/.gitignore
@@ -54,4 +54,5 @@ sanity-test
 signal-test
 socket-test
 wayland-scanner
+wayland-tracer
 protocol/*.[ch]
diff --git a/Makefile.am b/Makefile.am
index c15d8b8..f234599 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -70,6 +70,16 @@ $(BUILT_SOURCES) : wayland-scanner
 pkgconfig_DATA += src/wayland-scanner.pc
 else
 wayland_scanner = wayland-scanner
+bin_PROGRAMS =
+endif
+
+if ENABLE_TRACER
+wayland_tracer = $(top_builddir)/wayland-tracer
+bin_PROGRAMS += wayland-tracer
+wayland_tracer_SOURCES = src/tracer.c
+wayland_tracer_LDADD = libwayland-util.la $(FFI_LIBS)
+else
+wayland_tracer = wayland-tracer
 endif
 
 protocol/%-protocol.c : $(top_srcdir)/protocol/%.xml
diff --git a/configure.ac b/configure.ac
index e16c5b5..b3e81a7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -64,7 +64,14 @@ AC_ARG_ENABLE([documentation],
 	      [],
 	      [enable_documentation=yes])
 
+AC_ARG_ENABLE([tracer],
+              [AC_HELP_STRING([--disable-tracer],
+                              [Disable compilation of wayland-tracer])],
+              [],
+              [enable_tracer=yes])
+
 AM_CONDITIONAL(ENABLE_SCANNER, test "x$enable_scanner" = xyes)
+AM_CONDITIONAL(ENABLE_TRACER, test "x$enable_tracer" = xyes)
 
 AC_ARG_WITH(icondir, [  --with-icondir=<dir>    Look for cursor icons here],
 		     [  ICONDIR=$withval],
diff --git a/src/tracer.c b/src/tracer.c
new file mode 100644
index 0000000..23de75d
--- /dev/null
+++ b/src/tracer.c
@@ -0,0 +1,351 @@
+/*
+ * Copyright © 2014 Boyan Ding
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/epoll.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "wayland-os.h"
+#include "wayland-private.h"
+#include "wayland-util.h"
+
+#define TRACER_SERVER_SIDE 0
+#define TRACER_CLIENT_SIDE 1
+
+struct tracer_connection {
+	struct wl_connection *wl_conn;
+	struct tracer_connection *peer;
+	int side;
+};
+
+struct tracer {
+	struct tracer_connection *client_conn;
+	struct tracer_connection *server_conn;
+	int32_t epollfd;
+};
+
+static int
+tracer_dump_bin(struct tracer_connection *connection)
+{
+	int i, len, fdlen, fd;
+	char buf[4096];
+	struct wl_connection *wl_conn= connection->wl_conn;
+	struct tracer_connection *peer = connection->peer;
+
+	len = wl_buffer_size(&wl_conn->in);
+	if (len == 0) 
+		return 0;
+
+	wl_connection_copy(wl_conn, buf, len);
+
+	printf("%s Data dumped: %d bytes:\n",
+	       connection->side == TRACER_SERVER_SIDE ? "=>" : "<=", len);
+	for (i = 0; i < len; i++) {
+		printf("%02x ", (unsigned char)buf[i]);
+	}
+	printf("\n");
+	wl_connection_consume(wl_conn, len);
+	wl_connection_write(peer->wl_conn, buf, len);
+
+	fdlen = wl_buffer_size(&wl_conn->fds_in);
+
+	wl_buffer_copy(&wl_conn->fds_in, buf, fdlen);
+	fdlen /= sizeof(int32_t);
+
+	if (fdlen != 0)
+		printf("%d Fds in control data:", fdlen);
+
+	for (i = 0; i < fdlen; i++) {
+		fd = ((int *) buf)[i];
+		printf("%d ", fd);
+		wl_connection_put_fd(peer->wl_conn, fd);
+	}
+	printf("\n");
+
+	wl_conn->fds_in.tail += fdlen * sizeof(int32_t);
+	wl_connection_flush(peer->wl_conn);
+
+	return len;
+}
+
+/* The following two functions are taken from wayland-client.c*/
+static int
+tracer_connect_to_socket(const char *name)
+{
+	struct sockaddr_un addr;
+	socklen_t size;
+	const char *runtime_dir;
+	int name_size, fd;
+
+	runtime_dir = getenv("XDG_RUNTIME_DIR");
+	if (!runtime_dir) {
+		fprintf(stderr, "error: XDG_RUNTIME_DIR not set in the environment.\n");
+		/* to prevent programs reporting
+		 * "failed to create display: Success" */
+		errno = ENOENT;
+		return -1;
+	}
+
+	if (name == NULL)
+		name = getenv("WAYLAND_DISPLAY");
+	if (name == NULL)
+		name = "wayland-0";
+
+	fd = wl_os_socket_cloexec(PF_LOCAL, SOCK_STREAM, 0);
+	if (fd < 0)
+		return -1;
+
+	memset(&addr, 0, sizeof addr);
+	addr.sun_family = AF_LOCAL;
+	name_size =
+		snprintf(addr.sun_path, sizeof addr.sun_path,
+			 "%s/%s", runtime_dir, name) + 1;
+
+	assert(name_size > 0);
+	if (name_size > (int)sizeof addr.sun_path) {
+		fprintf(stderr, "error: socket path \"%s/%s\" plus null terminator"
+		       " exceeds 108 bytes\n", runtime_dir, name);
+		close(fd);
+		/* to prevent programs reporting
+		 * "failed to add socket: Success" */
+		errno = ENAMETOOLONG;
+		return -1;
+	};
+
+	size = offsetof (struct sockaddr_un, sun_path) + name_size;
+
+	if (connect(fd, (struct sockaddr *) &addr, size) < 0) {
+		close(fd);
+		return -1;
+	}
+
+	return fd;
+}
+
+static int
+tracer_connect_server(const char *name)
+{
+	char *connection, *end;
+	int flags, fd;
+
+	connection = getenv("WAYLAND_SOCKET");
+	if (connection) {
+		fd = strtol(connection, &end, 0);
+		if (*end != '\0')
+			return -1;
+
+		flags = fcntl(fd, F_GETFD);
+		if (flags != -1)
+			fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
+		unsetenv("WAYLAND_SOCKET");
+	} else
+		fd = tracer_connect_to_socket(name);
+
+	return fd;
+}
+
+static struct tracer_connection*
+tracer_connection_create(int fd, int side)
+{
+	struct tracer_connection *connection;
+
+	connection = malloc(sizeof *connection);
+	if (connection == NULL) {
+		errno = ENOMEM;
+		return NULL;
+	}
+
+	connection->wl_conn = wl_connection_create(fd);
+	if (connection->wl_conn == NULL)
+		return NULL;
+
+	connection->side = side;
+
+	return connection;
+}
+
+static void
+tracer_connection_destroy(struct tracer_connection *connection)
+{
+	wl_connection_destroy(connection->wl_conn);
+	free(connection);
+}
+
+static int
+tracer_epoll_add_fd(struct tracer *tracer, int fd, void *userdata)
+{
+	struct epoll_event ev;
+
+	ev.events = EPOLLIN;
+	ev.data.ptr = userdata;
+
+	return epoll_ctl(tracer->epollfd, EPOLL_CTL_ADD, fd, &ev);
+}
+
+static struct tracer*
+tracer_create(int serverfd, int clientfd)
+{
+	struct tracer *tracer;
+
+	tracer = malloc(sizeof *tracer);
+
+	if (tracer == NULL) {
+		errno = ENOMEM;
+		return NULL;
+	}
+
+	tracer->server_conn = tracer_connection_create(serverfd,
+						       TRACER_SERVER_SIDE);
+	if (tracer->server_conn == NULL)
+		goto err_conn;
+
+	tracer->client_conn = tracer_connection_create(clientfd,
+						       TRACER_CLIENT_SIDE);
+	if (tracer->client_conn == NULL)
+		goto err_conn;
+
+	tracer->server_conn->peer = tracer->client_conn;
+	tracer->client_conn->peer = tracer->server_conn;
+
+	tracer->epollfd = epoll_create1(0);
+	if (tracer->epollfd < 0) {
+		fprintf(stderr, "Failed to create epollfd: %m\n");
+		goto err_epoll_create;
+	}
+
+	if (tracer_epoll_add_fd(tracer, serverfd, tracer->server_conn) < 0) {
+		fprintf(stderr, "Failed to poll serverfd: %m\n");
+		goto err_epoll;
+	}
+
+	if (tracer_epoll_add_fd(tracer, clientfd, tracer->client_conn) < 0) {
+		fprintf(stderr, "Failed to poll clientfd: %m\n");
+		goto err_epoll;
+	}
+
+	return tracer;
+
+err_conn:
+	free(tracer);
+	return NULL;
+
+err_epoll_create:
+	tracer_connection_destroy(tracer->server_conn);
+	tracer_connection_destroy(tracer->client_conn);
+	free(tracer);
+	return NULL;
+
+err_epoll:
+	close(tracer->epollfd);
+	tracer_connection_destroy(tracer->server_conn);
+	tracer_connection_destroy(tracer->client_conn);
+	free(tracer);
+	return NULL;
+}
+
+static void
+tracer_handle_data(struct tracer_connection *connection)
+{
+	wl_connection_read(connection->wl_conn);
+
+	while(tracer_dump_bin(connection) > 0);
+}
+
+static void
+tracer_run(struct tracer *tracer)
+{
+	struct epoll_event ev;
+	int nfds;
+
+	for (;;) {
+		nfds = epoll_wait(tracer->epollfd, &ev, 1, -1);
+
+		if (nfds < 0) {
+			fprintf(stderr, "Failed to poll: %m\n");
+			return ;
+		}
+
+		tracer_handle_data((struct tracer_connection*) ev.data.ptr);
+	}
+}
+
+int
+main(int argc, char *argv[])
+{
+	int sock_vec[2];
+	int serverfd, clientfd;
+	int ret;
+	char sockfdstr[10];
+	pid_t pid;
+	struct tracer *tracer;
+
+	ret = socketpair(PF_LOCAL, SOCK_STREAM, 0, sock_vec);
+	if (ret != 0) {
+		fprintf(stderr, "Failed to create socket pair: %m\n");
+		exit(1);
+	}
+
+	pid = fork();
+	if (pid < 0) {
+		fprintf(stderr, "Failed to fork program: %m\n");
+		exit(2);
+	}
+
+	if (pid == 0) {
+		close(sock_vec[0]);
+		sprintf(sockfdstr, "%d", sock_vec[1]);
+		setenv("WAYLAND_SOCKET", sockfdstr, 1);
+
+		execvp(argv[1], &argv[1]);
+		exit(0);
+	}
+
+	close(sock_vec[1]);
+	clientfd = sock_vec[0];
+
+	serverfd = tracer_connect_server(NULL);
+	if (serverfd < 0) {
+		fprintf(stderr, "Failed to connect to wayland server: %m\n");
+		exit(5);
+	}
+
+	tracer = tracer_create(serverfd, clientfd);
+	if (tracer == NULL) {
+		fprintf(stderr, "Failed to create tracer: %m\n");
+		exit(6);
+	}
+
+	tracer_run(tracer);
+
+	return 0;
+}
+
-- 
2.0.1




More information about the wayland-devel mailing list