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