Skip to content
Snippets Groups Projects
it_test.c 16.82 KiB
/* vi:set ts=8 sw=8 expandtab: */
/* Unit test tool for interval tree.
 * Written by jay <jxiong@clusterfs.com> 
 */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>

#include <libcfs/kp30.h>
#include <../ldlm/interval_tree.c>

#define dprintf(fmt, args...) //printf(fmt, ##args)
#define error(fmt, args...) do {                        \
        fflush(stdout), fflush(stderr);                 \
        fprintf(stderr, "\nError:" fmt, ##args);        \
        abort();                                        \
} while(0)

#define __F(ext)         (ext)->start, (ext)->end
#define __S              "["LPX64":"LPX64"]"

#define ALIGN_SIZE       4096
#define ALIGN_MASK       (~(ALIGN_SIZE - 1))

static struct it_node {
        struct interval_node node;
        struct list_head list;
        int hit, valid;
} *it_array;
static int it_count;
CFS_LIST_HEAD(header);
static unsigned long max_count = ULONG_MAX & ALIGN_MASK;
static int have_wide_lock = 0;

static void it_test_clear(void)
{
        int i = 0;
        for (i = 0; i < it_count; i++)
                it_array[i].hit = 0;
}

static enum interval_iter cb(struct interval_node *n, void *args)
{
        struct it_node *node = (struct it_node *)n;
        static int count = 1;

        if (node->hit == 1) {
                dprintf("NODE "__S" has ever been accessed\n",
                        __F(&n->in_extent));
                return INTERVAL_ITER_CONT;
                error("duplicate node accessing found\n");
                return INTERVAL_ITER_STOP;
        }
        
        if (node->valid == 0) {
                error("A deleted node "__S" being accessed\n",
                       __F(&n->in_extent));
                return INTERVAL_ITER_STOP;
        }

        if (count++ == 8) {
                dprintf("\n");
                count = 1;
        }
        dprintf(""__S" ", __F(&n->in_extent));
        fflush(stdout);

        node->hit = 1;
        return INTERVAL_ITER_CONT;
}

static int it_test_search(struct interval_node *root)
{
        struct it_node *n;
        struct interval_node_extent ext;
        int times = 10, i, err = 0;

        while (times--) {
                it_test_clear();
                ext.start = (random() % max_count) & ALIGN_MASK;
                ext.end = random() % (max_count - ext.start + 2) + ext.start;
                ext.end &= ALIGN_MASK;
                if (ext.end > max_count)
                        ext.end = max_count;

                dprintf("\n\nSearching the node overlapped "__S" ..\n",
                        __F(&ext));

                interval_search(root, &ext, cb, NULL);

                dprintf("\nverifing ...");
        
                /* verify */
                for (i = 0; i < it_count; i++) {
                        n = &it_array[i];
                        if (n->valid == 0)
                                continue;

                        if (extent_overlapped(&ext, &n->node.in_extent) && 
                            n->hit == 0)
                                error("node "__S" overlaps" __S","
                                      "but never to be hit.\n", 
                                      __F(&n->node.in_extent),
                                      __F(&ext));

                        if (!extent_overlapped(&ext, &n->node.in_extent) && 
                            n->hit)
                                error("node "__S" overlaps" __S", but hit.\n", 
                                      __F(&n->node.in_extent),
                                      __F(&ext));
                }
                if (err) error("search error\n");
                dprintf("ok.\n");
        }

        return 0;
}

static int it_test_iterate(struct interval_node *root)
{
        int i;

        dprintf("\n\nIterate testing start..\n");

        it_test_clear();
        interval_iterate(root, cb, NULL);

        /* verify */
        for (i = 0; i < it_count; i++) {
                if (it_array[i].valid == 0)
                        continue;
                if (it_array[i].hit == 0)
                        error("Node "__S" is not accessed\n",
                              __F(&it_array[i].node.in_extent));
        }

        return 0;
}
static int it_test_iterate_reverse(struct interval_node *root)
{
        int i;

        dprintf("\n\niterate reverse testing start..\n");
        it_test_clear();
        interval_iterate_reverse(root, cb, NULL);

        /* verify */
        for (i = 0; i < it_count; i++) {
                if (it_array[i].valid == 0)
                        continue;
                if (it_array[i].hit == 0)
                        error("Not every extent is accessed\n");
        }

        return 0;
}

static int it_test_find(struct interval_node *root)
{
        int idx;
        struct interval_node_extent *ext;

        dprintf("\ninterval_find testing start ..\n");
        for (idx = 0; idx < it_count; idx++) {
                if (it_array[idx].valid == 0)
                        continue;

                ext = &it_array[idx].node.in_extent;
                dprintf("Try to find "__S"\n", __F(ext));
                if (!interval_find(root, ext))
                        error("interval_find, try to find "__S"\n", __F(ext));
        }
        return 0;
}

/* sanity test is tightly coupled with implementation, so when you changed
 * the interval tree implementation, change this code also. */
static enum interval_iter sanity_cb(struct interval_node *node, void *args)
{
        __u64 max_high = node->in_max_high;
        struct interval_node *tmp, *parent;
        int left = 1, has = 0, nr = 0;

        parent = node->in_parent;
        node->in_parent = NULL;
        interval_for_each(tmp, node) {
                if ((left && node_compare(tmp, node) > 0) ||
                    (!left && node_compare(tmp, node) < 0))
                        error("interval tree sanity test\n");

                if (tmp->in_max_high > max_high) {
                        dprintf("max high sanity check, max_high is %llu,"
                                "child max_high: %llu"__S"\n",
                                max_high, tmp->in_max_high,
                                __F(&tmp->in_extent));
                        goto err;
                } else if (tmp->in_max_high == max_high) {
                        has = 1;
                }

                if (tmp == node) {
                        left = 0;
                        continue;
                }
        }

        if (!has) {
                int count = 1;
err:
                dprintf("node"__S":%llu Child list:\n",
                        node->in_extent.start,
                        node->in_extent.end,
                        node->in_max_high);

                interval_for_each(tmp, node) {
                        dprintf(""__S":%llu ",
                                __F(&tmp->in_extent),
                                tmp->in_max_high);
                        if (count++ == 8) {
                                dprintf("\n");
                                count = 1;
                        }
                }

                error("max high sanity check, has == %d\n", has);
        }
        node->in_parent = parent;

        tmp = node;
        while (tmp) {
                if (node_is_black(tmp))
                        nr++;
                else if ((tmp->in_left && node_is_red(tmp->in_left)) ||
                         (tmp->in_right && node_is_red(tmp->in_right)))
                        error("wrong tree, a red node has red child\n");
                tmp = tmp->in_left;
        }

        tmp = node;
        while (tmp) {
                if (node_is_black(tmp))
                        nr--;
                tmp = tmp->in_right;
        }
        if (nr)
                error("wrong tree, unbalanced!\n");
        
        return 0;
}

static int it_test_sanity(struct interval_node *root)
{
        it_test_clear();
        interval_iterate(root, sanity_cb, NULL);
        return 0;
}

static int it_test_search_hole(struct interval_node *root)
{
        int i, count = 10;
        struct interval_node_extent ext, ext2;
        struct it_node *n;
        __u64 low = 0, high = ~0;

        do {
                if (--count == 0)
                        return 0;

                ext.start = random() % max_count;
                ext.end = ext.start;
        } while (interval_is_overlapped(root, &ext));
        ext2 = ext;

        interval_expand(root, &ext, NULL);
        dprintf("Extending "__S" to .."__S"\n", __F(&ext2), __F(&ext));
        for (i = 0; i < it_count; i++) {
                n = &it_array[i];
                if (n->valid == 0)
                        continue;

                if (extent_overlapped(&ext, &n->node.in_extent)) {
                        error("Extending "__S" to .."__S" overlaps node"__S"\n",
                                __F(&ext2), __F(&ext), __F(&n->node.in_extent));
                }

                if (n->node.in_extent.end < ext2.start)
                        low = max_u64(n->node.in_extent.end + 1, low);

                if (n->node.in_extent.start > ext2.end)
                        high = min_u64(n->node.in_extent.start - 1, high);
        }

        /* only expanding high right now */
        if (ext2.start != ext.start || high != ext.end) {
                ext2.start = low, ext2.end = high;
                error("Real extending result:"__S", expected:"__S"\n",
                       __F(&ext), __F(&ext2));
        }

        return 0;
}

static int contended_count = 0; 
#define LOOP_COUNT 1000
static enum interval_iter perf_cb(struct interval_node *n, void *args)
{
        unsigned long count = LOOP_COUNT;
        while (count--);
        contended_count++;
        return INTERVAL_ITER_CONT;
}

static inline long tv_delta(struct timeval *s, struct timeval *e)
{
        long c = e->tv_sec - s->tv_sec;
        c *= 1000;
        c += (long int)(e->tv_usec - s->tv_usec) / 1000;
        dprintf("\tStart: %lu:%lu -> End: %lu:%lu\n", 
                s->tv_sec, s->tv_usec, e->tv_sec, e->tv_usec);
        return c;
}

static int it_test_performance(struct interval_node *root, unsigned long len)
{
        int i = 0, interval_time, list_time;
        struct interval_node_extent ext;
        struct it_node *n;
        struct timeval start, end;
        unsigned long count;
        
        ext.start = (random() % (max_count - len)) & ALIGN_MASK;
        ext.end = (ext.start + len) & ALIGN_MASK;
        if (have_wide_lock) {
                ext.start = (max_count - len) & ALIGN_MASK;
                ext.end = max_count;
        }

        dprintf("Extent search"__S"\n", __F(&ext));

        /* list */
        contended_count = 0;
        gettimeofday(&start, NULL);
        list_for_each_entry(n, &header, list) {
                if (extent_overlapped(&ext, &n->node.in_extent)) {
                        count = LOOP_COUNT;
                        while (count--);
                        contended_count++;
                }
        }
        gettimeofday(&end, NULL);
        list_time = tv_delta(&start, &end);
        i = contended_count;

        /* interval */
        contended_count = 0;
        gettimeofday(&start, NULL);
        interval_search(root, &ext, perf_cb, &contended_count);
        gettimeofday(&end, NULL);
        interval_time = tv_delta(&start, &end);

        if (i != contended_count)
                error("count of contended lock don't match(%d: %d)\n",
                      i, contended_count);

        printf("\tList vs Int. search: \n\t\t"
               "(%d vs %d)ms, %d contended lock.\n",
                list_time, interval_time, contended_count);

        return 0;
}

static struct interval_node *it_test_helper(struct interval_node *root)
{
        int idx, count = 0;
        struct it_node *n;

        count = random() % it_count;
        while (count--) {
                idx = random() % it_count;
                n = &it_array[idx];
                if (n->valid) {
                        if (!interval_find(root, &n->node.in_extent))
                                error("Cannot find an existent node\n");
                        dprintf("Erasing a node "__S"\n", 
                                __F(&n->node.in_extent));
                        interval_erase(&n->node, &root);
                        n->valid = 0;
                        list_del_init(&n->list);
                } else {
                        __u64 low, high;
                        low = (random() % max_count) & ALIGN_MASK;
                        high = ((random() % max_count + 1) & ALIGN_MASK) + low;
                        if (high > max_count)
                                high = max_count;
                        interval_set(&n->node, low, high);
                        while (interval_insert(&n->node, &root))
                                interval_set(&n->node, low, ++high);
                        dprintf("Adding a node "__S"\n", 
                                __F(&n->node.in_extent));
                        n->valid = 1;
                        list_add(&n->list, &header);
                }
        }

        return root;
}

static struct interval_node *it_test_init(int count)
{
        int i;
        uint64_t high, low, len;
        struct it_node *n;
        struct interval_node *root = NULL;

        it_count = count;
        it_array = (struct it_node *)malloc(sizeof(struct it_node) * count);
        if (it_array == NULL)
                error("it_array == NULL, no memory\n");

        have_wide_lock = 0;
        for (i = 0; i < count; i++) {
                n = &it_array[i];
                do {
                        low = (random() % max_count + 1) & ALIGN_MASK;
                        len = (random() % 256 + 1) * ALIGN_SIZE;
                        if (!have_wide_lock && !(random() % count)) {
                                low = 0;
                                len = max_count;
                                have_wide_lock = 1;
                        }
                        high = low + (len & ALIGN_MASK);

                        interval_set(&n->node, low, high);
                } while (interval_insert(&n->node, &root));
                n->hit = 0;
                n->valid = 1;
                if (i == 0)
                        list_add_tail(&n->list, &header);
                else
                        list_add_tail(&n->list, &it_array[rand()%i].list);
        }

        return root;
}

static void it_test_fini(void)
{
        free(it_array);
        it_array = NULL;
        it_count = 0;
        max_count = 0;
}

int main(int argc, char *argv[])
{
        int count = 5, perf = 0;
        struct interval_node *root;
        struct timeval tv;

        gettimeofday(&tv, NULL);
        srandom(tv.tv_usec);

        if (argc == 2) {
                if (strcmp(argv[1], "-p"))
                        error("Unknow options, usage: %s [-p]\n", argv[0]);
                perf = 1;
                count = 1;
        }

        if (perf) {
                int M = 1024 * 1024;
                root = it_test_init(1000000);
                printf("1M locks with 4K request size\n");
                it_test_performance(root, 4096);
                printf("1M locks with 128K request size\n");
                it_test_performance(root, 128 * 1024);
                printf("1M locks with 256K request size\n");
                it_test_performance(root, 256 * 1024);
                printf("1M locks with 1M request size\n");
                it_test_performance(root, 1 * M);
                printf("1M locks with 16M request size\n");
                it_test_performance(root, 16 * M);
                printf("1M locks with 32M request size\n");
                it_test_performance(root, 32 * M);
                printf("1M locks with 64M request size\n");
                it_test_performance(root, 64 * M);
                printf("1M locks with 128M request size\n");
                it_test_performance(root, 128 * M);
                printf("1M locks with 256M request size\n");
                it_test_performance(root, 256 * M);
                printf("1M locks with 512M request size\n");
                it_test_performance(root, 512 * M);
                printf("1M locks with 1G request size\n");
                it_test_performance(root, 1024 * M);
                printf("1M locks with 2G request size\n");
                it_test_performance(root, 2048 * M);
                printf("1M locks with 3G request size\n");
                it_test_performance(root, 3072 * M);
                printf("1M locks with 4G request size\n");
                it_test_performance(root, max_count - 1);
                it_test_fini();
                return 0;
        }

        root = it_test_init(random() % 100000 + 1000);
        while (count--) {
                it_test_sanity(root);
                it_test_iterate(root);
                it_test_iterate_reverse(root);
                it_test_find(root);
                it_test_search_hole(root);
                it_test_search(root);
                root = it_test_helper(root);
        }
        it_test_fini();

        return 0;
}