1 /* 2 * Copyright (C) 2009-2010, Frederic Weisbecker <fweisbec@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 "callchain.h" 19 20 bool ip_callchain__valid(struct ip_callchain *chain, event_t *event) 21 { 22 unsigned int chain_size = event->header.size; 23 chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event; 24 return chain->nr * sizeof(u64) <= chain_size; 25 } 26 27 #define chain_for_each_child(child, parent) \ 28 list_for_each_entry(child, &parent->children, brothers) 29 30 static void 31 rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, 32 enum chain_mode mode) 33 { 34 struct rb_node **p = &root->rb_node; 35 struct rb_node *parent = NULL; 36 struct callchain_node *rnode; 37 u64 chain_cumul = cumul_hits(chain); 38 39 while (*p) { 40 u64 rnode_cumul; 41 42 parent = *p; 43 rnode = rb_entry(parent, struct callchain_node, rb_node); 44 rnode_cumul = cumul_hits(rnode); 45 46 switch (mode) { 47 case CHAIN_FLAT: 48 if (rnode->hit < chain->hit) 49 p = &(*p)->rb_left; 50 else 51 p = &(*p)->rb_right; 52 break; 53 case CHAIN_GRAPH_ABS: /* Falldown */ 54 case CHAIN_GRAPH_REL: 55 if (rnode_cumul < chain_cumul) 56 p = &(*p)->rb_left; 57 else 58 p = &(*p)->rb_right; 59 break; 60 case CHAIN_NONE: 61 default: 62 break; 63 } 64 } 65 66 rb_link_node(&chain->rb_node, parent, p); 67 rb_insert_color(&chain->rb_node, root); 68 } 69 70 static void 71 __sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node, 72 u64 min_hit) 73 { 74 struct callchain_node *child; 75 76 chain_for_each_child(child, node) 77 __sort_chain_flat(rb_root, child, min_hit); 78 79 if (node->hit && node->hit >= min_hit) 80 rb_insert_callchain(rb_root, node, CHAIN_FLAT); 81 } 82 83 /* 84 * Once we get every callchains from the stream, we can now 85 * sort them by hit 86 */ 87 static void 88 sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node, 89 u64 min_hit, struct callchain_param *param __used) 90 { 91 __sort_chain_flat(rb_root, node, min_hit); 92 } 93 94 static void __sort_chain_graph_abs(struct callchain_node *node, 95 u64 min_hit) 96 { 97 struct callchain_node *child; 98 99 node->rb_root = RB_ROOT; 100 101 chain_for_each_child(child, node) { 102 __sort_chain_graph_abs(child, min_hit); 103 if (cumul_hits(child) >= min_hit) 104 rb_insert_callchain(&node->rb_root, child, 105 CHAIN_GRAPH_ABS); 106 } 107 } 108 109 static void 110 sort_chain_graph_abs(struct rb_root *rb_root, struct callchain_node *chain_root, 111 u64 min_hit, struct callchain_param *param __used) 112 { 113 __sort_chain_graph_abs(chain_root, min_hit); 114 rb_root->rb_node = chain_root->rb_root.rb_node; 115 } 116 117 static void __sort_chain_graph_rel(struct callchain_node *node, 118 double min_percent) 119 { 120 struct callchain_node *child; 121 u64 min_hit; 122 123 node->rb_root = RB_ROOT; 124 min_hit = ceil(node->children_hit * min_percent); 125 126 chain_for_each_child(child, node) { 127 __sort_chain_graph_rel(child, min_percent); 128 if (cumul_hits(child) >= min_hit) 129 rb_insert_callchain(&node->rb_root, child, 130 CHAIN_GRAPH_REL); 131 } 132 } 133 134 static void 135 sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_node *chain_root, 136 u64 min_hit __used, struct callchain_param *param) 137 { 138 __sort_chain_graph_rel(chain_root, param->min_percent / 100.0); 139 rb_root->rb_node = chain_root->rb_root.rb_node; 140 } 141 142 int register_callchain_param(struct callchain_param *param) 143 { 144 switch (param->mode) { 145 case CHAIN_GRAPH_ABS: 146 param->sort = sort_chain_graph_abs; 147 break; 148 case CHAIN_GRAPH_REL: 149 param->sort = sort_chain_graph_rel; 150 break; 151 case CHAIN_FLAT: 152 param->sort = sort_chain_flat; 153 break; 154 case CHAIN_NONE: 155 default: 156 return -1; 157 } 158 return 0; 159 } 160 161 /* 162 * Create a child for a parent. If inherit_children, then the new child 163 * will become the new parent of it's parent children 164 */ 165 static struct callchain_node * 166 create_child(struct callchain_node *parent, bool inherit_children) 167 { 168 struct callchain_node *new; 169 170 new = zalloc(sizeof(*new)); 171 if (!new) { 172 perror("not enough memory to create child for code path tree"); 173 return NULL; 174 } 175 new->parent = parent; 176 INIT_LIST_HEAD(&new->children); 177 INIT_LIST_HEAD(&new->val); 178 179 if (inherit_children) { 180 struct callchain_node *next; 181 182 list_splice(&parent->children, &new->children); 183 INIT_LIST_HEAD(&parent->children); 184 185 chain_for_each_child(next, new) 186 next->parent = new; 187 } 188 list_add_tail(&new->brothers, &parent->children); 189 190 return new; 191 } 192 193 194 struct resolved_ip { 195 u64 ip; 196 struct map_symbol ms; 197 }; 198 199 struct resolved_chain { 200 u64 nr; 201 struct resolved_ip ips[0]; 202 }; 203 204 205 /* 206 * Fill the node with callchain values 207 */ 208 static void 209 fill_node(struct callchain_node *node, struct resolved_chain *chain, int start) 210 { 211 unsigned int i; 212 213 for (i = start; i < chain->nr; i++) { 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 = chain->ips[i].ip; 222 call->ms = chain->ips[i].ms; 223 list_add_tail(&call->list, &node->val); 224 } 225 node->val_nr = chain->nr - start; 226 if (!node->val_nr) 227 pr_warning("Warning: empty node in callchain tree\n"); 228 } 229 230 static void 231 add_child(struct callchain_node *parent, struct resolved_chain *chain, 232 int start) 233 { 234 struct callchain_node *new; 235 236 new = create_child(parent, false); 237 fill_node(new, chain, start); 238 239 new->children_hit = 0; 240 new->hit = 1; 241 } 242 243 /* 244 * Split the parent in two parts (a new child is created) and 245 * give a part of its callchain to the created child. 246 * Then create another child to host the given callchain of new branch 247 */ 248 static void 249 split_add_child(struct callchain_node *parent, struct resolved_chain *chain, 250 struct callchain_list *to_split, int idx_parents, int idx_local) 251 { 252 struct callchain_node *new; 253 struct list_head *old_tail; 254 unsigned int idx_total = idx_parents + idx_local; 255 256 /* split */ 257 new = create_child(parent, true); 258 259 /* split the callchain and move a part to the new child */ 260 old_tail = parent->val.prev; 261 list_del_range(&to_split->list, old_tail); 262 new->val.next = &to_split->list; 263 new->val.prev = old_tail; 264 to_split->list.prev = &new->val; 265 old_tail->next = &new->val; 266 267 /* split the hits */ 268 new->hit = parent->hit; 269 new->children_hit = parent->children_hit; 270 parent->children_hit = cumul_hits(new); 271 new->val_nr = parent->val_nr - idx_local; 272 parent->val_nr = idx_local; 273 274 /* create a new child for the new branch if any */ 275 if (idx_total < chain->nr) { 276 parent->hit = 0; 277 add_child(parent, chain, idx_total); 278 parent->children_hit++; 279 } else { 280 parent->hit = 1; 281 } 282 } 283 284 static int 285 __append_chain(struct callchain_node *root, struct resolved_chain *chain, 286 unsigned int start); 287 288 static void 289 __append_chain_children(struct callchain_node *root, 290 struct resolved_chain *chain, 291 unsigned int start) 292 { 293 struct callchain_node *rnode; 294 295 /* lookup in childrens */ 296 chain_for_each_child(rnode, root) { 297 unsigned int ret = __append_chain(rnode, chain, start); 298 299 if (!ret) 300 goto inc_children_hit; 301 } 302 /* nothing in children, add to the current node */ 303 add_child(root, chain, start); 304 305 inc_children_hit: 306 root->children_hit++; 307 } 308 309 static int 310 __append_chain(struct callchain_node *root, struct resolved_chain *chain, 311 unsigned int start) 312 { 313 struct callchain_list *cnode; 314 unsigned int i = start; 315 bool found = false; 316 317 /* 318 * Lookup in the current node 319 * If we have a symbol, then compare the start to match 320 * anywhere inside a function. 321 */ 322 list_for_each_entry(cnode, &root->val, list) { 323 struct symbol *sym; 324 325 if (i == chain->nr) 326 break; 327 328 sym = chain->ips[i].ms.sym; 329 330 if (cnode->ms.sym && sym) { 331 if (cnode->ms.sym->start != sym->start) 332 break; 333 } else if (cnode->ip != chain->ips[i].ip) 334 break; 335 336 if (!found) 337 found = true; 338 i++; 339 } 340 341 /* matches not, relay on the parent */ 342 if (!found) 343 return -1; 344 345 /* we match only a part of the node. Split it and add the new chain */ 346 if (i - start < root->val_nr) { 347 split_add_child(root, chain, cnode, start, i - start); 348 return 0; 349 } 350 351 /* we match 100% of the path, increment the hit */ 352 if (i - start == root->val_nr && i == chain->nr) { 353 root->hit++; 354 return 0; 355 } 356 357 /* We match the node and still have a part remaining */ 358 __append_chain_children(root, chain, i); 359 360 return 0; 361 } 362 363 static void filter_context(struct ip_callchain *old, struct resolved_chain *new, 364 struct map_symbol *syms) 365 { 366 int i, j = 0; 367 368 for (i = 0; i < (int)old->nr; i++) { 369 if (old->ips[i] >= PERF_CONTEXT_MAX) 370 continue; 371 372 new->ips[j].ip = old->ips[i]; 373 new->ips[j].ms = syms[i]; 374 j++; 375 } 376 377 new->nr = j; 378 } 379 380 381 int append_chain(struct callchain_node *root, struct ip_callchain *chain, 382 struct map_symbol *syms) 383 { 384 struct resolved_chain *filtered; 385 386 if (!chain->nr) 387 return 0; 388 389 filtered = zalloc(sizeof(*filtered) + 390 chain->nr * sizeof(struct resolved_ip)); 391 if (!filtered) 392 return -ENOMEM; 393 394 filter_context(chain, filtered, syms); 395 396 if (!filtered->nr) 397 goto end; 398 399 __append_chain_children(root, filtered, 0); 400 end: 401 free(filtered); 402 403 return 0; 404 } 405