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