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

Silvan Jegen s.jegen at gmail.com
Sun Jul 20 03:53:27 PDT 2014


Hi

and thanks for this! I hope to learn more about the protocol by using
this tool.

I encountered two simple issues when applying your patches (see below).

On Sun, Jul 20, 2014 at 10:18:03AM +0800, Boyan Ding wrote:
> 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)

wayland-tracer wouldn't compile without including the -lrt switch (for
the time.h functions) like this.

wayland_tracer_LDADD = libwayland-util.la $(FFI_LIBS) -lrt

I am not sure whether this issue is specific to my setup or if this is
the best way to fix it.


> +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) 

There is a trailing space here (git complained when applying the patch).


> +		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
> 
> 
> _______________________________________________
> wayland-devel mailing list
> wayland-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/wayland-devel


More information about the wayland-devel mailing list