linux/tools/perf/util/block-range.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include "block-range.h"
   3#include "annotate.h"
   4#include <assert.h>
   5#include <stdlib.h>
   6
   7struct {
   8        struct rb_root root;
   9        u64 blocks;
  10} block_ranges;
  11
  12static void block_range__debug(void)
  13{
  14        /*
  15         * XXX still paranoid for now; see if we can make this depend on
  16         * DEBUG=1 builds.
  17         */
  18#if 1
  19        struct rb_node *rb;
  20        u64 old = 0; /* NULL isn't executable */
  21
  22        for (rb = rb_first(&block_ranges.root); rb; rb = rb_next(rb)) {
  23                struct block_range *entry = rb_entry(rb, struct block_range, node);
  24
  25                assert(old < entry->start);
  26                assert(entry->start <= entry->end); /* single instruction block; jump to a jump */
  27
  28                old = entry->end;
  29        }
  30#endif
  31}
  32
  33struct block_range *block_range__find(u64 addr)
  34{
  35        struct rb_node **p = &block_ranges.root.rb_node;
  36        struct rb_node *parent = NULL;
  37        struct block_range *entry;
  38
  39        while (*p != NULL) {
  40                parent = *p;
  41                entry = rb_entry(parent, struct block_range, node);
  42
  43                if (addr < entry->start)
  44                        p = &parent->rb_left;
  45                else if (addr > entry->end)
  46                        p = &parent->rb_right;
  47                else
  48                        return entry;
  49        }
  50
  51        return NULL;
  52}
  53
  54static inline void rb_link_left_of_node(struct rb_node *left, struct rb_node *node)
  55{
  56        struct rb_node **p = &node->rb_left;
  57        while (*p) {
  58                node = *p;
  59                p = &node->rb_right;
  60        }
  61        rb_link_node(left, node, p);
  62}
  63
  64static inline void rb_link_right_of_node(struct rb_node *right, struct rb_node *node)
  65{
  66        struct rb_node **p = &node->rb_right;
  67        while (*p) {
  68                node = *p;
  69                p = &node->rb_left;
  70        }
  71        rb_link_node(right, node, p);
  72}
  73
  74/**
  75 * block_range__create
  76 * @start: branch target starting this basic block
  77 * @end:   branch ending this basic block
  78 *
  79 * Create all the required block ranges to precisely span the given range.
  80 */
  81struct block_range_iter block_range__create(u64 start, u64 end)
  82{
  83        struct rb_node **p = &block_ranges.root.rb_node;
  84        struct rb_node *n, *parent = NULL;
  85        struct block_range *next, *entry = NULL;
  86        struct block_range_iter iter = { NULL, NULL };
  87
  88        while (*p != NULL) {
  89                parent = *p;
  90                entry = rb_entry(parent, struct block_range, node);
  91
  92                if (start < entry->start)
  93                        p = &parent->rb_left;
  94                else if (start > entry->end)
  95                        p = &parent->rb_right;
  96                else
  97                        break;
  98        }
  99
 100        /*
 101         * Didn't find anything.. there's a hole at @start, however @end might
 102         * be inside/behind the next range.
 103         */
 104        if (!*p) {
 105                if (!entry) /* tree empty */
 106                        goto do_whole;
 107
 108                /*
 109                 * If the last node is before, advance one to find the next.
 110                 */
 111                n = parent;
 112                if (entry->end < start) {
 113                        n = rb_next(n);
 114                        if (!n)
 115                                goto do_whole;
 116                }
 117                next = rb_entry(n, struct block_range, node);
 118
 119                if (next->start <= end) { /* add head: [start...][n->start...] */
 120                        struct block_range *head = malloc(sizeof(struct block_range));
 121                        if (!head)
 122                                return iter;
 123
 124                        *head = (struct block_range){
 125                                .start          = start,
 126                                .end            = next->start - 1,
 127                                .is_target      = 1,
 128                                .is_branch      = 0,
 129                        };
 130
 131                        rb_link_left_of_node(&head->node, &next->node);
 132                        rb_insert_color(&head->node, &block_ranges.root);
 133                        block_range__debug();
 134
 135                        iter.start = head;
 136                        goto do_tail;
 137                }
 138
 139do_whole:
 140                /*
 141                 * The whole [start..end] range is non-overlapping.
 142                 */
 143                entry = malloc(sizeof(struct block_range));
 144                if (!entry)
 145                        return iter;
 146
 147                *entry = (struct block_range){
 148                        .start          = start,
 149                        .end            = end,
 150                        .is_target      = 1,
 151                        .is_branch      = 1,
 152                };
 153
 154                rb_link_node(&entry->node, parent, p);
 155                rb_insert_color(&entry->node, &block_ranges.root);
 156                block_range__debug();
 157
 158                iter.start = entry;
 159                iter.end   = entry;
 160                goto done;
 161        }
 162
 163        /*
 164         * We found a range that overlapped with ours, split if needed.
 165         */
 166        if (entry->start < start) { /* split: [e->start...][start...] */
 167                struct block_range *head = malloc(sizeof(struct block_range));
 168                if (!head)
 169                        return iter;
 170
 171                *head = (struct block_range){
 172                        .start          = entry->start,
 173                        .end            = start - 1,
 174                        .is_target      = entry->is_target,
 175                        .is_branch      = 0,
 176
 177                        .coverage       = entry->coverage,
 178                        .entry          = entry->entry,
 179                };
 180
 181                entry->start            = start;
 182                entry->is_target        = 1;
 183                entry->entry            = 0;
 184
 185                rb_link_left_of_node(&head->node, &entry->node);
 186                rb_insert_color(&head->node, &block_ranges.root);
 187                block_range__debug();
 188
 189        } else if (entry->start == start)
 190                entry->is_target = 1;
 191
 192        iter.start = entry;
 193
 194do_tail:
 195        /*
 196         * At this point we've got: @iter.start = [@start...] but @end can still be
 197         * inside or beyond it.
 198         */
 199        entry = iter.start;
 200        for (;;) {
 201                /*
 202                 * If @end is inside @entry, split.
 203                 */
 204                if (end < entry->end) { /* split: [...end][...e->end] */
 205                        struct block_range *tail = malloc(sizeof(struct block_range));
 206                        if (!tail)
 207                                return iter;
 208
 209                        *tail = (struct block_range){
 210                                .start          = end + 1,
 211                                .end            = entry->end,
 212                                .is_target      = 0,
 213                                .is_branch      = entry->is_branch,
 214
 215                                .coverage       = entry->coverage,
 216                                .taken          = entry->taken,
 217                                .pred           = entry->pred,
 218                        };
 219
 220                        entry->end              = end;
 221                        entry->is_branch        = 1;
 222                        entry->taken            = 0;
 223                        entry->pred             = 0;
 224
 225                        rb_link_right_of_node(&tail->node, &entry->node);
 226                        rb_insert_color(&tail->node, &block_ranges.root);
 227                        block_range__debug();
 228
 229                        iter.end = entry;
 230                        goto done;
 231                }
 232
 233                /*
 234                 * If @end matches @entry, done
 235                 */
 236                if (end == entry->end) {
 237                        entry->is_branch = 1;
 238                        iter.end = entry;
 239                        goto done;
 240                }
 241
 242                next = block_range__next(entry);
 243                if (!next)
 244                        goto add_tail;
 245
 246                /*
 247                 * If @end is in beyond @entry but not inside @next, add tail.
 248                 */
 249                if (end < next->start) { /* add tail: [...e->end][...end] */
 250                        struct block_range *tail;
 251add_tail:
 252                        tail = malloc(sizeof(struct block_range));
 253                        if (!tail)
 254                                return iter;
 255
 256                        *tail = (struct block_range){
 257                                .start          = entry->end + 1,
 258                                .end            = end,
 259                                .is_target      = 0,
 260                                .is_branch      = 1,
 261                        };
 262
 263                        rb_link_right_of_node(&tail->node, &entry->node);
 264                        rb_insert_color(&tail->node, &block_ranges.root);
 265                        block_range__debug();
 266
 267                        iter.end = tail;
 268                        goto done;
 269                }
 270
 271                /*
 272                 * If there is a hole between @entry and @next, fill it.
 273                 */
 274                if (entry->end + 1 != next->start) {
 275                        struct block_range *hole = malloc(sizeof(struct block_range));
 276                        if (!hole)
 277                                return iter;
 278
 279                        *hole = (struct block_range){
 280                                .start          = entry->end + 1,
 281                                .end            = next->start - 1,
 282                                .is_target      = 0,
 283                                .is_branch      = 0,
 284                        };
 285
 286                        rb_link_left_of_node(&hole->node, &next->node);
 287                        rb_insert_color(&hole->node, &block_ranges.root);
 288                        block_range__debug();
 289                }
 290
 291                entry = next;
 292        }
 293
 294done:
 295        assert(iter.start->start == start && iter.start->is_target);
 296        assert(iter.end->end == end && iter.end->is_branch);
 297
 298        block_ranges.blocks++;
 299
 300        return iter;
 301}
 302
 303
 304/*
 305 * Compute coverage as:
 306 *
 307 *    br->coverage / br->sym->max_coverage
 308 *
 309 * This ensures each symbol has a 100% spot, to reflect that each symbol has a
 310 * most covered section.
 311 *
 312 * Returns [0-1] for coverage and -1 if we had no data what so ever or the
 313 * symbol does not exist.
 314 */
 315double block_range__coverage(struct block_range *br)
 316{
 317        struct symbol *sym;
 318
 319        if (!br) {
 320                if (block_ranges.blocks)
 321                        return 0;
 322
 323                return -1;
 324        }
 325
 326        sym = br->sym;
 327        if (!sym)
 328                return -1;
 329
 330        return (double)br->coverage / symbol__annotation(sym)->max_coverage;
 331}
 332