|
@@ -0,0 +1,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);
|
|
|
+}
|