123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329 |
- #include "block-range.h"
- #include "annotate.h"
- struct {
- struct rb_root root;
- u64 blocks;
- } block_ranges;
- static void block_range__debug(void)
- {
- /*
- * XXX still paranoid for now; see if we can make this depend on
- * DEBUG=1 builds.
- */
- #if 1
- struct rb_node *rb;
- u64 old = 0; /* NULL isn't executable */
- for (rb = rb_first(&block_ranges.root); rb; rb = rb_next(rb)) {
- struct block_range *entry = rb_entry(rb, struct block_range, node);
- assert(old < entry->start);
- assert(entry->start <= entry->end); /* single instruction block; jump to a jump */
- old = entry->end;
- }
- #endif
- }
- struct block_range *block_range__find(u64 addr)
- {
- struct rb_node **p = &block_ranges.root.rb_node;
- struct rb_node *parent = NULL;
- struct block_range *entry;
- while (*p != NULL) {
- parent = *p;
- entry = rb_entry(parent, struct block_range, node);
- if (addr < entry->start)
- p = &parent->rb_left;
- else if (addr > entry->end)
- p = &parent->rb_right;
- else
- return entry;
- }
- return NULL;
- }
- static inline void rb_link_left_of_node(struct rb_node *left, struct rb_node *node)
- {
- struct rb_node **p = &node->rb_left;
- while (*p) {
- node = *p;
- p = &node->rb_right;
- }
- rb_link_node(left, node, p);
- }
- static inline void rb_link_right_of_node(struct rb_node *right, struct rb_node *node)
- {
- struct rb_node **p = &node->rb_right;
- while (*p) {
- node = *p;
- p = &node->rb_left;
- }
- rb_link_node(right, node, p);
- }
- /**
- * block_range__create
- * @start: branch target starting this basic block
- * @end: branch ending this basic block
- *
- * Create all the required block ranges to precisely span the given range.
- */
- struct block_range_iter block_range__create(u64 start, u64 end)
- {
- struct rb_node **p = &block_ranges.root.rb_node;
- struct rb_node *n, *parent = NULL;
- struct block_range *next, *entry = NULL;
- struct block_range_iter iter = { NULL, NULL };
- while (*p != NULL) {
- parent = *p;
- entry = rb_entry(parent, struct block_range, node);
- if (start < entry->start)
- p = &parent->rb_left;
- else if (start > entry->end)
- p = &parent->rb_right;
- else
- break;
- }
- /*
- * Didn't find anything.. there's a hole at @start, however @end might
- * be inside/behind the next range.
- */
- if (!*p) {
- if (!entry) /* tree empty */
- goto do_whole;
- /*
- * If the last node is before, advance one to find the next.
- */
- n = parent;
- if (entry->end < start) {
- n = rb_next(n);
- if (!n)
- goto do_whole;
- }
- next = rb_entry(n, struct block_range, node);
- if (next->start <= end) { /* add head: [start...][n->start...] */
- struct block_range *head = malloc(sizeof(struct block_range));
- if (!head)
- return iter;
- *head = (struct block_range){
- .start = start,
- .end = next->start - 1,
- .is_target = 1,
- .is_branch = 0,
- };
- rb_link_left_of_node(&head->node, &next->node);
- rb_insert_color(&head->node, &block_ranges.root);
- block_range__debug();
- iter.start = head;
- goto do_tail;
- }
- do_whole:
- /*
- * The whole [start..end] range is non-overlapping.
- */
- entry = malloc(sizeof(struct block_range));
- if (!entry)
- return iter;
- *entry = (struct block_range){
- .start = start,
- .end = end,
- .is_target = 1,
- .is_branch = 1,
- };
- rb_link_node(&entry->node, parent, p);
- rb_insert_color(&entry->node, &block_ranges.root);
- block_range__debug();
- iter.start = entry;
- iter.end = entry;
- goto done;
- }
- /*
- * We found a range that overlapped with ours, split if needed.
- */
- if (entry->start < start) { /* split: [e->start...][start...] */
- struct block_range *head = malloc(sizeof(struct block_range));
- if (!head)
- return iter;
- *head = (struct block_range){
- .start = entry->start,
- .end = start - 1,
- .is_target = entry->is_target,
- .is_branch = 0,
- .coverage = entry->coverage,
- .entry = entry->entry,
- };
- entry->start = start;
- entry->is_target = 1;
- entry->entry = 0;
- rb_link_left_of_node(&head->node, &entry->node);
- rb_insert_color(&head->node, &block_ranges.root);
- block_range__debug();
- } else if (entry->start == start)
- entry->is_target = 1;
- iter.start = entry;
- do_tail:
- /*
- * At this point we've got: @iter.start = [@start...] but @end can still be
- * inside or beyond it.
- */
- entry = iter.start;
- for (;;) {
- /*
- * If @end is inside @entry, split.
- */
- if (end < entry->end) { /* split: [...end][...e->end] */
- struct block_range *tail = malloc(sizeof(struct block_range));
- if (!tail)
- return iter;
- *tail = (struct block_range){
- .start = end + 1,
- .end = entry->end,
- .is_target = 0,
- .is_branch = entry->is_branch,
- .coverage = entry->coverage,
- .taken = entry->taken,
- .pred = entry->pred,
- };
- entry->end = end;
- entry->is_branch = 1;
- entry->taken = 0;
- entry->pred = 0;
- rb_link_right_of_node(&tail->node, &entry->node);
- rb_insert_color(&tail->node, &block_ranges.root);
- block_range__debug();
- iter.end = entry;
- goto done;
- }
- /*
- * If @end matches @entry, done
- */
- if (end == entry->end) {
- entry->is_branch = 1;
- iter.end = entry;
- goto done;
- }
- next = block_range__next(entry);
- if (!next)
- goto add_tail;
- /*
- * If @end is in beyond @entry but not inside @next, add tail.
- */
- if (end < next->start) { /* add tail: [...e->end][...end] */
- struct block_range *tail;
- add_tail:
- tail = malloc(sizeof(struct block_range));
- if (!tail)
- return iter;
- *tail = (struct block_range){
- .start = entry->end + 1,
- .end = end,
- .is_target = 0,
- .is_branch = 1,
- };
- rb_link_right_of_node(&tail->node, &entry->node);
- rb_insert_color(&tail->node, &block_ranges.root);
- block_range__debug();
- iter.end = tail;
- goto done;
- }
- /*
- * If there is a hole between @entry and @next, fill it.
- */
- if (entry->end + 1 != next->start) {
- struct block_range *hole = malloc(sizeof(struct block_range));
- if (!hole)
- return iter;
- *hole = (struct block_range){
- .start = entry->end + 1,
- .end = next->start - 1,
- .is_target = 0,
- .is_branch = 0,
- };
- rb_link_left_of_node(&hole->node, &next->node);
- rb_insert_color(&hole->node, &block_ranges.root);
- block_range__debug();
- }
- entry = next;
- }
- done:
- assert(iter.start->start == start && iter.start->is_target);
- assert(iter.end->end == end && iter.end->is_branch);
- block_ranges.blocks++;
- return iter;
- }
- /*
- * Compute coverage as:
- *
- * br->coverage / br->sym->max_coverage
- *
- * This ensures each symbol has a 100% spot, to reflect that each symbol has a
- * most covered section.
- *
- * Returns [0-1] for coverage and -1 if we had no data what so ever or the
- * symbol does not exist.
- */
- double block_range__coverage(struct block_range *br)
- {
- struct symbol *sym;
- if (!br) {
- if (block_ranges.blocks)
- return 0;
- return -1;
- }
- sym = br->sym;
- if (!sym)
- return -1;
- return (double)br->coverage / symbol__annotation(sym)->max_coverage;
- }
|