fis-gtm/sr_port/mprof_tree.c

326 lines
9.6 KiB
C
Raw Normal View History

/****************************************************************
* *
* Copyright 2001, 2011 Fidelity Information Services, Inc *
* *
* This source code contains the intellectual property *
* of its copyright holder(s), and is made available *
* under a license. If you do not know the terms of *
* the license, please stop and do not read further. *
* *
****************************************************************/
#include "mdef.h"
#include "gtm_string.h"
#include "error.h"
#include "mprof.h"
#include "min_max.h" /* necessary for MIN which is necessary for MIDENT_CMP */
/* macros indicating descend direction */
#define LEFT 0
#define RIGHT 1
#define NEITHER -1
#define BASE 1
/* macros indicating node comparison direction */
#define LESS -1
#define MORE 1
#define EQUAL 0
/* macros indicating node involvement in insertion */
#define IN_PATH -1
#define NOT_IN_PATH 1
#define IS_INS_NODE 0
static mident *tmp_rout_name, *tmp_label_name;
STATICFNDCL mprof_tree *rotate_2(mprof_tree **, int);
STATICFNDCL mprof_tree *rotate_3(mprof_tree **, int, int);
STATICFNDCL int mprof_tree_compare(mprof_tree *, trace_entry *);
STATICFNDCL void mprof_tree_rebalance_path(mprof_tree *, trace_entry *);
STATICFNDCL void mprof_tree_rebalance(mprof_tree **, trace_entry *);
/* Creates a new generic node in the MPROF tree based on the information passed in arg. */
mprof_tree *new_node(trace_entry *arg)
{
mprof_tree *tree;
/* First, we need to pcalloc new space for the label/routine if we have not
* found an occurrence that is in the tree already */
if (NULL == tmp_rout_name)
tmp_rout_name = arg->rout_name;
if (NULL != tmp_rout_name && tmp_rout_name == arg->rout_name)
{
arg->rout_name = (mident *)pcalloc(SIZEOF(mident));
arg->rout_name->len = tmp_rout_name->len;
arg->rout_name->addr = (char *)pcalloc((unsigned int)tmp_rout_name->len);
memcpy(arg->rout_name->addr, tmp_rout_name->addr, tmp_rout_name->len);
}
if (NULL == tmp_label_name)
tmp_label_name = arg->label_name;
if (NULL != tmp_label_name && tmp_label_name == arg->label_name)
{
arg->label_name = (mident *)pcalloc(SIZEOF(mident));
arg->label_name->len = tmp_label_name->len;
arg->label_name->addr = (char *)pcalloc((unsigned int)tmp_label_name->len);
memcpy(arg->label_name->addr, tmp_label_name->addr, tmp_label_name->len);
}
tree = (mprof_tree *)pcalloc(SIZEOF(mprof_tree));
tree->e.rout_name = arg->rout_name;
tree->e.label_name = arg->label_name;
tree->e.line_num = arg->line_num;
tree->e.count = tree->e.usr_time = tree->e.sys_time = tree->e.loop_level = 0;
tree->e.raddr = NULL;
tree->link[LEFT] = tree->link[RIGHT] = tree->loop_link = NULL;
tree->desc_dir = NEITHER;
tree->ins_path_hint = NOT_IN_PATH;
return tree;
}
/* Creates a FOR-specific node in the MPROF tree based on the information passed in arg and
* return_address for this loop. */
mprof_tree *new_for_node(trace_entry *arg, char *ret_addr)
{
mprof_tree *node;
node = (mprof_tree *)new_node(arg);
node->e.raddr = (char *)ret_addr;
return node;
}
/* Traverses the tree and saves every value in the global */
void mprof_tree_walk(mprof_tree *node)
{
mprof_tree *loop_link;
while (TRUE)
{
if (NULL != node->link[LEFT])
mprof_tree_walk(node->link[LEFT]);
crt_gbl(node, FALSE);
loop_link = node->loop_link;
while (NULL != loop_link)
{
crt_gbl(loop_link, TRUE);
loop_link = loop_link->loop_link;
}
if (NULL != node->link[RIGHT])
node = node->link[RIGHT];
else
break;
}
return;
}
/* Does a single left or right rotation, depending on the index passed. The diagram
* of the rotation (for index == RIGHT) is presented below:
*
* B D
* / \ / \
* A D ==> B E
* / \ / \
* C E A C
*/
STATICFNDEF mprof_tree *rotate_2(mprof_tree **path_top, int index)
{
mprof_tree *B, *C, *D, *E;
B = *path_top;
D = B->link[index];
C = D->link[BASE - index];
E = D->link[index];
*path_top = D;
D->link[BASE - index] = B;
B->link[index] = C;
B->desc_dir = NEITHER;
D->desc_dir = NEITHER;
return E;
}
/* Does a double left or right rotation, depending on the index passed. The diagram
* of the rotation (for index == RIGHT) is presented below:
*
* B _D_
* / \ / \
* A F B F
* / \ ===> / \ / \
* D G A C E G
* / \
* C E
*/
STATICFNDEF mprof_tree *rotate_3(mprof_tree **path_top, int index, int third)
{
mprof_tree *B, *C, *D, *E, *F;
B = *path_top;
F = B->link[index];
D = F->link[BASE - index];
/* nodes C and E can be NULL */
C = D->link[BASE - index];
E = D->link[index];
*path_top = D;
D->link[BASE - index] = B;
D->link[index] = F;
B->link[index] = C;
F->link[BASE - index] = E;
D->desc_dir = NEITHER;
/* assume both subtrees are balanced */
B->desc_dir = F->desc_dir = NEITHER;
/* tree became balanced */
if (third == NEITHER)
return NULL;
if (third == index)
{ /* E holds the insertion so B is now unbalanced */
B->desc_dir = BASE - index;
return E;
} else
{ /* C holds the insertion so F is now unbalanced */
F->desc_dir = index;
return C;
}
}
/* Compares the contents of the specified node with the information stored in arg.
* Returns 0 on a match, 1 if arg was evaluated as "greater" than node, and -1 otherwise. */
STATICFNDEF int mprof_tree_compare(mprof_tree *node, trace_entry *arg)
{
int diff;
if (node->e.rout_name == arg->rout_name)
diff = EQUAL;
else
{
MIDENT_CMP(node->e.rout_name, arg->rout_name, diff);
/* it's the same routine name, so they need not be different memory locations */
if (EQUAL == diff)
arg->rout_name = node->e.rout_name;
}
/* the routine names are the same */
if (EQUAL == diff)
{ /* pointers to the label name are the same */
if (node->e.label_name == arg->label_name)
diff = EQUAL;
else
{
MIDENT_CMP(node->e.label_name, arg->label_name, diff);
/* it's the same label name, so they need not be different memory locations */
if (EQUAL == diff)
arg->label_name = node->e.label_name;
}
/* the label names are the same */
if (EQUAL == diff)
{
diff = arg->line_num - node->e.line_num;
if (EQUAL != diff)
diff = diff > EQUAL ? MORE : LESS;
}
}
return diff;
}
/* Adjusts the descending directions for nodes in the path in which the node has just been added */
STATICFNDEF void mprof_tree_rebalance_path(mprof_tree *path, trace_entry *arg)
{
int dir;
/* Each node in path is currently balanced, so we will descend in the direction
* of the newly inserted node, marking every node as unbalanced in that direction
* along the way. */
while (path)
{
if (IS_INS_NODE == path->ins_path_hint)
break;
if (NULL == path->link[LEFT] || NOT_IN_PATH == path->link[LEFT]->ins_path_hint)
dir = RIGHT;
else
dir = LEFT;
path->desc_dir = dir;
path = path->link[dir];
}
return;
}
/* Determines at which point (if any) tree became unbalanced, and balances it. */
STATICFNDEF void mprof_tree_rebalance(mprof_tree **path_top, trace_entry *arg)
{
mprof_tree *path;
int first, second, third, diff;
path = *path_top;
/* entire tree is balanced, so only update desc_dir */
if (NEITHER == path->desc_dir)
{
mprof_tree_rebalance_path(path, arg);
return;
}
/* we still had the room for the new node, so only update desc_dir */
first = EQUAL < mprof_tree_compare(path, arg) ? RIGHT : LEFT;
if (path->desc_dir != first)
{
path->desc_dir = NEITHER;
mprof_tree_rebalance_path(path->link[first], arg);
return;
}
/* tree became unbalanced, but it is a simpler case, so we need a single rotation */
second = EQUAL < mprof_tree_compare(path->link[first], arg) ? RIGHT : LEFT;
if (first == second)
{
path = rotate_2(path_top, first);
mprof_tree_rebalance_path(path, arg);
return;
}
/* we will need a double rotation */
path = path->link[first]->link[second];
diff = mprof_tree_compare(path, arg);
if (EQUAL == diff)
third = NEITHER; /* node we are interested in is the node we just added */
else
third = EQUAL < diff ? RIGHT : LEFT; /* node was added in the sibling spot of the parent node */
path = rotate_3(path_top, first, third);
mprof_tree_rebalance_path(path, arg);
return;
}
/* Attempts to find a node that matches arg's specification. If the node is not found, we
* create one; in either case the pointer to the desired node is returned. */
mprof_tree *mprof_tree_insert(mprof_tree **treep, trace_entry *arg)
{
int diff, dir;
mprof_tree *tree = *treep;
mprof_tree **path_top = treep;
tmp_rout_name = arg->rout_name;
tmp_label_name = arg->label_name;
while (tree)
{
tree->ins_path_hint = IN_PATH; /* this node is within the insertion path */
diff = mprof_tree_compare(tree, arg);
if (EQUAL == diff)
break;
if (NEITHER != tree->desc_dir)
path_top = treep;
dir = EQUAL < diff ? RIGHT : LEFT;
treep = &tree->link[dir];
if (NULL != tree->link[BASE - dir])
tree->link[BASE - dir]->ins_path_hint = NOT_IN_PATH; /* the sibling node is not in the insertion path */
tree = *treep;
}
/* did not find the node; will create one */
if (NULL == tree)
{
tree = new_node(arg);
tree->desc_dir = NEITHER;
tree->ins_path_hint = IS_INS_NODE;
*treep = tree;
mprof_tree_rebalance(path_top, arg);
}
/* If the rout_name or label_name already exists in the tree, it would have been changed to point to the reused
* name in the tree. Otherwise, if it doesn't already exist in the tree, new_node() would have created new copies
* and would have changed anyway. */
assert(tmp_rout_name != arg->rout_name);
assert(tmp_label_name != arg->label_name);
tmp_rout_name = tmp_label_name = NULL;
return tree;
}