xref: /openbmc/linux/tools/perf/util/block-range.c (revision 984a785f)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
270fbe057SPeter Zijlstra #include "block-range.h"
370fbe057SPeter Zijlstra #include "annotate.h"
4e7a795d3SArnaldo Carvalho de Melo #include <assert.h>
5e7a795d3SArnaldo Carvalho de Melo #include <stdlib.h>
670fbe057SPeter Zijlstra 
770fbe057SPeter Zijlstra struct {
870fbe057SPeter Zijlstra 	struct rb_root root;
970fbe057SPeter Zijlstra 	u64 blocks;
1070fbe057SPeter Zijlstra } block_ranges;
1170fbe057SPeter Zijlstra 
block_range__debug(void)1270fbe057SPeter Zijlstra static void block_range__debug(void)
1370fbe057SPeter Zijlstra {
14*984a785fSIan Rogers #ifndef NDEBUG
1570fbe057SPeter Zijlstra 	struct rb_node *rb;
1670fbe057SPeter Zijlstra 	u64 old = 0; /* NULL isn't executable */
1770fbe057SPeter Zijlstra 
1870fbe057SPeter Zijlstra 	for (rb = rb_first(&block_ranges.root); rb; rb = rb_next(rb)) {
1970fbe057SPeter Zijlstra 		struct block_range *entry = rb_entry(rb, struct block_range, node);
2070fbe057SPeter Zijlstra 
2170fbe057SPeter Zijlstra 		assert(old < entry->start);
2270fbe057SPeter Zijlstra 		assert(entry->start <= entry->end); /* single instruction block; jump to a jump */
2370fbe057SPeter Zijlstra 
2470fbe057SPeter Zijlstra 		old = entry->end;
2570fbe057SPeter Zijlstra 	}
2670fbe057SPeter Zijlstra #endif
2770fbe057SPeter Zijlstra }
2870fbe057SPeter Zijlstra 
block_range__find(u64 addr)2970fbe057SPeter Zijlstra struct block_range *block_range__find(u64 addr)
3070fbe057SPeter Zijlstra {
3170fbe057SPeter Zijlstra 	struct rb_node **p = &block_ranges.root.rb_node;
3270fbe057SPeter Zijlstra 	struct rb_node *parent = NULL;
3370fbe057SPeter Zijlstra 	struct block_range *entry;
3470fbe057SPeter Zijlstra 
3570fbe057SPeter Zijlstra 	while (*p != NULL) {
3670fbe057SPeter Zijlstra 		parent = *p;
3770fbe057SPeter Zijlstra 		entry = rb_entry(parent, struct block_range, node);
3870fbe057SPeter Zijlstra 
3970fbe057SPeter Zijlstra 		if (addr < entry->start)
4070fbe057SPeter Zijlstra 			p = &parent->rb_left;
4170fbe057SPeter Zijlstra 		else if (addr > entry->end)
4270fbe057SPeter Zijlstra 			p = &parent->rb_right;
4370fbe057SPeter Zijlstra 		else
4470fbe057SPeter Zijlstra 			return entry;
4570fbe057SPeter Zijlstra 	}
4670fbe057SPeter Zijlstra 
4770fbe057SPeter Zijlstra 	return NULL;
4870fbe057SPeter Zijlstra }
4970fbe057SPeter Zijlstra 
rb_link_left_of_node(struct rb_node * left,struct rb_node * node)5070fbe057SPeter Zijlstra static inline void rb_link_left_of_node(struct rb_node *left, struct rb_node *node)
5170fbe057SPeter Zijlstra {
5270fbe057SPeter Zijlstra 	struct rb_node **p = &node->rb_left;
5370fbe057SPeter Zijlstra 	while (*p) {
5470fbe057SPeter Zijlstra 		node = *p;
5570fbe057SPeter Zijlstra 		p = &node->rb_right;
5670fbe057SPeter Zijlstra 	}
5770fbe057SPeter Zijlstra 	rb_link_node(left, node, p);
5870fbe057SPeter Zijlstra }
5970fbe057SPeter Zijlstra 
rb_link_right_of_node(struct rb_node * right,struct rb_node * node)6070fbe057SPeter Zijlstra static inline void rb_link_right_of_node(struct rb_node *right, struct rb_node *node)
6170fbe057SPeter Zijlstra {
6270fbe057SPeter Zijlstra 	struct rb_node **p = &node->rb_right;
6370fbe057SPeter Zijlstra 	while (*p) {
6470fbe057SPeter Zijlstra 		node = *p;
6570fbe057SPeter Zijlstra 		p = &node->rb_left;
6670fbe057SPeter Zijlstra 	}
6770fbe057SPeter Zijlstra 	rb_link_node(right, node, p);
6870fbe057SPeter Zijlstra }
6970fbe057SPeter Zijlstra 
7070fbe057SPeter Zijlstra /**
7170fbe057SPeter Zijlstra  * block_range__create
7270fbe057SPeter Zijlstra  * @start: branch target starting this basic block
7370fbe057SPeter Zijlstra  * @end:   branch ending this basic block
7470fbe057SPeter Zijlstra  *
7570fbe057SPeter Zijlstra  * Create all the required block ranges to precisely span the given range.
7670fbe057SPeter Zijlstra  */
block_range__create(u64 start,u64 end)7770fbe057SPeter Zijlstra struct block_range_iter block_range__create(u64 start, u64 end)
7870fbe057SPeter Zijlstra {
7970fbe057SPeter Zijlstra 	struct rb_node **p = &block_ranges.root.rb_node;
8070fbe057SPeter Zijlstra 	struct rb_node *n, *parent = NULL;
8170fbe057SPeter Zijlstra 	struct block_range *next, *entry = NULL;
8270fbe057SPeter Zijlstra 	struct block_range_iter iter = { NULL, NULL };
8370fbe057SPeter Zijlstra 
8470fbe057SPeter Zijlstra 	while (*p != NULL) {
8570fbe057SPeter Zijlstra 		parent = *p;
8670fbe057SPeter Zijlstra 		entry = rb_entry(parent, struct block_range, node);
8770fbe057SPeter Zijlstra 
8870fbe057SPeter Zijlstra 		if (start < entry->start)
8970fbe057SPeter Zijlstra 			p = &parent->rb_left;
9070fbe057SPeter Zijlstra 		else if (start > entry->end)
9170fbe057SPeter Zijlstra 			p = &parent->rb_right;
9270fbe057SPeter Zijlstra 		else
9370fbe057SPeter Zijlstra 			break;
9470fbe057SPeter Zijlstra 	}
9570fbe057SPeter Zijlstra 
9670fbe057SPeter Zijlstra 	/*
9770fbe057SPeter Zijlstra 	 * Didn't find anything.. there's a hole at @start, however @end might
9870fbe057SPeter Zijlstra 	 * be inside/behind the next range.
9970fbe057SPeter Zijlstra 	 */
10070fbe057SPeter Zijlstra 	if (!*p) {
10170fbe057SPeter Zijlstra 		if (!entry) /* tree empty */
10270fbe057SPeter Zijlstra 			goto do_whole;
10370fbe057SPeter Zijlstra 
10470fbe057SPeter Zijlstra 		/*
10570fbe057SPeter Zijlstra 		 * If the last node is before, advance one to find the next.
10670fbe057SPeter Zijlstra 		 */
10770fbe057SPeter Zijlstra 		n = parent;
10870fbe057SPeter Zijlstra 		if (entry->end < start) {
10970fbe057SPeter Zijlstra 			n = rb_next(n);
11070fbe057SPeter Zijlstra 			if (!n)
11170fbe057SPeter Zijlstra 				goto do_whole;
11270fbe057SPeter Zijlstra 		}
11370fbe057SPeter Zijlstra 		next = rb_entry(n, struct block_range, node);
11470fbe057SPeter Zijlstra 
11570fbe057SPeter Zijlstra 		if (next->start <= end) { /* add head: [start...][n->start...] */
11670fbe057SPeter Zijlstra 			struct block_range *head = malloc(sizeof(struct block_range));
11770fbe057SPeter Zijlstra 			if (!head)
11870fbe057SPeter Zijlstra 				return iter;
11970fbe057SPeter Zijlstra 
12070fbe057SPeter Zijlstra 			*head = (struct block_range){
12170fbe057SPeter Zijlstra 				.start		= start,
12270fbe057SPeter Zijlstra 				.end		= next->start - 1,
12370fbe057SPeter Zijlstra 				.is_target	= 1,
12470fbe057SPeter Zijlstra 				.is_branch	= 0,
12570fbe057SPeter Zijlstra 			};
12670fbe057SPeter Zijlstra 
12770fbe057SPeter Zijlstra 			rb_link_left_of_node(&head->node, &next->node);
12870fbe057SPeter Zijlstra 			rb_insert_color(&head->node, &block_ranges.root);
12970fbe057SPeter Zijlstra 			block_range__debug();
13070fbe057SPeter Zijlstra 
13170fbe057SPeter Zijlstra 			iter.start = head;
13270fbe057SPeter Zijlstra 			goto do_tail;
13370fbe057SPeter Zijlstra 		}
13470fbe057SPeter Zijlstra 
13570fbe057SPeter Zijlstra do_whole:
13670fbe057SPeter Zijlstra 		/*
13770fbe057SPeter Zijlstra 		 * The whole [start..end] range is non-overlapping.
13870fbe057SPeter Zijlstra 		 */
13970fbe057SPeter Zijlstra 		entry = malloc(sizeof(struct block_range));
14070fbe057SPeter Zijlstra 		if (!entry)
14170fbe057SPeter Zijlstra 			return iter;
14270fbe057SPeter Zijlstra 
14370fbe057SPeter Zijlstra 		*entry = (struct block_range){
14470fbe057SPeter Zijlstra 			.start		= start,
14570fbe057SPeter Zijlstra 			.end		= end,
14670fbe057SPeter Zijlstra 			.is_target	= 1,
14770fbe057SPeter Zijlstra 			.is_branch	= 1,
14870fbe057SPeter Zijlstra 		};
14970fbe057SPeter Zijlstra 
15070fbe057SPeter Zijlstra 		rb_link_node(&entry->node, parent, p);
15170fbe057SPeter Zijlstra 		rb_insert_color(&entry->node, &block_ranges.root);
15270fbe057SPeter Zijlstra 		block_range__debug();
15370fbe057SPeter Zijlstra 
15470fbe057SPeter Zijlstra 		iter.start = entry;
15570fbe057SPeter Zijlstra 		iter.end   = entry;
15670fbe057SPeter Zijlstra 		goto done;
15770fbe057SPeter Zijlstra 	}
15870fbe057SPeter Zijlstra 
15970fbe057SPeter Zijlstra 	/*
16070fbe057SPeter Zijlstra 	 * We found a range that overlapped with ours, split if needed.
16170fbe057SPeter Zijlstra 	 */
16270fbe057SPeter Zijlstra 	if (entry->start < start) { /* split: [e->start...][start...] */
16370fbe057SPeter Zijlstra 		struct block_range *head = malloc(sizeof(struct block_range));
16470fbe057SPeter Zijlstra 		if (!head)
16570fbe057SPeter Zijlstra 			return iter;
16670fbe057SPeter Zijlstra 
16770fbe057SPeter Zijlstra 		*head = (struct block_range){
16870fbe057SPeter Zijlstra 			.start		= entry->start,
16970fbe057SPeter Zijlstra 			.end		= start - 1,
17070fbe057SPeter Zijlstra 			.is_target	= entry->is_target,
17170fbe057SPeter Zijlstra 			.is_branch	= 0,
17270fbe057SPeter Zijlstra 
17370fbe057SPeter Zijlstra 			.coverage	= entry->coverage,
17470fbe057SPeter Zijlstra 			.entry		= entry->entry,
17570fbe057SPeter Zijlstra 		};
17670fbe057SPeter Zijlstra 
17770fbe057SPeter Zijlstra 		entry->start		= start;
17870fbe057SPeter Zijlstra 		entry->is_target	= 1;
17970fbe057SPeter Zijlstra 		entry->entry		= 0;
18070fbe057SPeter Zijlstra 
18170fbe057SPeter Zijlstra 		rb_link_left_of_node(&head->node, &entry->node);
18270fbe057SPeter Zijlstra 		rb_insert_color(&head->node, &block_ranges.root);
18370fbe057SPeter Zijlstra 		block_range__debug();
18470fbe057SPeter Zijlstra 
18570fbe057SPeter Zijlstra 	} else if (entry->start == start)
18670fbe057SPeter Zijlstra 		entry->is_target = 1;
18770fbe057SPeter Zijlstra 
18870fbe057SPeter Zijlstra 	iter.start = entry;
18970fbe057SPeter Zijlstra 
19070fbe057SPeter Zijlstra do_tail:
19170fbe057SPeter Zijlstra 	/*
19270fbe057SPeter Zijlstra 	 * At this point we've got: @iter.start = [@start...] but @end can still be
19370fbe057SPeter Zijlstra 	 * inside or beyond it.
19470fbe057SPeter Zijlstra 	 */
19570fbe057SPeter Zijlstra 	entry = iter.start;
19670fbe057SPeter Zijlstra 	for (;;) {
19770fbe057SPeter Zijlstra 		/*
19870fbe057SPeter Zijlstra 		 * If @end is inside @entry, split.
19970fbe057SPeter Zijlstra 		 */
20070fbe057SPeter Zijlstra 		if (end < entry->end) { /* split: [...end][...e->end] */
20170fbe057SPeter Zijlstra 			struct block_range *tail = malloc(sizeof(struct block_range));
20270fbe057SPeter Zijlstra 			if (!tail)
20370fbe057SPeter Zijlstra 				return iter;
20470fbe057SPeter Zijlstra 
20570fbe057SPeter Zijlstra 			*tail = (struct block_range){
20670fbe057SPeter Zijlstra 				.start		= end + 1,
20770fbe057SPeter Zijlstra 				.end		= entry->end,
20870fbe057SPeter Zijlstra 				.is_target	= 0,
20970fbe057SPeter Zijlstra 				.is_branch	= entry->is_branch,
21070fbe057SPeter Zijlstra 
21170fbe057SPeter Zijlstra 				.coverage	= entry->coverage,
21270fbe057SPeter Zijlstra 				.taken		= entry->taken,
21370fbe057SPeter Zijlstra 				.pred		= entry->pred,
21470fbe057SPeter Zijlstra 			};
21570fbe057SPeter Zijlstra 
21670fbe057SPeter Zijlstra 			entry->end		= end;
21770fbe057SPeter Zijlstra 			entry->is_branch	= 1;
21870fbe057SPeter Zijlstra 			entry->taken		= 0;
21970fbe057SPeter Zijlstra 			entry->pred		= 0;
22070fbe057SPeter Zijlstra 
22170fbe057SPeter Zijlstra 			rb_link_right_of_node(&tail->node, &entry->node);
22270fbe057SPeter Zijlstra 			rb_insert_color(&tail->node, &block_ranges.root);
22370fbe057SPeter Zijlstra 			block_range__debug();
22470fbe057SPeter Zijlstra 
22570fbe057SPeter Zijlstra 			iter.end = entry;
22670fbe057SPeter Zijlstra 			goto done;
22770fbe057SPeter Zijlstra 		}
22870fbe057SPeter Zijlstra 
22970fbe057SPeter Zijlstra 		/*
23070fbe057SPeter Zijlstra 		 * If @end matches @entry, done
23170fbe057SPeter Zijlstra 		 */
23270fbe057SPeter Zijlstra 		if (end == entry->end) {
23370fbe057SPeter Zijlstra 			entry->is_branch = 1;
23470fbe057SPeter Zijlstra 			iter.end = entry;
23570fbe057SPeter Zijlstra 			goto done;
23670fbe057SPeter Zijlstra 		}
23770fbe057SPeter Zijlstra 
23870fbe057SPeter Zijlstra 		next = block_range__next(entry);
23970fbe057SPeter Zijlstra 		if (!next)
24070fbe057SPeter Zijlstra 			goto add_tail;
24170fbe057SPeter Zijlstra 
24270fbe057SPeter Zijlstra 		/*
24370fbe057SPeter Zijlstra 		 * If @end is in beyond @entry but not inside @next, add tail.
24470fbe057SPeter Zijlstra 		 */
24570fbe057SPeter Zijlstra 		if (end < next->start) { /* add tail: [...e->end][...end] */
24670fbe057SPeter Zijlstra 			struct block_range *tail;
24770fbe057SPeter Zijlstra add_tail:
24870fbe057SPeter Zijlstra 			tail = malloc(sizeof(struct block_range));
24970fbe057SPeter Zijlstra 			if (!tail)
25070fbe057SPeter Zijlstra 				return iter;
25170fbe057SPeter Zijlstra 
25270fbe057SPeter Zijlstra 			*tail = (struct block_range){
25370fbe057SPeter Zijlstra 				.start		= entry->end + 1,
25470fbe057SPeter Zijlstra 				.end		= end,
25570fbe057SPeter Zijlstra 				.is_target	= 0,
25670fbe057SPeter Zijlstra 				.is_branch	= 1,
25770fbe057SPeter Zijlstra 			};
25870fbe057SPeter Zijlstra 
25970fbe057SPeter Zijlstra 			rb_link_right_of_node(&tail->node, &entry->node);
26070fbe057SPeter Zijlstra 			rb_insert_color(&tail->node, &block_ranges.root);
26170fbe057SPeter Zijlstra 			block_range__debug();
26270fbe057SPeter Zijlstra 
26370fbe057SPeter Zijlstra 			iter.end = tail;
26470fbe057SPeter Zijlstra 			goto done;
26570fbe057SPeter Zijlstra 		}
26670fbe057SPeter Zijlstra 
26770fbe057SPeter Zijlstra 		/*
26870fbe057SPeter Zijlstra 		 * If there is a hole between @entry and @next, fill it.
26970fbe057SPeter Zijlstra 		 */
27070fbe057SPeter Zijlstra 		if (entry->end + 1 != next->start) {
27170fbe057SPeter Zijlstra 			struct block_range *hole = malloc(sizeof(struct block_range));
27270fbe057SPeter Zijlstra 			if (!hole)
27370fbe057SPeter Zijlstra 				return iter;
27470fbe057SPeter Zijlstra 
27570fbe057SPeter Zijlstra 			*hole = (struct block_range){
27670fbe057SPeter Zijlstra 				.start		= entry->end + 1,
27770fbe057SPeter Zijlstra 				.end		= next->start - 1,
27870fbe057SPeter Zijlstra 				.is_target	= 0,
27970fbe057SPeter Zijlstra 				.is_branch	= 0,
28070fbe057SPeter Zijlstra 			};
28170fbe057SPeter Zijlstra 
28270fbe057SPeter Zijlstra 			rb_link_left_of_node(&hole->node, &next->node);
28370fbe057SPeter Zijlstra 			rb_insert_color(&hole->node, &block_ranges.root);
28470fbe057SPeter Zijlstra 			block_range__debug();
28570fbe057SPeter Zijlstra 		}
28670fbe057SPeter Zijlstra 
28770fbe057SPeter Zijlstra 		entry = next;
28870fbe057SPeter Zijlstra 	}
28970fbe057SPeter Zijlstra 
29070fbe057SPeter Zijlstra done:
29170fbe057SPeter Zijlstra 	assert(iter.start->start == start && iter.start->is_target);
29270fbe057SPeter Zijlstra 	assert(iter.end->end == end && iter.end->is_branch);
29370fbe057SPeter Zijlstra 
29470fbe057SPeter Zijlstra 	block_ranges.blocks++;
29570fbe057SPeter Zijlstra 
29670fbe057SPeter Zijlstra 	return iter;
29770fbe057SPeter Zijlstra }
29870fbe057SPeter Zijlstra 
29970fbe057SPeter Zijlstra 
30070fbe057SPeter Zijlstra /*
30170fbe057SPeter Zijlstra  * Compute coverage as:
30270fbe057SPeter Zijlstra  *
30370fbe057SPeter Zijlstra  *    br->coverage / br->sym->max_coverage
30470fbe057SPeter Zijlstra  *
30570fbe057SPeter Zijlstra  * This ensures each symbol has a 100% spot, to reflect that each symbol has a
30670fbe057SPeter Zijlstra  * most covered section.
30770fbe057SPeter Zijlstra  *
30870fbe057SPeter Zijlstra  * Returns [0-1] for coverage and -1 if we had no data what so ever or the
30970fbe057SPeter Zijlstra  * symbol does not exist.
31070fbe057SPeter Zijlstra  */
block_range__coverage(struct block_range * br)31170fbe057SPeter Zijlstra double block_range__coverage(struct block_range *br)
31270fbe057SPeter Zijlstra {
31370fbe057SPeter Zijlstra 	struct symbol *sym;
31470fbe057SPeter Zijlstra 
31570fbe057SPeter Zijlstra 	if (!br) {
31670fbe057SPeter Zijlstra 		if (block_ranges.blocks)
31770fbe057SPeter Zijlstra 			return 0;
31870fbe057SPeter Zijlstra 
31970fbe057SPeter Zijlstra 		return -1;
32070fbe057SPeter Zijlstra 	}
32170fbe057SPeter Zijlstra 
32270fbe057SPeter Zijlstra 	sym = br->sym;
32370fbe057SPeter Zijlstra 	if (!sym)
32470fbe057SPeter Zijlstra 		return -1;
32570fbe057SPeter Zijlstra 
32670fbe057SPeter Zijlstra 	return (double)br->coverage / symbol__annotation(sym)->max_coverage;
32770fbe057SPeter Zijlstra }
328