diff --git a/Makefile b/Makefile index 671f3a5..571d9db 100644 --- a/Makefile +++ b/Makefile @@ -18,13 +18,26 @@ # along with this program; if not, write to the Free Software # Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -all: - cd tests && make all - cd benchmarks && make all +CC=gcc +CFLAGS=-Wall -Werror -g -O2 +LDFLAGS= + +all: tests benchmarks + +.PHONY: tests benchmarks + +tests benchmarks: bin/run-tests + $(MAKE) -C $@ all + + +bin/run-tests: bin/talloc/talloc.o bin/run-tests.o bin/capabilities.o bin/test.o + $(LINK.o) -o $@ $^ clean: cd tests && make clean cd benchmarks && make clean + rm -f bin/capabilities bin/run-tests + rm -f bin/*.o check: clean all ./run-tests diff --git a/README b/README index 984ab0c..8420b07 100644 --- a/README +++ b/README @@ -11,11 +11,11 @@ See the COPYING file for the full text of the GNU General Public License. Running: - * ./run-tests + * bin/run-tests - to run all tests - * ./run-tests + * bin/run-tests - run all tests in - * ./run-tests + * bin/run-tests - run a single test file * The -c option will cause the test suite to continue on failures @@ -46,13 +46,25 @@ Writing tests: where options may be one or more of: expected_rc= # expect a rc of instead of 0 - disabled=1 # disable the test - needs_root=1 # disable the test if run by a non-root user + signal= # expect the test be be killed by + disabled # disable the test + + you can also make tests conditionally run if various capabilities are + available on the system at run-time. These are specicied by cap_* options. + At present, we have the following capabilities: + cap_root # run the test if uid==0 + cap_affinity # run the test only on affinity-enabled hardware + + Capabilities can be inverted, using the '!' character. For example, + the option "!cap_root" will run the test only if uid!=0. for example, from the testsuite's own test.conf: 01-fail: expected_rc=1 - 02-root: needs_root=1 - 03-skip: disabled=1 + 02-root: cap_root + 03-skip: disabled + 06-cap: cap_never + 07-nocap: !cap_always + 08-signal: signal=15 If a test is not listed in test.conf (or there is no test.conf), then the default options will be used. diff --git a/bin/capabilities.c b/bin/capabilities.c new file mode 100644 index 0000000..ae23428 --- /dev/null +++ b/bin/capabilities.c @@ -0,0 +1,132 @@ +/* + * Testsuite for the Linux SPU filesystem + * + * Copyright (C) IBM Corporation, 2008 + * + * Author: Jeremy Kerr + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include +#include +#include +#include + +#include +#include + +static int cap_always(void) +{ + return 1; +} + +static int cap_never(void) +{ + return 0; +} + +static int cap_root(void) +{ + return getuid() == 0; +} + +static int cap_affinity(void) +{ + struct stat statbuf; + + /* test to see if we have NUMA nodes in /sys */ + return stat("/sys/devices/system/node", &statbuf) == 0; +} + +struct capability { + const char *name; + int (*fn)(void); + int value; + int valid; +}; + +static struct capability caps[] = { + { + .name = "always", + .fn = cap_always + }, + { + .name = "never", + .fn = cap_never + }, + { + .name = "root", + .fn = cap_root + }, + { + .name = "affinity", + .fn = cap_affinity + }, + { + .name = NULL + } +}; + +static int __cap_available(struct capability *cap) +{ + if (!cap->valid) { + cap->value = cap->fn(); + cap->valid = 1; + } + + return cap->value; +} + +int cap_available(const char *name) +{ + struct capability *cap; + + for (cap = caps; cap->name; cap++) { + if (!strcmp(cap->name, name)) + return __cap_available(cap); + } + + return 0; +} + +#if 0 + +void print_all_caps(void) +{ + struct capability *cap; + + for (cap = caps; cap->name; cap++) + printf("%s=%d\n", cap->name, get_capability(cap)); +} + +int print_cap(const char *name) +{ + struct capability *cap; + + for (cap = caps; cap->name; cap++) { + if (!strcasecmp(cap->name, name)) { + printf("%s=%d\n", cap->name, get_capability(cap)); + return 0; + } + } + + fprintf(stderr, "No such capability '%s'\n", name); + return -1; +} + +#endif + diff --git a/bin/capabilities.h b/bin/capabilities.h new file mode 100644 index 0000000..42ee611 --- /dev/null +++ b/bin/capabilities.h @@ -0,0 +1,28 @@ +/* + * Testsuite for the Linux SPU filesystem + * + * Copyright (C) IBM Corporation, 2008 + * + * Author: Jeremy Kerr + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _CAPABILITIES_H +#define _CAPABILITIES_H + +int cap_available(const char *name); + +#endif /* _CAPABILITIES_H */ diff --git a/bin/run-tests.c b/bin/run-tests.c new file mode 100644 index 0000000..43bf585 --- /dev/null +++ b/bin/run-tests.c @@ -0,0 +1,518 @@ +/* + * Testsuite for the Linux SPU filesystem + * + * Copyright (C) IBM Corporation, 2008 + * + * Author: Jeremy Kerr + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "talloc/talloc.h" +#include "test.h" + + +#define TEST_DIR "tests" +#define BENCHMARK_DIR "benchmarks" + +#define max(x, y) ((x) > (y) ? (x) : (y)) + +static int qsort_testcmp(const void *a, const void *b) +{ + return strcmp((*(const struct test **)a)->path, + (*(const struct test **)b)->path); +} + +static int is_test(char *file, struct stat *statbuf) +{ + return S_ISREG(statbuf->st_mode) && + (statbuf->st_mode & S_IXUSR) && + isdigit(file[0]) && isdigit(file[1]); +} + +static struct test **find_tests(char *testpath, int type) +{ + int n_tests, n_dirs, d; + struct dirent *dirent; + struct test **tests; + struct stat statbuf; + char **dirs; + DIR *dir; + + tests = NULL; + n_tests = 0; + + dirs = talloc_realloc(NULL, NULL, char *, 1); + dirs[0] = testpath; + n_dirs = 1; + + for (d = 0; d < n_dirs; d++) { + + dir = opendir(dirs[d]); + if (!dir) { + perror("opendir"); + return NULL; + } + + while ((dirent = readdir(dir))) { + char *path, *file; + + file = dirent->d_name; + + if (*file == '.') + continue; + + path = talloc_asprintf(NULL, "%s/%s", dirs[d], file); + + if (stat(path, &statbuf)) { + talloc_free(path); + continue; + } + + if (S_ISDIR(statbuf.st_mode)) { + dirs = talloc_realloc(NULL, dirs, + char *, n_dirs + 1); + talloc_steal(dirs, path); + dirs[n_dirs] = path; + n_dirs++; + + } else if (is_test(file, &statbuf)) { + + tests = talloc_realloc(NULL, tests, + struct test *, n_tests + 1); + tests[n_tests] = test_create(tests, path, type); + n_tests++; + + } else { + talloc_free(path); + } + + } + closedir(dir); + } + + qsort(tests, n_tests, sizeof(struct test *), qsort_testcmp); + + tests = talloc_realloc(NULL, tests, struct test *, n_tests + 1); + tests[n_tests] = NULL; + + talloc_free(dirs); + + return tests; +} + +void sigchld_handler(int signum) +{ + /* we don't need to do anything here, just break out of the sleep() */ +} + +static int read_test_output(struct test *test, int timeout) +{ + const int readsize = 4096; + struct pollfd fds[1]; + int rc, n_fds = 0; + + if (test->run.stdout_fd > 0) { + fds[0].fd = test->run.stdout_fd; + fds[0].events = POLLIN | POLLERR | POLLHUP; + n_fds++; + } + + rc = poll(fds, n_fds, timeout); + + if (rc < 0) { + if (errno != EINTR) + perror("poll"); + return 0; + } + + if (rc == 0) + return 0; + + if (fds[0].revents & POLLERR) + return -1; + + if (test->output.bufsize - test->output.len < readsize) { + int alloc_size = max(readsize, test->output.bufsize * 2); + + test->output.buf = talloc_realloc(test, test->output.buf, + char, alloc_size); + + test->output.bufsize = alloc_size; + } + + rc = read(test->run.stdout_fd, + test->output.buf + test->output.len, readsize); + + if (rc < 0) { + perror("read"); + return -1; + } else if (rc == 0) { + return -1; + } else { + test->output.len += rc; + } + + return 0; +} + +static int dmesg_check() +{ + static char *buf; + static int bufsize; + int kernel_bufsize, rc; + + kernel_bufsize = klogctl(10, NULL, 0); + if (kernel_bufsize != bufsize) { + bufsize = kernel_bufsize; + buf = talloc_realloc(NULL, buf, char, bufsize); + } + + rc = klogctl(3, buf, bufsize); + if (rc < 0) { + perror("klogctl"); + return -1; + } + + return (strstr(buf, "Badness") != NULL); +} + +static int run_test(struct test *test, int dir_fd) +{ + int rc, pipe_fds[2]; + struct timeval tv, timeout_tv; + siginfo_t siginfo; + struct sigaction sa = { + .sa_handler = sigchld_handler, + }; + + test_header(test); + + test_read_config(test); + if (!test_should_run(test)) { + return 0; + } + + if (pipe(pipe_fds)) { + perror("pipe"); + return -1; + } + + test->run.stdout_fd = pipe_fds[0]; + + test->run.pid = fork(); + if (test->run.pid < 0) { + perror("fork"); + return -1; + } + + if (test->run.pid == 0) { + /* child process: exec the test */ + char *arg, *path, *dir, *c; + + close(pipe_fds[0]); + + close(STDIN_FILENO); + if (dup2(pipe_fds[1], STDOUT_FILENO) < 0) { + perror("dup 2"); + exit(EXIT_FAILURE); + } + if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0) { + perror("dup 1"); + exit(EXIT_FAILURE); + } + + close(pipe_fds[1]); + + if (fchdir(dir_fd)) { + perror("fchdir"); + exit(EXIT_FAILURE); + } + + dir = talloc_strdup(NULL, test->path); + c = strrchr(dir, '/'); + if (c) { + *c = '\0'; + path = talloc_asprintf(dir, "../%s:%s", + dir, getenv("PATH")); + setenv("PATH", path, 1); + } + talloc_free(dir); + + arg = talloc_asprintf(test->path, "../%s", test->path); + execl(arg, arg, NULL); + exit(EXIT_FAILURE); + } + + close(pipe_fds[1]); + + if (sigaction(SIGCHLD, &sa, NULL)) { + perror("sigaction"); + return -1; + } + + gettimeofday(&timeout_tv, NULL); + + timeout_tv.tv_sec += test->timeout; + + for (;;) { + int timeout; + + siginfo.si_pid = 0; + + rc = waitid(P_PID, test->run.pid, &siginfo, WEXITED | WNOHANG); + + if (rc != 0) { + perror("waitpid"); + break; + } + + /* if the test has exited, check the exit status */ + if (siginfo.si_pid) { + /* exited due to exit() */ + if (siginfo.si_code == CLD_EXITED) { + if (siginfo.si_status != test->expected_rc) { + test_fail(test, "rc %d, " + "expected %d", + siginfo.si_status, + test->expected_rc); + rc = -1; + + } else if (dmesg_check()) { + test_fail(test, "dmesg"); + rc = -1; + + } else { + test_pass(test); + rc = 0; + } + + /* exited due to signal */ + } else if ((siginfo.si_code == CLD_KILLED)) { + if (siginfo.si_status == test->expected_sig) { + test_pass(test); + rc = 0; + } else if (siginfo.si_status == SIGALRM) { + test_fail(test, "timeout"); + rc = -1; + } else { + test_fail(test, "killed, sig %d", + siginfo.si_status); + rc = -1; + } + } + test->run.pid = 0; + break; + } + + /* see if we've timed-out */ + gettimeofday(&tv, NULL); + if (timercmp(&tv, &timeout_tv, >)) { + kill(test->run.pid, SIGALRM); + } + + timeout = 1 + (1000 * (timeout_tv.tv_sec - tv.tv_sec)); + + read_test_output(test, timeout); + } + + close(test->run.stdout_fd); + + return rc; +} + +static int run_tests(struct test **tests, int cont_on_failure) +{ + struct test **test; + char working_dir[256]; + int dirfd, rc; + time_t t; + struct tm *tm; + + if (dmesg_check()) { + fprintf(stderr, "dmesg check failed: not running tests\n"); + return -1; + } + + t = time(NULL); + tm = localtime(&t); + + strftime(working_dir, sizeof(working_dir) - 1, + "run.%Y-%m-%d.%H:%m:%S", tm); + + if (mkdir(working_dir, 0755) && errno != EEXIST) { + perror("mkdir"); + return -1; + } + + dirfd = open(working_dir, O_RDONLY); + if (dirfd < 0) { + perror("open"); + return -1; + } + + rc = 0; + + for (test = tests; *test; test++) { + + rc |= run_test(*test, dirfd); + + if (rc && !cont_on_failure) + return -1; + } + + close(dirfd); + + return rc; +} + +static void usage(const char *progname) +{ + fprintf(stderr, "usage: %s [--benchmark] [--continue] [testdir]\n", + progname); +} + + +static void print_summary(struct test **tests) +{ + int n_tests, n_pass, n_fail, n_skip; + struct test **test; + + n_tests = n_pass = n_fail = n_skip = 0; + + for (test = tests; *test; test++) { + if ((*test)->result == TEST_NOTRUN) + continue; + + n_tests++; + if ((*test)->result == TEST_PASSED) + n_pass++; + else if ((*test)->result == TEST_FAILED) + n_fail++; + else if ((*test)->result == TEST_SKIPPED) + n_skip++; + + } + + printf("%d tests run: %d passed, %d failed, %d skipped\n", + n_tests, n_pass, n_fail, n_skip); +} + +struct option options[] = { + { + .name = "continue", + .has_arg = 0, + .val = 'c' + }, + { + .name = "benchmark", + .has_arg = 0, + .val = 'b' + }, + { + .name = "help", + .has_arg = 0, + .val = 'h' + }, + { + .name = NULL, + .val = 0 + } +}; + + +int main(int argc, char **argv) +{ + struct test **tests, **benchmarks; + char *testdir; + int benchmark, cont; + int c, rc; + + cont = 0; + benchmark = 0; + + for (;;) { + c = getopt_long(argc, argv, "cbh", options, NULL); + if (c == -1) + break; + + switch (c) { + case 'c': + cont = 1; + break; + case 'b': + benchmark = 1; + break; + case 'h': + usage(argv[0]); + return EXIT_SUCCESS; + default: + usage(argv[0]); + return EXIT_FAILURE; + } + } + + testdir = TEST_DIR; + + if (optind == argc - 1) { + testdir = argv[optind]; + + } else if (optind != argc) { + usage(argv[0]); + return EXIT_FAILURE; + } + + tests = find_tests(testdir, TEST_TYPE_TEST); + if (!tests) { + fprintf(stderr, "No tests found\n"); + return EXIT_FAILURE; + } + + rc = run_tests(tests, cont); + + print_summary(tests); + talloc_free(tests); + + if (rc || !benchmark) + return rc; + + printf("--- Running benchmarks\n"); + benchmarks = find_tests(BENCHMARK_DIR, TEST_TYPE_BENCHMARK); + run_tests(benchmarks, 0); + talloc_free(benchmarks); + + return 0; +} diff --git a/bin/talloc/talloc.c b/bin/talloc/talloc.c new file mode 100644 index 0000000..655530d --- /dev/null +++ b/bin/talloc/talloc.c @@ -0,0 +1,1519 @@ +/* + Samba Unix SMB/CIFS implementation. + + Samba trivial allocation library - new interface + + NOTE: Please read talloc_guide.txt for full documentation + + Copyright (C) Andrew Tridgell 2004 + Copyright (C) Stefan Metzmacher 2006 + + ** NOTE! The following LGPL license applies to the talloc + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . +*/ + +/* + inspired by http://swapped.cc/halloc/ +*/ + +#define _GNU_SOURCE + +#include +#include + +#include "talloc.h" + +/* use this to force every realloc to change the pointer, to stress test + code that might not cope */ +#define ALWAYS_REALLOC 0 + + +#define MAX_TALLOC_SIZE 0x10000000 +#define TALLOC_MAGIC 0xe814ec70 +#define TALLOC_FLAG_FREE 0x01 +#define TALLOC_FLAG_LOOP 0x02 +#define TALLOC_MAGIC_REFERENCE ((const char *)1) + +/* by default we abort when given a bad pointer (such as when talloc_free() is called + on a pointer that came from malloc() */ +#ifndef TALLOC_ABORT +#define TALLOC_ABORT(reason) abort() +#endif + +#ifndef discard_const_p +#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) +# define discard_const_p(type, ptr) ((type *)((intptr_t)(ptr))) +#else +# define discard_const_p(type, ptr) ((type *)(ptr)) +#endif +#endif + +/* these macros gain us a few percent of speed on gcc */ +#if (__GNUC__ >= 3) +/* the strange !! is to ensure that __builtin_expect() takes either 0 or 1 + as its first argument */ +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) +#else +#define likely(x) x +#define unlikely(x) x +#endif + +/* this null_context is only used if talloc_enable_leak_report() or + talloc_enable_leak_report_full() is called, otherwise it remains + NULL +*/ +static void *null_context; +static void *autofree_context; + +struct talloc_reference_handle { + struct talloc_reference_handle *next, *prev; + void *ptr; +}; + +typedef int (*talloc_destructor_t)(void *); + +struct talloc_chunk { + struct talloc_chunk *next, *prev; + struct talloc_chunk *parent, *child; + struct talloc_reference_handle *refs; + talloc_destructor_t destructor; + const char *name; + size_t size; + unsigned flags; +}; + +/* 16 byte alignment seems to keep everyone happy */ +#define TC_HDR_SIZE ((sizeof(struct talloc_chunk)+15)&~15) +#define TC_PTR_FROM_CHUNK(tc) ((void *)(TC_HDR_SIZE + (char*)tc)) + +static void talloc_abort_double_free(void) +{ + TALLOC_ABORT("Bad talloc magic value - double free"); +} + +static void talloc_abort_unknown_value(void) +{ + TALLOC_ABORT("Bad talloc magic value - unknown value"); +} + +/* panic if we get a bad magic value */ +static inline struct talloc_chunk *talloc_chunk_from_ptr(const void *ptr) +{ + const char *pp = (const char *)ptr; + struct talloc_chunk *tc = discard_const_p(struct talloc_chunk, pp - TC_HDR_SIZE); + if (unlikely((tc->flags & (TALLOC_FLAG_FREE | ~0xF)) != TALLOC_MAGIC)) { + if (tc->flags & TALLOC_FLAG_FREE) { + talloc_abort_double_free(); + } else { + talloc_abort_unknown_value(); + } + } + return tc; +} + +/* hook into the front of the list */ +#define _TLIST_ADD(list, p) \ +do { \ + if (!(list)) { \ + (list) = (p); \ + (p)->next = (p)->prev = NULL; \ + } else { \ + (list)->prev = (p); \ + (p)->next = (list); \ + (p)->prev = NULL; \ + (list) = (p); \ + }\ +} while (0) + +/* remove an element from a list - element doesn't have to be in list. */ +#define _TLIST_REMOVE(list, p) \ +do { \ + if ((p) == (list)) { \ + (list) = (p)->next; \ + if (list) (list)->prev = NULL; \ + } else { \ + if ((p)->prev) (p)->prev->next = (p)->next; \ + if ((p)->next) (p)->next->prev = (p)->prev; \ + } \ + if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \ +} while (0) + + +/* + return the parent chunk of a pointer +*/ +static inline struct talloc_chunk *talloc_parent_chunk(const void *ptr) +{ + struct talloc_chunk *tc; + + if (unlikely(ptr == NULL)) { + return NULL; + } + + tc = talloc_chunk_from_ptr(ptr); + while (tc->prev) tc=tc->prev; + + return tc->parent; +} + +void *talloc_parent(const void *ptr) +{ + struct talloc_chunk *tc = talloc_parent_chunk(ptr); + return tc? TC_PTR_FROM_CHUNK(tc) : NULL; +} + +/* + find parents name +*/ +const char *talloc_parent_name(const void *ptr) +{ + struct talloc_chunk *tc = talloc_parent_chunk(ptr); + return tc? tc->name : NULL; +} + +/* + Allocate a bit of memory as a child of an existing pointer +*/ +static inline void *__talloc(const void *context, size_t size) +{ + struct talloc_chunk *tc; + + if (unlikely(context == NULL)) { + context = null_context; + } + + if (unlikely(size >= MAX_TALLOC_SIZE)) { + return NULL; + } + + tc = (struct talloc_chunk *)malloc(TC_HDR_SIZE+size); + if (unlikely(tc == NULL)) return NULL; + + tc->size = size; + tc->flags = TALLOC_MAGIC; + tc->destructor = NULL; + tc->child = NULL; + tc->name = NULL; + tc->refs = NULL; + + if (likely(context)) { + struct talloc_chunk *parent = talloc_chunk_from_ptr(context); + + if (parent->child) { + parent->child->parent = NULL; + tc->next = parent->child; + tc->next->prev = tc; + } else { + tc->next = NULL; + } + tc->parent = parent; + tc->prev = NULL; + parent->child = tc; + } else { + tc->next = tc->prev = tc->parent = NULL; + } + + return TC_PTR_FROM_CHUNK(tc); +} + +/* + setup a destructor to be called on free of a pointer + the destructor should return 0 on success, or -1 on failure. + if the destructor fails then the free is failed, and the memory can + be continued to be used +*/ +void _talloc_set_destructor(const void *ptr, int (*destructor)(void *)) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + tc->destructor = destructor; +} + +/* + increase the reference count on a piece of memory. +*/ +int talloc_increase_ref_count(const void *ptr) +{ + if (unlikely(!talloc_reference(null_context, ptr))) { + return -1; + } + return 0; +} + +/* + helper for talloc_reference() + + this is referenced by a function pointer and should not be inline +*/ +static int talloc_reference_destructor(struct talloc_reference_handle *handle) +{ + struct talloc_chunk *ptr_tc = talloc_chunk_from_ptr(handle->ptr); + _TLIST_REMOVE(ptr_tc->refs, handle); + return 0; +} + +/* + more efficient way to add a name to a pointer - the name must point to a + true string constant +*/ +static inline void _talloc_set_name_const(const void *ptr, const char *name) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + tc->name = name; +} + +/* + internal talloc_named_const() +*/ +static inline void *_talloc_named_const(const void *context, size_t size, const char *name) +{ + void *ptr; + + ptr = __talloc(context, size); + if (unlikely(ptr == NULL)) { + return NULL; + } + + _talloc_set_name_const(ptr, name); + + return ptr; +} + +/* + make a secondary reference to a pointer, hanging off the given context. + the pointer remains valid until both the original caller and this given + context are freed. + + the major use for this is when two different structures need to reference the + same underlying data, and you want to be able to free the two instances separately, + and in either order +*/ +void *_talloc_reference(const void *context, const void *ptr) +{ + struct talloc_chunk *tc; + struct talloc_reference_handle *handle; + if (unlikely(ptr == NULL)) return NULL; + + tc = talloc_chunk_from_ptr(ptr); + handle = (struct talloc_reference_handle *)_talloc_named_const(context, + sizeof(struct talloc_reference_handle), + TALLOC_MAGIC_REFERENCE); + if (unlikely(handle == NULL)) return NULL; + + /* note that we hang the destructor off the handle, not the + main context as that allows the caller to still setup their + own destructor on the context if they want to */ + talloc_set_destructor(handle, talloc_reference_destructor); + handle->ptr = discard_const_p(void, ptr); + _TLIST_ADD(tc->refs, handle); + return handle->ptr; +} + + +/* + internal talloc_free call +*/ +static inline int _talloc_free(void *ptr) +{ + struct talloc_chunk *tc; + + if (unlikely(ptr == NULL)) { + return -1; + } + + tc = talloc_chunk_from_ptr(ptr); + + if (unlikely(tc->refs)) { + int is_child; + /* check this is a reference from a child or grantchild + * back to it's parent or grantparent + * + * in that case we need to remove the reference and + * call another instance of talloc_free() on the current + * pointer. + */ + is_child = talloc_is_parent(tc->refs, ptr); + _talloc_free(tc->refs); + if (is_child) { + return _talloc_free(ptr); + } + return -1; + } + + if (unlikely(tc->flags & TALLOC_FLAG_LOOP)) { + /* we have a free loop - stop looping */ + return 0; + } + + if (unlikely(tc->destructor)) { + talloc_destructor_t d = tc->destructor; + if (d == (talloc_destructor_t)-1) { + return -1; + } + tc->destructor = (talloc_destructor_t)-1; + if (d(ptr) == -1) { + tc->destructor = d; + return -1; + } + tc->destructor = NULL; + } + + if (tc->parent) { + _TLIST_REMOVE(tc->parent->child, tc); + if (tc->parent->child) { + tc->parent->child->parent = tc->parent; + } + } else { + if (tc->prev) tc->prev->next = tc->next; + if (tc->next) tc->next->prev = tc->prev; + } + + tc->flags |= TALLOC_FLAG_LOOP; + + while (tc->child) { + /* we need to work out who will own an abandoned child + if it cannot be freed. In priority order, the first + choice is owner of any remaining reference to this + pointer, the second choice is our parent, and the + final choice is the null context. */ + void *child = TC_PTR_FROM_CHUNK(tc->child); + const void *new_parent = null_context; + if (unlikely(tc->child->refs)) { + struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs); + if (p) new_parent = TC_PTR_FROM_CHUNK(p); + } + if (unlikely(_talloc_free(child) == -1)) { + if (new_parent == null_context) { + struct talloc_chunk *p = talloc_parent_chunk(ptr); + if (p) new_parent = TC_PTR_FROM_CHUNK(p); + } + talloc_steal(new_parent, child); + } + } + + tc->flags |= TALLOC_FLAG_FREE; + free(tc); + return 0; +} + +/* + move a lump of memory from one talloc context to another return the + ptr on success, or NULL if it could not be transferred. + passing NULL as ptr will always return NULL with no side effects. +*/ +void *_talloc_steal(const void *new_ctx, const void *ptr) +{ + struct talloc_chunk *tc, *new_tc; + + if (unlikely(!ptr)) { + return NULL; + } + + if (unlikely(new_ctx == NULL)) { + new_ctx = null_context; + } + + tc = talloc_chunk_from_ptr(ptr); + + if (unlikely(new_ctx == NULL)) { + if (tc->parent) { + _TLIST_REMOVE(tc->parent->child, tc); + if (tc->parent->child) { + tc->parent->child->parent = tc->parent; + } + } else { + if (tc->prev) tc->prev->next = tc->next; + if (tc->next) tc->next->prev = tc->prev; + } + + tc->parent = tc->next = tc->prev = NULL; + return discard_const_p(void, ptr); + } + + new_tc = talloc_chunk_from_ptr(new_ctx); + + if (unlikely(tc == new_tc || tc->parent == new_tc)) { + return discard_const_p(void, ptr); + } + + if (tc->parent) { + _TLIST_REMOVE(tc->parent->child, tc); + if (tc->parent->child) { + tc->parent->child->parent = tc->parent; + } + } else { + if (tc->prev) tc->prev->next = tc->next; + if (tc->next) tc->next->prev = tc->prev; + } + + tc->parent = new_tc; + if (new_tc->child) new_tc->child->parent = NULL; + _TLIST_ADD(new_tc->child, tc); + + return discard_const_p(void, ptr); +} + + + +/* + remove a secondary reference to a pointer. This undo's what + talloc_reference() has done. The context and pointer arguments + must match those given to a talloc_reference() +*/ +static inline int talloc_unreference(const void *context, const void *ptr) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + struct talloc_reference_handle *h; + + if (unlikely(context == NULL)) { + context = null_context; + } + + for (h=tc->refs;h;h=h->next) { + struct talloc_chunk *p = talloc_parent_chunk(h); + if (p == NULL) { + if (context == NULL) break; + } else if (TC_PTR_FROM_CHUNK(p) == context) { + break; + } + } + if (h == NULL) { + return -1; + } + + return _talloc_free(h); +} + +/* + remove a specific parent context from a pointer. This is a more + controlled varient of talloc_free() +*/ +int talloc_unlink(const void *context, void *ptr) +{ + struct talloc_chunk *tc_p, *new_p; + void *new_parent; + + if (ptr == NULL) { + return -1; + } + + if (context == NULL) { + context = null_context; + } + + if (talloc_unreference(context, ptr) == 0) { + return 0; + } + + if (context == NULL) { + if (talloc_parent_chunk(ptr) != NULL) { + return -1; + } + } else { + if (talloc_chunk_from_ptr(context) != talloc_parent_chunk(ptr)) { + return -1; + } + } + + tc_p = talloc_chunk_from_ptr(ptr); + + if (tc_p->refs == NULL) { + return _talloc_free(ptr); + } + + new_p = talloc_parent_chunk(tc_p->refs); + if (new_p) { + new_parent = TC_PTR_FROM_CHUNK(new_p); + } else { + new_parent = NULL; + } + + if (talloc_unreference(new_parent, ptr) != 0) { + return -1; + } + + talloc_steal(new_parent, ptr); + + return 0; +} + +/* + add a name to an existing pointer - va_list version +*/ +static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); + +static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + tc->name = talloc_vasprintf(ptr, fmt, ap); + if (likely(tc->name)) { + _talloc_set_name_const(tc->name, ".name"); + } + return tc->name; +} + +/* + add a name to an existing pointer +*/ +const char *talloc_set_name(const void *ptr, const char *fmt, ...) +{ + const char *name; + va_list ap; + va_start(ap, fmt); + name = talloc_set_name_v(ptr, fmt, ap); + va_end(ap); + return name; +} + + +/* + create a named talloc pointer. Any talloc pointer can be named, and + talloc_named() operates just like talloc() except that it allows you + to name the pointer. +*/ +void *talloc_named(const void *context, size_t size, const char *fmt, ...) +{ + va_list ap; + void *ptr; + const char *name; + + ptr = __talloc(context, size); + if (unlikely(ptr == NULL)) return NULL; + + va_start(ap, fmt); + name = talloc_set_name_v(ptr, fmt, ap); + va_end(ap); + + if (unlikely(name == NULL)) { + _talloc_free(ptr); + return NULL; + } + + return ptr; +} + +/* + return the name of a talloc ptr, or "UNNAMED" +*/ +const char *talloc_get_name(const void *ptr) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + if (unlikely(tc->name == TALLOC_MAGIC_REFERENCE)) { + return ".reference"; + } + if (likely(tc->name)) { + return tc->name; + } + return "UNNAMED"; +} + + +/* + check if a pointer has the given name. If it does, return the pointer, + otherwise return NULL +*/ +void *talloc_check_name(const void *ptr, const char *name) +{ + const char *pname; + if (unlikely(ptr == NULL)) return NULL; + pname = talloc_get_name(ptr); + if (likely(pname == name || strcmp(pname, name) == 0)) { + return discard_const_p(void, ptr); + } + return NULL; +} + + +/* + this is for compatibility with older versions of talloc +*/ +void *talloc_init(const char *fmt, ...) +{ + va_list ap; + void *ptr; + const char *name; + + /* + * samba3 expects talloc_report_depth_cb(NULL, ...) + * reports all talloc'ed memory, so we need to enable + * null_tracking + */ + talloc_enable_null_tracking(); + + ptr = __talloc(NULL, 0); + if (unlikely(ptr == NULL)) return NULL; + + va_start(ap, fmt); + name = talloc_set_name_v(ptr, fmt, ap); + va_end(ap); + + if (unlikely(name == NULL)) { + _talloc_free(ptr); + return NULL; + } + + return ptr; +} + +/* + this is a replacement for the Samba3 talloc_destroy_pool functionality. It + should probably not be used in new code. It's in here to keep the talloc + code consistent across Samba 3 and 4. +*/ +void talloc_free_children(void *ptr) +{ + struct talloc_chunk *tc; + + if (unlikely(ptr == NULL)) { + return; + } + + tc = talloc_chunk_from_ptr(ptr); + + while (tc->child) { + /* we need to work out who will own an abandoned child + if it cannot be freed. In priority order, the first + choice is owner of any remaining reference to this + pointer, the second choice is our parent, and the + final choice is the null context. */ + void *child = TC_PTR_FROM_CHUNK(tc->child); + const void *new_parent = null_context; + if (unlikely(tc->child->refs)) { + struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs); + if (p) new_parent = TC_PTR_FROM_CHUNK(p); + } + if (unlikely(_talloc_free(child) == -1)) { + if (new_parent == null_context) { + struct talloc_chunk *p = talloc_parent_chunk(ptr); + if (p) new_parent = TC_PTR_FROM_CHUNK(p); + } + talloc_steal(new_parent, child); + } + } +} + +/* + Allocate a bit of memory as a child of an existing pointer +*/ +void *_talloc(const void *context, size_t size) +{ + return __talloc(context, size); +} + +/* + externally callable talloc_set_name_const() +*/ +void talloc_set_name_const(const void *ptr, const char *name) +{ + _talloc_set_name_const(ptr, name); +} + +/* + create a named talloc pointer. Any talloc pointer can be named, and + talloc_named() operates just like talloc() except that it allows you + to name the pointer. +*/ +void *talloc_named_const(const void *context, size_t size, const char *name) +{ + return _talloc_named_const(context, size, name); +} + +/* + free a talloc pointer. This also frees all child pointers of this + pointer recursively + + return 0 if the memory is actually freed, otherwise -1. The memory + will not be freed if the ref_count is > 1 or the destructor (if + any) returns non-zero +*/ +int talloc_free(void *ptr) +{ + return _talloc_free(ptr); +} + + + +/* + A talloc version of realloc. The context argument is only used if + ptr is NULL +*/ +void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name) +{ + struct talloc_chunk *tc; + void *new_ptr; + + /* size zero is equivalent to free() */ + if (unlikely(size == 0)) { + _talloc_free(ptr); + return NULL; + } + + if (unlikely(size >= MAX_TALLOC_SIZE)) { + return NULL; + } + + /* realloc(NULL) is equivalent to malloc() */ + if (ptr == NULL) { + return _talloc_named_const(context, size, name); + } + + tc = talloc_chunk_from_ptr(ptr); + + /* don't allow realloc on referenced pointers */ + if (unlikely(tc->refs)) { + return NULL; + } + + /* by resetting magic we catch users of the old memory */ + tc->flags |= TALLOC_FLAG_FREE; + +#if ALWAYS_REALLOC + new_ptr = malloc(size + TC_HDR_SIZE); + if (new_ptr) { + memcpy(new_ptr, tc, tc->size + TC_HDR_SIZE); + free(tc); + } +#else + new_ptr = realloc(tc, size + TC_HDR_SIZE); +#endif + if (unlikely(!new_ptr)) { + tc->flags &= ~TALLOC_FLAG_FREE; + return NULL; + } + + tc = (struct talloc_chunk *)new_ptr; + tc->flags &= ~TALLOC_FLAG_FREE; + if (tc->parent) { + tc->parent->child = tc; + } + if (tc->child) { + tc->child->parent = tc; + } + + if (tc->prev) { + tc->prev->next = tc; + } + if (tc->next) { + tc->next->prev = tc; + } + + tc->size = size; + _talloc_set_name_const(TC_PTR_FROM_CHUNK(tc), name); + + return TC_PTR_FROM_CHUNK(tc); +} + +/* + a wrapper around talloc_steal() for situations where you are moving a pointer + between two structures, and want the old pointer to be set to NULL +*/ +void *_talloc_move(const void *new_ctx, const void *_pptr) +{ + const void **pptr = discard_const_p(const void *,_pptr); + void *ret = _talloc_steal(new_ctx, *pptr); + (*pptr) = NULL; + return ret; +} + +/* + return the total size of a talloc pool (subtree) +*/ +size_t talloc_total_size(const void *ptr) +{ + size_t total = 0; + struct talloc_chunk *c, *tc; + + if (ptr == NULL) { + ptr = null_context; + } + if (ptr == NULL) { + return 0; + } + + tc = talloc_chunk_from_ptr(ptr); + + if (tc->flags & TALLOC_FLAG_LOOP) { + return 0; + } + + tc->flags |= TALLOC_FLAG_LOOP; + + total = tc->size; + for (c=tc->child;c;c=c->next) { + total += talloc_total_size(TC_PTR_FROM_CHUNK(c)); + } + + tc->flags &= ~TALLOC_FLAG_LOOP; + + return total; +} + +/* + return the total number of blocks in a talloc pool (subtree) +*/ +size_t talloc_total_blocks(const void *ptr) +{ + size_t total = 0; + struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr); + + if (tc->flags & TALLOC_FLAG_LOOP) { + return 0; + } + + tc->flags |= TALLOC_FLAG_LOOP; + + total++; + for (c=tc->child;c;c=c->next) { + total += talloc_total_blocks(TC_PTR_FROM_CHUNK(c)); + } + + tc->flags &= ~TALLOC_FLAG_LOOP; + + return total; +} + +/* + return the number of external references to a pointer +*/ +size_t talloc_reference_count(const void *ptr) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + struct talloc_reference_handle *h; + size_t ret = 0; + + for (h=tc->refs;h;h=h->next) { + ret++; + } + return ret; +} + +/* + report on memory usage by all children of a pointer, giving a full tree view +*/ +void talloc_report_depth_cb(const void *ptr, int depth, int max_depth, + void (*callback)(const void *ptr, + int depth, int max_depth, + int is_ref, + void *private_data), + void *private_data) +{ + struct talloc_chunk *c, *tc; + + if (ptr == NULL) { + ptr = null_context; + } + if (ptr == NULL) return; + + tc = talloc_chunk_from_ptr(ptr); + + if (tc->flags & TALLOC_FLAG_LOOP) { + return; + } + + callback(ptr, depth, max_depth, 0, private_data); + + if (max_depth >= 0 && depth >= max_depth) { + return; + } + + tc->flags |= TALLOC_FLAG_LOOP; + for (c=tc->child;c;c=c->next) { + if (c->name == TALLOC_MAGIC_REFERENCE) { + struct talloc_reference_handle *h = (struct talloc_reference_handle *)TC_PTR_FROM_CHUNK(c); + callback(h->ptr, depth + 1, max_depth, 1, private_data); + } else { + talloc_report_depth_cb(TC_PTR_FROM_CHUNK(c), depth + 1, max_depth, callback, private_data); + } + } + tc->flags &= ~TALLOC_FLAG_LOOP; +} + +static void talloc_report_depth_FILE_helper(const void *ptr, int depth, int max_depth, int is_ref, void *_f) +{ + const char *name = talloc_get_name(ptr); + FILE *f = (FILE *)_f; + + if (is_ref) { + fprintf(f, "%*sreference to: %s\n", depth*4, "", name); + return; + } + + if (depth == 0) { + fprintf(f,"%stalloc report on '%s' (total %6lu bytes in %3lu blocks)\n", + (max_depth < 0 ? "full " :""), name, + (unsigned long)talloc_total_size(ptr), + (unsigned long)talloc_total_blocks(ptr)); + return; + } + + fprintf(f, "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d) %p\n", + depth*4, "", + name, + (unsigned long)talloc_total_size(ptr), + (unsigned long)talloc_total_blocks(ptr), + (int)talloc_reference_count(ptr), ptr); + +#if 0 + fprintf(f, "content: "); + if (talloc_total_size(ptr)) { + int tot = talloc_total_size(ptr); + int i; + + for (i = 0; i < tot; i++) { + if ((((char *)ptr)[i] > 31) && (((char *)ptr)[i] < 126)) { + fprintf(f, "%c", ((char *)ptr)[i]); + } else { + fprintf(f, "~%02x", ((char *)ptr)[i]); + } + } + } + fprintf(f, "\n"); +#endif +} + +/* + report on memory usage by all children of a pointer, giving a full tree view +*/ +void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f) +{ + talloc_report_depth_cb(ptr, depth, max_depth, talloc_report_depth_FILE_helper, f); + fflush(f); +} + +/* + report on memory usage by all children of a pointer, giving a full tree view +*/ +void talloc_report_full(const void *ptr, FILE *f) +{ + talloc_report_depth_file(ptr, 0, -1, f); +} + +/* + report on memory usage by all children of a pointer +*/ +void talloc_report(const void *ptr, FILE *f) +{ + talloc_report_depth_file(ptr, 0, 1, f); +} + +/* + report on any memory hanging off the null context +*/ +static void talloc_report_null(void) +{ + if (talloc_total_size(null_context) != 0) { + talloc_report(null_context, stderr); + } +} + +/* + report on any memory hanging off the null context +*/ +static void talloc_report_null_full(void) +{ + if (talloc_total_size(null_context) != 0) { + talloc_report_full(null_context, stderr); + } +} + +/* + enable tracking of the NULL context +*/ +void talloc_enable_null_tracking(void) +{ + if (null_context == NULL) { + null_context = _talloc_named_const(NULL, 0, "null_context"); + } +} + +/* + disable tracking of the NULL context +*/ +void talloc_disable_null_tracking(void) +{ + _talloc_free(null_context); + null_context = NULL; +} + +/* + enable leak reporting on exit +*/ +void talloc_enable_leak_report(void) +{ + talloc_enable_null_tracking(); + atexit(talloc_report_null); +} + +/* + enable full leak reporting on exit +*/ +void talloc_enable_leak_report_full(void) +{ + talloc_enable_null_tracking(); + atexit(talloc_report_null_full); +} + +/* + talloc and zero memory. +*/ +void *_talloc_zero(const void *ctx, size_t size, const char *name) +{ + void *p = _talloc_named_const(ctx, size, name); + + if (p) { + memset(p, '\0', size); + } + + return p; +} + +/* + memdup with a talloc. +*/ +void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name) +{ + void *newp = _talloc_named_const(t, size, name); + + if (likely(newp)) { + memcpy(newp, p, size); + } + + return newp; +} + +static inline char *__talloc_strlendup(const void *t, const char *p, size_t len) +{ + char *ret; + + ret = (char *)__talloc(t, len + 1); + if (unlikely(!ret)) return NULL; + + memcpy(ret, p, len); + ret[len] = 0; + + _talloc_set_name_const(ret, ret); + return ret; +} + +/* + strdup with a talloc +*/ +char *talloc_strdup(const void *t, const char *p) +{ + if (unlikely(!p)) return NULL; + return __talloc_strlendup(t, p, strlen(p)); +} + +/* + strndup with a talloc +*/ +char *talloc_strndup(const void *t, const char *p, size_t n) +{ + if (unlikely(!p)) return NULL; + return __talloc_strlendup(t, p, strnlen(p, n)); +} + +static inline char *__talloc_strlendup_append(char *s, size_t slen, + const char *a, size_t alen) +{ + char *ret; + + ret = talloc_realloc(NULL, s, char, slen + alen + 1); + if (unlikely(!ret)) return NULL; + + /* append the string and the trailing \0 */ + memcpy(&ret[slen], a, alen); + ret[slen+alen] = 0; + + _talloc_set_name_const(ret, ret); + return ret; +} + +/* + * Appends at the end of the string. + */ +char *talloc_strdup_append(char *s, const char *a) +{ + if (unlikely(!s)) { + return talloc_strdup(NULL, a); + } + + if (unlikely(!a)) { + return s; + } + + return __talloc_strlendup_append(s, strlen(s), a, strlen(a)); +} + +/* + * Appends at the end of the talloc'ed buffer, + * not the end of the string. + */ +char *talloc_strdup_append_buffer(char *s, const char *a) +{ + size_t slen; + + if (unlikely(!s)) { + return talloc_strdup(NULL, a); + } + + if (unlikely(!a)) { + return s; + } + + slen = talloc_get_size(s); + if (likely(slen > 0)) { + slen--; + } + + return __talloc_strlendup_append(s, slen, a, strlen(a)); +} + +/* + * Appends at the end of the string. + */ +char *talloc_strndup_append(char *s, const char *a, size_t n) +{ + if (unlikely(!s)) { + return talloc_strdup(NULL, a); + } + + if (unlikely(!a)) { + return s; + } + + return __talloc_strlendup_append(s, strlen(s), a, strnlen(a, n)); +} + +/* + * Appends at the end of the talloc'ed buffer, + * not the end of the string. + */ +char *talloc_strndup_append_buffer(char *s, const char *a, size_t n) +{ + size_t slen; + + if (unlikely(!s)) { + return talloc_strdup(NULL, a); + } + + if (unlikely(!a)) { + return s; + } + + slen = talloc_get_size(s); + if (likely(slen > 0)) { + slen--; + } + + return __talloc_strlendup_append(s, slen, a, strnlen(a, n)); +} + +char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) +{ + int len; + char *ret; + va_list ap2; + char c; + + /* this call looks strange, but it makes it work on older solaris boxes */ + va_copy(ap2, ap); + len = vsnprintf(&c, 1, fmt, ap2); + va_end(ap2); + if (unlikely(len < 0)) { + return NULL; + } + + ret = (char *)__talloc(t, len+1); + if (unlikely(!ret)) return NULL; + + va_copy(ap2, ap); + vsnprintf(ret, len+1, fmt, ap2); + va_end(ap2); + + _talloc_set_name_const(ret, ret); + return ret; +} + + +/* + Perform string formatting, and return a pointer to newly allocated + memory holding the result, inside a memory pool. + */ +char *talloc_asprintf(const void *t, const char *fmt, ...) +{ + va_list ap; + char *ret; + + va_start(ap, fmt); + ret = talloc_vasprintf(t, fmt, ap); + va_end(ap); + return ret; +} + +static inline char *__talloc_vaslenprintf_append(char *s, size_t slen, + const char *fmt, va_list ap) + PRINTF_ATTRIBUTE(3,0); + +static inline char *__talloc_vaslenprintf_append(char *s, size_t slen, + const char *fmt, va_list ap) +{ + ssize_t alen; + va_list ap2; + char c; + + va_copy(ap2, ap); + alen = vsnprintf(&c, 1, fmt, ap2); + va_end(ap2); + + if (alen <= 0) { + /* Either the vsnprintf failed or the format resulted in + * no characters being formatted. In the former case, we + * ought to return NULL, in the latter we ought to return + * the original string. Most current callers of this + * function expect it to never return NULL. + */ + return s; + } + + s = talloc_realloc(NULL, s, char, slen + alen + 1); + if (!s) return NULL; + + va_copy(ap2, ap); + vsnprintf(s + slen, alen + 1, fmt, ap2); + va_end(ap2); + + _talloc_set_name_const(s, s); + return s; +} + +/** + * Realloc @p s to append the formatted result of @p fmt and @p ap, + * and return @p s, which may have moved. Good for gradually + * accumulating output into a string buffer. Appends at the end + * of the string. + **/ +char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) +{ + if (unlikely(!s)) { + return talloc_vasprintf(NULL, fmt, ap); + } + + return __talloc_vaslenprintf_append(s, strlen(s), fmt, ap); +} + +/** + * Realloc @p s to append the formatted result of @p fmt and @p ap, + * and return @p s, which may have moved. Always appends at the + * end of the talloc'ed buffer, not the end of the string. + **/ +char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap) +{ + size_t slen; + + if (unlikely(!s)) { + return talloc_vasprintf(NULL, fmt, ap); + } + + slen = talloc_get_size(s); + if (likely(slen > 0)) { + slen--; + } + + return __talloc_vaslenprintf_append(s, slen, fmt, ap); +} + +/* + Realloc @p s to append the formatted result of @p fmt and return @p + s, which may have moved. Good for gradually accumulating output + into a string buffer. + */ +char *talloc_asprintf_append(char *s, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + s = talloc_vasprintf_append(s, fmt, ap); + va_end(ap); + return s; +} + +/* + Realloc @p s to append the formatted result of @p fmt and return @p + s, which may have moved. Good for gradually accumulating output + into a buffer. + */ +char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + s = talloc_vasprintf_append_buffer(s, fmt, ap); + va_end(ap); + return s; +} + +/* + alloc an array, checking for integer overflow in the array size +*/ +void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name) +{ + if (count >= MAX_TALLOC_SIZE/el_size) { + return NULL; + } + return _talloc_named_const(ctx, el_size * count, name); +} + +/* + alloc an zero array, checking for integer overflow in the array size +*/ +void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name) +{ + if (count >= MAX_TALLOC_SIZE/el_size) { + return NULL; + } + return _talloc_zero(ctx, el_size * count, name); +} + +/* + realloc an array, checking for integer overflow in the array size +*/ +void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name) +{ + if (count >= MAX_TALLOC_SIZE/el_size) { + return NULL; + } + return _talloc_realloc(ctx, ptr, el_size * count, name); +} + +/* + a function version of talloc_realloc(), so it can be passed as a function pointer + to libraries that want a realloc function (a realloc function encapsulates + all the basic capabilities of an allocation library, which is why this is useful) +*/ +void *talloc_realloc_fn(const void *context, void *ptr, size_t size) +{ + return _talloc_realloc(context, ptr, size, NULL); +} + + +static int talloc_autofree_destructor(void *ptr) +{ + autofree_context = NULL; + return 0; +} + +static void talloc_autofree(void) +{ + _talloc_free(autofree_context); +} + +/* + return a context which will be auto-freed on exit + this is useful for reducing the noise in leak reports +*/ +void *talloc_autofree_context(void) +{ + if (autofree_context == NULL) { + autofree_context = _talloc_named_const(NULL, 0, "autofree_context"); + talloc_set_destructor(autofree_context, talloc_autofree_destructor); + atexit(talloc_autofree); + } + return autofree_context; +} + +size_t talloc_get_size(const void *context) +{ + struct talloc_chunk *tc; + + if (context == NULL) + return 0; + + tc = talloc_chunk_from_ptr(context); + + return tc->size; +} + +/* + find a parent of this context that has the given name, if any +*/ +void *talloc_find_parent_byname(const void *context, const char *name) +{ + struct talloc_chunk *tc; + + if (context == NULL) { + return NULL; + } + + tc = talloc_chunk_from_ptr(context); + while (tc) { + if (tc->name && strcmp(tc->name, name) == 0) { + return TC_PTR_FROM_CHUNK(tc); + } + while (tc && tc->prev) tc = tc->prev; + if (tc) { + tc = tc->parent; + } + } + return NULL; +} + +/* + show the parentage of a context +*/ +void talloc_show_parents(const void *context, FILE *file) +{ + struct talloc_chunk *tc; + + if (context == NULL) { + fprintf(file, "talloc no parents for NULL\n"); + return; + } + + tc = talloc_chunk_from_ptr(context); + fprintf(file, "talloc parents of '%s'\n", talloc_get_name(context)); + while (tc) { + fprintf(file, "\t'%s'\n", talloc_get_name(TC_PTR_FROM_CHUNK(tc))); + while (tc && tc->prev) tc = tc->prev; + if (tc) { + tc = tc->parent; + } + } + fflush(file); +} + +/* + return 1 if ptr is a parent of context +*/ +int talloc_is_parent(const void *context, const void *ptr) +{ + struct talloc_chunk *tc; + + if (context == NULL) { + return 0; + } + + tc = talloc_chunk_from_ptr(context); + while (tc) { + if (TC_PTR_FROM_CHUNK(tc) == ptr) return 1; + while (tc && tc->prev) tc = tc->prev; + if (tc) { + tc = tc->parent; + } + } + return 0; +} diff --git a/bin/talloc/talloc.h b/bin/talloc/talloc.h new file mode 100644 index 0000000..b8f7b22 --- /dev/null +++ b/bin/talloc/talloc.h @@ -0,0 +1,182 @@ +#ifndef _TALLOC_H_ +#define _TALLOC_H_ +/* + Unix SMB/CIFS implementation. + Samba temporary memory allocation functions + + Copyright (C) Andrew Tridgell 2004-2005 + Copyright (C) Stefan Metzmacher 2006 + + ** NOTE! The following LGPL license applies to the talloc + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . +*/ + +#include +#include +#include + +/* this is only needed for compatibility with the old talloc */ +typedef void TALLOC_CTX; + +/* + this uses a little trick to allow __LINE__ to be stringified +*/ +#ifndef __location__ +#define __TALLOC_STRING_LINE1__(s) #s +#define __TALLOC_STRING_LINE2__(s) __TALLOC_STRING_LINE1__(s) +#define __TALLOC_STRING_LINE3__ __TALLOC_STRING_LINE2__(__LINE__) +#define __location__ __FILE__ ":" __TALLOC_STRING_LINE3__ +#endif + +#ifndef TALLOC_DEPRECATED +#define TALLOC_DEPRECATED 0 +#endif + +#ifndef PRINTF_ATTRIBUTE +#if (__GNUC__ >= 3) +/** Use gcc attribute to check printf fns. a1 is the 1-based index of + * the parameter containing the format, and a2 the index of the first + * argument. Note that some gcc 2.x versions don't handle this + * properly **/ +#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2))) +#else +#define PRINTF_ATTRIBUTE(a1, a2) +#endif +#endif + +/* try to make talloc_set_destructor() and talloc_steal() type safe, + if we have a recent gcc */ +#if (__GNUC__ >= 3) +#define _TALLOC_TYPEOF(ptr) __typeof__(ptr) +#define talloc_set_destructor(ptr, function) \ + do { \ + int (*_talloc_destructor_fn)(_TALLOC_TYPEOF(ptr)) = (function); \ + _talloc_set_destructor((ptr), (int (*)(void *))_talloc_destructor_fn); \ + } while(0) +/* this extremely strange macro is to avoid some braindamaged warning + stupidity in gcc 4.1.x */ +#define talloc_steal(ctx, ptr) ({ _TALLOC_TYPEOF(ptr) __talloc_steal_ret = (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr)); __talloc_steal_ret; }) +#else +#define talloc_set_destructor(ptr, function) \ + _talloc_set_destructor((ptr), (int (*)(void *))(function)) +#define _TALLOC_TYPEOF(ptr) void * +#define talloc_steal(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr)) +#endif + +#define talloc_reference(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_reference((ctx),(ptr)) +#define talloc_move(ctx, ptr) (_TALLOC_TYPEOF(*(ptr)))_talloc_move((ctx),(void *)(ptr)) + +/* useful macros for creating type checked pointers */ +#define talloc(ctx, type) (type *)talloc_named_const(ctx, sizeof(type), #type) +#define talloc_size(ctx, size) talloc_named_const(ctx, size, __location__) +#define talloc_ptrtype(ctx, ptr) (_TALLOC_TYPEOF(ptr))talloc_size(ctx, sizeof(*(ptr))) + +#define talloc_new(ctx) talloc_named_const(ctx, 0, "talloc_new: " __location__) + +#define talloc_zero(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type) +#define talloc_zero_size(ctx, size) _talloc_zero(ctx, size, __location__) + +#define talloc_zero_array(ctx, type, count) (type *)_talloc_zero_array(ctx, sizeof(type), count, #type) +#define talloc_array(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type) +#define talloc_array_size(ctx, size, count) _talloc_array(ctx, size, count, __location__) +#define talloc_array_ptrtype(ctx, ptr, count) (_TALLOC_TYPEOF(ptr))talloc_array_size(ctx, sizeof(*(ptr)), count) + +#define talloc_realloc(ctx, p, type, count) (type *)_talloc_realloc_array(ctx, p, sizeof(type), count, #type) +#define talloc_realloc_size(ctx, ptr, size) _talloc_realloc(ctx, ptr, size, __location__) + +#define talloc_memdup(t, p, size) _talloc_memdup(t, p, size, __location__) + +#define talloc_set_type(ptr, type) talloc_set_name_const(ptr, #type) +#define talloc_get_type(ptr, type) (type *)talloc_check_name(ptr, #type) + +#define talloc_find_parent_bytype(ptr, type) (type *)talloc_find_parent_byname(ptr, #type) + +#if TALLOC_DEPRECATED +#define talloc_zero_p(ctx, type) talloc_zero(ctx, type) +#define talloc_p(ctx, type) talloc(ctx, type) +#define talloc_array_p(ctx, type, count) talloc_array(ctx, type, count) +#define talloc_realloc_p(ctx, p, type, count) talloc_realloc(ctx, p, type, count) +#define talloc_destroy(ctx) talloc_free(ctx) +#define talloc_append_string(c, s, a) (s?talloc_strdup_append(s,a):talloc_strdup(c, a)) +#endif + +/* The following definitions come from talloc.c */ +void *_talloc(const void *context, size_t size); +void _talloc_set_destructor(const void *ptr, int (*destructor)(void *)); +int talloc_increase_ref_count(const void *ptr); +size_t talloc_reference_count(const void *ptr); +void *_talloc_reference(const void *context, const void *ptr); +int talloc_unlink(const void *context, void *ptr); +const char *talloc_set_name(const void *ptr, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); +void talloc_set_name_const(const void *ptr, const char *name); +void *talloc_named(const void *context, size_t size, + const char *fmt, ...) PRINTF_ATTRIBUTE(3,4); +void *talloc_named_const(const void *context, size_t size, const char *name); +const char *talloc_get_name(const void *ptr); +void *talloc_check_name(const void *ptr, const char *name); +void *talloc_parent(const void *ptr); +const char *talloc_parent_name(const void *ptr); +void *talloc_init(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2); +int talloc_free(void *ptr); +void talloc_free_children(void *ptr); +void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name); +void *_talloc_steal(const void *new_ctx, const void *ptr); +void *_talloc_move(const void *new_ctx, const void *pptr); +size_t talloc_total_size(const void *ptr); +size_t talloc_total_blocks(const void *ptr); +void talloc_report_depth_cb(const void *ptr, int depth, int max_depth, + void (*callback)(const void *ptr, + int depth, int max_depth, + int is_ref, + void *private_data), + void *private_data); +void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f); +void talloc_report_full(const void *ptr, FILE *f); +void talloc_report(const void *ptr, FILE *f); +void talloc_enable_null_tracking(void); +void talloc_disable_null_tracking(void); +void talloc_enable_leak_report(void); +void talloc_enable_leak_report_full(void); +void *_talloc_zero(const void *ctx, size_t size, const char *name); +void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name); +void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name); +void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name); +void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name); +void *talloc_realloc_fn(const void *context, void *ptr, size_t size); +void *talloc_autofree_context(void); +size_t talloc_get_size(const void *ctx); +void *talloc_find_parent_byname(const void *ctx, const char *name); +void talloc_show_parents(const void *context, FILE *file); +int talloc_is_parent(const void *context, const void *ptr); + +char *talloc_strdup(const void *t, const char *p); +char *talloc_strdup_append(char *s, const char *a); +char *talloc_strdup_append_buffer(char *s, const char *a); + +char *talloc_strndup(const void *t, const char *p, size_t n); +char *talloc_strndup_append(char *s, const char *a, size_t n); +char *talloc_strndup_append_buffer(char *s, const char *a, size_t n); + +char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); +char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); +char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); + +char *talloc_asprintf(const void *t, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); +char *talloc_asprintf_append(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); +char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); + +#endif diff --git a/bin/test.c b/bin/test.c new file mode 100644 index 0000000..061ef74 --- /dev/null +++ b/bin/test.c @@ -0,0 +1,297 @@ +/* + * Testsuite for the Linux SPU filesystem + * + * Copyright (C) IBM Corporation, 2008 + * + * Author: Jeremy Kerr + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include + +#include "talloc/talloc.h" +#include "test.h" +#include "capabilities.h" + +/* give benchmarks more time to run */ +#define DEFAULT_TEST_TIMEOUT 10 +#define DEFAULT_BENCHMARK_TIMEOUT 60 + +#define streq(a,b) (!strcmp((a),(b))) + +struct test *test_create(void *ctx, char *path, int type) +{ + struct test *test; + + test = talloc(ctx, struct test); + test->type = type; + test->path = path; + talloc_steal(test, path); + + /* set defaults */ + test->expected_rc = 0; + test->disabled = 0; + test->caps_required = NULL; + test->n_caps_required = 0; + + test->output.buf = NULL; + test->output.len = 0; + test->output.bufsize = 0; + + test->run.stdout_fd = -1; + + test->result = TEST_NOTRUN; + + if (type == TEST_TYPE_BENCHMARK) + test->timeout = DEFAULT_BENCHMARK_TIMEOUT; + else + test->timeout = DEFAULT_TEST_TIMEOUT; + + return test; +} + +void test_add_required_cap(struct test *test, char *cap, int invert) +{ + int n; + + n = test->n_caps_required++; + test->caps_required = talloc_realloc(test, test->caps_required, + struct required_cap, test->n_caps_required); + test->caps_required[n].cap = cap; + test->caps_required[n].invert = invert; +} + +void test_header(struct test *test) +{ + printf("%-50s", test->path); + fflush(stdout); +} + +void test_pass(struct test *test) +{ + test->result = TEST_PASSED; + + if (test->type == TEST_TYPE_TEST) { + printf("PASS\n"); + } else { + int end; + char *result_time = talloc_strndup(test, test->output.buf, + test->output.len + 1); + + while (isspace(*result_time)) + result_time++; + + result_time[test->output.len] = '\0'; + + end = test->output.len - 1; + while (isspace(result_time[end])) { + result_time[end] = '\0'; + end --; + } + + printf("%s\n", result_time); + } +} + +void test_fail(struct test *test, char *fmt, ...) +{ + va_list ap; + char *msg; + + va_start(ap, fmt); + msg = talloc_vasprintf(NULL, fmt, ap); + va_end(ap); + + test->result = TEST_FAILED; + printf("FAIL (%s)\n", msg); + + if (test->output.len > 0) { + printf("test output:\n"); + fwrite(test->output.buf, sizeof(char), + test->output.len, stdout); + } + + talloc_free(msg); +} + +void test_skip(struct test *test, char *fmt, ...) +{ + va_list ap; + char *msg; + + va_start(ap, fmt); + msg = talloc_vasprintf(NULL, fmt, ap); + va_end(ap); + + test->result = TEST_SKIPPED; + printf("SKIPPED (%s)\n", msg); + + talloc_free(msg); +} + +static int is_param_str(char *str, char *param, char **value_str) +{ + int len = strlen(param); + if (strncmp(str, param, len)) + return 0; + + if (str[len] != '=') + return 0; + + /* store start of parameter value in value_str*/ + *value_str = str + len + 1; + + return 1; + +} + +static void handle_testconf_token(struct test *test, char *tok) +{ + char *val; + + /* handle flags */ + if (streq(tok, "disabled")) { + test->disabled = 1; + return; + } + + /* handle parameters */ + if (is_param_str(tok, "timeout", &val)) { + test->timeout = atoi(val); + return; + + } + + if (is_param_str(tok, "expected_rc", &val)) { + test->expected_rc = atoi(val); + return; + + } + + if (is_param_str(tok, "signal", &val)) { + test->expected_sig = atoi(val); + return; + + } + + /* handle capabilities */ + if (!strncmp(tok, "cap_", strlen("cap_"))) { + test_add_required_cap(test, tok + strlen("cap_"), 0); + return; + } + + if (!strncmp(tok, "!cap_", strlen("!cap_"))) { + test_add_required_cap(test, tok + strlen("!cap_"), 1); + return; + } + + fprintf(stderr, "Unknown test parameter %s\n", tok); +} + +void test_read_config(struct test *test) +{ + char *c, *dir, *file, *testconffile, *filepattern; + int fd, len, fp_len; + char buf[4096]; + + dir = talloc_strdup(NULL, test->path); + c = strrchr(dir, '/'); + if (!c) + return; + + *c = '\0'; + file = c + 1; + + testconffile = talloc_asprintf(dir, "%s/tests.conf", dir); + + fd = open(testconffile, O_RDONLY); + if (fd < 0) { + talloc_free(dir); + return; + } + + /* fixme: config files larger that 4096 bytes */ + len = read(fd, buf, sizeof(buf)); + + close(fd); + + c = buf; + filepattern = talloc_asprintf(dir, "%s:", file); + fp_len = strlen(filepattern); + + for (c = buf; c && *c;) { + char *newline; + + /* skip whitespace */ + while (isspace(*c)) + c++; + + newline = strchr(c, '\n'); + + /* if it's a comment, skip this line */ + if (*c == '#') { + + /* we only want lines begining with "file:" */ + } else if (!strncmp(c, filepattern, fp_len)) { + char *tok; + char *delim = " \t"; + + /* null-terminate this line */ + if (newline) + *newline = '\0'; + + c += fp_len; + + for (tok = strtok(c, delim); tok; + tok = strtok(NULL, delim)) + handle_testconf_token(test, tok); + } + + c = newline; + if (c) + c++; + } +} + +int test_should_run(struct test *test) +{ + int x; + + if (test->disabled) { + test_skip(test, "disabled"); + return 0; + } + + for (x = 0; x < test->n_caps_required; x++) { + struct required_cap *req_cap = &test->caps_required[x]; + + if (!!cap_available(req_cap->cap) == !!req_cap->invert) { + test_skip(test, "requires %s%s", + req_cap->invert ? "!" : "", + req_cap->cap); + test->result = TEST_SKIPPED; + return 0; + } + } + + return 1; +} + + diff --git a/bin/test.h b/bin/test.h new file mode 100644 index 0000000..2f5f791 --- /dev/null +++ b/bin/test.h @@ -0,0 +1,81 @@ +/* + * Testsuite for the Linux SPU filesystem + * + * Copyright (C) IBM Corporation, 2008 + * + * Author: Jeremy Kerr + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _TEST_H +#define _TEST_H + +#include "talloc/talloc.h" + +struct test { + enum { + TEST_TYPE_TEST, + TEST_TYPE_BENCHMARK + } type; + char *path; + int expected_rc; + int expected_sig; + int timeout; + int disabled; + + struct required_cap { + char *cap; + int invert; + } *caps_required; + int n_caps_required; + + struct { + int stdout_fd; + int pid; + char *path; + } run; + + struct { + char *buf; + int len; + int bufsize; + } output; + + enum { + TEST_NOTRUN, + TEST_PASSED, + TEST_FAILED, + TEST_SKIPPED + } result; +}; + +struct test *test_create(void *ctx, char *path, int type); + +void test_add_required_cap(struct test *test, char *cap, int invert); + +void test_header(struct test *test); + +void test_pass(struct test *test); + +void test_fail(struct test *test, char *fmt, ...); + +void test_skip(struct test *test, char *fmt, ...); + +void test_read_config(struct test *test); + +int test_should_run(struct test *test); + +#endif /* _TEST_H */ diff --git a/run-tests b/run-tests deleted file mode 100755 index 2dc4aa5..0000000 --- a/run-tests +++ /dev/null @@ -1,182 +0,0 @@ -#!/bin/bash -# -# Testsuite for the Linux SPU filesystem -# -# Copyright (C) IBM Corporation, 2007 -# -# Author: Jeremy Kerr -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -test_basedir=test-output.$(date +"%Y%m%d%H%M%S") -test_outfile=test.out - -function pass() { - echo "PASS" -} - -function fail() { - echo "FAIL ($1)" - - # show output, if there is any - if [ -s "$test_basedir/$test_outfile" ] - then - echo "test output:" - sed -e 's/^/> /' "$test_basedir/$test_outfile" - fi - - # abort if we weren't started with the -c option - if [ $cont -eq 0 ] - then - exit 1 - fi -} - -function skip() { - echo "SKIPPED ($1)" -} - -# functions to to some simple testing on kernel logs -dmesg_filter='grep Badness' -dmesg_file="$test_basedir/dmesg.orig" - -function check_dmesg() { - mv "$dmesg_file" "$dmesg_file.orig" - dmesg | $dmesg_filter > "$dmesg_file" - cmp -s "$dmesg_file.orig" "$dmesg_file" &>/dev/null -} -function init_dmesg() { - dmesg | $dmesg_filter > "$dmesg_file" -} - -# flag to continue on test failures -cont=0 -# run benchmarks ? -benchmark=0 - -while getopts cb opt; -do - case "$opt" in - c) - cont=1 - ;; - b) - benchmark=1 - ;; - esac - echo $opt -done - -shift $(($OPTIND - 1)) - -mkdir "$test_basedir" || exit 1 - -init_dmesg - -if [ ${#@} -gt 0 ] -then - t_dir="$@" -else - t_dir="tests" -fi -b_dir="benchmarks" - -find $t_dir -type f -perm /0111 -name '[0-9]*' | sort | -while read test -do - printf "%-45s" "${test#tests/}" - - testname=$(basename "$test") - testdir=$(dirname "$test") - testconfig="$testdir/tests.conf" - - # set default options - expected_rc=0 - needs_root=0 - disabled=0 - timeout=10 - - # read options for this test, if present - if [ -e $testconfig ] - then - sed_expr='s/^'"$testname"': \(.*\)/\1;/p' - eval $(sed -ne "$sed_expr" "$testconfig") - fi - - if [ $disabled -ne 0 ] - then - skip "disabled" - continue - fi - - if [ $needs_root -ne 0 ] && [ $UID -ne 0 ] - then - skip "needs root privs" - continue - fi - - test_wd="$test_basedir/$testdir" - rsync -a "$testdir" "$test_basedir/tests" - - # run the test in a new session - setsid sh -c "cd '$test_wd' && './$testname' \ - > '../../$test_outfile' 2>&1" & - testgrp=$! - - # send a SIGALRM after $timeout seconds - # trap 'exit 0 HUP' - setsid sh -c "trap 'exit 0' HUP; sleep $timeout; kill -ALRM -$testgrp" \ - >/dev/null 2>&1 & - alrmgrp=$! - - wait $testgrp - rc=$? - - kill -HUP -$alrmgrp 2>/dev/null - - if [ $rc -eq 142 -a $expected_rc -ne 142 ] - then - fail "timeout" - continue - fi - - if [ $rc -ne $expected_rc ] - then - fail "exit status. expected $expected_rc, got $rc" - continue - fi - - if ! check_dmesg - then - fail "dmesg" - continue - fi - - pass -done - -rc=$? - -[ "$rc" -ne 0 -o "$benchmark" -ne 1 ] && exit $rc - -echo '--- Running benchmarks' - -find $b_dir -type f -perm /0111 -name '[0-9]*' | sort | -while read benchmark; -do - printf "%-45s" "${benchmark#benchmarks/}" - - $benchmark -done diff --git a/tests/00-testsuite/06-cap.c b/tests/00-testsuite/06-cap.c new file mode 100644 index 0000000..59e8ee4 --- /dev/null +++ b/tests/00-testsuite/06-cap.c @@ -0,0 +1,27 @@ +/* + * Testsuite for the Linux SPU filesystem + * + * Copyright (C) IBM Corporation, 2007 + * + * Author: Jeremy Kerr + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include + +int main(void) +{ + return EXIT_FAILURE; +} diff --git a/tests/00-testsuite/07-nocap.c b/tests/00-testsuite/07-nocap.c new file mode 100644 index 0000000..59e8ee4 --- /dev/null +++ b/tests/00-testsuite/07-nocap.c @@ -0,0 +1,27 @@ +/* + * Testsuite for the Linux SPU filesystem + * + * Copyright (C) IBM Corporation, 2007 + * + * Author: Jeremy Kerr + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include + +int main(void) +{ + return EXIT_FAILURE; +} diff --git a/tests/00-testsuite/08-signal.c b/tests/00-testsuite/08-signal.c new file mode 100644 index 0000000..9cd1985 --- /dev/null +++ b/tests/00-testsuite/08-signal.c @@ -0,0 +1,32 @@ +/* + * Testsuite for the Linux SPU filesystem + * + * Copyright (C) IBM Corporation, 2008 + * + * Author: Jeremy Kerr + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include + +int main(void) +{ + kill(getpid(), SIGTERM); + return EXIT_FAILURE; +} diff --git a/tests/00-testsuite/Makefile b/tests/00-testsuite/Makefile index 0acdab3..8ad0561 100644 --- a/tests/00-testsuite/Makefile +++ b/tests/00-testsuite/Makefile @@ -19,6 +19,6 @@ # Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -obj += 00-pass 01-fail 02-root 03-skip +obj += 00-pass 01-fail 02-root 03-skip 06-cap 07-nocap 08-signal diff --git a/tests/00-testsuite/tests.conf b/tests/00-testsuite/tests.conf index e44e4e8..402f101 100644 --- a/tests/00-testsuite/tests.conf +++ b/tests/00-testsuite/tests.conf @@ -1,3 +1,6 @@ 01-fail: expected_rc=1 -02-root: needs_root=1 -03-skip: disabled=1 +02-root: cap_root +03-skip: disabled +06-cap: cap_never +07-nocap: !cap_always +08-signal: signal=15 diff --git a/tests/01-mount/tests.conf b/tests/01-mount/tests.conf index 05fb678..1599460 100644 --- a/tests/01-mount/tests.conf +++ b/tests/01-mount/tests.conf @@ -1,2 +1,2 @@ -01-mount.sh: needs_root=1 -02-mount-options.sh: needs_root=1 +01-mount.sh: cap_root +02-mount-options.sh: cap_root diff --git a/tests/02-spu_create/tests.conf b/tests/02-spu_create/tests.conf index e2761b6..4e86301 100644 --- a/tests/02-spu_create/tests.conf +++ b/tests/02-spu_create/tests.conf @@ -1 +1 @@ -06-isolation-unavailable: needs_root=1 +06-isolation-unavailable: cap_root diff --git a/tests/07-lsmap/tests.conf b/tests/07-lsmap/tests.conf index 4250ed0..8082d3f 100644 --- a/tests/07-lsmap/tests.conf +++ b/tests/07-lsmap/tests.conf @@ -1,3 +1,3 @@ -05-size: disabled=1 -06-seek-end: disabled=1 -07-seek-cur: disabled=1 +05-size: disabled +06-seek-end: disabled +07-seek-cur: disabled diff --git a/tests/11-coredump/01-coredump-foffset-bug.sh b/tests/11-coredump/01-coredump-foffset-bug.sh index 1e36d41..83457b7 100755 --- a/tests/11-coredump/01-coredump-foffset-bug.sh +++ b/tests/11-coredump/01-coredump-foffset-bug.sh @@ -22,7 +22,7 @@ ulimit -c unlimited -pid=$(./spu-coredump) +pid=$(spu-coredump) corefile="core.$pid" @@ -31,7 +31,7 @@ if [[ ! -f $corefile ]]; then exit 2 fi -./parse-core --find-exec "$corefile" +parse-core --find-exec "$corefile" rc=$? rm -f $corefile diff --git a/tests/11-coredump/02-note-contents.sh b/tests/11-coredump/02-note-contents.sh index 8208fe7..71b0ef4 100755 --- a/tests/11-coredump/02-note-contents.sh +++ b/tests/11-coredump/02-note-contents.sh @@ -22,7 +22,7 @@ ulimit -c unlimited -pid=$(./spu-coredump) +pid=$(spu-coredump) corefile="core.$pid" @@ -31,7 +31,7 @@ if [[ ! -f $corefile ]]; then exit 2 fi -./parse-core --check-notes $corefile +parse-core --check-notes $corefile rc=$? rm -f $corefile diff --git a/tests/11-coredump/03-non-spu-coredump.sh b/tests/11-coredump/03-non-spu-coredump.sh index 8debb15..5c5dc83 100755 --- a/tests/11-coredump/03-non-spu-coredump.sh +++ b/tests/11-coredump/03-non-spu-coredump.sh @@ -27,7 +27,7 @@ corelist="" function get_note_size { - pid=$(./coredump) + pid=$(coredump) corefile="core.$pid" diff --git a/tests/11-coredump/tests.conf b/tests/11-coredump/tests.conf index f5b28bd..aacfedd 100644 --- a/tests/11-coredump/tests.conf +++ b/tests/11-coredump/tests.conf @@ -1,3 +1,3 @@ 01-coredump-foffset-bug.sh: timeout=30 02-note-contents.sh: timeout=30 -03-non-spu-coredump.sh: timeout=30 needs_root=1 +03-non-spu-coredump.sh: timeout=30 cap_root diff --git a/tests/12-nosched/tests.conf b/tests/12-nosched/tests.conf index fc19717..90c654c 100644 --- a/tests/12-nosched/tests.conf +++ b/tests/12-nosched/tests.conf @@ -1,2 +1,2 @@ -01-nosched-create: needs_root=1 -02-nosched-create-run: needs_root=1 +01-nosched-create: cap_root +02-nosched-create-run: cap_root diff --git a/tests/20-scheduler/01-sched-multiple.sh b/tests/20-scheduler/01-sched-multiple.sh index 667855b..14730ad 100755 --- a/tests/20-scheduler/01-sched-multiple.sh +++ b/tests/20-scheduler/01-sched-multiple.sh @@ -29,6 +29,6 @@ tests="1 2 $(($n_spes - 1)) $n_spes $(($n_spes + 1)) $((n_spes * 2))" for i in $tests do - ./gen_datafile.py run.data $i 1024 - ./sched_multiple run.data + gen_datafile.py run.data $i 1024 + sched_multiple run.data done