block-range.c (7024B)
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}