[Evolution-hackers] file descriptor debugger




attached is a very simple file descriptor debugger that helped me trace down a problem in evolution.

its more lightweight and a little more useful than strace for this purpose, since you get a backtrace of where things happened.  the trace isn't always very complete (symbols not resolving) but if you attach with gdb you can look the numbers up with "info symbol", so it is still useful.

very speficially only for glibc systems, and not sure on how architecturally portable.

/*

compile with:

fdtrace.o: fdtrace.c
	$(CC) -g -o $@ $^ -shared -g -lpthread -ldl

use with:

LD_PRELOAD=fdtrace.o program

*/

#define _GNU_SOURCE

#include <dlfcn.h>

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <stdarg.h>

#include <pthread.h>

#define STACK_DEPTH (10)
#define FD_LIMIT (1024)

/* incomplete list of functions which can allocate fd's */
static int (*r_open)(const char *p, int f, ...);
static int (*r_close)(int);
static int (*r_pipe)(int fd[2]);
static int (*r_dup)(int);
static int (*r_dup2)(int, int);
static int (*r_mkstemp)(char *);

struct _track {
	int open:1;
	int close:1;
	void *opened[STACK_DEPTH];
	void *closed[STACK_DEPTH];
};

static struct _track fds[FD_LIMIT];
static void *tos;
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

static void init(void)
{
	static int init = 0;

	if (!init) {
		r_open = dlsym(RTLD_NEXT, "open");
		r_close = dlsym(RTLD_NEXT, "close");
		r_pipe = dlsym(RTLD_NEXT, "pipe");
		r_dup = dlsym(RTLD_NEXT, "dup");
		r_dup2 = dlsym(RTLD_NEXT, "dup2");
		r_mkstemp = dlsym(RTLD_NEXT, "mkstemp");

		init = 1;
	}
}

static void
savestack(void **stack)
{
	void **base;
	int i;
	void **tos = (void *)~0;
	extern void *__libc_stack_end;

	tos = (void **)__libc_stack_end;
	base = ((void **)&tos)+3;
#if 0
	i = 0;
	while ((tos-i) >= base) {
		printf(" %p: %p\n", &tos[-i], tos[-i]);
		i++;
	}
#endif

	for (i=0;i<STACK_DEPTH && base && base < tos;i++) {
		stack[i] = base[1];
		base = base[0];
	}
}

static void
dumpstack(void **stack)
{
	int i;

	for (i=0;i<STACK_DEPTH && stack[i];i++) {
		Dl_info di;

		if (dladdr(stack[i], &di) != 0) {
			printf(" %p: %s (%p:%s)\n", stack[i], di.dli_sname, di.dli_saddr, di.dli_fname);
		} else {
			printf(" %p: ????\n", stack[i]);
		}
	}
}

static void
openfd(int fd)
{
	fds[fd].open = 1;
	fds[fd].close = 0;
	savestack(fds[fd].opened);
}

int open(const char *p, int f, ...)
{
	int fd;
	va_list ap;
	mode_t mode;

	pthread_mutex_lock(&lock);

	init();

	if (f & O_CREAT) {

		va_start(ap, f);
		mode = va_arg(ap, mode_t);
		va_end(ap);
		fd = r_open(p, f, mode);
	} else {
		fd = r_open(p, f);
	}

	printf("open(%s) = %d\n", p, fd);

	if (fd != -1)
		openfd(fd);

	pthread_mutex_unlock(&lock);

	return fd;
}

int close(int fd)
{
	int res;
	void *stack[STACK_DEPTH];

	pthread_mutex_lock(&lock);

	init();

	res = r_close(fd);
	printf("close(%d) = %d\n", fd, res);

	if (fd != -1 && fd<FD_LIMIT) {
		if (fds[fd].open) {
			fds[fd].open = 0;
			fds[fd].close = 1;
			savestack(fds[fd].closed);
		} else if (fds[fd].close) {
			if (res != 0) {
				printf("re-closing previously closed fd %d\n", fd);
				savestack(stack);
				dumpstack(stack);
				printf("was closed at:\n");
				dumpstack(fds[fd].closed);
				printf("was opened at:\n");
				dumpstack(fds[fd].opened);
				/*abort();*/
			}
		} else {
			if (res != 0) {
				printf("closing un-opened fd %d\n", fd);
				savestack(stack);
				dumpstack(stack);
				/*abort();*/
			}
		}
	}

	pthread_mutex_unlock(&lock);

	return res;
}

#if 0
/* for some reason this breaks evolution */
int pipe(int fd[2])
{
	int res, i;

	/*pthread_mutex_lock(&lock);*/

	res = r_pipe(fd);
#if 0
	if (res == 0) {
		openfd(fd[0]);
		openfd(fd[1]);
	}
#endif
	/*pthread_mutex_unlock(&lock);*/

	return res;
}
#endif

int dup(int oldfd)
{
	int res;

	pthread_mutex_lock(&lock);

	res = r_dup(oldfd);
	if (res != -1) {
		openfd(res);
	}

	pthread_mutex_unlock(&lock);

	return res;
}

int dup2(int oldfd, int newfd)
{
	int res;

	pthread_mutex_lock(&lock);

	res = r_dup2(oldfd, newfd);
	printf("dup2(%d,%d) = %d\n", oldfd, newfd, res);
	if (res != -1) {
		openfd(res);
	}

	pthread_mutex_unlock(&lock);

	return res;
}

int mkstemp(char *template)
{
	int res;

	pthread_mutex_lock(&lock);

	res = r_mkstemp(template);
	printf("mkstemp(%s) = %d\n", template, res);
	if (res != -1) {
		openfd(res);
	}

	pthread_mutex_unlock(&lock);

	return res;
}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]