reenabled interval tree tests

This commit is contained in:
Anthony Pesch 2016-12-17 14:27:54 -08:00
parent ac6bd3a677
commit 292ce8c38d
8 changed files with 328 additions and 286 deletions

View File

@ -408,9 +408,9 @@ if(BUILD_TESTS)
## build test binary
set(RETEST_SOURCES
test/test_dead_code_elimination_pass.c
test/test_interval_tree.c
test/test_list.c
test/test_load_store_elimination_pass.c
#test/test_interval_tree.cc
#test/test_minmax_heap.cc
#test/test_sh4.cc
#${asm_inc}

View File

@ -15,58 +15,56 @@ struct rb_callbacks interval_tree_cb = {
&interval_tree_augment_rotate,
};
static interval_type_t interval_tree_node_max(struct interval_node *n) {
static interval_type_t interval_node_max(struct interval_node *n) {
return n ? n->max : 0;
}
static int interval_tree_node_size(struct interval_node *n) {
static int interval_node_size(struct interval_node *n) {
return n ? n->size : 0;
}
static int interval_tree_node_height(struct interval_node *n) {
static int interval_node_height(struct interval_node *n) {
return n ? n->height : 0;
}
static void interval_tree_fix_counts(struct interval_node *n) {
static void interval_node_fix_counts(struct interval_node *n) {
if (!n) {
return;
}
struct interval_node *l = INTERVAL_NODE(n->base.left);
struct interval_node *r = INTERVAL_NODE(n->base.right);
struct interval_node *l = INTERVAL_NODE(n->rb.left);
struct interval_node *r = INTERVAL_NODE(n->rb.right);
n->size = 1 + interval_tree_node_size(l) + interval_tree_node_size(r);
n->height =
1 + MAX(interval_tree_node_height(l), interval_tree_node_height(r));
n->max =
MAX(MAX(n->high, interval_tree_node_max(l)), interval_tree_node_max(r));
n->size = 1 + interval_node_size(l) + interval_node_size(r);
n->height = 1 + MAX(interval_node_height(l), interval_node_height(r));
n->max = MAX(MAX(n->high, interval_node_max(l)), interval_node_max(r));
}
static void interval_tree_augment_propagate(struct rb_tree *t,
struct rb_node *rb_n) {
struct interval_node *n = rb_entry(rb_n, struct interval_node, base);
struct interval_node *n = rb_entry(rb_n, struct interval_node, rb);
while (n) {
interval_tree_fix_counts(n);
n = INTERVAL_NODE(n->base.parent);
interval_node_fix_counts(n);
n = INTERVAL_NODE(n->rb.parent);
}
}
static void interval_tree_augment_rotate(struct rb_tree *t,
struct rb_node *rb_oldn,
struct rb_node *rb_newn) {
struct interval_node *oldn = rb_entry(rb_oldn, struct interval_node, base);
struct interval_node *newn = rb_entry(rb_newn, struct interval_node, base);
struct interval_node *oldn = rb_entry(rb_oldn, struct interval_node, rb);
struct interval_node *newn = rb_entry(rb_newn, struct interval_node, rb);
interval_tree_fix_counts(oldn);
interval_tree_fix_counts(newn);
interval_tree_fix_counts(INTERVAL_NODE(newn->base.parent));
interval_node_fix_counts(oldn);
interval_node_fix_counts(newn);
interval_node_fix_counts(INTERVAL_NODE(newn->rb.parent));
}
static int interval_tree_cmp(const struct rb_node *rb_lhs,
const struct rb_node *rb_rhs) {
struct interval_node *lhs = rb_entry(rb_lhs, struct interval_node, base);
struct interval_node *rhs = rb_entry(rb_rhs, struct interval_node, base);
struct interval_node *lhs = rb_entry(rb_lhs, struct interval_node, rb);
struct interval_node *rhs = rb_entry(rb_rhs, struct interval_node, rb);
if (lhs->low < rhs->low) {
return -1;
@ -86,74 +84,19 @@ static int interval_tree_intersects(const struct interval_node *n,
return high >= n->low && n->high >= low;
}
static struct interval_node *interval_tree_min_interval(struct interval_node *n,
interval_type_t low,
interval_type_t high) {
struct interval_node *min = NULL;
while (n) {
int intersects = interval_tree_intersects(n, low, high);
if (intersects) {
min = n;
}
// if n->left->max < low, there is no match in the left subtree, there
// could be one in the right
if (!n->base.left || INTERVAL_NODE(n->base.left)->max < low) {
// don't go right if the current node intersected
if (intersects) {
break;
}
n = INTERVAL_NODE(n->base.right);
}
// else if n->left-max >= low, there could be one in the left subtree. if
// there isn't one in the left, there wouldn't be one in the right
else {
n = INTERVAL_NODE(n->base.left);
}
}
return min;
struct interval_node *interval_tree_iter_next(struct interval_tree_it *it) {
it->n = interval_tree_next_interval(it->n, it->low, it->high);
return it->n;
}
static struct interval_node *interval_tree_next_interval(
struct interval_node *n, interval_type_t low, interval_type_t high) {
while (n) {
// try to find the minimum node in the right subtree
if (n->base.right) {
struct interval_node *min =
interval_tree_min_interval(INTERVAL_NODE(n->base.right), low, high);
if (min) {
return min;
}
}
// else, move up the tree until a left child link is traversed
struct interval_node *c = n;
n = INTERVAL_NODE(n->base.parent);
while (n && INTERVAL_NODE(n->base.right) == c) {
c = n;
n = INTERVAL_NODE(n->base.parent);
}
if (n && interval_tree_intersects(n, low, high)) {
return n;
}
}
return NULL;
}
void interval_tree_insert(struct rb_tree *t, struct interval_node *n) {
rb_insert(t, RB_NODE(n), &interval_tree_cb);
}
void interval_tree_remove(struct rb_tree *t, struct interval_node *n) {
rb_unlink(t, RB_NODE(n), &interval_tree_cb);
}
void interval_tree_clear(struct rb_tree *t) {
t->root = NULL;
struct interval_node *interval_tree_iter_first(struct rb_tree *t,
interval_type_t low,
interval_type_t high,
struct interval_tree_it *it) {
it->low = low;
it->high = high;
it->n = interval_tree_min_interval(INTERVAL_NODE(t->root), low, high);
return it->n;
}
struct interval_node *interval_tree_find(struct rb_tree *t, interval_type_t low,
@ -161,8 +104,8 @@ struct interval_node *interval_tree_find(struct rb_tree *t, interval_type_t low,
struct interval_node *n = INTERVAL_NODE(t->root);
while (n) {
struct interval_node *l = INTERVAL_NODE(n->base.left);
struct interval_node *r = INTERVAL_NODE(n->base.right);
struct interval_node *l = INTERVAL_NODE(n->rb.left);
struct interval_node *r = INTERVAL_NODE(n->rb.right);
if (interval_tree_intersects(n, low, high)) {
return n;
@ -176,17 +119,88 @@ struct interval_node *interval_tree_find(struct rb_tree *t, interval_type_t low,
return NULL;
}
struct interval_node *interval_tree_iter_first(struct rb_tree *t,
interval_type_t low,
interval_type_t high,
struct interval_tree_it *it) {
it->low = low;
it->high = high;
it->n = interval_tree_min_interval(INTERVAL_NODE(t->root), low, high);
return it->n;
struct interval_node *interval_tree_next_interval(struct interval_node *n,
interval_type_t low,
interval_type_t high) {
while (n) {
/* try to find the minimum node in the right subtree */
if (n->rb.right) {
struct interval_node *min =
interval_tree_min_interval(INTERVAL_NODE(n->rb.right), low, high);
if (min) {
return min;
}
}
/* else, move up the tree until a left child link is traversed */
struct interval_node *c = n;
n = INTERVAL_NODE(n->rb.parent);
while (n && INTERVAL_NODE(n->rb.right) == c) {
c = n;
n = INTERVAL_NODE(n->rb.parent);
}
if (n && interval_tree_intersects(n, low, high)) {
return n;
}
}
return NULL;
}
struct interval_node *interval_tree_iter_next(struct interval_tree_it *it) {
it->n = interval_tree_next_interval(it->n, it->low, it->high);
return it->n;
struct interval_node *interval_tree_min_interval(struct interval_node *n,
interval_type_t low,
interval_type_t high) {
struct interval_node *min = NULL;
while (n) {
int intersects = interval_tree_intersects(n, low, high);
if (intersects) {
min = n;
}
/* if n->left->max < low, there is no match in the left subtree, there
could be one in the right */
if (!n->rb.left || INTERVAL_NODE(n->rb.left)->max < low) {
/* don't go right if the current node intersected */
if (intersects) {
break;
}
n = INTERVAL_NODE(n->rb.right);
}
/* else if n->left-max >= low, there could be one in the left subtree. if
there isn't one in the left, there wouldn't be one in the right */
else {
n = INTERVAL_NODE(n->rb.left);
}
}
return min;
}
int interval_tree_height(struct rb_tree *t) {
struct interval_node *root = INTERVAL_NODE(t->root);
return interval_node_height(root);
}
int interval_tree_size(struct rb_tree *t) {
struct interval_node *root = INTERVAL_NODE(t->root);
return interval_node_size(root);
}
interval_type_t interval_tree_max(struct rb_tree *t) {
struct interval_node *root = INTERVAL_NODE(t->root);
return interval_node_max(root);
}
void interval_tree_clear(struct rb_tree *t) {
t->root = NULL;
}
void interval_tree_remove(struct rb_tree *t, struct interval_node *n) {
rb_unlink(t, RB_NODE(n), &interval_tree_cb);
}
void interval_tree_insert(struct rb_tree *t, struct interval_node *n) {
rb_insert(t, RB_NODE(n), &interval_tree_cb);
}

View File

@ -4,12 +4,12 @@
#include <stdint.h>
#include "core/rb_tree.h"
#define INTERVAL_NODE(n) ((struct interval_node *)n)
#define INTERVAL_NODE(n) (container_of_safe((n), struct interval_node, rb))
typedef uintptr_t interval_type_t;
struct interval_node {
struct rb_node base;
struct rb_node rb;
interval_type_t low;
interval_type_t high;
interval_type_t max;
@ -26,6 +26,18 @@ struct interval_tree_it {
void interval_tree_insert(struct rb_tree *t, struct interval_node *n);
void interval_tree_remove(struct rb_tree *t, struct interval_node *n);
void interval_tree_clear(struct rb_tree *t);
interval_type_t interval_tree_max(struct rb_tree *t);
int interval_tree_size(struct rb_tree *t);
int interval_tree_height(struct rb_tree *t);
struct interval_node *interval_tree_min_interval(struct interval_node *n,
interval_type_t low,
interval_type_t high);
struct interval_node *interval_tree_next_interval(struct interval_node *n,
interval_type_t low,
interval_type_t high);
struct interval_node *interval_tree_find(struct rb_tree *t, interval_type_t low,
interval_type_t high);

View File

@ -41,6 +41,9 @@ struct rb_node *rb_last(struct rb_tree *t);
struct rb_node *rb_prev(struct rb_node *n);
struct rb_node *rb_next(struct rb_node *n);
#define rb_for_each(it, t) \
for (struct rb_node *it = rb_first((t)); it; it = rb_next(it))
#define rb_entry(n, type, member) container_of_safe(n, type, member)
#define rb_first_entry(t, type, member) rb_entry(rb_first(t), type, member)

View File

@ -24,28 +24,27 @@ struct memory_watcher {
struct list live_watches;
};
static struct memory_watcher *s_watcher;
static struct memory_watcher *watcher;
static int watcher_handle_exception(void *ctx, struct exception *ex);
static void watcher_create() {
s_watcher = calloc(1, sizeof(struct memory_watcher));
watcher = calloc(1, sizeof(struct memory_watcher));
s_watcher->exc_handler =
exception_handler_add(NULL, &watcher_handle_exception);
watcher->exc_handler = exception_handler_add(NULL, &watcher_handle_exception);
for (int i = 0; i < MAX_WATCHES; i++) {
struct memory_watch *watch = &s_watcher->watches[i];
list_add(&s_watcher->free_watches, &watch->list_it);
struct memory_watch *watch = &watcher->watches[i];
list_add(&watcher->free_watches, &watch->list_it);
}
}
static void watcher_destroy() {
exception_handler_remove(s_watcher->exc_handler);
exception_handler_remove(watcher->exc_handler);
free(s_watcher);
free(watcher);
s_watcher = NULL;
watcher = NULL;
}
static int watcher_handle_exception(void *ctx, struct exception *ex) {
@ -53,7 +52,7 @@ static int watcher_handle_exception(void *ctx, struct exception *ex) {
struct interval_tree_it it;
struct interval_node *n = interval_tree_iter_first(
&s_watcher->tree, ex->fault_addr, ex->fault_addr, &it);
&watcher->tree, ex->fault_addr, ex->fault_addr, &it);
while (n) {
handled = 1;
@ -61,11 +60,11 @@ static int watcher_handle_exception(void *ctx, struct exception *ex) {
struct interval_node *next = interval_tree_iter_next(&it);
struct memory_watch *watch = container_of(n, struct memory_watch, tree_it);
// call callback for this access watch
/* call callback for this access watch */
watch->cb(ex, watch->data);
if (watch->type == WATCH_SINGLE_WRITE) {
// restore page permissions
/* restore page permissions */
uintptr_t aligned_begin = n->low;
size_t aligned_size = (n->high - n->low) + 1;
CHECK(protect_pages((void *)aligned_begin, aligned_size, ACC_READWRITE));
@ -76,7 +75,7 @@ static int watcher_handle_exception(void *ctx, struct exception *ex) {
n = next;
}
if (s_watcher && !s_watcher->tree.root) {
if (watcher && !watcher->tree.root) {
watcher_destroy();
}
@ -85,53 +84,53 @@ static int watcher_handle_exception(void *ctx, struct exception *ex) {
struct memory_watch *add_single_write_watch(const void *ptr, size_t size,
memory_watch_cb cb, void *data) {
if (!s_watcher) {
if (!watcher) {
watcher_create();
}
// page align the range to be watched
/* page align the range to be watched */
size_t page_size = get_page_size();
uintptr_t aligned_begin = align_down((uintptr_t)ptr, page_size);
uintptr_t aligned_end = align_up((uintptr_t)ptr + size, page_size) - 1;
size_t aligned_size = (aligned_end - aligned_begin) + 1;
// disable writing to the pages
/* disable writing to the pages */
CHECK(protect_pages((void *)aligned_begin, aligned_size, ACC_READONLY));
// allocate new access watch
/* allocate new access watch */
struct memory_watch *watch =
list_first_entry(&s_watcher->free_watches, struct memory_watch, list_it);
list_first_entry(&watcher->free_watches, struct memory_watch, list_it);
CHECK_NOTNULL(watch);
watch->type = WATCH_SINGLE_WRITE;
watch->cb = cb;
watch->data = data;
// remove from free list
list_remove(&s_watcher->free_watches, &watch->list_it);
/* remove from free list */
list_remove(&watcher->free_watches, &watch->list_it);
// add to live list
list_add(&s_watcher->live_watches, &watch->list_it);
/* add to live list */
list_add(&watcher->live_watches, &watch->list_it);
// add to interval tree
/* add to interval tree */
watch->tree_it.low = aligned_begin;
watch->tree_it.high = aligned_end;
interval_tree_insert(&s_watcher->tree, &watch->tree_it);
interval_tree_insert(&watcher->tree, &watch->tree_it);
return watch;
}
void remove_memory_watch(struct memory_watch *watch) {
// remove from interval tree
interval_tree_remove(&s_watcher->tree, &watch->tree_it);
/* remove from interval tree */
interval_tree_remove(&watcher->tree, &watch->tree_it);
// remove from live list
list_remove(&s_watcher->live_watches, &watch->list_it);
/* remove from live list */
list_remove(&watcher->live_watches, &watch->list_it);
// add to free list
list_add(&s_watcher->free_watches, &watch->list_it);
/* add to free list */
list_add(&watcher->free_watches, &watch->list_it);
if (!s_watcher->tree.root) {
if (!watcher->tree.root) {
watcher_destroy();
}
}

View File

@ -9,7 +9,7 @@ void test_register(struct test *test) {
int main() {
list_for_each_entry(test, &tests, struct test, it) {
LOG_INFO("[%s]", test->name);
LOG_INFO("%s..", test->name);
test->run();
LOG_INFO(ANSI_COLOR_GREEN "OK" ANSI_COLOR_RESET);
}

165
test/test_interval_tree.c Normal file
View File

@ -0,0 +1,165 @@
#include <math.h>
#include "retest.h"
#define VERIFY_INTRUSIVE_TREE
#include "core/interval_tree.h"
#define LOW 0x0
#define HIGH 0x10000
#define INTERVAL 0x2000
#define MAX_NODES 0x1000
static struct interval_node nodes[MAX_NODES];
static void init_interval_tree(struct rb_tree *t) {
for (int i = 0; i < MAX_NODES; i++) {
uint32_t low = 0;
uint32_t high = 0;
while (high <= low) {
low = LOW + (rand() % (HIGH - LOW));
high = low + INTERVAL;
}
struct interval_node *n = &nodes[i];
n->low = low;
n->high = high;
interval_tree_insert(t, n);
}
}
TEST(test_interval_tree_size) {
struct rb_tree tree = {0};
init_interval_tree(&tree);
int size = interval_tree_size(&tree);
CHECK_EQ(size, MAX_NODES);
}
TEST(test_interval_tree_height) {
struct rb_tree tree = {0};
init_interval_tree(&tree);
int height = interval_tree_height(&tree);
int size = interval_tree_size(&tree);
CHECK(height <= 2 * log2(size + 1));
}
TEST(test_interval_tree_remove) {
struct rb_tree tree = {0};
init_interval_tree(&tree);
/* remove all nodes and ensure size is updated in the process */
int expected_size = interval_tree_size(&tree);
int current_size = 0;
struct interval_tree_it it;
struct interval_node *n = interval_tree_iter_first(&tree, LOW, HIGH, &it);
while (n) {
struct interval_node *next = interval_tree_iter_next(&it);
interval_tree_remove(&tree, n);
current_size = interval_tree_size(&tree);
CHECK_EQ(current_size, --expected_size);
n = next;
}
}
TEST(test_interval_tree_clear) {
struct rb_tree tree = {0};
init_interval_tree(&tree);
interval_tree_clear(&tree);
int size = interval_tree_size(&tree);
CHECK_EQ(size, 0);
}
TEST(test_interval_tree_find) {
struct rb_tree tree = {0};
init_interval_tree(&tree);
for (uint32_t i = 0; i < HIGH; i += 0x1000) {
/* manually generate a list of expected nodes */
static struct interval_node *expected[MAX_NODES];
int num_expected = 0;
rb_for_each(rb, &tree) {
struct interval_node *n = INTERVAL_NODE(rb);
if (i < n->low || i > n->high) {
continue;
}
expected[num_expected++] = n;
}
/* query the tree for nodes */
struct interval_tree_it it;
struct interval_node *n = interval_tree_iter_first(&tree, i, i, &it);
int found = 0;
while (n) {
/* validate that it's in the expected set */
for (int j = 0; j < num_expected; j++) {
if (expected[j] == n) {
found++;
break;
}
}
n = interval_tree_iter_next(&it);
}
CHECK_EQ(found, num_expected);
}
}
TEST(test_interval_tree_find_destructive) {
struct rb_tree tree = {0};
init_interval_tree(&tree);
for (uint32_t i = 0; i < HIGH; i += 0x1000) {
/* manually generate a list of results */
static struct interval_node *expected[MAX_NODES];
int num_expected = 0;
rb_for_each(rb, &tree) {
struct interval_node *n = INTERVAL_NODE(rb);
if (i < n->low || i > n->high) {
continue;
}
expected[num_expected++] = n;
}
/* query the tree for nodes and compare with the expected results */
int found = 0;
while (1) {
struct interval_node *root = INTERVAL_NODE(tree.root);
struct interval_node *n = interval_tree_min_interval(root, i, i);
if (!n) {
break;
}
/* validate that it's in the expected set */
for (int j = 0; j < num_expected; j++) {
if (expected[j] == n) {
found++;
break;
}
}
/* delete the current interval to move onto the next */
interval_tree_remove(&tree, n);
}
/* validate that the same number of nodes were matched */
CHECK_EQ(found, num_expected);
}
}

View File

@ -1,151 +0,0 @@
#include <gtest/gtest.h>
#include <math.h>
#include <set>
extern "C" {
#define VERIFY_INTRUSIVE_TREE
#include "core/interval_tree.h"
}
using namespace re;
typedef IntervalTree<void *> TestTree;
typedef TestTree::node_type TestNode;
class IntervalTreeTest : public ::testing::Test {
public:
IntervalTreeTest() {}
static const int LOW = 0x0;
static const int HIGH = 0x10000;
static const int INTERVAL = 0x2000;
void SetUp() {
// insert dummy intervals
for (int i = 0; i < 0x1000; i++) {
uint32_t low = 0;
uint32_t high = 0;
while (high <= low) {
low = LOW + (rand() % (HIGH - LOW));
high = low + INTERVAL;
}
TestNode *n = intervals.Insert(low, high, nullptr);
nodes.insert(n);
}
}
TestTree intervals;
std::set<TestNode *> nodes;
};
TEST_F(IntervalTreeTest, ValidateRelations) {
// make sure all children parent pointers match
for (auto n : nodes) {
if (n->left) {
ASSERT_EQ(n->left->parent, n);
}
if (n->right) {
ASSERT_EQ(n->right->parent, n);
}
}
}
TEST_F(IntervalTreeTest, Size) {
ASSERT_EQ(intervals.Size(), (int)nodes.size());
}
TEST_F(IntervalTreeTest, Height) {
int height = intervals.Height();
int size = intervals.Size();
ASSERT_TRUE(height <= 2 * log2(size + 1));
}
TEST_F(IntervalTreeTest, Remove) {
// remove all results and ensure size is updated in the process
int size = static_cast<int>(nodes.size());
for (auto n : nodes) {
intervals.Remove(n);
ASSERT_EQ(intervals.Size(), --size);
}
}
TEST_F(IntervalTreeTest, Clear) {
int original_size = intervals.Size();
intervals.Clear();
ASSERT_NE(original_size, intervals.Size());
ASSERT_EQ(0, intervals.Size());
}
TEST_F(IntervalTreeTest, Find) {
for (uint32_t i = 0; i < HIGH; i += 0x1000) {
// manually generate a list of results
std::set<TestNode *> expected;
for (auto n : nodes) {
if (i < n->low || i > n->high) {
continue;
}
expected.insert(n);
}
// query the tree for nodes and compare with the expected results
int found = 0;
TestNode *n = intervals.Find(i, i);
while (n) {
// validate that it's in the expected set
auto it = expected.find(n);
ASSERT_NE(it, expected.end());
found++;
// remove from nodes so the node isn't expected by the next loop
auto it2 = nodes.find(n);
ASSERT_NE(it2, nodes.end());
nodes.erase(it2);
// remove from intervals so Find() can locate the next node
intervals.Remove(n);
// locate the next node
n = intervals.Find(i, i);
}
// validate that the same number of nodes were matched
ASSERT_EQ(found, (int)expected.size());
}
}
TEST_F(IntervalTreeTest, Iterate) {
for (uint32_t i = 0; i < HIGH; i += 0x1000) {
// manually generate a list of expected nodes
std::set<TestNode *> expected;
for (auto n : nodes) {
if (i < n->low || i > n->high) {
continue;
}
expected.insert(n);
}
// query the tree for nodes
auto range_it = intervals.intersect(i, i);
// compare the results
size_t size = 0;
for (auto it = range_it.first; it != range_it.second; ++it) {
auto it2 = expected.find(*it);
ASSERT_NE(it2, expected.end());
size++;
}
ASSERT_EQ(expected.size(), size);
}
}