Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (C) 2009-2011, Frederic Weisbecker <fweisbec (at) gmail.com>
      3  *
      4  * Handle the callchains from the stream in an ad-hoc radix tree and then
      5  * sort them in an rbtree.
      6  *
      7  * Using a radix for code path provides a fast retrieval and factorizes
      8  * memory use. Also that lets us use the paths in a hierarchical graph view.
      9  *
     10  */
     11 
     12 #include <stdlib.h>
     13 #include <stdio.h>
     14 #include <stdbool.h>
     15 #include <errno.h>
     16 #include <math.h>
     17 
     18 #include "hist.h"
     19 #include "util.h"
     20 #include "callchain.h"
     21 
     22 __thread struct callchain_cursor callchain_cursor;
     23 
     24 #define chain_for_each_child(child, parent)	\
     25 	list_for_each_entry(child, &parent->children, siblings)
     26 
     27 #define chain_for_each_child_safe(child, next, parent)	\
     28 	list_for_each_entry_safe(child, next, &parent->children, siblings)
     29 
     30 static void
     31 rb_insert_callchain(struct rb_root *root, struct callchain_node *chain,
     32 		    enum chain_mode mode)
     33 {
     34 	struct rb_node **p = &root->rb_node;
     35 	struct rb_node *parent = NULL;
     36 	struct callchain_node *rnode;
     37 	u64 chain_cumul = callchain_cumul_hits(chain);
     38 
     39 	while (*p) {
     40 		u64 rnode_cumul;
     41 
     42 		parent = *p;
     43 		rnode = rb_entry(parent, struct callchain_node, rb_node);
     44 		rnode_cumul = callchain_cumul_hits(rnode);
     45 
     46 		switch (mode) {
     47 		case CHAIN_FLAT:
     48 			if (rnode->hit < chain->hit)
     49 				p = &(*p)->rb_left;
     50 			else
     51 				p = &(*p)->rb_right;
     52 			break;
     53 		case CHAIN_GRAPH_ABS: /* Falldown */
     54 		case CHAIN_GRAPH_REL:
     55 			if (rnode_cumul < chain_cumul)
     56 				p = &(*p)->rb_left;
     57 			else
     58 				p = &(*p)->rb_right;
     59 			break;
     60 		case CHAIN_NONE:
     61 		default:
     62 			break;
     63 		}
     64 	}
     65 
     66 	rb_link_node(&chain->rb_node, parent, p);
     67 	rb_insert_color(&chain->rb_node, root);
     68 }
     69 
     70 static void
     71 __sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node,
     72 		  u64 min_hit)
     73 {
     74 	struct callchain_node *child;
     75 
     76 	chain_for_each_child(child, node)
     77 		__sort_chain_flat(rb_root, child, min_hit);
     78 
     79 	if (node->hit && node->hit >= min_hit)
     80 		rb_insert_callchain(rb_root, node, CHAIN_FLAT);
     81 }
     82 
     83 /*
     84  * Once we get every callchains from the stream, we can now
     85  * sort them by hit
     86  */
     87 static void
     88 sort_chain_flat(struct rb_root *rb_root, struct callchain_root *root,
     89 		u64 min_hit, struct callchain_param *param __maybe_unused)
     90 {
     91 	__sort_chain_flat(rb_root, &root->node, min_hit);
     92 }
     93 
     94 static void __sort_chain_graph_abs(struct callchain_node *node,
     95 				   u64 min_hit)
     96 {
     97 	struct callchain_node *child;
     98 
     99 	node->rb_root = RB_ROOT;
    100 
    101 	chain_for_each_child(child, node) {
    102 		__sort_chain_graph_abs(child, min_hit);
    103 		if (callchain_cumul_hits(child) >= min_hit)
    104 			rb_insert_callchain(&node->rb_root, child,
    105 					    CHAIN_GRAPH_ABS);
    106 	}
    107 }
    108 
    109 static void
    110 sort_chain_graph_abs(struct rb_root *rb_root, struct callchain_root *chain_root,
    111 		     u64 min_hit, struct callchain_param *param __maybe_unused)
    112 {
    113 	__sort_chain_graph_abs(&chain_root->node, min_hit);
    114 	rb_root->rb_node = chain_root->node.rb_root.rb_node;
    115 }
    116 
    117 static void __sort_chain_graph_rel(struct callchain_node *node,
    118 				   double min_percent)
    119 {
    120 	struct callchain_node *child;
    121 	u64 min_hit;
    122 
    123 	node->rb_root = RB_ROOT;
    124 	min_hit = ceil(node->children_hit * min_percent);
    125 
    126 	chain_for_each_child(child, node) {
    127 		__sort_chain_graph_rel(child, min_percent);
    128 		if (callchain_cumul_hits(child) >= min_hit)
    129 			rb_insert_callchain(&node->rb_root, child,
    130 					    CHAIN_GRAPH_REL);
    131 	}
    132 }
    133 
    134 static void
    135 sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_root *chain_root,
    136 		     u64 min_hit __maybe_unused, struct callchain_param *param)
    137 {
    138 	__sort_chain_graph_rel(&chain_root->node, param->min_percent / 100.0);
    139 	rb_root->rb_node = chain_root->node.rb_root.rb_node;
    140 }
    141 
    142 int callchain_register_param(struct callchain_param *param)
    143 {
    144 	switch (param->mode) {
    145 	case CHAIN_GRAPH_ABS:
    146 		param->sort = sort_chain_graph_abs;
    147 		break;
    148 	case CHAIN_GRAPH_REL:
    149 		param->sort = sort_chain_graph_rel;
    150 		break;
    151 	case CHAIN_FLAT:
    152 		param->sort = sort_chain_flat;
    153 		break;
    154 	case CHAIN_NONE:
    155 	default:
    156 		return -1;
    157 	}
    158 	return 0;
    159 }
    160 
    161 /*
    162  * Create a child for a parent. If inherit_children, then the new child
    163  * will become the new parent of it's parent children
    164  */
    165 static struct callchain_node *
    166 create_child(struct callchain_node *parent, bool inherit_children)
    167 {
    168 	struct callchain_node *new;
    169 
    170 	new = zalloc(sizeof(*new));
    171 	if (!new) {
    172 		perror("not enough memory to create child for code path tree");
    173 		return NULL;
    174 	}
    175 	new->parent = parent;
    176 	INIT_LIST_HEAD(&new->children);
    177 	INIT_LIST_HEAD(&new->val);
    178 
    179 	if (inherit_children) {
    180 		struct callchain_node *next;
    181 
    182 		list_splice(&parent->children, &new->children);
    183 		INIT_LIST_HEAD(&parent->children);
    184 
    185 		chain_for_each_child(next, new)
    186 			next->parent = new;
    187 	}
    188 	list_add_tail(&new->siblings, &parent->children);
    189 
    190 	return new;
    191 }
    192 
    193 
    194 /*
    195  * Fill the node with callchain values
    196  */
    197 static void
    198 fill_node(struct callchain_node *node, struct callchain_cursor *cursor)
    199 {
    200 	struct callchain_cursor_node *cursor_node;
    201 
    202 	node->val_nr = cursor->nr - cursor->pos;
    203 	if (!node->val_nr)
    204 		pr_warning("Warning: empty node in callchain tree\n");
    205 
    206 	cursor_node = callchain_cursor_current(cursor);
    207 
    208 	while (cursor_node) {
    209 		struct callchain_list *call;
    210 
    211 		call = zalloc(sizeof(*call));
    212 		if (!call) {
    213 			perror("not enough memory for the code path tree");
    214 			return;
    215 		}
    216 		call->ip = cursor_node->ip;
    217 		call->ms.sym = cursor_node->sym;
    218 		call->ms.map = cursor_node->map;
    219 		list_add_tail(&call->list, &node->val);
    220 
    221 		callchain_cursor_advance(cursor);
    222 		cursor_node = callchain_cursor_current(cursor);
    223 	}
    224 }
    225 
    226 static void
    227 add_child(struct callchain_node *parent,
    228 	  struct callchain_cursor *cursor,
    229 	  u64 period)
    230 {
    231 	struct callchain_node *new;
    232 
    233 	new = create_child(parent, false);
    234 	fill_node(new, cursor);
    235 
    236 	new->children_hit = 0;
    237 	new->hit = period;
    238 }
    239 
    240 /*
    241  * Split the parent in two parts (a new child is created) and
    242  * give a part of its callchain to the created child.
    243  * Then create another child to host the given callchain of new branch
    244  */
    245 static void
    246 split_add_child(struct callchain_node *parent,
    247 		struct callchain_cursor *cursor,
    248 		struct callchain_list *to_split,
    249 		u64 idx_parents, u64 idx_local, u64 period)
    250 {
    251 	struct callchain_node *new;
    252 	struct list_head *old_tail;
    253 	unsigned int idx_total = idx_parents + idx_local;
    254 
    255 	/* split */
    256 	new = create_child(parent, true);
    257 
    258 	/* split the callchain and move a part to the new child */
    259 	old_tail = parent->val.prev;
    260 	list_del_range(&to_split->list, old_tail);
    261 	new->val.next = &to_split->list;
    262 	new->val.prev = old_tail;
    263 	to_split->list.prev = &new->val;
    264 	old_tail->next = &new->val;
    265 
    266 	/* split the hits */
    267 	new->hit = parent->hit;
    268 	new->children_hit = parent->children_hit;
    269 	parent->children_hit = callchain_cumul_hits(new);
    270 	new->val_nr = parent->val_nr - idx_local;
    271 	parent->val_nr = idx_local;
    272 
    273 	/* create a new child for the new branch if any */
    274 	if (idx_total < cursor->nr) {
    275 		parent->hit = 0;
    276 		add_child(parent, cursor, period);
    277 		parent->children_hit += period;
    278 	} else {
    279 		parent->hit = period;
    280 	}
    281 }
    282 
    283 static int
    284 append_chain(struct callchain_node *root,
    285 	     struct callchain_cursor *cursor,
    286 	     u64 period);
    287 
    288 static void
    289 append_chain_children(struct callchain_node *root,
    290 		      struct callchain_cursor *cursor,
    291 		      u64 period)
    292 {
    293 	struct callchain_node *rnode;
    294 
    295 	/* lookup in childrens */
    296 	chain_for_each_child(rnode, root) {
    297 		unsigned int ret = append_chain(rnode, cursor, period);
    298 
    299 		if (!ret)
    300 			goto inc_children_hit;
    301 	}
    302 	/* nothing in children, add to the current node */
    303 	add_child(root, cursor, period);
    304 
    305 inc_children_hit:
    306 	root->children_hit += period;
    307 }
    308 
    309 static int
    310 append_chain(struct callchain_node *root,
    311 	     struct callchain_cursor *cursor,
    312 	     u64 period)
    313 {
    314 	struct callchain_cursor_node *curr_snap = cursor->curr;
    315 	struct callchain_list *cnode;
    316 	u64 start = cursor->pos;
    317 	bool found = false;
    318 	u64 matches;
    319 
    320 	/*
    321 	 * Lookup in the current node
    322 	 * If we have a symbol, then compare the start to match
    323 	 * anywhere inside a function, unless function
    324 	 * mode is disabled.
    325 	 */
    326 	list_for_each_entry(cnode, &root->val, list) {
    327 		struct callchain_cursor_node *node;
    328 		struct symbol *sym;
    329 
    330 		node = callchain_cursor_current(cursor);
    331 		if (!node)
    332 			break;
    333 
    334 		sym = node->sym;
    335 
    336 		if (cnode->ms.sym && sym &&
    337 		    callchain_param.key == CCKEY_FUNCTION) {
    338 			if (cnode->ms.sym->start != sym->start)
    339 				break;
    340 		} else if (cnode->ip != node->ip)
    341 			break;
    342 
    343 		if (!found)
    344 			found = true;
    345 
    346 		callchain_cursor_advance(cursor);
    347 	}
    348 
    349 	/* matches not, relay on the parent */
    350 	if (!found) {
    351 		cursor->curr = curr_snap;
    352 		cursor->pos = start;
    353 		return -1;
    354 	}
    355 
    356 	matches = cursor->pos - start;
    357 
    358 	/* we match only a part of the node. Split it and add the new chain */
    359 	if (matches < root->val_nr) {
    360 		split_add_child(root, cursor, cnode, start, matches, period);
    361 		return 0;
    362 	}
    363 
    364 	/* we match 100% of the path, increment the hit */
    365 	if (matches == root->val_nr && cursor->pos == cursor->nr) {
    366 		root->hit += period;
    367 		return 0;
    368 	}
    369 
    370 	/* We match the node and still have a part remaining */
    371 	append_chain_children(root, cursor, period);
    372 
    373 	return 0;
    374 }
    375 
    376 int callchain_append(struct callchain_root *root,
    377 		     struct callchain_cursor *cursor,
    378 		     u64 period)
    379 {
    380 	if (!cursor->nr)
    381 		return 0;
    382 
    383 	callchain_cursor_commit(cursor);
    384 
    385 	append_chain_children(&root->node, cursor, period);
    386 
    387 	if (cursor->nr > root->max_depth)
    388 		root->max_depth = cursor->nr;
    389 
    390 	return 0;
    391 }
    392 
    393 static int
    394 merge_chain_branch(struct callchain_cursor *cursor,
    395 		   struct callchain_node *dst, struct callchain_node *src)
    396 {
    397 	struct callchain_cursor_node **old_last = cursor->last;
    398 	struct callchain_node *child, *next_child;
    399 	struct callchain_list *list, *next_list;
    400 	int old_pos = cursor->nr;
    401 	int err = 0;
    402 
    403 	list_for_each_entry_safe(list, next_list, &src->val, list) {
    404 		callchain_cursor_append(cursor, list->ip,
    405 					list->ms.map, list->ms.sym);
    406 		list_del(&list->list);
    407 		free(list);
    408 	}
    409 
    410 	if (src->hit) {
    411 		callchain_cursor_commit(cursor);
    412 		append_chain_children(dst, cursor, src->hit);
    413 	}
    414 
    415 	chain_for_each_child_safe(child, next_child, src) {
    416 		err = merge_chain_branch(cursor, dst, child);
    417 		if (err)
    418 			break;
    419 
    420 		list_del(&child->siblings);
    421 		free(child);
    422 	}
    423 
    424 	cursor->nr = old_pos;
    425 	cursor->last = old_last;
    426 
    427 	return err;
    428 }
    429 
    430 int callchain_merge(struct callchain_cursor *cursor,
    431 		    struct callchain_root *dst, struct callchain_root *src)
    432 {
    433 	return merge_chain_branch(cursor, &dst->node, &src->node);
    434 }
    435 
    436 int callchain_cursor_append(struct callchain_cursor *cursor,
    437 			    u64 ip, struct map *map, struct symbol *sym)
    438 {
    439 	struct callchain_cursor_node *node = *cursor->last;
    440 
    441 	if (!node) {
    442 		node = calloc(1, sizeof(*node));
    443 		if (!node)
    444 			return -ENOMEM;
    445 
    446 		*cursor->last = node;
    447 	}
    448 
    449 	node->ip = ip;
    450 	node->map = map;
    451 	node->sym = sym;
    452 
    453 	cursor->nr++;
    454 
    455 	cursor->last = &node->next;
    456 
    457 	return 0;
    458 }
    459