callchain.c 3.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
/*
 * Copyright (C) 2009, Frederic Weisbecker <fweisbec@gmail.com>
 *
 * Handle the callchains from the stream in an ad-hoc radix tree and then
 * sort them in an rbtree.
 *
 */

#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <errno.h>

#include "callchain.h"


static void rb_insert_callchain(struct rb_root *root, struct callchain_node *chain)
{
	struct rb_node **p = &root->rb_node;
	struct rb_node *parent = NULL;
	struct callchain_node *rnode;

	while (*p) {
		parent = *p;
		rnode = rb_entry(parent, struct callchain_node, rb_node);

		if (rnode->hit < chain->hit)
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}

	rb_link_node(&chain->rb_node, parent, p);
	rb_insert_color(&chain->rb_node, root);
}

/*
 * Once we get every callchains from the stream, we can now
 * sort them by hit
 */
void sort_chain_to_rbtree(struct rb_root *rb_root, struct callchain_node *node)
{
	struct callchain_node *child;

	list_for_each_entry(child, &node->children, brothers)
		sort_chain_to_rbtree(rb_root, child);

	if (node->hit)
		rb_insert_callchain(rb_root, node);
}

static struct callchain_node *create_child(struct callchain_node *parent)
{
	struct callchain_node *new;

	new = malloc(sizeof(*new));
	if (!new) {
		perror("not enough memory to create child for code path tree");
		return NULL;
	}
	new->parent = parent;
	INIT_LIST_HEAD(&new->children);
	INIT_LIST_HEAD(&new->val);
	list_add_tail(&new->brothers, &parent->children);

	return new;
}

static void
fill_node(struct callchain_node *node, struct ip_callchain *chain, int start)
{
	int i;

	for (i = start; i < chain->nr; i++) {
		struct callchain_list *call;

		call = malloc(sizeof(*chain));
		if (!call) {
			perror("not enough memory for the code path tree");
			return;
		}
		call->ip = chain->ips[i];
		list_add_tail(&call->list, &node->val);
	}
	node->val_nr = i - start;
}

static void add_child(struct callchain_node *parent, struct ip_callchain *chain)
{
	struct callchain_node *new;

	new = create_child(parent);
	fill_node(new, chain, parent->val_nr);

	new->hit = 1;
}

static void
split_add_child(struct callchain_node *parent, struct ip_callchain *chain,
		struct callchain_list *to_split, int idx)
{
	struct callchain_node *new;

	/* split */
	new = create_child(parent);
	list_move_tail(&to_split->list, &new->val);
	new->hit = parent->hit;
	parent->hit = 0;
	parent->val_nr = idx;

	/* create the new one */
	add_child(parent, chain);
}

static int
__append_chain(struct callchain_node *root, struct ip_callchain *chain,
		int start);

static int
__append_chain_children(struct callchain_node *root, struct ip_callchain *chain)
{
	struct callchain_node *rnode;

	/* lookup in childrens */
	list_for_each_entry(rnode, &root->children, brothers) {
		int ret = __append_chain(rnode, chain, root->val_nr);
		if (!ret)
			return 0;
	}
	return -1;
}

static int
__append_chain(struct callchain_node *root, struct ip_callchain *chain,
		int start)
{
	struct callchain_list *cnode;
	int i = start;
	bool found = false;

	/* lookup in the current node */
	list_for_each_entry(cnode, &root->val, list) {
		if (cnode->ip != chain->ips[i++])
			break;
		if (!found)
			found = true;
		if (i == chain->nr)
			break;
	}

	/* matches not, relay on the parent */
	if (!found)
		return -1;

	/* we match only a part of the node. Split it and add the new chain */
	if (i < root->val_nr) {
		split_add_child(root, chain, cnode, i);
		return 0;
	}

	/* we match 100% of the path, increment the hit */
	if (i == root->val_nr) {
		root->hit++;
		return 0;
	}

	return __append_chain_children(root, chain);
}

void append_chain(struct callchain_node *root, struct ip_callchain *chain)
{
	if (__append_chain_children(root, chain) == -1)
		add_child(root, chain);
}