Signed-off-by: Boyan Ding <stu_...@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 _______________________________________________ wayland-devel mailing list wayland-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/wayland-devel