blob: 94abda28b7cafc2719668eb6175194252caa19d2 [file] [log] [blame]
#include "globals.h"
#include "annotations.h"
#include "lib/dr_annotations.h"
#include "jit_opt.h"
#define DYNAMORIO_ANNOTATE_MANAGE_CODE_AREA_NAME "dynamorio_annotate_manage_code_area"
#define DYNAMORIO_ANNOTATE_UNMANAGE_CODE_AREA_NAME "dynamorio_annotate_unmanage_code_area"
#ifdef ANNOTATIONS
static void
annotation_manage_code_area(void *start, size_t size)
{
LOG(GLOBAL, LOG_ANNOTATIONS, 2,
"Add code area " PFX "-" PFX " to JIT managed regions\n", start,
(app_pc)start + size);
set_region_jit_managed(start, size);
}
static void
annotation_unmanage_code_area(void *start, size_t size)
{
dcontext_t *dcontext = get_thread_private_dcontext();
if (!is_jit_managed_area(start))
return;
LOG(GLOBAL, LOG_ANNOTATIONS, 2,
"Remove code area " PFX "-" PFX " from JIT managed regions\n", start,
(app_pc)start + size);
d_r_mutex_lock(&thread_initexit_lock);
flush_fragments_and_remove_region(dcontext, start, size, true /*own initexit_lock*/,
false /*keep futures*/);
d_r_mutex_unlock(&thread_initexit_lock);
jitopt_clear_span(start, (app_pc)start + size);
}
#endif
/***************************************************************************
* Fragment Tree
*/
/* list of traces that contain the associated bb_node_t (via its "traces" field) */
typedef struct _trace_list_t {
app_pc trace_tag;
struct _trace_list_t *next;
} trace_list_t;
/* tree node representing one JIT basic block */
typedef struct _bb_node_t {
app_pc start; /* fragment start (in app space) */
app_pc end; /* fragment end (in app space) */
app_pc max; /* max fragment end in this subtree (in app space) */
bool red;
struct _bb_node_t *left;
struct _bb_node_t *right;
struct _bb_node_t *parent;
trace_list_t *traces; /* list of traces containing this bb */
} bb_node_t;
/* red-black tree of JIT basic blocks */
typedef struct _fragment_tree_t {
bb_node_t *root;
bb_node_t *nil;
void *node_heap; /* for quick allocation of nodes */
void *trace_heap; /* for quick allocation of trace list items */
} fragment_tree_t;
static fragment_tree_t *
fragment_tree_create()
{
fragment_tree_t *tree =
HEAP_TYPE_ALLOC(GLOBAL_DCONTEXT, fragment_tree_t, ACCT_OTHER, UNPROTECTED);
memset(tree, 0, sizeof(fragment_tree_t));
tree->node_heap = special_heap_init(sizeof(bb_node_t), true /* lock */,
false /* -x */, true /* persistent */);
tree->trace_heap = special_heap_init(sizeof(bb_node_t), true /* lock */,
false /* -x */, true /* persistent */);
tree->nil = special_heap_alloc(tree->node_heap);
memset(tree->nil, 0, sizeof(bb_node_t));
tree->root = tree->nil;
return tree;
}
static void
fragment_tree_destroy(fragment_tree_t *tree)
{
special_heap_free(tree->node_heap, tree->nil);
special_heap_exit(tree->node_heap);
special_heap_exit(tree->trace_heap);
HEAP_TYPE_FREE(GLOBAL_DCONTEXT, tree, fragment_tree_t, ACCT_OTHER, UNPROTECTED);
}
/* Return the first node in the tree overlapping the span. */
static bb_node_t *
fragment_tree_overlap_lookup(fragment_tree_t *tree, app_pc start, app_pc end)
{
bb_node_t *walk = tree->root;
ASSERT(start < end);
while (walk != tree->nil) {
if (start < walk->end && end > walk->start)
return walk;
if (start < walk->left->max)
walk = walk->left;
else
walk = walk->right;
}
return tree->nil;
}
#ifdef STANDALONE_UNIT_TEST
/* Lookup a node in the tree by exact match. For testing only. */
static bb_node_t *
fragment_tree_lookup(fragment_tree_t *tree, app_pc start, app_pc end)
{
bb_node_t *walk = tree->root;
ASSERT(start < end);
while (walk != tree->nil) {
if (start < walk->start || (start == walk->start && end < walk->end))
walk = walk->left;
else if (start == walk->start && end == walk->end)
return walk;
else
walk = walk->right;
}
return NULL;
}
#endif /* STANDALONE_UNIT_TEST */
/* Locally update the maximum end pc for the subtree defined by node (i.e., node->max),
* assuming that the maximum of node's two children (including nil) are currently correct.
*/
static inline void
fragment_tree_update_node_max(fragment_tree_t *tree, bb_node_t *node)
{
if (node != tree->nil)
node->max = MAX(MAX(node->left->max, node->right->max), node->end);
}
/* Rotate the tree left around a node. */
static void
fragment_tree_rotate_left(fragment_tree_t *tree, bb_node_t *node)
{
bb_node_t *pivot = node->right;
node->right = pivot->left; /* Remove the pivot from below the node; */
if (node->right != tree->nil) /* the pivot's child becomes node's child. */
node->right->parent = node;
pivot->parent = node->parent; /* Insert the pivot above the node; */
if (node == tree->root) /* the node's parent becomes the pivot's parent. */
tree->root = pivot;
else if (node == node->parent->left)
node->parent->left = pivot;
else
node->parent->right = pivot;
pivot->left = node;
node->parent = pivot;
fragment_tree_update_node_max(tree, node);
fragment_tree_update_node_max(tree, pivot);
}
/* Rotate the tree right around a node. */
static void
fragment_tree_rotate_right(fragment_tree_t *tree, bb_node_t *node)
{
bb_node_t *pivot = node->left;
node->left = pivot->right; /* Remove the pivot from below the node; */
if (node->left != tree->nil) /* the pivot's child becomes node's child. */
node->left->parent = node;
pivot->parent = node->parent; /* Insert the pivot above the node; */
if (node == tree->root) /* the node's parent becomes the pivot's parent. */
tree->root = pivot;
else if (node == node->parent->left)
node->parent->left = pivot;
else
node->parent->right = pivot;
pivot->right = node;
node->parent = pivot;
fragment_tree_update_node_max(tree, node);
fragment_tree_update_node_max(tree, pivot);
}
/* Insert new_node without rebalancing. */
static inline void
fragment_tree_insert_unbalanced(fragment_tree_t *tree, bb_node_t *new_node)
{
bb_node_t *walk = tree->root;
if (tree->root == tree->nil) {
tree->root = new_node;
} else {
while (true) {
if (walk->max < new_node->end)
walk->max = new_node->end;
if (new_node->start < walk->start ||
(new_node->start == walk->start && new_node->end < walk->end)) {
if (walk->left == tree->nil) {
walk->left = new_node;
break;
}
walk = walk->left;
} else {
ASSERT(!(new_node->start == walk->start && new_node->end == walk->end));
if (walk->right == tree->nil) {
walk->right = new_node;
break;
}
walk = walk->right;
}
}
}
new_node->parent = walk;
}
/* Rebalance the tree after the insertion of new_node. */
static inline void
fragment_tree_insert_rebalance(fragment_tree_t *tree, bb_node_t *new_node)
{
bb_node_t *walk = new_node, *uncle;
/* The new node is red, so it may be necessary to resolve consecutive red nodes. First
* try to borrow adjacent blacks from higher up in new_node's path: walk up the tree
* and recolor every other uncle black, recoloring its parent red instead. If a black
* uncle is found, resolve locally by rotating the tree above and below that uncle.
*/
while (walk->parent->red) {
if (walk->parent == walk->parent->parent->left) {
uncle = walk->parent->parent->right;
if (uncle->red) { /* Easy case: recolor uncle and grandpa, ascend 2 levels */
walk->parent->red = false;
uncle->red = false;
walk->parent->parent->red = true;
walk = walk->parent->parent;
} else {
/* Walk's uncle is black, so recoloring is interrupted. Move parent's
* red out of walk's path by turning walk's grandparent red and
* rotating the grandparent down into the uncle's path.
*/
if (walk == walk->parent->right) {
/* But wait--walk is about to get pulled into uncle's path along
* with parent's red, which defeats the purpose. Adjust locally
* by rotating walk to the other side of parent.
*/
ASSERT(walk->red); /* should preserve the local red-black scenario */
/* because it becomes walk->parent->right, and walk->parent is red */
ASSERT(!walk->left->red);
walk = walk->parent;
fragment_tree_rotate_left(tree, walk);
}
/* because these will become children of walk's now-red grandparent */
ASSERT(!(walk->parent->right->red || walk->parent->parent->right->red));
walk->parent->red = false; /* recolor and rotate grandparent right */
walk->parent->parent->red = true;
fragment_tree_rotate_right(tree, walk->parent->parent);
}
} else { /* mirror of above */
uncle = walk->parent->parent->left;
if (uncle->red) {
walk->parent->red = false;
uncle->red = false;
walk->parent->parent->red = true;
walk = walk->parent->parent;
} else {
if (walk == walk->parent->left) {
ASSERT(walk->red && !walk->right->red);
walk = walk->parent;
fragment_tree_rotate_right(tree, walk);
}
ASSERT(!(walk->parent->left->red || walk->parent->parent->left->red));
walk->parent->red = false;
walk->parent->parent->red = true;
fragment_tree_rotate_left(tree, walk->parent->parent);
}
}
}
tree->root->red = false;
}
/* Create a node with the specified span. */
static bb_node_t *
fragment_tree_node_create(fragment_tree_t *tree, app_pc start, app_pc end)
{
bb_node_t *new_node = special_heap_alloc(tree->node_heap);
ASSERT(start < end);
memset(new_node, 0, sizeof(bb_node_t));
new_node->left = new_node->right = tree->nil;
new_node->red = true;
new_node->start = start;
new_node->end = new_node->max = end;
return new_node;
}
/* Destroy the node and its list of containing trace tags. */
static void
fragment_tree_node_destroy(fragment_tree_t *tree, bb_node_t *node)
{
trace_list_t *walk = node->traces, *next;
while (walk != NULL) {
next = walk->next;
special_heap_free(tree->trace_heap, walk);
walk = next;
}
special_heap_free(tree->node_heap, node);
}
/* Create a node with the specified span and insert it, rebalancing as necessary. */
static bb_node_t *
fragment_tree_insert(fragment_tree_t *tree, app_pc start, app_pc end)
{
bb_node_t *new_node = fragment_tree_node_create(tree, start, end);
fragment_tree_insert_unbalanced(tree, new_node);
fragment_tree_insert_rebalance(tree, new_node);
return new_node;
}
/* Return the maximum node in the specified subtree. */
static inline bb_node_t *
fragment_subtree_max(fragment_tree_t *tree, bb_node_t *node)
{
ASSERT(node != tree->nil);
while (node->right != tree->nil) {
node = node->right;
}
return node;
}
static void
fragment_tree_transplant(fragment_tree_t *tree, bb_node_t *eviction,
bb_node_t *transplant)
{
if (eviction->parent == tree->nil)
tree->root = transplant;
else if (eviction == eviction->parent->left)
eviction->parent->left = transplant;
else
eviction->parent->right = transplant;
transplant->parent = eviction->parent;
}
/* Rebalance the tree after the deletion of the specified node. */
static void
fragment_tree_delete_rebalance(fragment_tree_t *tree, bb_node_t *node)
{
bool is_left, sibling_trio_has_red;
bb_node_t *sibling;
do { /* goal: pull an additional black into the simple path from root to node */
is_left = node == node->parent->left;
sibling = (is_left ? node->parent->right : node->parent->left);
if (sibling->red) {
node->parent->red = true; /* sibling can be the additional black, */
sibling->red = false; /* so recolor and pull it into the path */
if (is_left) {
sibling = sibling->left; /* next line changes node's sibling */
fragment_tree_rotate_left(tree, node->parent);
} else {
sibling = sibling->right; /* next line changes node's sibling */
fragment_tree_rotate_right(tree, node->parent);
}
}
sibling_trio_has_red = sibling->red || sibling->left->red || sibling->right->red;
if (node->parent->red || sibling_trio_has_red)
break; /* can rebalance locally at this point */
sibling->red = true;
if (node->parent->parent == tree->nil)
return; /* done because root's children are now red */
node = node->parent;
} while (true);
/* node is still missing a black, but can gain one locally now */
if (node->parent->red && !sibling_trio_has_red) {
sibling->red = true; /* sibling can take parent's red, such that */
node->parent->red = false; /* parent becomes node's additional black */
} else {
ASSERT(!sibling->red);
/* sibling is black, so swap with parent, then pull parent into node's path */
/* but wait--sibling's path will lose a black if its
* opposite child (node's nephew) is also black, so...
*/
if (is_left) {
if (sibling->left->red && !sibling->right->red) {
sibling->red = true; /* ...make sibling's opposite child red */
sibling->left->red = false; /* w/o affecting black depth on any path */
fragment_tree_rotate_right(tree, sibling);
sibling = sibling->parent;
}
} else if (sibling->right->red && !sibling->left->red) {
sibling->red = true; /* ...make sibling's opposite child red */
sibling->right->red = false; /* w/o affecting black depth on any path */
fragment_tree_rotate_left(tree, sibling);
sibling = sibling->parent;
}
/* sibling's opposite child (node's nephew) is now certainly red */
ASSERT((is_left && sibling->right->red) || (!is_left && sibling->left->red));
sibling->red = node->parent->red;
node->parent->red = false; /* add black to node's path but not sibling's path: */
if (is_left) { /* make parent black and rotate it into node's path */
ASSERT(sibling->right->red);
sibling->right->red = false;
fragment_tree_rotate_left(tree, node->parent);
} else {
ASSERT(sibling->left->red);
sibling->left->red = false;
fragment_tree_rotate_right(tree, node->parent);
}
}
}
/* Remove the specified node from the tree and destroy it. */
static inline void
fragment_tree_delete(fragment_tree_t *tree, bb_node_t *node)
{
bb_node_t *transplant, *deletion = node, *walk;
if (node->left != tree->nil && node->right != tree->nil) {
bb_node_t *predecessor = fragment_subtree_max(tree, node->left);
trace_list_t *trace_swap = node->traces;
node->start = predecessor->start;
node->end = predecessor->end;
node->traces = predecessor->traces;
predecessor->traces = trace_swap;
deletion = predecessor;
}
ASSERT(deletion->left == tree->nil || deletion->right == tree->nil);
deletion->max = 0;
walk = deletion->parent;
while (walk != tree->nil) {
fragment_tree_update_node_max(tree, walk);
walk = walk->parent;
}
transplant = deletion->right == tree->nil ? deletion->left : deletion->right;
if (!deletion->red) { /* losing a black node, so rebalance */
deletion->red = transplant->red;
if (deletion->parent != tree->nil)
fragment_tree_delete_rebalance(tree, deletion);
}
fragment_tree_transplant(tree, deletion, transplant);
if (deletion->parent == tree->nil && transplant != tree->nil) {
transplant->red = false; /* transplanted into the root, so set it black */
} else {
walk = transplant->parent;
while (walk != tree->nil) {
fragment_tree_update_node_max(tree, walk);
walk = walk->parent;
}
}
if (deletion != node) {
/* The deletion was swapped for its predecessor above--but the caller expects
* node to be destroyed, not its predecessor. So swap again: copy predecessor's
* data back into predecessor's original memory and re-link its tree limbs.
*/
bb_node_t *restore = deletion;
trace_list_t *trace_swap = restore->traces;
memcpy(restore, node, sizeof(bb_node_t));
if (node == tree->root)
tree->root = restore;
else if (node == restore->parent->left)
node->parent->left = restore;
else
node->parent->right = restore;
if (restore->right != tree->nil)
restore->right->parent = restore;
if (restore->left != tree->nil)
restore->left->parent = restore;
node->traces = trace_swap;
}
fragment_tree_node_destroy(tree, node);
}
#ifdef STANDALONE_UNIT_TEST
/* Remove and destroy all nodes from the tree. */
static void
fragment_tree_clear(fragment_tree_t *tree)
{
if (tree->root != tree->nil) {
bool is_left = false;
bb_node_t *walk = tree->root;
do {
if (walk->left != tree->nil) {
walk = walk->left;
is_left = true;
} else if (walk->right != tree->nil) {
walk = walk->right;
is_left = false;
} else {
walk = walk->parent;
if (walk == tree->nil) {
tree->root = tree->nil;
break;
} else if (is_left) {
fragment_tree_node_destroy(tree, walk->left);
walk->left = tree->nil;
} else {
fragment_tree_node_destroy(tree, walk->right);
walk->right = tree->nil;
}
is_left = walk == walk->parent->left;
}
} while (true);
}
ASSERT(tree->root == tree->nil);
}
#endif /* STANDALONE_UNIT_TEST */
/***************************************************************************
* Cache Consistency
*/
static fragment_tree_t *fragment_tree;
void
jitopt_init()
{
if (DYNAMO_OPTION(opt_jit)) {
fragment_tree = fragment_tree_create();
#ifdef ANNOTATIONS
dr_annotation_register_call(DYNAMORIO_ANNOTATE_MANAGE_CODE_AREA_NAME,
(void *)annotation_manage_code_area, false, 2,
DR_ANNOTATION_CALL_TYPE_FASTCALL);
dr_annotation_register_call(DYNAMORIO_ANNOTATE_UNMANAGE_CODE_AREA_NAME,
(void *)annotation_unmanage_code_area, false, 2,
DR_ANNOTATION_CALL_TYPE_FASTCALL);
#endif /* ANNOTATIONS */
}
}
void
jitopt_exit()
{
if (DYNAMO_OPTION(opt_jit))
fragment_tree_destroy(fragment_tree);
}
void
jitopt_add_dgc_bb(app_pc start, app_pc end, bool is_trace_head)
{
ASSERT(DYNAMO_OPTION(opt_jit));
fragment_tree_insert(fragment_tree, start, end);
}
uint
jitopt_clear_span(app_pc start, app_pc end)
{
bb_node_t *overlap;
uint removal_count = 0;
ASSERT(DYNAMO_OPTION(opt_jit));
do {
/* XXX i#1114: maybe more efficient to delete deepest overlapping node first */
overlap = fragment_tree_overlap_lookup(fragment_tree, start, end);
if (overlap == fragment_tree->nil)
break;
fragment_tree_delete(fragment_tree, overlap);
removal_count++;
} while (true);
return removal_count;
}
#ifdef STANDALONE_UNIT_TEST
/***************************************************************************
* Fragment Tree Unit Test
*/
# define FRAGMENT_TREE_TEST_NODE_COUNT 0x900
static void
unit_test_find_node_in_list(bb_node_t **node_list, bb_node_t *lookup, uint list_length)
{
uint i;
for (i = 0; i < list_length; i++) {
if (node_list[i] == lookup)
return;
}
ASSERT(false);
}
/* Verify the black depth along each path of the tree. */
static void
unit_test_verify_black_depth()
{
ASSERT(!fragment_tree->root->red);
if (fragment_tree->root != fragment_tree->nil) {
int current_black_count = 0, tree_black_count = -1, dfs_queue_index = 0;
bb_node_t *walk = fragment_tree->root;
int dfs_queue_black_counts[FRAGMENT_TREE_TEST_NODE_COUNT];
bb_node_t *dfs_queue[FRAGMENT_TREE_TEST_NODE_COUNT];
while (true) {
if (walk->red)
ASSERT(!(walk->left->red || walk->right->red));
else
current_black_count++;
if (walk->right == fragment_tree->nil) {
if (tree_black_count < 0)
tree_black_count = current_black_count;
else
ASSERT(current_black_count == tree_black_count);
} else {
dfs_queue[dfs_queue_index] = walk->right;
dfs_queue_black_counts[dfs_queue_index] = current_black_count;
dfs_queue_index++;
}
if (walk->left == fragment_tree->nil) {
if (tree_black_count < 0)
tree_black_count = current_black_count;
else
ASSERT(current_black_count == tree_black_count);
if (dfs_queue_index == 0)
break;
dfs_queue_index--;
walk = dfs_queue[dfs_queue_index];
current_black_count = dfs_queue_black_counts[dfs_queue_index];
} else {
walk = walk->left;
}
}
}
}
static void
unit_test_lookup_all_nodes(bb_node_t **node_list, uint list_length)
{
uint i, list_node_count = 0, tree_node_count = 0, dfs_queue_index = 0;
bb_node_t *lookup, *dfs_queue[FRAGMENT_TREE_TEST_NODE_COUNT];
for (i = 0; i < list_length; i++) {
if (node_list[i] != NULL) {
lookup = fragment_tree_lookup(fragment_tree, node_list[i]->start,
node_list[i]->end);
ASSERT(lookup == node_list[i]);
list_node_count++;
}
}
if (fragment_tree->root != fragment_tree->nil) {
lookup = fragment_tree->root;
while (true) {
unit_test_find_node_in_list(node_list, lookup, list_length);
tree_node_count++;
if (lookup->right != fragment_tree->nil)
dfs_queue[dfs_queue_index++] = lookup->right;
if (lookup->left != fragment_tree->nil)
lookup = lookup->left;
else if (dfs_queue_index == 0)
break;
else
lookup = dfs_queue[--dfs_queue_index];
}
}
ASSERT(tree_node_count == list_node_count);
unit_test_verify_black_depth();
}
static app_pc
unit_test_get_random_pc(app_pc range_start, uint max_range_size)
{
return range_start + get_random_offset(max_range_size);
}
static bool
unit_test_insert_random_node(bb_node_t **node_list, app_pc random_base, uint random_span,
uint index)
{
app_pc random_start = unit_test_get_random_pc(random_base, random_span);
app_pc random_end = unit_test_get_random_pc(random_start + 2, 0x40);
if (fragment_tree_lookup(fragment_tree, random_start, random_end) == NULL) {
node_list[index] = fragment_tree_insert(fragment_tree, random_start, random_end);
return true;
} else {
d_r_set_random_seed((uint)query_time_millis());
return false;
}
}
/* Inserts the specified number of new random nodes. */
static void
unit_test_insert_random_nodes(bb_node_t **node_list, uint insert_count)
{
uint i;
ASSERT(fragment_tree->root == fragment_tree->nil);
for (i = 0; i < insert_count; i++) {
if (unit_test_insert_random_node(node_list, 0, 0xffffffff, i)) {
if ((i + 1) % 20 == 0)
unit_test_lookup_all_nodes(node_list, i + 1);
} else {
i--; /* found exact match, so rewind and try another */
}
}
}
/* returns the number of nodes removed */
static uint
unit_test_remove_occupied_span(bb_node_t **node_list, uint list_length)
{
uint i, list_removal_count = 0, tree_removal_count;
app_pc start, end;
bb_node_t *first = NULL, *second = NULL, *swap;
/* randomly choose a span containing some nodes and clear it */
for (i = 0; i < list_length; i++) {
if (node_list[i] != NULL) {
first = node_list[i++];
break;
}
}
for (; i < list_length; i++) {
if (node_list[i] != NULL) {
second = node_list[i];
break;
}
}
ASSERT(first != NULL && second != NULL);
if (second->start < first->start ||
(second->start == first->start && second->end < first->end)) {
swap = first;
first = second;
second = swap;
}
/* randomly pick before, on, or after an occupied pc, to test all overlap cases */
start = unit_test_get_random_pc(first->start == 0 ? 0 : first->start - 1, 2);
end = unit_test_get_random_pc(second->end - 1,
(ptr_uint_t)second->end < 0xffffffff ? 2 : 1);
for (i = 0; i < list_length; i++) { /* walk and clear the span from the list */
if (node_list[i] != NULL && start < node_list[i]->end &&
end > node_list[i]->start) {
list_removal_count++;
node_list[i] = NULL;
}
}
tree_removal_count = jitopt_clear_span(start, end); /* test the deployed code */
ASSERT(list_removal_count == tree_removal_count);
return tree_removal_count;
}
/* Randomly removes spans of nodes until at most 1 node remains. */
static void
unit_test_remove_random_spans(bb_node_t **node_list, uint list_length)
{
uint i, node_count = list_length, verify_counter = 0;
app_pc start, end;
bool overlap;
while (node_count > 1) {
verify_counter++;
/* attempt to remove a random unoccupied span */
while (true) {
/* fish for an unoccupied span */
start = unit_test_get_random_pc(0, 0xffffffff);
end = unit_test_get_random_pc(start + 0x10, 0x40);
overlap = false;
for (i = 0; i < list_length; i++) {
if (node_list[i] != NULL && start < node_list[i]->end &&
end > node_list[i]->start) {
overlap = true;
break;
}
}
if (!overlap) {
jitopt_clear_span(start, end); /* test the deployed code */
if (verify_counter % 20 == 0)
unit_test_lookup_all_nodes(node_list, list_length); /* verify */
break;
} /* else fish again for an unoccupied span */
}
node_count -= unit_test_remove_occupied_span(node_list, list_length);
if (verify_counter % 20 == 0)
unit_test_lookup_all_nodes(node_list, list_length); /* verify */
}
}
/* Packs a small span with overlapping nodes, then removes all nodes from a subspan
* and adds that many new random nodes back.
*/
static void
unit_test_churn_narrow_span(bb_node_t **node_list, uint list_length)
{
uint i, j, k, node_count, random_span = list_length * 8;
app_pc random_base = (app_pc)get_random_offset(0xf0000000);
ASSERT(fragment_tree->root == fragment_tree->nil);
for (i = 0; i < list_length; i++) { /* pack a small span */
if (unit_test_insert_random_node(node_list, (app_pc)random_base, 10 + random_span,
i)) {
if ((i + 1) % 20 == 0)
unit_test_lookup_all_nodes(node_list, i + 1);
} else {
i--; /* found exact match, so rewind and try another */
}
}
for (i = 0; i < 10; i++) {
node_count = unit_test_remove_occupied_span(node_list, list_length);
for (j = 0; j < node_count; j++) {
for (k = 0; k < list_length; k++) { /* find an empty slot */
if (node_list[k] == NULL)
break;
}
if (unit_test_insert_random_node(node_list, (app_pc)random_base,
10 + random_span, k)) {
if ((i + 1) % 20 == 0)
unit_test_lookup_all_nodes(node_list, list_length);
} else {
j--; /* found exact match, so rewind and try another */
}
}
}
}
void
unit_test_jit_fragment_tree()
{
uint i;
bb_node_t *node_list[FRAGMENT_TREE_TEST_NODE_COUNT]; /* N.B.: may contain NULLs */
print_file(STDERR, "test DGC fragment tree: ");
dynamo_options.opt_jit = true;
fragment_tree = fragment_tree_create();
d_r_set_random_seed((uint)query_time_millis());
for (i = 0; i < 3; i++) {
print_file(STDERR, "pass %d... ", i + 1);
unit_test_insert_random_nodes(node_list, FRAGMENT_TREE_TEST_NODE_COUNT);
unit_test_remove_random_spans(node_list, FRAGMENT_TREE_TEST_NODE_COUNT);
fragment_tree_clear(fragment_tree);
unit_test_churn_narrow_span(node_list, FRAGMENT_TREE_TEST_NODE_COUNT);
fragment_tree_clear(fragment_tree);
}
fragment_tree_destroy(fragment_tree);
print_file(STDERR, "\n");
}
#endif /* STANDALONE_UNIT_TEST */