1 /* 2 * Copyright (C) 2009-2011, Frederic Weisbecker <fweisbec (at) gmail.com> 3 * 4 * Handle the callchains from the stream in an ad-hoc radix tree and then 5 * sort them in an rbtree. 6 * 7 * Using a radix for code path provides a fast retrieval and factorizes 8 * memory use. Also that lets us use the paths in a hierarchical graph view. 9 * 10 */ 11 12 #include <stdlib.h> 13 #include <stdio.h> 14 #include <stdbool.h> 15 #include <errno.h> 16 #include <math.h> 17 18 #include "util.h" 19 #include "callchain.h" 20 21 bool ip_callchain__valid(struct ip_callchain *chain, 22 const union perf_event *event) 23 { 24 unsigned int chain_size = event->header.size; 25 chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event; 26 return chain->nr * sizeof(u64) <= chain_size; 27 } 28 29 #define chain_for_each_child(child, parent) \ 30 list_for_each_entry(child, &parent->children, siblings) 31 32 #define chain_for_each_child_safe(child, next, parent) \ 33 list_for_each_entry_safe(child, next, &parent->children, siblings) 34 35 static void 36 rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, 37 enum chain_mode mode) 38 { 39 struct rb_node **p = &root->rb_node; 40 struct rb_node *parent = NULL; 41 struct callchain_node *rnode; 42 u64 chain_cumul = callchain_cumul_hits(chain); 43 44 while (*p) { 45 u64 rnode_cumul; 46 47 parent = *p; 48 rnode = rb_entry(parent, struct callchain_node, rb_node); 49 rnode_cumul = callchain_cumul_hits(rnode); 50 51 switch (mode) { 52 case CHAIN_FLAT: 53 if (rnode->hit < chain->hit) 54 p = &(*p)->rb_left; 55 else 56 p = &(*p)->rb_right; 57 break; 58 case CHAIN_GRAPH_ABS: /* Falldown */ 59 case CHAIN_GRAPH_REL: 60 if (rnode_cumul < chain_cumul) 61 p = &(*p)->rb_left; 62 else 63 p = &(*p)->rb_right; 64 break; 65 case CHAIN_NONE: 66 default: 67 break; 68 } 69 } 70 71 rb_link_node(&chain->rb_node, parent, p); 72 rb_insert_color(&chain->rb_node, root); 73 } 74 75 static void 76 __sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node, 77 u64 min_hit) 78 { 79 struct callchain_node *child; 80 81 chain_for_each_child(child, node) 82 __sort_chain_flat(rb_root, child, min_hit); 83 84 if (node->hit && node->hit >= min_hit) 85 rb_insert_callchain(rb_root, node, CHAIN_FLAT); 86 } 87 88 /* 89 * Once we get every callchains from the stream, we can now 90 * sort them by hit 91 */ 92 static void 93 sort_chain_flat(struct rb_root *rb_root, struct callchain_root *root, 94 u64 min_hit, struct callchain_param *param __used) 95 { 96 __sort_chain_flat(rb_root, &root->node, min_hit); 97 } 98 99 static void __sort_chain_graph_abs(struct callchain_node *node, 100 u64 min_hit) 101 { 102 struct callchain_node *child; 103 104 node->rb_root = RB_ROOT; 105 106 chain_for_each_child(child, node) { 107 __sort_chain_graph_abs(child, min_hit); 108 if (callchain_cumul_hits(child) >= min_hit) 109 rb_insert_callchain(&node->rb_root, child, 110 CHAIN_GRAPH_ABS); 111 } 112 } 113 114 static void 115 sort_chain_graph_abs(struct rb_root *rb_root, struct callchain_root *chain_root, 116 u64 min_hit, struct callchain_param *param __used) 117 { 118 __sort_chain_graph_abs(&chain_root->node, min_hit); 119 rb_root->rb_node = chain_root->node.rb_root.rb_node; 120 } 121 122 static void __sort_chain_graph_rel(struct callchain_node *node, 123 double min_percent) 124 { 125 struct callchain_node *child; 126 u64 min_hit; 127 128 node->rb_root = RB_ROOT; 129 min_hit = ceil(node->children_hit * min_percent); 130 131 chain_for_each_child(child, node) { 132 __sort_chain_graph_rel(child, min_percent); 133 if (callchain_cumul_hits(child) >= min_hit) 134 rb_insert_callchain(&node->rb_root, child, 135 CHAIN_GRAPH_REL); 136 } 137 } 138 139 static void 140 sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_root *chain_root, 141 u64 min_hit __used, struct callchain_param *param) 142 { 143 __sort_chain_graph_rel(&chain_root->node, param->min_percent / 100.0); 144 rb_root->rb_node = chain_root->node.rb_root.rb_node; 145 } 146 147 int callchain_register_param(struct callchain_param *param) 148 { 149 switch (param->mode) { 150 case CHAIN_GRAPH_ABS: 151 param->sort = sort_chain_graph_abs; 152 break; 153 case CHAIN_GRAPH_REL: 154 param->sort = sort_chain_graph_rel; 155 break; 156 case CHAIN_FLAT: 157 param->sort = sort_chain_flat; 158 break; 159 case CHAIN_NONE: 160 default: 161 return -1; 162 } 163 return 0; 164 } 165 166 /* 167 * Create a child for a parent. If inherit_children, then the new child 168 * will become the new parent of it's parent children 169 */ 170 static struct callchain_node * 171 create_child(struct callchain_node *parent, bool inherit_children) 172 { 173 struct callchain_node *new; 174 175 new = zalloc(sizeof(*new)); 176 if (!new) { 177 perror("not enough memory to create child for code path tree"); 178 return NULL; 179 } 180 new->parent = parent; 181 INIT_LIST_HEAD(&new->children); 182 INIT_LIST_HEAD(&new->val); 183 184 if (inherit_children) { 185 struct callchain_node *next; 186 187 list_splice(&parent->children, &new->children); 188 INIT_LIST_HEAD(&parent->children); 189 190 chain_for_each_child(next, new) 191 next->parent = new; 192 } 193 list_add_tail(&new->siblings, &parent->children); 194 195 return new; 196 } 197 198 199 /* 200 * Fill the node with callchain values 201 */ 202 static void 203 fill_node(struct callchain_node *node, struct callchain_cursor *cursor) 204 { 205 struct callchain_cursor_node *cursor_node; 206 207 node->val_nr = cursor->nr - cursor->pos; 208 if (!node->val_nr) 209 pr_warning("Warning: empty node in callchain tree\n"); 210 211 cursor_node = callchain_cursor_current(cursor); 212 213 while (cursor_node) { 214 struct callchain_list *call; 215 216 call = zalloc(sizeof(*call)); 217 if (!call) { 218 perror("not enough memory for the code path tree"); 219 return; 220 } 221 call->ip = cursor_node->ip; 222 call->ms.sym = cursor_node->sym; 223 call->ms.map = cursor_node->map; 224 list_add_tail(&call->list, &node->val); 225 226 callchain_cursor_advance(cursor); 227 cursor_node = callchain_cursor_current(cursor); 228 } 229 } 230 231 static void 232 add_child(struct callchain_node *parent, 233 struct callchain_cursor *cursor, 234 u64 period) 235 { 236 struct callchain_node *new; 237 238 new = create_child(parent, false); 239 fill_node(new, cursor); 240 241 new->children_hit = 0; 242 new->hit = period; 243 } 244 245 /* 246 * Split the parent in two parts (a new child is created) and 247 * give a part of its callchain to the created child. 248 * Then create another child to host the given callchain of new branch 249 */ 250 static void 251 split_add_child(struct callchain_node *parent, 252 struct callchain_cursor *cursor, 253 struct callchain_list *to_split, 254 u64 idx_parents, u64 idx_local, u64 period) 255 { 256 struct callchain_node *new; 257 struct list_head *old_tail; 258 unsigned int idx_total = idx_parents + idx_local; 259 260 /* split */ 261 new = create_child(parent, true); 262 263 /* split the callchain and move a part to the new child */ 264 old_tail = parent->val.prev; 265 list_del_range(&to_split->list, old_tail); 266 new->val.next = &to_split->list; 267 new->val.prev = old_tail; 268 to_split->list.prev = &new->val; 269 old_tail->next = &new->val; 270 271 /* split the hits */ 272 new->hit = parent->hit; 273 new->children_hit = parent->children_hit; 274 parent->children_hit = callchain_cumul_hits(new); 275 new->val_nr = parent->val_nr - idx_local; 276 parent->val_nr = idx_local; 277 278 /* create a new child for the new branch if any */ 279 if (idx_total < cursor->nr) { 280 parent->hit = 0; 281 add_child(parent, cursor, period); 282 parent->children_hit += period; 283 } else { 284 parent->hit = period; 285 } 286 } 287 288 static int 289 append_chain(struct callchain_node *root, 290 struct callchain_cursor *cursor, 291 u64 period); 292 293 static void 294 append_chain_children(struct callchain_node *root, 295 struct callchain_cursor *cursor, 296 u64 period) 297 { 298 struct callchain_node *rnode; 299 300 /* lookup in childrens */ 301 chain_for_each_child(rnode, root) { 302 unsigned int ret = append_chain(rnode, cursor, period); 303 304 if (!ret) 305 goto inc_children_hit; 306 } 307 /* nothing in children, add to the current node */ 308 add_child(root, cursor, period); 309 310 inc_children_hit: 311 root->children_hit += period; 312 } 313 314 static int 315 append_chain(struct callchain_node *root, 316 struct callchain_cursor *cursor, 317 u64 period) 318 { 319 struct callchain_cursor_node *curr_snap = cursor->curr; 320 struct callchain_list *cnode; 321 u64 start = cursor->pos; 322 bool found = false; 323 u64 matches; 324 325 /* 326 * Lookup in the current node 327 * If we have a symbol, then compare the start to match 328 * anywhere inside a function. 329 */ 330 list_for_each_entry(cnode, &root->val, list) { 331 struct callchain_cursor_node *node; 332 struct symbol *sym; 333 334 node = callchain_cursor_current(cursor); 335 if (!node) 336 break; 337 338 sym = node->sym; 339 340 if (cnode->ms.sym && sym) { 341 if (cnode->ms.sym->start != sym->start) 342 break; 343 } else if (cnode->ip != node->ip) 344 break; 345 346 if (!found) 347 found = true; 348 349 callchain_cursor_advance(cursor); 350 } 351 352 /* matches not, relay on the parent */ 353 if (!found) { 354 cursor->curr = curr_snap; 355 cursor->pos = start; 356 return -1; 357 } 358 359 matches = cursor->pos - start; 360 361 /* we match only a part of the node. Split it and add the new chain */ 362 if (matches < root->val_nr) { 363 split_add_child(root, cursor, cnode, start, matches, period); 364 return 0; 365 } 366 367 /* we match 100% of the path, increment the hit */ 368 if (matches == root->val_nr && cursor->pos == cursor->nr) { 369 root->hit += period; 370 return 0; 371 } 372 373 /* We match the node and still have a part remaining */ 374 append_chain_children(root, cursor, period); 375 376 return 0; 377 } 378 379 int callchain_append(struct callchain_root *root, 380 struct callchain_cursor *cursor, 381 u64 period) 382 { 383 if (!cursor->nr) 384 return 0; 385 386 callchain_cursor_commit(cursor); 387 388 append_chain_children(&root->node, cursor, period); 389 390 if (cursor->nr > root->max_depth) 391 root->max_depth = cursor->nr; 392 393 return 0; 394 } 395 396 static int 397 merge_chain_branch(struct callchain_cursor *cursor, 398 struct callchain_node *dst, struct callchain_node *src) 399 { 400 struct callchain_cursor_node **old_last = cursor->last; 401 struct callchain_node *child, *next_child; 402 struct callchain_list *list, *next_list; 403 int old_pos = cursor->nr; 404 int err = 0; 405 406 list_for_each_entry_safe(list, next_list, &src->val, list) { 407 callchain_cursor_append(cursor, list->ip, 408 list->ms.map, list->ms.sym); 409 list_del(&list->list); 410 free(list); 411 } 412 413 if (src->hit) { 414 callchain_cursor_commit(cursor); 415 append_chain_children(dst, cursor, src->hit); 416 } 417 418 chain_for_each_child_safe(child, next_child, src) { 419 err = merge_chain_branch(cursor, dst, child); 420 if (err) 421 break; 422 423 list_del(&child->siblings); 424 free(child); 425 } 426 427 cursor->nr = old_pos; 428 cursor->last = old_last; 429 430 return err; 431 } 432 433 int callchain_merge(struct callchain_cursor *cursor, 434 struct callchain_root *dst, struct callchain_root *src) 435 { 436 return merge_chain_branch(cursor, &dst->node, &src->node); 437 } 438 439 int callchain_cursor_append(struct callchain_cursor *cursor, 440 u64 ip, struct map *map, struct symbol *sym) 441 { 442 struct callchain_cursor_node *node = *cursor->last; 443 444 if (!node) { 445 node = calloc(sizeof(*node), 1); 446 if (!node) 447 return -ENOMEM; 448 449 *cursor->last = node; 450 } 451 452 node->ip = ip; 453 node->map = map; 454 node->sym = sym; 455 456 cursor->nr++; 457 458 cursor->last = &node->next; 459 460 return 0; 461 } 462