X-Git-Url: http://git.ozlabs.org/?p=ccan;a=blobdiff_plain;f=ccan%2Ffailtest%2Ffailtest.c;h=4f71a63dc172896928cde965277b145093c13869;hp=2b78b8e3f8bdc655aaa9234ffa1335fe73c6ac8a;hb=1605358b20327a8d384750cc2f840ed3a2ea224f;hpb=5e1616698e06e2ce661122c6c9b04aae50271be8 diff --git a/ccan/failtest/failtest.c b/ccan/failtest/failtest.c index 2b78b8e3..4f71a63d 100644 --- a/ccan/failtest/failtest.c +++ b/ccan/failtest/failtest.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -19,6 +20,8 @@ bool (*failtest_hook)(struct failtest_call *history, unsigned num) = failtest_default_hook; +static int tracefd = -1; + unsigned int failtest_timeout_ms = 20000; const char *failpath; @@ -63,6 +66,7 @@ bool (*failtest_exit_check)(struct failtest_call *history, unsigned num); static struct failtest_call *history = NULL; static unsigned int history_num = 0; static int control_fd = -1; +static struct timeval start; static struct write_info *writes = NULL; static unsigned int writes_num = 0; @@ -143,18 +147,18 @@ static bool read_write_info(int fd) return true; } -static void print_reproduce(void) +static char *failpath_string(void) { unsigned int i; + char *ret = malloc(history_num + 1); - printf("To reproduce: --failpath="); for (i = 0; i < history_num; i++) { + ret[i] = info_to_arg[history[i].type]; if (history[i].fail) - printf("%c", toupper(info_to_arg[history[i].type])); - else - printf("%c", info_to_arg[history[i].type]); + ret[i] = toupper(ret[i]); } - printf("\n"); + ret[i] = '\0'; + return ret; } static void tell_parent(enum info_type type) @@ -166,13 +170,15 @@ static void tell_parent(enum info_type type) static void child_fail(const char *out, size_t outlen, const char *fmt, ...) { va_list ap; + char *path = failpath_string(); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "%.*s", (int)outlen, out); - print_reproduce(); + printf("To reproduce: --failpath=%s\n", path); + free(path); tell_parent(FAILURE); exit(1); } @@ -250,6 +256,18 @@ static void get_locks(void) lock_owner = getpid(); } +static void trace_str(const char *str) +{ + ssize_t ret; + + while ((ret = write(tracefd, str, strlen(str))) <= 0) { + str += ret; + if (!*str) + return; + } + err(1, "Writing trace."); +} + static bool should_fail(struct failtest_call *call) { int status; @@ -262,10 +280,16 @@ static bool should_fail(struct failtest_call *call) return false; if (failpath) { - if (tolower(*failpath) != info_to_arg[call->type]) - errx(1, "Failpath expected '%c' got '%c'\n", - info_to_arg[call->type], *failpath); - return isupper(*(failpath++)); + /* + means continue after end, like normal. */ + if (*failpath == '+') + failpath = NULL; + else { + if (tolower(*failpath) != info_to_arg[call->type]) + errx(1, "Failpath expected '%c' got '%c'\n", + info_to_arg[call->type], *failpath); + call->fail = isupper(*(failpath++)); + return call->fail; + } } if (!failtest_hook(history, history_num)) { @@ -285,6 +309,31 @@ static bool should_fail(struct failtest_call *call) err(1, "forking failed"); if (child == 0) { + if (tracefd != -1) { + struct timeval now; + char str[50], *p; + gettimeofday(&now, NULL); + if (now.tv_usec < start.tv_usec) { + now.tv_sec--; + now.tv_usec += 1000000; + } + now.tv_usec -= start.tv_usec; + now.tv_sec -= start.tv_sec; + sprintf(str, "%u (%u.%02u): ", getpid(), + (int)now.tv_sec, (int)now.tv_usec / 10000); + trace_str(str); + p = failpath_string(); + trace_str(p); + free(p); + trace_str("("); + p = strchr(history[history_num-1].file, '/'); + if (p) + trace_str(p+1); + else + trace_str(history[history_num-1].file); + sprintf(str, ":%u)\n", history[history_num-1].line); + trace_str(str); + } close(control[0]); close(output[0]); dup2(output[1], STDOUT_FILENO); @@ -418,17 +467,17 @@ void *failtest_realloc(void *ptr, size_t size, const char *file, unsigned line) return p->u.realloc.ret; } -int failtest_open(const char *pathname, int flags, +int failtest_open(const char *pathname, const char *file, unsigned line, ...) { struct failtest_call *p; struct open_call call; + va_list ap; call.pathname = strdup(pathname); - call.flags = flags; - if (flags & O_CREAT) { - va_list ap; - va_start(ap, line); + va_start(ap, line); + call.flags = va_arg(ap, int); + if (call.flags & O_CREAT) { call.mode = va_arg(ap, mode_t); va_end(ap); } @@ -441,7 +490,7 @@ int failtest_open(const char *pathname, int flags, /* FIXME: Play with error codes? */ p->error = EACCES; } else { - p->u.open.ret = open(pathname, flags, call.mode); + p->u.open.ret = open(pathname, call.flags, call.mode); } errno = p->error; return p->u.open.ret; @@ -466,14 +515,15 @@ int failtest_pipe(int pipefd[2], const char *file, unsigned line) return p->u.pipe.ret; } -ssize_t failtest_read(int fd, void *buf, size_t count, - const char *file, unsigned line) +ssize_t failtest_pread(int fd, void *buf, size_t count, off_t off, + const char *file, unsigned line) { struct failtest_call *p; struct read_call call; call.fd = fd; call.buf = buf; call.count = count; + call.off = off; p = add_history(FAILTEST_READ, file, line, &call); /* This is going to change seek offset, so save it. */ @@ -485,7 +535,7 @@ ssize_t failtest_read(int fd, void *buf, size_t count, p->u.read.ret = -1; p->error = EIO; } else { - p->u.read.ret = read(fd, buf, count); + p->u.read.ret = pread(fd, buf, count, off); } errno = p->error; return p->u.read.ret; @@ -497,8 +547,8 @@ static struct write_info *new_write(void) return &writes[writes_num++]; } -ssize_t failtest_write(int fd, const void *buf, size_t count, - const char *file, unsigned line) +ssize_t failtest_pwrite(int fd, const void *buf, size_t count, off_t off, + const char *file, unsigned line) { struct failtest_call *p; struct write_call call; @@ -507,6 +557,7 @@ ssize_t failtest_write(int fd, const void *buf, size_t count, call.fd = fd; call.buf = buf; call.count = count; + call.off = off; p = add_history(FAILTEST_WRITE, file, line, &call); offset = lseek(fd, 0, SEEK_CUR); @@ -570,12 +621,26 @@ ssize_t failtest_write(int fd, const void *buf, size_t count, return p->u.write.ret; } } - p->u.write.ret = write(fd, buf, count); + p->u.write.ret = pwrite(fd, buf, count, off); } errno = p->error; return p->u.write.ret; } +ssize_t failtest_read(int fd, void *buf, size_t count, + const char *file, unsigned line) +{ + return failtest_pread(fd, buf, count, lseek(fd, 0, SEEK_CUR), + file, line); +} + +ssize_t failtest_write(int fd, const void *buf, size_t count, + const char *file, unsigned line) +{ + return failtest_pwrite(fd, buf, count, lseek(fd, 0, SEEK_CUR), + file, line); +} + static struct lock_info *WARN_UNUSED_RESULT add_lock(struct lock_info *locks, int fd, off_t start, off_t end, int type) { @@ -738,10 +803,17 @@ int failtest_fcntl(int fd, const char *file, unsigned line, int cmd, ...) void failtest_init(int argc, char *argv[]) { - if (argc == 2 - && strncmp(argv[1], "--failpath=", strlen("--failpath=")) == 0) { - failpath = argv[1] + strlen("--failpath="); + unsigned int i; + + for (i = 1; i < argc; i++) { + if (!strncmp(argv[i], "--failpath=", strlen("--failpath="))) { + failpath = argv[i] + strlen("--failpath="); + } else if (strcmp(argv[i], "--tracepath") == 0) { + tracefd = dup(STDERR_FILENO); + failtest_timeout_ms = -1; + } } + gettimeofday(&start, NULL); } /* Free up memory, so valgrind doesn't report leaks. */